[攻防世界]进阶区babyfengshui
有好一段时间没写博客了(ಥ_ಥ)这儿啥也没有
~~老鸽王了哈~~ 最近打了个比赛,感觉自己对利用堆的方面还是比较薄弱,所以打算多做做堆的题
这个题主要就是利用堆溢出来拿shell,但这个题的方法不适用于libc2.26以上
能覆写、有栈保护、有nx,没随机地址
运行一下,发现是个菜单题,可以执行添加新用户、删除、更改等操作。
32位IDA打开看看,没有“/bin/sh”,可能要自己写入。
先看几个关键函数。这个添加新用户的函数,每次调用会创建两个堆,先malloc一个大小为a1的堆,然后malloc一个固定0x80的堆,大小为a1的堆用于存储description,姑且就把这个存储description的堆叫做Desc。而固定0x80的这个堆,暂且叫Note堆,他的前4位 (因为是32位程序,如果是64位程序,那么存储的地址就是8位) 存储的是Desc堆的地址,而后 0x80-0x4 即124位都存储的name的值。
然后看看delete函数,程序在free指针指向的空间后把指针置NULL了,没发现什么漏洞
在stdio.h头文件里,有一句"define NULL 0",所以这里置0相当于置NULL了
display函数,用于输出已有的用户姓名及描述
update函数,更改用户数据。里面有一句关键的判断,第14句判断 “(Desc堆地址+数据长度length)不能大于等于(Note地址- 4)”,但是这句判断仅适用于堆块相邻的情况下。
思路:
- Add两个新用户,分别为0号和1号,大小均为0x80 (算上堆头的话,实际大小0x8+0x80,32位程序堆头0x8)
- Add一个2号用户,用来写入”/bin/sh\x00”,申请的空间只要足够盛下”/bin/sh\x00”就行,至少要0x8
- Delete 0号用户,这样由于unlink就会形成一个0x80+0x80(即0x100)的空堆块
- Add一个Desc堆大小为0x100的3号用户,使其Desc堆和Note堆物理上不相邻
- 让3号用户的Desc堆溢出,覆盖至位于其高位的1号用户Note堆,修改原本指向1号Desc堆的指针,使其指向free的got地址,利用libcsearcher泄露libc (这题后来更新了,直接给了libc!所以也可以不用libcsearcher,具体的看exp里注释),计算system地址
- Updata 1号,由于Note堆中指向Desc的指针已经被覆写,其已经指向free的got表地址,所以此时用update修改内容,修改的不是description,而是free的got表内容,修改成system的地址,那么调用free函数的时候,实际上是调用的system,前面已经checksec过了,是可以修改got表内容的,如果是FULL RELRO就没法修改got表内容了
- Delete 2号用户,由于free已经变成了system,所以delete2号用户的Desc堆内容时,就会构成system(“/bin/sh”),拿到shell
图解细说:
先Add两个新用户,Desc均申请0x80,堆图如下。&Desc0即指向堆Desc0的指针。堆是从低地址向高地址生长的,刚好和栈相反。
Add 2号用户,用来存放”/bin/sh\x00”,这里Desc2只申请了0x8,装得下”/bin/sh\x00”就可以。
然后Del掉0号用户,unlink机制会让Desc0和Note0合并成一个0x108的块。
0x80+0x8+0x80 = 0x108现在Add 3号用户,申请Desc3的大小为0x108,malloc会先遍历bins来找合适的空闲块,而合并Desc0和Note0后的新堆块刚好,且Add 3号用户时,是先malloc的Desc3堆,所以Desc3就分配到了这个堆块,而Note3是固定的0x80,malloc遍历没有发现可用的空闲堆块,就会从TOP Chunk里分割出一块合适的,所以当前堆如下图:
如此一来,Desc3和Note3就不在物理上相邻了,这样Updata函数中的溢出判断就没用了,就可以让Desc3溢出,只要不溢出到Note3 - 4 的位置就行。Note1中储存指着Desc1的指针,可以从Desc3溢出来覆盖这个地址。题目没给libc的话,需要泄露libc,所以让指向Desc1的指针指向free的got表地址,如此一来,用Display函数输出 1号用户信息的时候,就会打印出free地址。(给了libc的话,也可以利用得到的free地址计算libc加载基址)
计算偏移:0x108+0x8+0x80+0x8 = 0x198
payload构造:payload = ‘a’*0x198+p32(elf.got[‘free’])用LibcSearcher泄露libc,计算system地址。Updata 1号用户,由于其指向Desc1的指针实际上指向的是free的got表地址,所以updata的free在got表中的地址,updata成得到的system的地址,这样调用free函数时,实际上调用的是system函数。
Delete 2号用户,会调用函数 free(Desc2),这样就会构成 system(“/bin/sh”),拿到shell,然后cat flag。
EXP:
1 | from pwn import * |
关于malloc申请堆块:可以看这儿