堆技巧 unlink
原理
我们在利用 unlink 所造成的漏洞时,其实就是对 chunk 进行内存布局,然后借助 unlink 操作来达成修改指针的效果。
古老的 unlink
假设现在有物理空间连续的两个 chunk(Q,Nextchunk),其中 Q 处于使用状态、 Nextchunk 处于释放状态。那么如果我们通过某种方式将 Nextchunk 的 fd 和 bk 指针修改为指定的值。
prev_size chunk Q
size = 0x81
User content
prev_size Nextchunk
size = 0x80
fd = target addr -12
bk = expect value
当我们 free(Q) 时,进行如下操作
- glibc 判断这个块是 small chunk
- 判断向前合并,发现前一个 chunk 处于使用状态,不需要合并。
- 判断向后合并,发现后一个 chunk 处于空闲状态,需要合并。
- 继而对 Nextchunk 采取 unlink 操作。
- FD = P->fd = target addr - 12
- BK = P->bk = expect value
- FD->bk = BK ,即 *(target addr -12 + 12) = expect value
- BK->fd = FD ,即 *(expect value + 8) = FD = target addr - 12
可以看到,我们可以通过 unlink 实现任意地址读写的目的,但是我们还是需要确保 expect value + 8 地址具有可写的权限。
比如我们将 target addr 设置为某个 got 表项,那么当程序调用对应的 libc 函数时,就会直接执行我们设置的 expect value 处的代码。但是需要注意,expect value + 8 处的值被破坏了,需要想办法绕过。
当前的 unlink
现在的 unlink 都有如下检查
// 由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致(size检查)
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \
malloc_printerr ("corrupted size vs. prev_size"); \
// 检查 fd 和 bk 指针(双向链表完整性检查)
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action, "corrupted double-linked list", P, AV); \
// largebin 中 next_size 双向链表完整性检查
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \
|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \
malloc_printerr (check_action, \
"corrupted double-linked list (not small)", \
P, AV);
可以发现,上述的利用方法绕不过双向链表完整性检查。我么需要通过伪造的方式绕过这一判断。
首先我们通过覆盖,将 nextchunk 的 FD 指针指向了 fakeFD ,将 nextchunk 的 BK 指针指向了 fakeBK。那么为了通过验证,我们需要:
- fakeFD -> bk == P ,即 *(fakeFD + 12) == P
- fdkeBK -> fd == P ,即 *(fakeBK + 8) == P
当满足上述两式时,可以进入 Unlink 的环节,进行如下操作:
- fakeFD -> bk == fakeBK ,即 *(fakeFD + 12) = fakeBK
- fakeBK -> fd == fakeFD ,即 *(fakeBK + 8) == fakeFD
所以为了绕过检测,我们只需要按如下方式修改
prev_size chunk Q
size = 0x81
User content
prev_size Nextchunk
size = 0x80
fd = nextchunk addr - 12
bk = nextchunk addr - 8
这样在 unlink 后,可以使 p 的指针指向比自己低 12 的地址处。
利用思路
条件
- Use after free ,可以修改 free 状态下 smallbin 或是 unsorted bin 的 fd 和 bk 指针
- 已知位置存在一个指针指向可进行 UAF 的 chunk。
效果
使得已指向 UAF chunk 的指针 ptr 变为 ptr - 0x18
思路
设指向可 UAF chunk 的指针的地址为 ptr
- 修改 fd 为 ptr - 0x18
- 修改 bk 为 ptr - 0x10
- 触发 unlink
此时 ptr 处的指针会变成 ptr - 0x18。