axb_2019_heap-unlink
1.最开始看源码,感觉还行,能看懂,但是一旦看其他人讲unlink,感觉完全对不上,分明就是瞎搞,之后调试才发现单独的unlink攻击并不是针对chunk的,而是针对具体题目的结构体的。并且要求程序可以修改掉chunk的size位,执行向上合并从而触发unlink。
2.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。
▲unlink源码:
1 | #注释头 |
A.首先通过fakechunk,也就是p,找到前一个chunk和后一个chunk:
1 | #注释头 |
这里的FD和BK分别为fakechunk的前一个chunk和后一个chunk,也就是chunklist_addr-0x18和chunklist_addr-0x10。
B.然后过检查:
1 | #注释头 |
能过检查的原因就是因为FD->bk就相当于是[chunklist_addr-0x18]+[0x18],就相当于chunklist_addr,也就是chunklist[0].chunk。而该地址中保存的内容就是fakechunk_addr。
注意chunk0_addr和fakechunk_addr不是一样的,因为有chunk结构的原因,如下图,可以看到实际上chunklist[n].chunk保存的值是chunk数据部分的地址,在这里也就相当于是fakechunk_addr。所以也就能过检查。同理BK->fd相当于是[chunklist_addr-0x10]+[0x10],等于chunklist_addr,该地址中保存的值就是fakechunk_addr。
C.过完检查之后,就来到赋值部分
1 | #注释头 |
那么现在FD->bk = BK相当于[chunklist_addr-0x18]+[0x18],也就是chunklist_addr中的值被赋值为chunklist_addr-0x10,之后BK->fd = FD,就是chunklist_addr中的值被赋值为chunklist_addr-0x18,所以总的来说,chunklist[0].chunk会指向chunklist_addr-0x18,也就是说我们的fakechunk指向chunklist_addr-0x18,这样就相当于可以通过修改fakechunk就可以修改chunklist这个bss段上的内容。而fakechunk又是chunk0的数据部分,完全在我们的掌控范围。
(5)现在修改chunk0数据就先当于修改chunklist这个bss段上的内容。
3.在这道题中,原本chunklist[0].chunk中保存的值是fakechunk_addr,我们可以修改chunklist[0].chunk中保存的值为free_hook地址,那么之后再修改chunk0数据部分就相当于修改free_hook中的内容了。那么现在就可以将free_hook保存的值修改为system的真实地址,这样在free时就相当于调用system函数,那么一旦free某个chunk,而该chunk的数据部分为binsh字符串,那么就相当于调用system(“/bin/sh”),从而getshell.
4.现在开始分析这道题目,get_input函数中存在off-by-one,banner()函数中存在格式化字符串漏洞:
get_input:
banner:
利用格式化字符漏洞泄露Libc从而得到其它所有地址。之后通过off-by-one进行unlink攻击,构造fakechunk到chunklist这个Bss段上,修改chunklist段上的chunklist[0].chunk,使其指向free_hook_addr。之后再通过修改chunk0从而修改free_hook为system真实地址,再申请某个数据部分为binsh字符串的chunk,释放掉就能getshell。
总exp如下:
(1)首先增删改查函数:
1 | #注释头 |
(2)利用格式化字符漏洞泄露栈上的__libc_main和main地址:
1 | #注释头 |
(3)利用off-by-one向上合并chunk0和chunk1,执行unlink攻击:
1 | #注释头 |
(4)修改chunklist[0].chunk指向free_hook:
1 | #注释头 |
(5)修改free_hook为system:
1 | #注释头 |
(6)利用free触发system,getshell
1 | #注释头 |