how2heap_libc2.23_summary
1.first_fit:
unsortedbin中切割原则,常用来泄露地址
2.fastbin_dup:
1 | #注释头 |
3.fastbin_dup_into_stack:
1 | #注释头 |
4.fastbin_dup_consolidate
1 | #注释头 |
5.house_of_einherjar:
在当前chunk中构造fakechunk,利用溢出漏洞修改下一个chunk的presize和pre_inuse。之后释放下一个chunk连上fakechunk一起进入Bins中,再通过当前chunk修改进入bins中chunk的fd,bk,从而实现任意地址读写。
(1)申请三个chunk,chunk1,chunk2,chunk3
(2)在chunk1中制造fakechunk//这里需要绕过一些检查
①构造:
1 | #注释头 |
绕过pre_size的检查:
1 | #注释头 |
这段代码意思就是当前chunk的size如果不等于下一个chunk的pre_size域,则出错
②构造:
1 | #注释头 |
绕过双向链表的检查:
1 | #注释头 |
这段代码意思就是下一个chunk,也就是BK,其fd不等于当前chunk地址,或者上一个chunk,也就是FD,其bk不等于当前chunk地址,满足其中一个就出错。
(3)利用溢出漏洞,改掉chunk2的pre_size和pre_inuse,free掉chunk2,这样fakechunk+chunk2一起被放入bins中,形成chunkA
(4)通过chunk1修改chunkA的fd,bk,使其指向想更改的地址addr
(5)再malloc(sizeof(fakechunk+chunk2))就可以得到首地址为fakechunk,大小为fakechunk+chunk2的chunkA了。之后再malloc一次就得到addr的chunk
6.house_of_force:通过修改topchunk的size域获得任意读写的chunk
(1)修改topchunk的size域,使之变成一个大数。
(2)malloc(-size)。要确保这个-size<topchunk.size(补码形式)
(3)之后topchunk就会被抬升到-(size-0x10)的位置(chunk内存对齐的原因,具体调试一下就能判断被抬升到哪里了:p main_arena.top)然后才会从被抬升之后的topchunk开始分配size大小的chunk。
(4)这时候再正常malloc,就可分配到从被抬升之后分配完size的topchunk地址处切割的chunk了,实现任意地址读写。
7.house_of_lore:
针对small bin进行攻击的,如果题目设置malloc大小限制大于fastbin时,就可以利用了。
(1)分配一个small bin大小的chunk_ptr,和另一个chunk 用来间隔 top chunk。
(2)在栈上伪造两个地址fake chunk1和fake chunk2,从smallbin中申请需要满足以下条件:
①// 获取 small bin 中倒数第二个 chunk 。
bck = victim->bk;
▲需要设置chunk_ptr->bk = fake chunk1
②// 检查 bck->fd 是不是 victim,防止伪造
1 | #注释头 |
▲需要设置fake chunk1->fd = chunk_ptr,同时由于之后申请fake chunk1时也需要满足这个条件,所以也需要设置fake chunk2->fd = fake chunk1,fake chunk1->bk = fake chunk2
(3)释放掉chunk_ptr,唯一需要漏洞利用的是设置victim->bk = fake chunk1,这里需要获取栈上的地址,并且存在类似UAF漏洞之类的来设置bk指针。设置之后现在smallbin链表中结构为fake chunk2->fake chunk1->chunk_ptr。
(4)之后申请sizeof(chunk_ptr),将之申请出来,再申请一次即可将fake chun1申请出来,实现任意地址申请堆块。
▲注意这里针对smallbin的攻击是不需要设置size位的,不存在这方面的条件。
8.house_of_orange:无free函数的泄露地址
(1)修改topchunk的size域,使之变小,但需要满足下列条件:
①伪造的 size 必须要对齐到内存页,例如topchunk_addr+topchunk_size=0x1000*x。
②size 要大于 MINSIZE(0x10)
③size 要小于之后申请的 chunk size + MINSIZE(0x10)
④size 的 prev inuse 位必须为 1
用于通过之后拓展topchunk的sysmalloc函数中的下列检查:
1 | #注释头 |
(2)申请一个大于伪造之后topchunk_size,小于128K(0x1f400)的chunk。前者是为了调用sysmalloc函数,后者是为了使得分配方式为brk拓展topchunk。
①brk是将数据段(.data)的最高地址指针_edata往高地址推,原先的topchunk被放入unsortedbin中,然后再从新的topchunk处开始分配。(一般会往高地址推0x21000这么大,基本就是再加一个初始topchunk大小)
②mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存,这样原先的topchunk不会被放到unsortedbin中,那么再分配就是从这个mmap中分配,没办法泄露地址了。
(3)再申请一块内存,就会从unsortedbin中的old_topchunk切割,得到的chunk的fd和bk都带有信息。如果程序没有将申请的chunk内存初始化,那么就可以泄露地址。另外由于在执行切割之前,old_topchunk会被先整理到smallbin或者largebin中(具体看old_chunk的size),所以申请回来的chunk中的fd和bk的内容应该是对应smallbin或者largebin链表头的地址,不是unsortedbin链表头main_aren+88的地址。这里需要具体调试一下好计算偏移,方便泄露地址。
9.house_of_roman:无leak的利用,爆破
(1)申请3个chunk,chunkA,chunkB,chunkC,分别为0x20,0xd0,0x70.(A用来触发单字节溢出,修改chunkB的size位)
(2)在chunkB+0x78处设置p64(0x61),用来过检查用的。(6+7=13(0xd))
(3)释放chunkB , chunkB进入unsortedbin , 这样chunkB的fd和bk处都有 main_arena 的地址。
(4)再次分配 0xd0 ,会分配到chunkB。以上步骤都是为了使得chunkB中带上main_arena的地址。
(5)再申请三个0x70大小的chunk,chunkD,chunkE,chunkF,chunkF用来占位用,防止被topchunk吞并
(6)释放掉chunkC,chunkD,使之进入fastbin中,为chunkD->chunkC。
(7)利用UAF修改chunkD的fd的低字节,使其指向chunkB,即将0xf0修改为0x20即可:
1 | #注释头 |
这里能够通过检查,因为前面将chunkB的size设置成了0x70,并且chunkB+0x78处也被设置成了0x61,所以可以通过fastbin的检查。伪造了一个0x70的chunk。那么0x70处的fastbin就变成chunkD->chunkB
(8)修改chunkB的fd指针最后两个字节为\xaa,\xed使其指向malloc_hook - 0x23处。这里就需要用到爆破了,\xaa中的第一个a是不确定的,有16种可能性。所以直接设置成a,成功概率应该为1/16。
而这里需要指向malloc_hook - 0x23的原因是因为既要包含__malloc_hook,又要使得这个chunk的size位为0x7f,观察内存可得,这是最优选择,选取的是_IO_wide_data_0+304这个数据中的7f,其它的7f都会带上其它的数据。
(9)修改完chunkB的fd,就又成功伪造了一个0x70大小的chunk,那么0x70的fastbin中就变成:chunkD->chunkB->malloc_hook - 0x23
(10)现在连续申请三次0x70大小的chunk,最后一个chunk就得到了malloc_hook - 0x23处的chunk,那么现在就可以计算偏移修改malloc+hook的值,修改最后三个字节,劫持为one_gadget。最后12位Bits可以通过固定偏移算出来,不会改变,但是前面的12位bits会改变,需要爆破。这里可以通过刚才的\xaa中的a来减少爆破次数,\xaa中第一个a已经确定,那么就可以通过偏移算出来随机化之后的one_gadget中的倒数第二个字节中的第一个16位数,这样就只需要爆破倒数第三个字节即可。那么总共需要的数学期望就应该是(0xff+1)*(0xf+1)=4096次。
(11)爆破成功后还需要触发__malloc_hook,通过连续free两次同一个chunk,使得malloc检查到错误(主要会调用 __libc_message 来执行abort函数)。就可以触发malloc_printerr函数,从而调用__malloc_hook函数,进而getshell。
10.house_of_spirit:
(1)通过栈溢出修改栈上某个chunk指针,使其指向一个可控内存的栈地址,再修改该栈地址对应chunk的size位。
(2)通过修改下一个chunk的size绕过检查,然后free掉该chunk指针,再申请回来,就可以控制中间区域的内容了。
fakechunkA uncontral_data fakechunkB
中心思想就是将不可控的内存纳入一个可控的Chunk中,从而实现可控。
▲
1 | #注释头 |
即fakechunkA的nextchunk,也就是fakechunkA+fakechunkA->size = fakechunkB的size位满足大于2*SIZE_SZ(64位程序中SIZE_SZ为8),小于av->system_mem(在main_arena中,默认为128kb),一般简单设置后都能满足。
11.large_bin_attack_1:任意可写地址写堆地址
(1)申请三个chunk,chunkA,chunkB,chunkC,大小依次为0x80,0x400,0x400,其中还需要插入其它的chunk防止合并,然后free掉chunkB,使其进入unsortedbin。
chunkA用来分割,chunkB和chunkC用来放入largebin中
(2)申请一个小于chunkA的chunk,触发unsortedbin整理,将chunkB整理到largebin中。然后chunkA被分割成chunkA_1,chunkA_2,chunkA_1返回给用户,chunkA_2进入unsortedbin中。
(3)free掉chunkC,使其进入unsortedbin中。
(4)利用漏洞修改chunkB的size,使其小于0x410。
(5)修改chunkB的bk为target_addr1-0x10,bk_nextsize为target_addr2_0x20。
(6)分配一个比chunA_2小的chunk,再次触发整理,将chunkC整理至largebin中。
(7)由于chunkB的size小于chunkC的size,所以会将chunkC插入largebin大小排列链表的头部,即以下代码:
▲大小排列链表的代码赋值过程:
1 | #注释头 |
这里的victim就是chunkC,fwd就是chunkB,那么fwd->bk_nextsize就等于target_addr2-0x20。
①第一行代码就是:
chunkC->bk_nextsize被赋值为target_addr2-0x20
②第二行代码就是:
(target_addr2-0x20)->fd_nextsize被赋值为chunkC_addr,相当于
((target_addr2-0x20)+0x20)=chunkC_addr,即(target_addr2)=chunC_addr
▲释放顺序排列链表的代码赋值过程:
1 | #注释头 |
以上就相当于是*(target_addr1)=chunC_addr
★综上所述,largebin attack的第一种利用方式就是将目的地址的值修改成一个堆地址。
12.large_bin_attack_2:实现overlap chunk
(1)申请四个chunk,chunkA,chunkB,chunkC,chunkD之后构造一个largebin大小排列的链表chunkA->chunkB,其中chunkA为0x420,chunkB为0x400,chunkC为0x400,未释放,chunkD用来占位防止合并。
(2)利用漏洞将chunkB的bk_nextsize伪造指向chunkC,同时将C的fd与bk构造好,将C的fd_nextsize与bk_nextsize赋值为0。
(3)当再次申请0x410大小的内存chunkE时,遍历chunkB->bk_nextsize会指向C,且C的大小满足需求,因此会调用unlink将chunkC从双链表取下,返回给用户。
(4)那么申请出来的chunkE的地址会为chunkC的地址,即chunkE和chunkC为同一内存块,实现overlap chunk的构造。
13.mmap_overlapping_chunks:暂时不好找
14.overlapping_chunks:
(1)free之前修改size,吞并邻块进入bin
(2)free之后修改size,吞并邻块申请出来。
15.poison_null_byte:实现overlap chunk
(1)malloc三个chunk,chunkA,chunkB,chunkC。
(2)利用漏洞,通过chunkB修改chunkC的pre_size位和pre_inuse位,使得chunkC的pre_size位为chunkA_size+chunkB_size。
(3)free掉chunkC,此时chunkC的pre_inuse位为free状态,触发向上合并,将chunkA,chunkB,chunkC一起free掉。从而实现chunkB的overlap。
但是ubuntu16.04打了补丁,需要绕过检查才行。
16.unsafe_unlink:通常存储chunk的结构体全局指针来进行unlink攻击。
(1)找到题目中的chunklist位置,并分清结构体中是size在前还是chunk在前。
(2)这里假设我们想要控制chunklist[0]中的chunk。申请chunk0,chunk1,chunk2。在chunk0中构造fakechunk,并设置:
1 | #注释头 |
(3)通过堆溢出或者off-by-one将chunk1的pre_size设置成fakechunk_size,将chunk1的size设置成fakechunk_size+chunk1_size。
(4)free掉chunk1,这样就会触发向上合并,将fakechunk和chunk1合并。同时,由于合并过程中调用了unlink函数,那么chunklist[0].chunk就会指向chunlist_addr-0x18,对应的就是我们的chunk0指向chunklist_addr-0x18。
(5)现在修改chunk0就相当于修改*(chunklist_addr-0x18),从而将chunk0指向任意地址,在任意地址实现读写。
17.unsorted_bin_attack:任意地址写&main_arena+0x58
(1)利用漏洞,修改处在unsortedbin中将要被拿出来的chunk的bk指针,使其指向traget-0x10
(2)申请这个将要被拿出来的chunk,会有如下代码被运行:
1 | #注释头 |
①第一行代码中victim就是该chunk,那么bck就会指向traget_addr-0x10。
②第四行代码就是将bck的值赋值给unsortedbin的bk指针,使其指向traget_addr-0x10。
③第五行代码就是将unsortedbin的地址赋值给bck->fd,也就是*(target_addr-0x10+0x10)被赋值为unsortedbin的地址,即&main_arena+0x58。
★综上,unsortedbin attack就是将任意地址处的值修改成&main_arena+0x58。
▲一般unsortedbin attack是用来配合fastbin attack来使用的。因为fastbin attack需要伪造fakechunk的size,使其等于0x71或0x7f用来绕过检查.那么这时候就可以用到unsortedbin attack任意地址写&main_arena+0x58这个技巧了,可以使得fakechunk的size等于0x7f,从而绕过检查。
18.unsorted_bin_into_stack:
(1)利用漏洞,修改unsortedbin中位于链表尾部的chunk_last的bk指针使其指向栈上伪造的一个fakechunk,使其size不等于原先unsortedbin中chunk_last的size。
(2)申请fakechunk的size大小的chunk,从unsortedbin链表尾部的chunk开始查找,首先chunk_last的size不符合,接着查找chunk_last->bk,也就是fakechunk,发现size符号,那么就返回该fakechunk给用户。
这里的fakechunk就可以伪造到栈上,从而控制栈上原先不可控的内容。
参考资料:
ctfwiki