![a5d24f5e7dffe1e6eaf5ac92a5332a04.png](https://i-blog.csdnimg.cn/blog_migrate/5020ff7db67b06ad457e5ea6e3651554.jpeg)
my blog:http://www.pwn4fun.com/
About the unlink
unlink,在CTF中是比较常见的知识点。
What’s the unlink
unlink_chunk函数用于将空闲的chunk从所在的bin中取出( 把一个双向链表中的空闲块拿出来),可以使用下图描述。
![66e7ffd9f0e092b54d3e7e2d1e0860d6.png](https://i-blog.csdnimg.cn/blog_migrate/e52ec7b5dcbe94ea14b82334d4bef5a2.jpeg)
可能触发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
![d85eda11e50aed9deebe5d508f9c8a6e.png](https://i-blog.csdnimg.cn/blog_migrate/1b7e9f09e61564609aaeec7d2d20eaad.jpeg)
Example
Analysis
例题为:2014 HITCON stkof
首先IDA静态分析一下程序。
![4cd3431c795d36bc84eef1f82b6c63a9.png](https://i-blog.csdnimg.cn/blog_migrate/93a4b233cd16aa70dd352e2082fbfa23.jpeg)
可以看到主要有三个功能:增、删、改。
首先看create:
![8e64ecef8497d89542330180d1523848.png](https://i-blog.csdnimg.cn/blog_migrate/ecede74d27dc911c8a7a36f6e72841a1.jpeg)
有一个指针数组存放了chunk的地址,新建的chunk没有大小限制也没有数量限制。
值得注意的是,下标从1开始。
接下来是edit:
![a4b43e2f0850dda775d805756dca92db.png](https://i-blog.csdnimg.cn/blog_migrate/1f0849fb4c333c8abf5f11d2490ea7c7.jpeg)
就是普通的修改,然后size自定义, 此处存在chunk overflow的情况。
最后是delete:
![718f21b01fc9f7fd1d34bc4121d99537.png](https://i-blog.csdnimg.cn/blog_migrate/eb1be45dd8aa0ec16ef0af9095adf34c.jpeg)
UAF。
保护机制:
![f247421b578c1319dca93948eb93de39.png](https://i-blog.csdnimg.cn/blog_migrate/0d2d6e27c4c9aba7e3fecb6559e23ec4.jpeg)
How to exploit
可以看到有堆块溢出以及UAF漏洞,可以新建两个chunk,在第一个chunk中伪造一个fake chunk绕过unlink检查进行unlink,最后修改GOT表leak libc以及get shell。
值得一提的是,因为程序没有进行setvbuf操作,所以输入和输出操作时,程序会申请缓冲区,有可能会影响我们的操作:
![ca2be9cb10d057dfd4b2b66c37bb49c9.png](https://i-blog.csdnimg.cn/blog_migrate/86bc63bcbaa3087b528e36c502445b5a.jpeg)
所以我们可以先去申请一个chunk申请缓冲区,以免影响操作。
![626a55dca72a56104ba1fcee9cda297b.png](https://i-blog.csdnimg.cn/blog_migrate/c19ec44a8a302873f531a53f6ac480b1.jpeg)
Start to exploit
![9950b9cac298a346a325eb7781fd9d37.png](https://i-blog.csdnimg.cn/blog_migrate/b608110a05590d791387b9ec74798877.jpeg)
这样就ok了。
接下来伪造fake chunk并进行unlink。
![84213c5acebec3034969b81c2f91c8ce.png](https://i-blog.csdnimg.cn/blog_migrate/b7d8001082adf4108d76b82162a297ba.jpeg)
此时free了chunk3,注意溢出修改chunk3的head部分,要注意修改prev_inuse位,即0x90而非0x91,让系统相信我们伪造的fake chunk是free状态的。
这时,ptr = ptr – 0x18 = chunk[0] – 0x8
修改ptr中的指针指向GOT表。
![1ccf9f2141d309de37cc28bfa3543f5c.png](https://i-blog.csdnimg.cn/blog_migrate/4ed4ad5d748e23c06673d2821c862700.jpeg)
![34febe8c4051bbecf38167921cf17166.png](https://i-blog.csdnimg.cn/blog_migrate/0bb3587840443d4a3a01c344d370f49d.jpeg)
然后修改GOT表的内容,因为数组指针下标从1开始(如下图)
![9141095f9e20039e0557d8f3992f89a2.png](https://i-blog.csdnimg.cn/blog_migrate/2e24273ac2618c277f67c171d664f275.jpeg)
但是我们修改ptr的时候并不会绕过下标0。
![5b4705e9b6fc9fcab20862f58a2df574.png](https://i-blog.csdnimg.cn/blog_migrate/8ebaf51bd972d7896ae3f2d6cb4a9611.png)
![aa026cb8a62ebb136a09749d3d89e23a.png](https://i-blog.csdnimg.cn/blog_migrate/c37696da1228b852d8fac70422dc2cb4.jpeg)
leak successfully。
![43feadc4b38d86411db371803ee2ca17.png](https://i-blog.csdnimg.cn/blog_migrate/2e3977a9dd6fa7248ffa73d71d10d58d.jpeg)
接下来就是常规get shell。
![850b7e5f5c9aef62dd0949b3655f1e51.png](https://i-blog.csdnimg.cn/blog_migrate/236cc943f55e3f40b3633f5eb8d145b2.jpeg)
Reference:CTF-WIKI