ez_heap
保护全开
程序逻辑:
- 读入0x30的字符串,进行字符串校验:以冒号为标志split,分成四份。最后输入字符串形如:
1 | xor = 0x111111111111111 |
创建0x180的chunk存放note 结构体每个note大小为0x30,note结构:
add,edit,delete,show操作
漏洞点:
editName中strlen可以用\x00绕过造成溢出
后门:remove操作中有一个一眼就很可疑的地方:lucky number,
如果这里的if判断不通过,直接return,看反编译的伪代码不容易看出来,看汇编代码+动态调试可以发现
if判断的参数初始值为1,如果执行过一次editName操作以后,这个参数会自减一次,此时remove函数结束时会执行 ret lucyNumber操作![[图片]](https://i-blog.csdnimg.cn/direct/bef4e13f101042a9ac378f08379d482c.png)
这里的xor操作,其中的一个操作数是可控的,dest


难点
editName操作和show操作只能执行一次
利用xor操作构造任意地址读写条件泄露libc地址
漏洞利用
第一阶段:
泄露heap基址和程序基址(PIE)
由于存在xor后的数据chunk的地址,结合动态调试,heap地址的后三字节是固定的,加上一字节的\x00溢出,
第一个note content chunk的起始地址为000+0x290(tcache bin管理结构)+0x190(note 结构体 chunk)=0x420
如果控制xor的操作数后两位为20,且第二个note content chunk与第一个同处于0x400-0x4ff空间上,当使用editName方法溢出第二个结构体chunk的name,此时这两个结构体chunk的note content chunk就指向了同一个,此时就构造了一个UAF,可以泄露heap地址和程序地址(程序地址用BSS的chunkList的地址)
此时edit和show都用过一次不能再用了,很自然的就想到再执行一次main函数,将luckynumber设置为main addr
第二阶段:
上面只是思考过程,此时才发现:
在回顾一下heap的构造,结构体chunk有一部分也处0x400-0x4ff,再溢出一次name则xor结果末两位是00,此时xor第一个操作数末两位是什么(假如设置为0xmn),那么这个溢出的结构体的conten就会指向0x4mn,第一阶段的泄露操作也可以通过最后一个结构体chunk泄露
这时就可以show和修改最后一个struct chunk的data content chunk的地址(要写入xor后的结果),将其指向got表结合show操作就可以泄露libc、heap、程序地址
最后将LUCKYNUMBER设置为onegadget即可
利用脚本
1 | def exp(): |
近队容器的礼仪
搭建环境
附件给出了pwn文件和libc文件夹,将libc.so.6和ld文件从libc文件夹中剪切出来,注意是剪切,确保libc文件夹中不再存在这两个文件,
直接运行使用:
1 | patchelf --set-interpreter ./ld-linux-x86-64.so.2 pwn |
python中pwnlib调用:
1 | # 也需要先像上面的shell命令一样先patchelf |
程序逻辑
1 | b'1. Exit\n' |
IDA的结果看起来太复杂了,先直接进行几次操作看看
先尝试下有没有UAF
结果一试还真有
漏洞点
- 刚刚提到的UAF,可以通过unsorted bin fd泄露libc和tcache bin fd泄露heap基址
- 堆溢出

很明显看到有个堆溢出
漏洞利用
先利用deque上的操作泄露地址
后用vector上的操作任意地址写(当时熬夜写的题,我也没去分析它到底能不能用deque的方法来,就是觉得给了这么多方法都用试试)
vecor申请的chunk结构是:一个0x20大小的结构体和对应size大小的animal,结构体中写着animal chunk的地址,因为能栈溢出,所以直接可以任意地址写
这里采取的操作时打enviorn打栈,从main的ret指令开始打;
算出environ到执行到main ret时栈地址的偏移量,然后构造栈满足ongadget的条件即可,也可以构造rop链
利用脚本
最后执行exit操作就可以执行到main的ret
注意执行execve的时候保证结尾是0x0而不是8吧,对齐一下
1 | def exp(): |