基本原理
unlink是一个宏操作,用于将某一个空闲 chunk 从其所处的双向链表中脱链,
我们来利用unlink 所造成的漏洞时,其实就是对进行 unlink chunk 进行内存布局,然后借助 unlink 操作来达成修改指针
的效果。
利用条件
UAF ,可修改 free 状态下 smallbin 或是 unsorted bin 的 fd 和 bk 指针
已知位置存在一个指针指向可进行 UAF 的 chunk
利用思路
设指向可 UAF chunk 的指针的地址为 ptr
修改 fd 为 ptr - 0x18
修改 bk 为 ptr - 0x10
触发 unlink
ptr 处的指针会变为 ptr - 0x18。
实现效果
使得已指向 UAF chunk 的指针 ptr 变为 ptr - 0x18
源码分析
源码路径
malloc.c
_init_free
#define unlink(AV, P, BK, FD) //line:1405
逐次分析如下
宏定义
#define unlink(AV, P, BK, FD)
P: 待脱链的空闲chunk的指针
BK:后一个chunk的指针
FD:前一个chunk的指针
大小检查
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))
malloc_printerr ("corrupted size vs. prev_size");
若物理相邻的后一个chunk的prev_size位的值与当前待脱链的空闲chunk的size不等时,报错
指针操作
FD = P->fd;
BK = P->bk;
`
首先通过 fd 以及 bk 指针获得 P 的前一个空闲 chunk 为 FD,以及后一个空闲 chunk 为 BK
图示如下:8 bytes / 小格(以64位程序为例)
关键检查
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr ("corrupted double-linked list");
这是一个关键 check ,利用者想要绕过此检查,需要构造合适的 fake chunk
如何绕过检查呢?
满足以下式子:
P->fd->bk == P <=> *(P->fd + 0x18) == P
p->bk->fd == P <=> *(p->bk + 0x10) == P
如果要让 P->fd+0x18
和 p->bk+0x10
指向同一个指向 P
的指针,那么需要将 fd 和bk 的内容分别修改为:
P->fd = &P - 0x18
P->bk = &P - 0x10
因此,我们只需要将 fd 的内容 设置为 (&p-0x18),将 bk 的内容设置为 (&p-0x10) 即可绕过安全检查
当满足以上条件时,才可以进入 Unlink 断链的环节:
因为 P 只可能从 smallbin 或者 largebin 中脱链,而这两个 bin 都是双向链表,因此脱链操作必须同时修改前后 chunk 的 fd 或者 bk 指针,即进行如下操作
FD->bk = BK <=> P->fd->bk = p->bk <=> *(P->fd + 0x18) = P->bk //Ⅰ
BK->fd = FD <=> P->bk->fd = p->fd <=> *(P->bk + 0x10) = P->fd //Ⅱ
对 Ⅰ式做换算,得到 P = &P - 0x10 ,如下
∵ P->fd = &P - 0x18
∴ *(&P - 0x18 + 0x18) = P->bk => P = P->bk
∵ P->bk = &P - 0x10
∴ P = &P - 0x10
对 Ⅱ 式做换算,得到 P = &P - 0x18 ,如下
∵ P->bk = &P - 0x10
∴ *(P->bk + 0x10) = P->fd => P = P->fd
∵ P->fd = &P - 0x18
∴ P = &P - 0x18
综上,断链之后 P 指针将指向 (&p-0x18) 的内存
假设我们设置 P = free_got, *(&P-0x18) = system,那么当下一次free一个堆块的时候,就会调用system。
largebin脱链
对于 smallbin 来说ÿ