以前一直搞不明白unlink
,直到学了点数据结构….
unlink
分为向前合并和向后合并两种脱链方式,是为了减少堆块的碎片化所提出的,当一个处于free状态的堆块的前后堆块被free
的时候,就是触发unlink
机制将两个小的堆块合并成一个大的堆块,unsafe unlink
问题就是出现在合并的时候检查没有到位导致的
wiki上的脱链示意图,当进行脱链的时候FD的bk
指向BK,BK的fd
指向FD
但是unlink
也不是随便进行的,还需要通过下面的检查才能进行unlink
,但在很早以前,它并没有这种机制,我们先通过介绍没有检查的情况,然后在此基础上进行修改,实现现在的unlink
攻击手法
1 2 3 4 5 6 7 8 9 10 11 12 13
| if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) malloc_printerr ("corrupted size vs. prev_size");
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) malloc_printerr (check_action, "corrupted double-linked list", P, AV);
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);
|
以前的unlink
以32位程序为例
假设申请了三个堆块,并存在堆溢出,我们先将chunk 2
给free
掉,然后利用堆溢出覆盖fd
和bk
我们回忆一下,unlink
会执行啥
1 2 3 4 5 6 7
| FD = P -> fd = free@got - 12
BK = P -> bk = system@got
FD -> bk = BK => (free@got - 12) + 12 = system@got
BK -> fd = FD => system@got + 8 = free@got
|
执行完之后,free@got
就变成了system@got
,还要一点需要注意的是system@got + 8 = free@got
,如果没有改回来有可能会发送一下奇奇怪怪的事情
目前的unlink
讲完没有检查的情况,接下来就是有检查的情况了,它只要检查的是FD->bk != P || BK->fd != P
只要绕过这个检查就可以了
1 2 3
| FD -> bk = BK => (free@got - 12) + 12
BK -> fd = FD => system@got + 8
|
我们看看刚刚的转换,明显不相等,是吧!
那按照这个检查,我们就可以进行伪造,这样一来就相等了对吧!
1 2 3
| FD -> bk => (p - 12) + 12 = p
BK -> fd => (p - 8) + 8 = p
|
那么最终实现的效果就是,跟刚刚的直接修改GOT表相比,只是修改了chunk
的指针,反差有点大,但是它还是有它的攻击效果的
例题为2016 ZCTF note2,参考《buu
刷题记之PWN系列》向前和向后合并的源码解析都在那,这里就不赘述了!
完整源码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #define unlink(AV, P, BK, FD) { if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) malloc_printerr ("corrupted size vs. prev_size"); FD = P->fd; BK = P->bk; if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) malloc_printerr ("corrupted double-linked list"); else { FD->bk = BK; BK->fd = FD; if (!in_smallbin_range (chunksize_nomask (P)) && __builtin_expect (P->fd_nextsize != NULL, 0)) { if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) malloc_printerr ("corrupted double-linked list (not small)"); if (FD->fd_nextsize == NULL) { if (P->fd_nextsize == P) FD->fd_nextsize = FD->bk_nextsize = FD; else { FD->fd_nextsize = P->fd_nextsize; FD->bk_nextsize = P->bk_nextsize; P->fd_nextsize->bk_nextsize = FD; P->bk_nextsize->fd_nextsize = FD; } } else { P->fd_nextsize->bk_nextsize = P->bk_nextsize; P->bk_nextsize->fd_nextsize = P->fd_nextsize; } } } }
|