首页
社区
课程
招聘
[原创]ctf中的large bins attack及lctf2017 2ez4u writeup
发表于: 2018-3-26 01:08 6848

[原创]ctf中的large bins attack及lctf2017 2ez4u writeup

2018-3-26 01:08
6848

编译:gcc -m32 fenpei.c -g -o fenpei

注意当14行,这条语句运行前,还没有large bins,因为会先把free的chunk加入unsorted bins,然后当再次分配时,遍历unsorted,如果没有才把free的chunk加入到其该去的bins(这里就是large bins)。

结论:

图中A1、B1、C1大小相同,是同一组chunk,A2是第二组,A3、B3大小相同,是第三组chunk。

large bins中的空闲chunk可能处于两个双向循环链表中,unlink时需要从两个链表中都删除,这里只分析large bin特有的删除操作,其他的参考我的另一篇文章

第一种:

即如果FD->fd_nextsize != NULL,说明FD是下一组尺寸相同的chunks的第一个chunk。

图示我要移除的P为A2,则FD是A3,FD的fd_nextsize!=NULL

P->fd_nextsize->bk_nextsize = P->bk_nextsize;

P->bk_nextsize->fd_nextsize = P->fd_nextsize;

第二种:

如果FD->fd_nextsize == NULL,且P是仅有的唯一一组尺寸相同的 chunks的第一个chunk。


图示的chunk大小都相同,若P为A1,FD即B1。
则此时P->fd_nextsize仍为P,移除P后,FD就是第一个chunk,所以将FD的fd_nextsize和bk_nextsize都由NULL修改为指向它自己。

第三种:

有多组chunks,且P为同组chunks的第一个chunk,且FD不是下一组尺寸相同的chunks的第一个chunk。

图示要移除的P为A2,则FD为B2。

FD继承P的fd_nextsize和bk_nextsize

修改P->fd_nextsize和P->bk_nextsize的指针

下述内容来源《glibc内存管理ptmalloc源代码分析》p87及glibc2.12.1源码

这里略去之前fastbins的合并和对unsorted的检索。
当这些都不能分配合适的chunk的时候,就到了下面的large bin分配实现。

关于堆利用的其他知识,可以参考清华的论文CTF-wiki

去掉之后,注意多nop几下……别让函数看上去断了,弄成我下面这样就行。

个人习惯先逆向分析一下结构体。




简单在注释里写了一下分析。

结论是:

动态分析时使用的脚本如下

可以通过动态调试的方式检验自己的分析结果,对一开始还是蛮有帮助,熟悉了之后就不用了,这种菜单题的结构体大同小异。

解释一下pwntools里面的几条语句

这是代码段的基地址(这里主要就是用作调试,所以本地调试需要关闭ASLR,不然这个地址会变化。)

因为可以看到文件里都是按照偏移来写的地址。

gdb.attach(io, 'b *0x%x' % (base_addr+0xD22))
使用gdb attach调试,b *是下断,这里我在malloc下断,attach上去之后再在里面下断也行,没区别。

另外使用IDA对二进制文件进行逆向分析的时候,可以把基地址重新选定,如下操作,可以看到现在基地址已经选定了。(注意这种情况下是在关闭ASLR调试时可以用,服务器上的地址并不是这个)


添加函数在上面已经分析了。

查看函数

删除函数

修改函数

可以看出在free chunk后并没有将存储指针的全局变量删除,还能够对其进行编辑,典型的UAF漏洞。

首先构造两个大小在同一个bins中的large chunk,将其释放后,这两个chunk先进入unsorted bins中,再申请一个不满足这两个chunk大小的chunk,则unsorted bins中的这两个chunk将会进入large bins中。
同时fd_nextsize和bk_nextsize将被赋值,因为指向这两个chunk的指针还存放在全局变量中,所以依然可以打印(UAF)

调试脚本如下:

gdb挂上后,先查看全局变量,找到堆

添加了一个large bin之后。

则index 9

index b

即:chunk_size链为

此处-->均代表bk_nextsize
同时由fd和bk可以看出在large bin链中的顺序,判据如下图:
index b的bk为main_arena,index 9的fd为main_arena

顺便index b大小为0x3e0,index 9的大小为0x3d0,b>a,这也证明了large bin确实是从大到小排序的。

这部分的调试过程比较简单,就在我脚本里那个地方下断,就可以在每次添加删除或修改执行完,返回生成菜单的代码处断下,查看每次修改结果,不再详细描述。

利用思路需要再整理一下,我们每次能够改变的是desc

在large bin attack开始前断下,并查看修改前的index b。

修改后,执行完edit(0xb, p64(chunk1_addr)),再在生成菜单的代码前断下,此时bk_nextsize指向伪造的chunk1,chunk1将在chunk1_addr这个地址构造。

最终构造出:

绕过p->fd->bk=p和p->bk->fd=p
以及p->bk_nextsize->fd_nextsize=p和p->fd_nextsize->bk_nextsize=p
同时要注意

注意这句话edit(0x7, '7'*0x198+p64(0x410)+p64(0x411))
它是为了保证size的大小一致,在“相邻”的下一个chunk设置好prev_size。
chunk1的addr为130,加上size即410就是540.

构造好之后,就是先删除6和3这两个大小在small bins范围里的chunk。
顺便一提,small bins是FIFO的规则,所以同一个链表中先被释放的chunk会先被分配出去。

然后再add(0x3f0, '3'*0x30+p64(0xdeadbeefdeadbeef))
因为我们之前说过了large bins的分配,首先找到first(bins),也就是free chunk的第一个,因为这是这条链里最大的,这里就是c30,然后从它的bk_nextsize开始遍历,即从130开始遍历,这里的130是我们伪造好的,它的大小为410,就被分配出去了。
检查一下,确实是这样,它被分配到了index 3的位置。

也可以看出触发了unlink

因为之前的index 3即0x0000555555757190就是small bin,而它被包含在我们伪造的chunk的大小(130-540)中,所以被leak出来,其fd的值就在libc中。

leak出的libc并不是基地址,还要减去偏移,我不大清楚应该这么算,但是关了ASLR之后,我们可以看到基地址,先leak一遍然后算出编译,然后再重新运行leak脚本,把这个偏移值减去即可,有人知道怎么算的话,可以告知我一下~谢谢。

最终leak出的libc地址为:0x00007ffff7a0d000

利用这个malloc出来的chunk来修改fastbin的fd
通过修改fd,来malloc出top前一块空间,然后这样就可以修改main_arena上的top为free_hook上面一些的地方。
通过几次malloc,修改free_hook为system的地址

这部分的堆构造比较复杂,不过只要注意到是怎么在我们malloc出的fake_chunk里free出一个fastbin,后面就可以通过更改fastbin的fd指针,结合UAF实现任意地址写了。

https://github.com/LCTF/LCTF2017/tree/master/src/pwn/2ez4u

这篇wp也发在了我的博客上,欢迎mark~
http://eternalsakura13.com/2018/03/21/lctf2/

 
 
 
 
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2019-2-2 16:15 被admin编辑 ,原因: 图片本地化
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//