Vtable Hijack

concept

in newer version of libc, we can no longer overwrite the file->vtable address with our fake vtable, this because libc has introduced a mitigation IO_validate_vtable that will check whether the vtable that is stored in the FILE is in the correct region or not.

_wide_data is a struct which is similar to _IO_FILE which is created to handle WCHAR or unicodes. Both struct have vtables, _wide_data uses _IO_wfile_jumps vtable while _IO_FILE uses _IO_file_jumps.

There's two macro that is being used to call these methods within the vtables:

  • _IO_JUMPS_FUNC

  • _IO_WIDE_JUMPS_FUNC

the IO_validate_vtable mitigation only exist in the _IO_JUMPS_FUNC macro.

one thing to note is that _IO_wfile_jumps.__overflow method will eventually calls file->_wide_data->_wide_vtable->__doallocate.

This if way we can:

  • file->vtable = file->_wide_data->_wide_vtable

  • file->_wide_data->_wide_vtable = &fake_wide_vtable

and because the calls to _wide_data->_wide_vtable don't have any checks, we can take control of execution.


fwrite

summary

  1. Set f->flags to _IO_MAGIC & ~_IO_CURRENTLY_PUTTING & ~_IO_UNBUFFERED which is 0xFBAD0000 & ~0x0800 & ~0x0002

  2. Set f->wide_data to a controllable fake wide_data

  3. Set f->wide_data->_IO_buf_base and f->wide_data->_IO_write_base to 0x0

  4. Set f->vtable to _IO_wfile_jumps

  5. Set f->wide_data->wide_vtable to a controllabe fake wide_vtable

  6. Set f->wide_data->wide_vtable->__doallocate to system or win

circle-info

note:

  • If space is limited, fake wide_data and fake wide_vtable can be overlapped

  • make sure f->_lock contains a valid address that points to NULL

call trace

we can start from it's definition and implementation:

which then calls _IO_sputn() which is a jump to __xsputn section of the f->vtable

and assuming, we have overwritten f->vtable with _IO_WIDE_JUMPS_FUNC it will call _IO_wfile_xsputn()

which then calls _IO_wdefault_xsputn()

which then calls __woverflow()

when calls _IO_OVERFLOW ();

again, remember we have overwritten f->vtable with _IO_WIDE_JUMPS_FUNC and because of it the __overflow section of f->vtable will refer to _IO_wfile_overflow() and so the execution continues from there on

assuming we set the correct f->flags and other requirement it will eventually call _IO_wdoallocbuf()

again, setting the correct requirements that satisfied the if statements will call _IO_WDOALLOCATE()

which will then call f->wide_data->wide_vtable->__doallocate. and since its within our control, we control the execution.


others

todo

Last updated