Heap Exploitation CheatSheet
Glibc Check List
函数名 | 检查 | 报错信息 |
---|---|---|
unlink | p->size == nextchunk->pre_size | corrupted size vs prev_size |
unlink | p->fd->bk == p 且 p->bk->fd == p | corrupted double-linked list |
_int_malloc | 当从fastbin分配内存时 ,找到的那个fastbin chunk的size要等于其位于的fastbin 的大小,比如在0x20的 fastbin中其大小就要为0x20 | malloc():memory corruption (fast) |
_int_malloc | 当从 smallbin 分配 chunk( victim) 时, 要求 victim->bk->fd == victim | malloc(): smallbin double linked list corrupted |
_int_malloc | 当迭代 unsorted bin 时 ,迭代中的 chunk (cur)要满足,cur->size 在 [2*SIZE_SZ, av->system_mem] 中 | malloc(): memory corruption |
_int_malloc | 当迭代 unsorted bin 时 ,迭代中的 chunk (cur)要满足,nextchunk(cur)->size 在 [2*SIZE_SZ, av->system_mem] 中 | malloc(): invalid next size (unsorted) |
_int_malloc | 当迭代 unsorted bin 时 ,迭代中的 chunk (cur)要满足,cur->bk->fd = cur, cur->fd = unsortedbin的头 | malloc(): unsorted double linked list corrupted |
_int_malloc | 当迭代 unsorted bin 时 ,迭代中的 chunk (cur)要满足,下一个块的pre_inuse位应该为0 | malloc(): invalid next->prev_inuse (unsorted) |
_int_malloc | 当迭代 unsorted bin 时 ,迭代中的 chunk (cur)不满足要求,放入对应的bin时,要求bck-fd = cur, bck为放入前的cur->bk | malloc(): corrupted unsorted chunks 3 |
_int_free | 当插入一个 chunk 到 fastbin时,判断fastbin的 head 是不是和 释放的 chunk 相等 | double free or corruption (fasttop) |
_int_free | 判断 next_chunk->pre_inuse == 1 | double free or corruption (!prev) |
Double Free
原理
free过后没有清空指针,导致产生悬垂指针,如果我们利用对这个指针继续进行edit,free等操作,就可以触发漏洞。
Fast Bin
double free中最重要的就是fast bin, 在没有tcache的glibc中fastbin的管理方式为将fastbin chunk的大小分为7类,0x20,0x30,0x40,0x50,0x60,0x70
。每一类用单项链表进行连接,malloc_chunk
中的fd
指针充当前向指针这个角色,所以在利用double free时,如果我们控制了fd
指针,那么我们就可以分配任意地址,进而做到任意地址写。
任意地址写
由于我们必须在free
后再去修改fd
指针才有意义,对此我们修改的方式有以下几种。
题目可以编辑
free
了的堆块。程序存在堆溢出,从上一个堆块进行修改。
fastdup
- 分配两个大小相同为size的chunk,
chunk0
,chunk1。
free(0), free(1), free(0)
。fastbin中对应链表形如0->1->0
。- 分配大小为size的
chunk2
。那么chunk2=chunk0
。 - 此时修改
chunk2
的fd指针,再分配三个堆块即可分配到指定地址。
- 分配两个大小相同为size的chunk,
在进行任意地址分配时,通常会遇到的问题是glibc会检查对应地址的size部分是否与fastbin匹配。所以我们对分配的地址有一定的要求。列一些常见的
分配到 _IO_stdout
可以看到有一个0x7f可以被利用,IO_2_1_stdout_ - 0x50+13
1 | 0x7ffff7dd25dd <_IO_2_1_stderr_+157>: 0xfff7dd1660000000 0x000000000000007f |
分配到_malloc_hook
这有两个0x7f, __malloc_hook-0x23
1 | 0x7f2ed4322aed <_IO_wide_data_0+301>: 0x2ed4321260000000 0x000000000000007f |
同时修改_malloc_hook, _realloc_hook
这两个hook是连着的,将_malloc_hook修改成realloc中的某个位置,_realloc_hook修改成one_gadget。这样做的理由是,realloc的开头如下。我们可以跳到我们选择的位置来控制栈的分布。
1 | 0x7ffff7a916c0 <realloc> push r15 |
修改vtable
IO_2_1_stdout - 0x90+13
1 | 0x7f43239086bd <_IO_2_1_stdout_+157>: 0x43239077a0000000 0x000000000000007f |
泄露地址
如果程序无法打印已经free的堆块的地址,那么通常的办法是制造unsorted bin带出main_arena的地址,从而获取libc。如果程序限制了可分配的大小,那么有必要fastbin制造unsorted bin。然后进行part overwrite等等。
Fastbin to Anybin
制造unsorted bin的思路为修改一个fastbin的size,然后再free此堆块。所以只需要利用fastbin dup任意地址分配堆块到某个堆块的上面,然后就能够编辑size部分,将size改大后就成为了各种bin,free后就成为了unsorted_bin。
这种方法可以使用fastbin在堆上构造任意形式的堆块,虽然可能比较麻烦,而且存在大量的溢出,在此基础上可以使用其他方法。
Unsortedbin to Fastbin
分配内存是,如果进入了unsorted bin分配。
那么遍历 unsorted bin
, 如果此时的 unsorted bin
只有一项,且他就是 av->last_remainder
,同时大小满足
1 | (unsigned long) (size) > (unsigned long) (nb + MINSIZE) |
就对当前 unsorted bin
进行切割,然后返回切割后的 unsorted bin
。
所以就搞定了,可以在fastbin中利用glibc地址。
heap paradise(程序没有打印功能)
程序每次输出前会把_IO_write_base
到_IO_write_ptr
的内容输出。所以可以利用这点进行修改。
利用fastbin这个修改_IO_stdout
的_flags
和_IO_write_base
。
把_IO_write_base
的最低位改成\x00
,就可以在任意输出的时候泄露一个libc的地址了。
修改got表泄露地址
malloc大堆块(HITCON CTF 2019)
将一个大尺寸传递给malloc
(但小于某个特定尺寸),malloc
则实际上会调用mmap
以映射一个全新的内存区域。反复试验可以使mmap块与libc完美对齐:
Small Bin
构造Unlink
- 首先分配两个
0x90
的chunk (p0, p1)
,然后释放掉,会进行合并,形成 一个0x120
的unsorted bin
- 然后分配一个
0x120
的chunk (p2)
, 则p0=p2
, 此时p0
所在的chunk
可以包含p1
的chunk
- 然后在
p0
所在的chunk
伪造一个free chunk
, 设置好fd
和bk
, 然后释放p1
触发unlink
这种方法在只有fastbin的时候也能用,属于fastbin attack简化版。
Unsorted Bin Attack
1 |
|
修改global_max_fast
初始状态时
unsorted bin 的 fd 和 bk 均指向 unsorted bin 本身。
执行 free(p)
由于释放的 chunk 大小不属于 fast bin 范围内,所以会首先放入到 unsorted bin 中。
修改 p[1]
经过修改之后,原来在 unsorted bin 中的 p 的 bk 指针就会指向 target addr-16 处伪造的 chunk,即 Target Value 处于伪造 chunk 的 fd 处。
申请 400 大小的 chunk
此时,所申请的 chunk 处于 small bin 所在的范围,其对应的 bin 中暂时没有 chunk,所以会去 unsorted bin 中找,发现 unsorted bin 不空,于是把 unsorted bin 中的最后一个 chunk 拿出来。
- victim = unsorted_chunks(av)->bk=p
- bck = victim->bk=p->bk = target addr-16
- unsorted_chunks(av)->bk = bck=target addr-16
- bck->fd = *(target addr -16+16) = unsorted_chunks(av);
House Of Orange
在没有free的情况下构造unsorted bin,当分配的内存大于topchunk时,程序就可能把topchunk放入unsortedbin。
伪造的 top chunk size 的要求
- 伪造的 size 必须要对齐到内存页
- size 要大于 MINSIZE(0x10)
- size 要小于之后申请的 chunk size + MINSIZE(0x10)
- size 的 prev inuse 位必须为 1
之后原有的 top chunk 就会执行_int_free
从而顺利进入 unsorted bin 中。
这种方法,可以用来泄露libc地址。
Off-By-Null
原理
off-by-null的通常利用方法为修改下一个块的pre-in-used位,然后进行一系列的应用。常见的方法是构造overlap chunk。
此时我们free这个pre-in-used位被清零的块,那么他就会尝试与前一个块进行合并,此时我们可以选择使用一些方法。
Unlink
作用
ptr 处的指针会变为 ptr - 0x18。
方法
- 修改 fd 为 ptr - 0x18
- 修改 bk 为 ptr - 0x10
- 触发 unlink(free(ptr后面那个块))
这种方法在程序使用线性结构存储指针时效果较好。
House Of Einherjar
堆上任意地址分配。可以产生堆溢出,制造double free之类的。如果程序不能编辑堆块可以用这个方法来编辑堆块。
1 | int main() { |
Overlap Chunk
Idea1
House Of Einherjar需要泄露出堆地址,如果没有堆地址,我们也有办法。
- 先分配三个块A,B,C。
- 释放A,让A进入unsorted bin。(限制A的大小不能太小,此时A有满足要求的fd, bk)
- 编辑或重新分配B off-by-null 到C,编辑C的pre_size使其能够够到A。(B size不对齐)
- free C。(向前合并到A,进入unsorted bin)
- 再分配A到C那么大的堆块就可以编辑B,形成overlap。
这种方法更麻烦,对堆块的要求更多。
idea2 (lctf2018)
有时会看到题目禁止输入’\x00’此时上面的方法都将失效,所以使用如下办法。
- 将
A -> B -> C
三块 unsorted bin chunk 依次进行释放 - A 和 B 合并,此时 C 前的 prev_size 写入为 0x200
- A 、 B 、 C 合并,步骤 2 中写入的 0x200 依然保持
- 利用 unsorted bin 切分,分配出 A
- 利用 unsorted bin 切分,分配出 B,注意此时不要覆盖到之前的 0x200
- 将 A 再次释放为 unsorted bin 的堆块,使得 fd 和 bk 为有效链表指针
- 此时 C 前的 prev_size 依然为 0x200(未使用到的值),A B C 的情况:
A (free) -> B (allocated) -> C (free)
,如果使得 B 进行溢出,则可以将已分配的 B 块包含在合并后的释放状态 unsorted bin 块中。
Hint
在存在Off-by-null时,通常存在的情况是show的时候无法输出有效内容,所以通常使用的方法是使用
House Of Einherjar从高地址的块向上合并形成unsortedbin带出libc的地址,然后就能泄露glibc的地址。(secret_of_my_heart)
Tcache
- glibc 2.27的tcache没有double free的检查,利用更加容易,直接对同一个指针free两次即可。
- 想让tcache进入普通的bin,需要让tcache填满,例如free一个指针8次。或者修改
tcache_perthread_structure
.
Hint
- 关于double free, glibc2.29的这段代码或让double free必将会被检查出来。
1 | tcache_entry *tmp; |
此外2.29的有了更多的unsorted bin的检查,值得注意。
- calloc再size小于0x800时不适用tcache.
- Post title:Heap Exploitation CheatSheet
- Post author:Zheng Yu
- Create time:2019-11-09 16:34:06
- Post link:https://dataisland.org/2019/11/09/Heap-Attack-Summary/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.