Unlink攻击的理解与应用

Unlink攻击的理解与应用

原理

1.简介

​ unlink,俗称脱链,就是将链表头处的free堆块从unsorted bin中脱离出来,然后和物理地址相邻的新free堆块合并(向前合并或者向后合并)成一个大堆块,再放入unsorted bin 中。

2.危害原理

​ 通过为伪造free状态的fake_chunk,伪造fd和bk指针,通过绕过unlink的检测来实现unlink,unlink就会往p所在位置写入p-0x18(具体怎么计算的在后面会有详细解释),从而实现任意地址写的漏洞。

3.漏洞产生原因

​ 修改了堆块头的pre_in_use位,例如off by null,off by one ,堆溢出。

现在来看下具体实现unlink操作的源码(在看源码前建议将堆块头的标志位作用再重新熟悉一遍)

#!c
    /*malloc.c  int_free函数中*/
/*这里p指向当前malloc_chunk结构体,bck和fwd分别为当前chunk的向后和向前一个free chunk*/
    if (!prev_inuse(p)) {
      prevsize = p->prev_size;//注意在源码中是通过prevsize字段找前一个堆块的位置
	  size += prevsize;
//修改指向当前chunk的指针,指向前一个chunk。
      p = chunk_at_offset(p, -((long) prevsize)); 
      unlink(p, bck, fwd);
}   

// 相关函数说明:
#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s)))
/* unlink操作的实质就是:将P所指向的chunk从双向链表中移除,这里BK与FD用作临时变量 */
#define unlink(P, BK, FD) {
    FD = P->fd;
    BK = P->bk;
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))//条件FD->bk==BK->fd==p
        malloc_printerr(check_action, "corrupted double-linked list", P, AV);
    FD->bk = BK;
    BK->fd = FD;
}

源码后两行个人感觉有点难理解,FD和BK是怎么寻址找到FD->bk和BK->fd的呢?

    FD->bk = BK;
    BK->fd = FD;

答案就是偏移,在一个堆结构的结构体中,是通过变量类型对应的大小找相应的偏移的。回顾堆结构,fd在相对于堆0x10的位置,bk在相对于堆0x18的位置。

因此

FD->bk<==>*(FD+0x18)
BK->fd<==>*(BK+0x10)

图示

img

second_chunk进行unlink操作,效果:

img

绕过和利用

题目常常会用一个全局数组或者链表记录各个堆块的位置,这里以指针数组list为例,假如有list依次记录着各个堆块的起始位置。

heaparray[n] = malloc(size);

创建了三个堆块

add(0x40,b'aaaa')#chunk 0
add(0x80,b'bbbb')#chunk 1
add(0x40,b'cccc')#chunk 2

效果

image-20260129201309421

现在在堆块0里创建一个已经free的fake chunk,伪造如下:

chunk=0x6020c0#fake chunk在chunk 0中,chunk 0在chunk中(*chunk==chunk0)
fd=chunk-0x18# 为什么这么构造下面说
bk=chunk-0x10

效果

image-20260129201227328

此时delete chunk1,就会对chunk1触发unlink 操作。

为什么fd=chunk-0x18,bk=chunk-0x10呢?因为这样才能绕过FD->bk==BK->fd==p检查

FD=chunk-0x18=0x6020a8
BK=chunk-0x10=0x6020a0


FD->bk=FD+0x18=0x6020c0=chunk
BK->fd=BK+0x10=0x6020c0=chunk

这样就绕过了这个检查,然后

    FD->bk = chunk=0x6020c0
    BK=0x6020a0
   ==> *chunk=BK,即chunk->BK->...
   
    BK->fd =chunk=0x6020c0
    FD=0x6020a8
    ==> *chunk=FD,即chunk->FD->...

因此最终效果就是*chunk=chunk-0x18.即chunk里面写入了chunk-0x18的值

如图

image-20260129203018389

这时候由于我们没有释放chunk 0,因此我们仍然可以通过list数组寻址chunk0进行修改,但这时候我们就不是在堆里面进行修改了,而是在我们上面对chunk进行修改后,里面写入的chunk-0x18的地方。

我们在chunk-0x18的地方写入然后再次覆盖chunk的值,这样就实现了任意地址写。

效果

image-20260129203713176

还有最后一个问题是为什么很多unlink攻击要创建4个堆块,四个堆块分别有什么要注意的地方?

chunk0;能写入fake chunk

chunk1:要满足能放入unsorted bin大小的堆块

chunk2:防止chunk1释放后也和top chunk直接合并

chunk3:防止堆块重叠

参考文章

深入理解unlink漏洞-CSDN博客

posted @ 2026-01-29 21:03  sprYAaaa  阅读(18)  评论(0)    收藏  举报