how2heap笔记
文章目录
First fit
当一个应用请求一个k字节的块时,分配器搜索空闲链表,查找一个足够大可以放置所请求块的空闲块。分配器执行这种搜索的方式是由放置策略(placement policy)确定的。一些常见的策略是首次适配(first fit)、下一次适配(next fit)和最佳适配(best fit)。首次适配从头开始搜索空闲链表,选择第一个合适的空闲块….
《深入理解计算机系统 第二版(CSAPP)》P567
glibc使用的策略就是first fit,这种特性使我们可以很好地预测分配器将会使用空闲列表中的哪个块。一个常见情景是在存在堆指针UAF的情况下,我们可以控制其他块的内容。
代码中我们先请求了两个块a和b,释放a后,a被放入空闲链表,此时请求一个比a小的块c,分配器会使用原来a使用的空间,此时如果a存在UAF,我们将有两个指向同一片内存区域的指针(a和c),这种情形往往是一次攻击的开端。
Fastbin dup
绕过fastbin double free检查的一种方式。
请求两个大小合适的块a和b,释放a进入fastbin,此时如果再次释放a,就会触发double free检查。但是malloc.c:3935
这个判断只检查了bin的顶端和要释放的指针是否相同,所以如果我们按照a->b->a的顺序释放,检查就会被绕过。
fastbin_dup_into_stack
fastbin dup的进一步利用。
在fastbin dup构造出double free的基础上(假设0x60200在fastbin中存在两次),我们第一次请求到0x60200的地址时,把0x60200的fd位置写入某个地址x,因为0x60200仍在fastbin中,所以在再次请求到0x60200后,fastbin top变成了x,意味着我们下次的请求到的地址是x。选择合适的x值,我们就可能获得任意内存读写的机会。(这里有一个需要绕过的检查是需要让x+sizeof(long)处的值为这个fastbin的大小来“骗”过分配器x指向的是一个合法的块。)
fastbin_dup_into_stack.c中的x是&stack_var-8
fastbin_dup_consolidate
绕过fastbin double free检查的另一种方式。
当请求一个大块时会触发malloc_consolidate,把fastbin中的空闲块都整理到unsorted bin中。存在这样的场景:我们请求一个块a并释放让他进入fastbin,此时请求一个大块触发malloc_consolidate让这个空闲块被转移到unsorted bin中,这样再次释放块a的时候,fastbin top不是块a,所以可以利绕过检查把a被放置到fastbin中,此时块a同时处于fastbin和unsorted bin中,double free达成。
unsafe_unlink
有两个相邻的堆块a和b,a存在溢出可以把b的in_use位覆盖成0,可以在a中伪造一个fake chunk,并改写b的pre_size让它认为它前面的空闲块就是这个fake chunk。此时释放块b,就会对fake chunk进行unlink。
unlink宏在进行双向链表合法性的检查后把目标块从链表中取出,通过在fake chunk的fd和bk处填入合适的值,我们就可以绕过检查并且达成一个场景—自己写自己
假设有一个我们可以控制的指针p,让fd=p-0x18, bk=p-0x10,就可以满足fd->bk==p和bk->fd==p,之后的交换指针之后p=p-0x18,这意味着p指向了自己之前的区域,在合适的条件下,我们就得到了一个任意地址读写。
house_of_spirit
在栈上伪造一个fake chunk,释放它进入fastbins,这样如果再次申请就会申请到这片可控区域🚧
poison_null_byte
通过溢出相邻free chunk的chunk_size的低1位为0。制造fake chunk最后达成块重叠
after b1 = malloc(0x100)
after b2 = malloc(0x80)
after free(b1)
after free(c)
,b1和c合并,b2被忽视😟
after d=malloc(0x300)
,d和b2重叠
house_of_lore
在栈上设计两个fake chunk,写small bin中块的bk指向fake chunk 1,通过两次malloc请求得到栈上的内存。
下面描述一下house_of_lore.c的过程
改掉victim->bk之后
after void *p3 = malloc(100);
after char *p4 = malloc(100);
,此时p4指向栈上的地址
最后两次malloc都涉及了small bin的取用,注意small bins是顺着头结点按bk来“取件”🛴
overlapping_chunks
通过修改unsorted bin中chunk的size,使得在获取新块的时候得到与已有chunk重叠的块。
修改p2 size之前
修改p2 size为evil_chunk_size之后。unsortedbin中的空闲块大小为0x181,覆盖p3的区域
p4=malloc(0x178)
之后, p4使用了空闲块,此时p4包含p3
overlapping_chunks_2
修改chunk的大小,使他错误地包含其他使用中的chunk,经过释放再重新获取得到的新块和老的chunk重叠
before change p2 size
p2 size被改之后,此时p2和p4 “相邻”
free(p2)
之后,p2和p4合并成一个空闲块,大小为0xbd0,p3被包含在其中
此时申请一个合适大小的块便会和p3重叠。
house_of_force
通过修改top_chunk为特别大的数使得可以是堆请求落到bss处
修改top chunk size之前
修改top chunk size为-1(0xffffffffffffffff),此时即使申请一个特别大的块,也不会触发mmap,这里我们想让堆请求到bss区域,而在内存布局中bss地址比heap地址低,所以请求的大小是一个负值,无符号表现为0xfff….这样的形式。
如果我们想让堆分配到bss上的dest处,malloc的size应该多大呢,下面是计算过程:
|
|
后面通过两次malloc,就可以达成目标。
第一次:malloc(evil_size)
,此时top chunk在bss_var-0x10处
第二次:malloc(100)
,成功!
unsorted_bin_into_stack
修改unsorted bin中chunk的size和bk指向一个fake chunk,请求一个大小在(size, fake_chunk size)之间的块,使得fake chunk被征用。
修改victim的size和bk之前
修改victim的size和bk之后,减少victim的size为0x20并把victim的bk指向stack_buffer,在stack_buffer处伪造了一个大小为0x110的chunk
现在请求一个0x100的,会在unsortedbin中顺着bk找可用的块,victim不满足,命中stack_buffer指向的fake chunk。注意stack_buffer[3]的位置需要提前写入一个可写的地址(这个原因没在源码中找到),不一定非得是stack_buffer的地址。
unsorted_bin_attack
修改unsorted bin中chunk的bk使其指向target-0x10,此chunk被取用时发生unlink,taget被赋值改写。
写p的bk使其指向stack_var-0x10处
此时malloc(400)
,p指向的chunk被使用。unlink过程中发生victim->bk->fd = victim->fd
,stack_var被赋值为0x00007ffff7dd1b78。
这里改写的值是不可控制,一般为unsorted bin的链表头。在真实场景中,这招通常用来改写global_max_fast来为后面的攻击做铺垫。
large_bin_attack
通过改写large bin中chunk的bk和bk_nextsize使得在large bin中插入新节点的时候目标地址被改写。改写的值为堆地址。常被用来改写global_max_fast的值以进行fast bin attack
|
|
malloc(0x90)
之后,p1被切割使用,p2被放入largebins
free(p3)
此时改写p2的size、bk、bk_nextsize。
|
|
malloc(0x90)
后,p1继续被分割,p3被整理到large bin中。p3被插入large bin的头部,发生
victim->bk->fd=p3
和victim->bk_nextsize->fd=p3
,所以stack_var1和stack_var2都被改写为p3的地址
house_of_einherjar
溢出相邻下一个chunk的size低位字节为\x00,并伪造pre_size,使得下一个chunk释放的时候发生合并,指针指向设计好的位置。
溢出低位字节并写入伪造的pre_size,使得pre chunk指向fake_chunk,注意fake_chunk->size需要和b的pre size对应上
此时free(b)
,top_chunk指向fake_chunk
文章作者 b1tg
上次更新 2019-10-30