unlink
Unlink
释放堆
释放堆时会检查相邻地址的chunk是否处于空闲状态,若是就会合并这两个chunk。合并时会进行unlink操作,将相邻地址的chunk进行unlink解链操作从bins中拿出来。
堆合并分为向前合并和向后合并。
- 向后合并指的是在释放P时和他的pre_chunk合并(也就是相邻小地址的chunk)
- 向前合并指的是在释放P时和他的next_chunk合并(也就是相邻大地址的chunk)
Unlink的流程
1 |
|
(代码来源于这里)
首先会检查当前chunk的大小是否和相邻next_chunk的pre_size的大小进行比较。然后会检查FD->bk == P || BK->fd == P
。
因此想要执行unlink攻击,需要设置fake free chunk的size字段和相邻next_chunk的pre_size大小相同。并且需要设置P->fd = ptr - 0x18
P->bk = ptr - 0x10
来绕过第二个检查。
1 |
|
因此
1 |
|
可绕过检查。
解链后,FD->bk = BK
即*ptr = ptr - 0x10
, BK->fd=FD
即*ptr = ptr - 0x18
。
unlink 后,对 ptr 指向的内存进行写入,如 ‘A’*0x18 + free@got
, 使得 ptr 指向 free@got
, 再次对 ptr 指向的内存进行写入,可以把 free@got
修改为 system
的地址,之后调用 free
可任意命令执行。
例子
这里用到的例子是how2heap中的unsafe_unlink
首先gcc unsafe_unlink.c -o unsafe_unlink -g
编译,然后gdb调试。
chunk0_ptr是全局指针变量,在这里是为了伪造和chunk1_ptr相邻且已经free的chunk,因此在free(chunk1_ptr)
时就会触发fake chunk的unlink操作。
这段代码是为了伪造fake chunk,绕过上面提到的检查。
这段代码是为了修改chunk1_ptr的chunk头部来使chunk1的prev_size字段值等于fake chunk的大小并且prev_inuse字段为0来使fake chunk为空闲状态。
我们用gdb调试,可以看到chunk0和chunk1两个chunk
然后填充chunk0中的fake chunk
然后伪造chunk1的prev_size位和prev_inuse位
之后free(chunk1_ptr)
就会使fake chunk触发unlink操作,使得chunk0_ptr指针指向chunk0_ptr[2],也就是&chunk0_ptr - 0x18
此时让chunk0_ptr[3] = victim_string,也就是(chunk0_ptr + 0x18) = *(&chunk0_ptr - 0x18 + 0x18) = chunk0_ptr = victim_string
可能画个图好理解一点
Tips
在这里说一下为什么chunk0_ptr[2]要填&chunk0_ptr-0x18,前面为什么要加取址符?
是因为在检查时,会检查FD->bk == P
,即*((p->fd)+0x18)==p,也就是FD指针+0x18的位置所填的内容应该是p,如果chunk0_ptr[2]也就是p->fd填的是p-0x18的话,左边=p!=p=右边,所以这里需要填的应该是&chunk0_ptr-0x18
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!