0ctf heapstorm笔记

前言

最近找到了0CTF2018的原题,对旧题进行了重新的学习,记录一下在学习过程中遇到的问题和新的理解。
在做题中参照了sakura大神看雪上的writeup,https://bbs.pediy.com/thread-225973.htm

程序流程

初始化

程序申请了0x1337000空间保存堆管理结构。同时调用mallopt 禁用了fastbin。

初始化过程

在0x1337000+800处开始填入随机数,
r1,r2,r3,r3,r1,r2…….

r1用来同堆的地址异或保存在0x13370800+0x20+0x16*i。

r2用来同堆的大小异或保存在0x13370800+0x28+0x16*i。

要求r3处同后面r3异或等于0x13377331时才能够进行堆块查看操作。
初始化过程

程序菜单

菜单
常规的4项功能,alloc,update,delete,view

菜单
alloc调用calloc函数可申请12-4096大小的堆块

菜单
update存在off-by-one漏洞,但无法控制下一堆块的pre_size.

菜单
delete删除堆块,堆块地址通过和r1异或都得到

菜单
view功能需要0x13370810 xor 0x13370818为0x13377331,所以需要通过内存写来修改两地址为0,0x13377331.用来泄露libc地址。

漏洞利用

原理

由于fastbin被禁用,且堆块数组保存的是异或后的堆块地址,不能够使用unlink修改。通过学习,了解到可以使用unsorted bin+ large bin实现任意地址分配达到任意写的目的。

漏洞原理:控制unsorted chunk的bk指针,可以伪造bk指针指向的堆块在unsorted bin链表中。
victim = unsorted_chunks (av)->bk)
bck = victim->bk;
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);

unsorted bin的遍历从链表尾开始遍历,通过bk指针进行。

如果我们伪造unsorted chunk的bk指针为x,bck=x,
unsorted_chunks (av)->bk=x.

若我们伪造适当大小堆块在地址x且就可实现在任意地址芬分配。

我们还需要一次的内存写机会在再次遍历unsorted bin前,向x-0x8位置写入size。

若拆卸属于large chunk,由于还需要进行fd_nextsize和bk_nextsize链表的拆卸。

victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
具体分配可参照之前的malloc源码笔记
fwd为fd_nextsize双向链表中victim的下一个chunk
即原large bin链表中的chunk。若fwd可控,d->fd->bk_nextsize=victim,可向任意地址写victim的地址,堆块一般分配在大小0x55.。的内存空间上,我们可以通过错位使unsorted伪造堆块size为0x55,这样我们请求对应大小就可以实现任意地址分配。

选择写入0x1337800左右,修改r1,r2=0,0x1337810处为0,0x1337818为13377331.
此时我们可以通过view函数查看libc地址,并通过写入free_hook为system完成利用。

过程

我们需要分配处一个大小处在large bin的unsorted bin,并分配一个大小类似的large chunk连接在large bin链表。ps:unsorted bin中chunk大小必须大于large
bin 中chunk。
且两个堆块都可通过堆块重叠的方式写。
可以通过off-by-one实现。
由于pre——size不可控。实现堆块重叠方法如下
alloc(0x18)#1
alloc(0x500)#2
alloc(0x18)#3
alloc(0x18)#4
update(1,’a’0x4f0+p64(0x4f0)) // 在0x500处增加一个伪造堆块,使之后再分配不会修改2的pre_size
free(2) //free 2后会修改3的presize为0x510,
edit(1,’a’
0x12) //覆盖2的size为0x500,
alloc(0x20)#2
alloc(0x4c0)/加起来大小正好为0x500,请空unsorted bin 链表。
free(2)创造freechunk,绕过unlink
free(3)//由于 pre——size为0x511,会整体unlink成一个大的unsorted chunk
这时就实现了一个堆块重叠。
之后类似就可以达到能够利用的程度。

问题

为什么unsorted bin中的chunk一定要大于largebin中的
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av)
bck= unsorted_chunks (av)->bk->bk,即伪造chunk->bk,伪造chunk->bk不能为0,需为可写地址。0x133707f8指向地址需可写。
若大小小
if ((unsigned long) (size) < (unsigned long) (bck->bk->size))
{
fwd = bck;//fwd为largr bin头
bck = bck->bk;
。。。。
fwd->bk = victim;
bck->fd = victim;
不能写入0x133707f8
若大小大
fwd = fwd->fd;
bck = fwd->bk;//bck->fd可通过伪造的large chunk指针再次写入一个地址
。。
fwd->bk = victim;
bck->fd = victim;
原因:若插入的size在链表中最小,它的前后chunk通过,
为largebin及largebin->bk无伪造机会。只有一次控制修改内存机会。

若插入size不为最小,通过它后面的chunk,及chunk->bk来确定它的位置,bck->fd = victim;故有两次控制修改内存机会,一次修改伪造chunk->bk指向可写,一次控制伪造chunk->size为0x56.ps:size必须为0x56,因为后三位标志位原因。会有检测

总结:一共实现了三个内存地址的写入,unsorted chunk卸下写入一个,链表添加,写入两个。最后实现伪造堆块,分配出来。