When a new FILE structure is allocated and its pointer returned from fopen(). , glibc has actually allocated an internal structure called struct _IO_FILE_plus
, which contains struct _IO_FILE
and a pointer to struct _IO_jump_t
, which in turn contains a list of pointers for all the functions attached to the FILE. Overwrites a ‘FILE’ pointer (stdin, stdout, stderr or any other file handler opened by fopen()
) to point to own forged structure for gain control over execution flow.
File structure contains vtable
, which is a pointer to a table which contains functions which are called when the original ‘FILE’ pointer is used to perform different operations (such as fread, fwrite, …).
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
/* The following pointers correspond to the C++ streambuf protocol. */
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. */
int _fileno;
_IO_FILE_plus
struct is the extended version of _IO_FILE
struct, which is _IO_FILE
+ vtable
(virtual table = array of pointers to the helper fucntions during executing the IO operation):
// in FILE.h
typedef struct _IO_FILE FILE;
// This contains a pointer to the function jump table used.
struct _IO_FILE_plus
{
FILE file;
const struct _IO_jump_t *vtable;
};
How the vtable
will be filled during creating a new extended File
struct. Depending on the method u use. Ex: fopen(), the vtable
initialized with the existed vtable
called _IO_file_jumps
(other vtavle
like _IO_str_jumps
).
_IO_FILE *
__fopen_internal (const char *filename, const char *mode, int is32)
{
...
_IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
...
}
[_IO_jump_t](https://code.woboq.org/userspace/glibc/libio/libioP.h.html#_IO_jump_t)/ [glibc2.35](https://elixir.bootlin.com/glibc/glibc-2.35/source/libio/libioP.h#L293):
- stdin/stdout/stderr, fopen also use it.
- Extra Virtual function table.
- The export symbol of this type in the libc file is [_IO_file_jumps](https://code.woboq.org/userspace/glibc/libio/fileops.c.html#_IO_file_jumps).
_chain
(linked list). The head of linked list is called _IO_list_all
struct _IO_FILE
{ ...
struct _IO_FILE *_chain;
...
}
Some good targets in FILE structure:
_lock
(usually need to construct it for exploitation)
c struct _IO_FILE { ... _IO_lock_t *_lock; }
_lock
= 0x88This is also a FILE structure in glibc. We can overwrite the global variable in glibc to control the flow execution. (in glibc symbol table)
Control the linked list of File stream:
Powerful function:
_IO_flush_all_lockp
for (fp = (FILE *) _IO_list_all; fp != NULL; fp = fp->_chain)
{
run_fp = fp;
...
if (( (fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0
&& (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base) )
) && _IO_OVERFLOW (fp, EOF) == EOF)
result = EOF;
}
It will process all FILE in FILE linked list. We can construct the linked list to do oriented programing. Then line 9 will trigger virtual function.
The vtable must be in libc _IO_vtable
section. Otherwise, it will check if the vtable permits virtual function call.
IO_validate_vtable (const struct _IO_jump_t *vtable)
{
/* Fast path: The vtable pointer is within the __libc_IO_vtables
section. */
uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
uintptr_t ptr = (uintptr_t) vtable;
uintptr_t offset = ptr - (uintptr_t) __start___libc_IO_vtables;
if (__glibc_unlikely (offset >= section_length))
/* The vtable pointer is not in the expected section. Use the
slow path, which will terminate the process if necessary. */
_IO_vtable_check ();
return vtable;
}
_IO_vtable_check
For compatibility:
/* Honor the compatibility flag. */
void (*flag) (void) = atomic_load_relaxed (&IO_accept_foreign_vtables);
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (flag); // Demangle w/ pointer guard
#endif
if (flag == &_IO_vtable_check)
return;
Dl_info di;
struct link_map *l;
if (!rtld_active ()
|| (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0
&& l->l_ns != LM_ID_BASE))
return;
https://outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/
https://www.slideshare.net/AngelBoy1/play-with-file-structure-yet-another-binary-exploit-technique
research
pwn