文章

unlink

一些堆溢出方面的关于unlink的知识

原理

glibc中间维护的bins其实是用来存放malloc时从heap中割下来的堆,为了避免在heap中割下了太多的连续的free chunk,系统会将free chunk自动合并到top chunk中去(top chunk指系统heap中最高位未分配的那个chunk)或者合并成为更大的一个chunk。

条件:

相邻的两块chunk 一块为top chunk 一块为free chunk 或者两块两块 free chunk。所以我们要做的是先连续割两块chunk,保证他们是连续的,以保证他们会unlink。

在溢出时之所以要覆写p位,就是为了能够触发unlink

流程:

如果上一块是free chunk
  合并上一块chunk并对上一块做unlink
如果下一块是
  top:合并到top
  一般chunk:
    是free chunk:合并下一块chunk,并对下一块做unlin。最终加入unsortedbin
    正在使用中:加入unsortedbin

最终结果

所有的同样大小的free chunk递归合并

例子

假定有三块连续的smallchunk
第一块第二块freed,第三块issued。
***********************************
prevsize = 0
size = 0x80
fd = &smallbin
bk = &smallbin
***********************************
prevsize = 0
size = 0x80
***********************************
prevsize = 0
size = 0x81(p位+1代表上一块已经使用已经使用)
***********************************

free(p)(第二块),就会导致一二两块合并,从而触发unlink
系统检验p+size 与p-prevsize得到了上下两个块的头

p+0x10为FD,p+0x18为BK

unlink(P,FD,BK)
{
    P = p
    FD = P -> BK
    BK = P -> FD
    FD -> BK = BK
    BK -> FD = FD
}

相当于将这个块从bin中脱链。然后系统会将上一个块更改size之类的,然后链到unsortedbin上。

预期利用方式

将程序劫持到shellcode中

shellcode是一段用于利用软件漏洞而执行的代码,以其经常让攻击者获得shell而得名。 shellcode常常使用机器语言编写。

利用unlink导致返回了weichunk中指向的地址。最后触发任意写,将必须触发的函数在got表中值改到shellcode,或者更改函数指针变量的值导致触发system()。

利用思路:

首先很直观地想到通过堆溢出q堆盖掉后面的FD和BK,将fd改为gou entry -12,将bk 改为 shellcode 的地址,将size低位改为1。在free(q)时会发生如下过程:

free(q)
FD = P->fd = got entry -12
BK = P->bk =sc(shellcode) addr
FD->bk=BK(got hijacking)
got entry -12 +12 = sc addr
BK->fd=FD
sc addr+8=got entry -12

在此过程后,会会进行一次任意写将FD+0x10的地方写入shellcode
然而在现实情况中,现代的glibc会有着针对chunk的检查以及其他保护机制,使得该方法无法使用。
因此我们的利用方式是
***********************************
prevsize = 0
size = 0x80

***********************************
prevsize = 0
size = 0x91
< r指向此处>
***********************************
prevsize = 0
size = 0x80(p位+1代表上一块已经使用已经使用)
< q指向此处>
***********************************
通过对于第二个chunk进行堆溢出将第二个块整体盖掉然后在其中伪造一个chunk并且改写第三个堆块的prev_size
最后变为
***********************************
prevsize = 0
size = 0x80

***********************************
prevsize = 0
size = 0x91
fake prev_size = 0x90< r指向此处>
fake size = 0x80
fake fd = &r-0x18
fake bk = &r-0x10
***********************************
prevsize = 0x80
size = 0x80(p位+1代表上一块已经使用已经使用)
< q指向此处>
***********************************
这样子在可以绕过两个检查,成功将r并入了chunk中从而进行任意写。
具体流程:
free(q)
找到q与r都是freed
找到了最下面的q,通过q-0x10找到的prev_size。q-0x10-prev_size找到r。对于r执行unlink
跳过了check:
r->fd->bk == r = *(&r-0x18+0x18) ==r
f->bk->fd == r = *(&r-0x18+0x18) ==r

FD->bk = BK =》(&r-0x18+0x18)=&r-0x10
BK->fd = FD =》
(&r-0x10+0x10)=&r-0x18

大功告成

License:  CC BY 4.0