First fit
原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 演示glibc 的分配机制 glibc 使用首次适应算法选择空闲的堆块 如果有一个空闲堆块且足够大,那么 malloc 将选择它 如果存在 use-after-free 的情况那可以利用这一特性 首先申请两个比较大的 chunk 第一个 a = malloc(0x512) 在: 0x1682010 第二个 b = malloc(0x256) 在: 0x1682530 我们可以继续分配它 现在我们把 "AAAAAAAA" 这个字符串写到 a 那里 第一次申请的 0x1682010 指向 AAAAAAAA 接下来 free 掉第一个... 接下来只要我们申请一块小于 0x512 的 chunk,那就会分配到原本 a 那里: 0x1682010 第三次 c = malloc(0x500) 在: 0x1682010 我们这次往里写一串 "CCCCCCCC" 到刚申请的 c 中 第三次申请的 c 0x1682010 指向 CCCCCCCC 第一次申请的 a 0x1682010 指向 CCCCCCCC 可以看到,虽然我们刚刚看的是 a 的,但它的内容却是 "CCCCCCCC"
|
操作
- 存在uaf,首先释放一个堆块p1,里面有内容
- 再申请一个相同大小的堆块p2
- 这两个堆块实际上指向同一个内存区域
结果
这两个堆块实际上指向同一个内存区域
UAF
原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 申请0x20大小的内存p1 的地址: 0x1f11010 把p1[1]赋值为Printf函数,然后打印出"Hello CTFshow" Hello CTFshow
free 掉 p1 因为并没有置为null,所以p1[1]仍然是Printf函数,仍然可以输出打印了"Hello CTFshow again" Hello CTFshow again 接下来再去malloc一个p2,会把释放掉的p1给分配出来,可以看到他俩是同一地址的 p2 的地址: 0x1f11010 p1 的地址: 0x1f11010 然后把p2[1]给改成demoflag也就是system函数
Then get the flag && enjoy it !
|
操作
- free掉chunk1
- 再申请一个相同大小的chunk2,修改内容
- 再使用chunk1,会输出修改内容
结果
chunk1和chunk2是同一个chunk
Double Free
原理
1 2 3 4 5 6 7 8 9 10 11 12 13
| 演示 fastbin 的 double free 首先申请 3 个 chunk 第一个 malloc(8): 0x188f010 第二个 malloc(8): 0x188f030 第三个 malloc(8): 0x188f050 free 掉第一个 当我们再次 free 0x188f010 的时候, 程序将会崩溃因为 0x188f010 在 free 链表的第一个位置上 我们先 free 0x188f030. 现在我们就可以再次 free 0x188f010 了, 因为他现在不在 free 链表的第一个位置上 现在空闲链表是这样的 [ 0x188f010, 0x188f030, 0x188f010 ]. 如果我们 malloc 三次, 我们会得到两次 0x188f010 第一次 malloc(8): 0x188f010 第二次 malloc(8): 0x188f030 第三次 malloc(8): 0x188f010
|
操作
- 申请3个chunk,p1,p2,p3
- free p1,再freep2,形成 p2 -> p1
- 再free p1 形成 p1 -> p2 -> p1
- 连续申请三次chunk
结果
得到两个相同地址的chunk
Fastbin_dup_into_stack – Double free
原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 通过欺骗 malloc 使得返回一个指向受控位置的指针(本例为栈上) 通过 malloc 申请到 0x7ffec12adb40. 先申请3 个 chunk chunk a: 0x209a010 chunk b: 0x209a030 chunk c: 0x209a050 free 掉 chunk a 如果还对 0x209a010 进行 free, 程序会崩溃。因为 0x209a010 现在是 fastbin 的第一个 先对 b 0x209a030 进行 free 接下来就可以对 0x209a010 再次进行 free 了, 现在已经不是它在 fastbin 的第一个了 现在 fastbin 的链表是 [ 0x209a010, 0x209a030, 0x209a010 ] 接下来通过修改 0x209a010 上的内容来进行攻击. 第一次 malloc(8): 0x209a010 第二次 malloc(8): 0x209a030 现在 fastbin 表中只剩 [ 0x209a010 ] 了 接下来往 0x209a010 栈上写一个假的 size,这样 malloc 会误以为那里有一个空闲的 chunk,从而申请到栈上去 现在覆盖 0x209a010 前面的 8 字节,修改 fd 指针指向 stack_var 前面 0x20 的位置 第三次 malloc(8): 0x209a010, 把栈地址放到 fastbin 链表中 这一次 malloc(8) 就申请到了栈上去: 0x7ffec12adb40
|
操作
- 申请3个chunk,p1,p2,p3
- free p1,再freep2,形成 p2 -> p1
- 再free p1 形成 p1 -> p2 -> p1
- 修改p1的fd指向任意地址,栈上都可
- 连续申请三次chunk
- 第四次申请chunk会申请到目标地址
结果
任意地址读写
Fastbin_dup_consolidate
原理
1 2 3 4 5 6
| 申请两个 fastbin 范围内的 chunk: p1=0xbba010 p2=0xbba030 先 free p1 去申请 largebin 大小的 chunk,触发 malloc_consolidate(): p3=0xbba050 因为 malloc_consolidate(), p1 会被放到 unsorted bin 中 这时候 p1 不在 fastbin 链表的头部了,所以可以再次 free p1 造成 double free 现在 fastbin 和 unsortedbin 中都放着 p1 的指针,所以我们可以 malloc 两次都到 p1: 0xbba010 0xbba010
|
操作
- 1.free 一个fastbin大小的chunk 1
- 2.申请一个largin bin 大小的chunk,此时因为 malloc_consolidate(), chunk1 会被放到 unsorted bin 中
- 再次free chunk1
结果
现在 fastbin 和 unsortedbin 中都放着 p1 的指针,所以我们可以 malloc 两次都到 p1: 0xbba010 0xbba010,任意地址读写
Unsafe_Unlink
原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 当你在已知位置有指向某个区域的指针时,可以调用 unlink 最常见的情况是易受攻击的缓冲区,可能会溢出并具有全局指针 本练习的重点是使用 free 破坏全局 chunk0_ptr 来实现任意内存写入
全局变量 chunk0_ptr 在 0x6020d0, 指向 0x161e010 我们想要破坏的 chunk 在 0x161e0a0 在 chunk0 那里伪造一个 chunk 我们设置 fake chunk 的 'next_free_chunk' (也就是 fd) 指向 &chunk0_ptr 使得 P->fd->bk = P. 我们设置 fake chunk 的 'previous_free_chunk' (也就是 bk) 指向 &chunk0_ptr 使得 P->bk->fd = P. 通过上面的设置可以绕过检查: (P->fd->bk != P || P->bk->fd != P) == False Fake chunk 的 fd: 0x6020b8 Fake chunk 的 bk: 0x6020c0
现在假设 chunk0 中存在一个溢出漏洞,可以更改 chunk1 的数据 通过修改 chunk1 中 prev_size 的大小使得 chunk1 在 free 的时候误以为 前面的 free chunk 是从我们伪造的 free chunk 开始的 如果正常的 free chunk0 的话 chunk1 的 prev_size 应该是 0x90 但现在被改成了 0x80 接下来通过把 chunk1 的 prev_inuse 改成 0 来把伪造的堆块标记为空闲的堆块
现在释放掉 chunk1,会触发 unlink,合并两个 free chunk 此时,我们可以用 chunk0_ptr 覆盖自身以指向任意位置 chunk0_ptr 现在指向我们想要的位置,我们用它来覆盖我们的 victim string。 之前的值是: Hello!~ 新的值是: BBBBAAAA
|
操作
fd = goal - 0x18
bk = goal - 0x10
结果
任意地址写
house_of_spirit
原理
1 2 3 4 5 6 7 8 9 10 11 12
| 这个例子演示了 house of spirit 攻击 我们将构造一个 fake chunk 然后释放掉它,这样再次申请的时候就会申请到它 覆盖一个指向 fastbin 的指针 这块区域 (长度为: 80) 包含两个 chunk. 第一个在 0x7fff5f0e7268 第二个在 0x7fff5f0e72a8. 构造 fake chunk 的 size,要比 chunk 大 0x10(因为 chunk 头),同时还要保证属于 fastbin,对于 fastbin 来说 prev_inuse 不会改变,但是其他两个位需要注意都要位 0 next chunk 的大小也要注意,要大于 0x10 小于 av->system_mem(128kb) 现在,我们拿伪造的那个 fake chunk 的地址进行 free, 0x7fff5f0e7270. free! 现在 malloc 的时候将会把 0x7fff5f0e7270 给返回回来 malloc(0x30): 0x7fff5f0e7270 Finish!
|
操作
构造fake fastbin chunk,free掉这个chunk,再次申请可以拿回这个chunk
前提有一个可控的指针
结果
任意地址写,前提有可控指针
Posion_null_byte
原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 当存在 off by null 的时候可以使用该技术 申请 0x100 的 chunk a a 在: 0x1eb2010 因为我们想要溢出 chunk a,所以需要知道他的实际大小: 0x108 b: 0x1eb2120 c: 0x1eb2330 另外再申请了一个 chunk c:0x1eb2440,防止 free 的时候与 top chunk 发生合并的情况 会检查 chunk size 与 next chunk 的 prev_size 是否相等,所以要在后面一个 0x200 来绕过检查 b 的 size: 0x211 假设我们写 chunk a 的时候多写了一个 0x00 在 b 的 size 的 p 位上 b 现在的 size: 0x200 c 的 prev_size 是 0x210 但他根据 chunk b 的 size 找的时候会找到 b+0x1f0 那里,我们将会成功绕过 chunk 的检测 chunksize(P) == 0x200 == 0x200 == prev_size (next_chunk(P)) 申请一个 0x100 大小的 b1: 0x1eb2120 现在我们 malloc 了 b1 他将会放在 b 的位置,这时候 c 的 prev_size 依然是: 0x210 但是我们之前写 0x200 那个地方已经改成了: f0 接下来 malloc 'b2', 作为 'victim' chunk. b2 申请在: 0x1eb2230 现在 b2 填充的内容是: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 现在对 b1 和 c 进行 free 因为 c 的 prev_size 是 0x210,所以会把他俩给合并,但是这时候里面还包含 b2 呐. 这时候我们申请一个 0x300 大小的 chunk 就可以覆盖着 b2 了 d 申请到了: 0x1eb2120,我们填充一下 d 为 "D" 现在 b2 的内容就是: DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
|
操作
- 申请0x100大小的chunk a,0x200大小的chunk b,chunk c 防合并
- free b
- 通过off-by-one覆写chunk b的size从0x211->0x200
- chunk b中b+0x1f0的位置放prev_size = 0x200 我们将会成功绕过 chunk 的检测 chunksize(P) == 0x200 == 0x200 == prev_size (next_chunk(P))
- 申请一个0x100的b1,b1会放到b的位置,c的prev_size仍然是0x210,但是我们之前写 0x200 那个地方已经改成了: f0
- 申请b2,作为 ‘victim’ chunk
- free b1 和 c,由于c的prev_size是0x210,会合并b1和c,此时b2仍在
结果
在一个大的free堆块中存在一个未被free的堆块
House_of_lore
原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 定义了两个数组stack_buffer_1 在 0x7ffc7a946070 stack_buffer_2 在 0x7ffc7a946050 申请第一块属于 fastbin 的 chunk 在 0x211c010 在栈上伪造一块 fake chunk 设置 fd 指针指向 victim chunk,来绕过 small bin 的检查,这样的话就能把堆栈地址放在到 small bin 的列表上 设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1 来绕过最后一个 malloc 中 small bin corrupted, 返回指向栈上假块的指针另外再分配一块,避免与 top chunk 合并 0x211c080 Free victim chunk 0x211c010, 他会被插入到 fastbin 中
此时 victim chunk 的 fd、bk 为零 victim->fd: (nil) victim->bk: (nil)
这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 0x211c010 现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址 victim->fd: 0x7f6610ee7bd8 victim->bk: 0x7f6610ee7bd8
现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上 然后申请跟第一个 chunk 大小一样的 chunk 他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk 最后 malloc 一次会返回 victim->bk 指向的那里 p4 = malloc(100)
在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 0x7f6610ee7bd8
p4 在栈上 0x7ffc7a946080
|
操作
- 在栈上定义了两个数组 stack1,stack2
- 申请了一块 fastbin chunk,在栈上伪造一块 fake chunk,设置stack1 fd -> victim chunk,绕过small bin检查
- 设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2 & 设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1
- 再分配个chunk,避免和top chunk 合并
- free victim chunk,会被放入fastbin中同时fd、bk为0
- 再申请一个large bin chunk触发xx使得victim chunk 进入 unsortedbin
- fd 和 bk 被更新为main_arena_88
- 存在一个漏洞可以使得victim的bk -> stack 1
- 申请一个大小相同的chunk取出victim chunk,并且它的bk为修改掉的victim的bk
- 再次malloc一次会返回 victim -> bk 指向的那里,也就是stack1,stack2 fd 指针也更改main_arena_88
结果
任意地址malloc
Overlapping_chunks
原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 这是一个简单的堆块重叠问题,首先申请 3 个 chunk 这三个 chunk 分别申请到了: p1:0x2088010 p2:0x2088110 p3:0x2088210 给他们分别填充"1""2""3"
free 掉 p2 p2 被放到 unsorted bin 中 现在假设有一个堆溢出漏洞,可以覆盖 p2 为了保证堆块稳定性,我们至少需要让 prev_inuse 为 1,确保 p1 不会被认为是空闲的堆块 我们将 p2 的大小设置为 385, 这样的话我们就能用 376 大小的空间
现在让我们分配另一个块,其大小等于块p2注入大小的数据大小 malloc 将会把前面 free 的 p2 分配给我们(p2 的 size 已经被改掉了)
p4 分配在 0x2088110 到 0x2088288 这一区域 p3 从 0x2088210 到 0x2088288 p4 应该与 p3 重叠,在这种情况下 p4 包括所有 p3 这时候通过编辑 p4 就可以修改 p3 的内容,修改 p3 也可以修改 p4 的内容
接下来验证一下,现在 p3 与 p4: p4 = 22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222 p3 = 3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333�
如果我们使用 memset(p4, '4', 376), 将会: p4 = 44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444� p3 = 4444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444�
|
操作
- 申请三个堆块大小为0xf8,0xf8,0x78
- free p2,p2被放到 unsorted bin 中
- 假设存在一个堆溢出漏洞,可以覆盖p2
结果
堆块重叠
Overlapping_chunks_2
原理
操作
结果
Mmap_overlapping_chunks
原理
操作
结果
Unsorted_bin_attack
原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| unsorted bin attack 实现了把一个超级大的数(unsorted bin 的地址)写到一个地方 实际上这种攻击方法常常用来修改 global_max_fast 来为进一步的 fastbin attack 做准备
我们准备把这个地方 0x7ffe5b09ba18 的值 0 更改为一个很大的数
一开始先申请一个比较正常的 chunk: 0x14fe010 再分配一个避免与 top chunk 合并
当我们释放掉第一个 chunk 之后他会被放到 unsorted bin 中,同时它的 bk 指针为 0x7efe12f93b78 现在假设有个漏洞,可以让我们修改 free 了的 chunk 的 bk 指针 我们把目标地址(想要改为超大值的那个地方)减去 0x10 写到 bk 指针:0x7ffe5b09ba08
再去 malloc 的时候可以发现那里的值已经改变为 unsorted bin 的地址 0x7ffe5b09ba18: 0x7efe12f93b78
|
操作
- 申请一个chunk p1 (0x410),再申请一个chunk p2避免与top chunk 合并
- free p1,p1会被放入 unsorted bin 中,同时fd 和 bk指针为main_arena_88
- 假设有个漏洞,可以修改p1的bk指针
- 修改 bk -> (goal - 0x10)
- 再malloc相同大小的chunk p,goal已经为unsorted bin 的地址
结果
修改任意位置为 一个很大的数
Large_bin_attack
原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 跟 unsorted bin attack 实现的功能差不多,都是把一个地址的值改为一个很大的数
先来看一下目标: stack_var1 (0x7fff83c2e410): 0 stack_var2 (0x7fff83c2e418): 0
分配第一个 large chunk: 0x6ac000 再分配一个 fastbin 大小的 chunk,来避免 free 的时候下一个 large chunk 与第一个合并了
申请第二个 large chunk 在: 0x6ac360 同样在分配一个 fastbin 大小的 chunk 防止合并掉
最后申请第三个 large chunk 在: 0x6ac7a0 申请一个 fastbin 大小的防止 free 的时候第三个 large chunk 与 top chunk 合并
free 掉第一个和第二个 chunk,他们会被放在 unsorted bin 中 [ 0x6ac360 <--> 0x6ac000 ]
现在去申请一个比他俩小的,然后会把第一个分割出来,第二个则被整理到 largebin 中,第一个剩下的会放回到 unsortedbin 中 [ 0x6ac0a0 ]
free 掉第三个,他会被放到 unsorted bin 中: [ 0x6ac7a0 <--> 0x6ac0a0 ]
假设有个漏洞,可以覆盖掉第二个 chunk 的 "size" 以及 "bk"、"bk_nextsize" 指针 减少释放的第二个 chunk 的大小强制 malloc 把将要释放的第三个 large chunk 插入到 largebin 列表的头部(largebin 会按照大小排序)。覆盖掉栈变量。覆盖 bk 为 stack_var1-0x10,bk_nextsize 为 stack_var2-0x20
再次 malloc,会把释放的第三个 chunk 插入到 largebin 中,同时我们的目标已经改写了: stack_var1 (0x7fff83c2e410): 0x6ac7a0 stack_var2 (0x7fff83c2e418): 0x6ac7a0
|
操作
- 分配第一个large bin chunk,再申请一个fast bin chunk 隔绝,避免和下一个large bin chunk合并
- 分配第二个large bin chunk,再申请一个fast bin chunk 隔绝,避免和下一个large bin chunk合并
- 分配第三个large bin chunk,再申请一个fast bin chunk 隔绝,避免和下一个large bin chunk合并
- free chunk1 和 chunk2 均被放入unsorted bin 中
- 现在去申请一个比他俩小的,然后会把第一个分割出来,第二个则被整理到 largebin 中,第一个剩下的会放回到 unsortedbin 中
- free chunk3 放到unsorted bin 中
- 存在漏洞,可以覆盖掉第二个chunk 的size bk bk_nextsize
- 减少释放的第二个 chunk 的大小强制 malloc 把将要释放的第三个 large chunk 插入到 largebin 列表的头部(largebin 会按照大小排序)。覆盖掉栈变量。覆盖 bk 为 stack_var1-0x1
- 再次 malloc,会把释放的第三个 chunk 插入到 largebin 中,同时我们的目标已经改写了
结果
栈上地址被覆盖