my blog:http://www.pwn4fun.com/
About the unlink
unlink,在CTF中是比较常见的知识点。
What’s the unlink
unlink_chunk函数用于将空闲的chunk从所在的bin中取出( 把一个双向链表中的空闲块拿出来),可以使用下图描述。
可能触发unlink的情形:
- malloc_consolidate()函数将fastbin中的空闲chunk整理到unsorted_bin时
- malloc()函数将unsorted中的空闲chunk整理到smallbin或者largebin中
- malloc()函数获取堆空间时
下面是源码:
/* Take a chunk off a bin list. */ static void unlink_chunk (mstate av, mchunkptr p) { //检查chunk的size和next_chunk的prev_size是否一致 if (chunksize (p) != prev_size (next_chunk (p))) malloc_printerr (“corrupted size vs. prev_size”); mchunkptr fd = p->fd; mchunkptr bk = p->bk; //检查fd和bk(双向链表完整性) if (__builtin_expect (fd->bk != p || bk->fd != p, 0)) malloc_printerr (“corrupted double-linked list”); fd->bk = bk; bk->fd = fd; if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL) { //检查largebin中next_size双向链表的完整性 if (p->fd_nextsize->bk_nextsize != p || p->bk_nextsize->fd_nextsize != p) 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; } } }
根据上面的源码,可以看到相应的检查。
首先是判断size是否相同,当前chunk的size以及next chunk的prev_size。if (chunksize (p) != prev_size (next_chunk (p))) malloc_printerr (“corrupted size vs. prev_size”);
其次是双向链表的完整性,检查fd、bk。if (__builtin_expect (fd->bk != p || bk->fd != p, 0))malloc_printerr (“corrupted double-linked list”);
也就是说,我们需要绕过两个check。
我自己的理解,unlink的检查是这样的,设在chunk0中伪造chunk,free(chunk1),根据prev_size位调节指针减去prev_size到fake_chunk的head部分,而是再根据fakechunk的size位继续调节指针+size,此时,按照正常情况应该到了nextchunk(chunk1)的prevsize位,因此我们在此伪造一个prev_size与fakechunk的size相同,即可绕过检查。当然,我认为,令size和prev_size相同,也是可以的。当然这样要求存在chunk overflow的情况。
How to use it
Example
Analysis
例题为:2014 HITCON stkof
首先IDA静态分析一下程序。
可以看到主要有三个功能:增、删、改。
首先看create:
有一个指针数组存放了chunk的地址,新建的chunk没有大小限制也没有数量限制。
值得注意的是,下标从1开始。
接下来是edit:
就是普通的修改,然后size自定义, 此处存在chunk overflow的情况。
最后是delete:
UAF。
保护机制:
How to exploit
可以看到有堆块溢出以及UAF漏洞,可以新建两个chunk,在第一个chunk中伪造一个fake chunk绕过unlink检查进行unlink,最后修改GOT表leak libc以及get shell。
值得一提的是,因为程序没有进行setvbuf操作,所以输入和输出操作时,程序会申请缓冲区,有可能会影响我们的操作:
所以我们可以先去申请一个chunk申请缓冲区,以免影响操作。
Start to exploit
这样就ok了。
接下来伪造fake chunk并进行unlink。
此时free了chunk3,注意溢出修改chunk3的head部分,要注意修改prev_inuse位,即0x90而非0x91,让系统相信我们伪造的fake chunk是free状态的。
这时,ptr = ptr – 0x18 = chunk[0] – 0x8
修改ptr中的指针指向GOT表。
然后修改GOT表的内容,因为数组指针下标从1开始(如下图)
但是我们修改ptr的时候并不会绕过下标0。
leak successfully。
接下来就是常规get shell。
Reference:CTF-WIKI