HyggeHalcyon
GithubLinkedIn
  • 🕊️whoami
  • 🚩CTFs
    • 2025
      • ARKAVIDIA Quals
      • TECHOMFEST Quals
    • 2024
      • BackdoorCTF
      • World Wide CTF
      • 7th Cyber Mimic Defense
      • TSA Cyber Champion
      • Cyber Jawara International
      • National Cyber Week Quals
      • COMPFEST 16 Finals
      • HackToday Finals
      • UIUCTF
      • TBTL CTF
      • BSidesSF CTF
      • UMD CTF
      • UMassCTF
      • b01lers CTF
      • AmateursCTF
      • UNbreakable International - Team Phase
    • 2023
      • HackToday CTF Quals
        • Vnote
        • TahuBulat
        • Rangkaian Digital
      • Tenable CTF
        • Skiddyana Pwnz and the Loom of Fate
        • Braggart
      • CiGITS
        • afafafaf
        • popping around shell
        • well_known
      • TJCTF
        • flip out
        • shelly
        • groppling-hook
        • formatter
        • teenage-game
      • SanDiegoCTF
        • money printer
        • turtle shell
      • DeadSec CTF
        • one punch
      • FindIT CTF Quals
        • Debugging Spiders
        • Everything Machine
        • Furr(y)verse
        • Bypass the Py
        • Joy Sketching in the Matrix
        • Detective Handal
        • I Like Matrix
        • CRYptograPI
        • Date Night
        • Web-Find IT
        • Mental Health Check
        • NCS Cipher
        • Discovered
  • 🔍NOTES
    • FSOP
      • Structures
      • GDB
      • Arbitrary Read/Write
      • Vtable Hijack
    • Heap Feng Shui
      • Libc Leak
    • Kernel Space
      • Privilege Escalation
      • Objects
      • Escaping Seccomp
    • V8
      • Documentation
      • TurboFan
      • SandBox (Ubercage)
  • 📚Resources
    • Cyber Security
      • General
      • Red Teaming
        • CheatSheet
        • Payload Database
        • Quality of Life
      • Binary Exploitation
        • Return Oriented Programming
        • File Structure Oriented Programming
        • Heap Exploitation
        • Linux Kernel Exploitation
        • Windows Exploitation
        • V8 Browser
      • Reverse Engineering
        • Windows Executable
        • Malware Analysis
        • Tools
      • Web Exploitation
      • Malware Development
      • Detection Engineering
      • Blockchain / Web3
      • Cryptography
    • Software Engineering
  • 📋Planning
    • Quick Notes
Powered by GitBook
On this page
  • concept
  • fwrite
  • summary
  • call trace
  • others
  • todo
  1. NOTES
  2. FSOP

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

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:

size_t
_IO_fwrite (const void *buf, size_t size, size_t count, FILE *fp)
{
  // ...
  _IO_acquire_lock (fp);
  if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
    written = _IO_sputn (fp, (const char *) buf, request); // <-- OUR INTEREST
  _IO_release_lock (fp);
  // ...
}

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

#define _IO_XSPUTN(FP, DATA, N) JUMP2 (__xsputn, FP, DATA, N)

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

size_t
_IO_wfile_xsputn (FILE *f, const void *data, size_t n)
{
  // ...

  if (n <= 0)
    return 0;

  if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
    {
      // ...
      if (count >= n)
	{
	  const wchar_t *p;
	  for (p = s + n; p > s; )
	    {
	      if (*--p == L'\n')
		{
                  // ...
		}
	    }
	}
    }
  
  if (count > 0)
    {
      if (count > to_do)
	// ...
      if (count > 20)
	{
	  // ...
	}
      else
	{
		// ...
	}
      to_do -= count;
    }
  if (to_do > 0)
    to_do -= _IO_wdefault_xsputn (f, s, to_do); // <-- OUR INTEREST
  
  // ...
}

which then calls _IO_wdefault_xsputn()

size_t
_IO_wdefault_xsputn (FILE *f, const void *data, size_t n)
{
  // ..
  if (more <= 0)
    return 0;
  for (;;)
    {
      // ..
      if (count > 0)
	{
	  // ..
        }
      if (more == 0 || __woverflow (f, *s++) == WEOF) // <-- OUR INTEREST
      
      // ..
}

which then calls __woverflow()

wint_t
__woverflow (FILE *f, wint_t wch)
{
  if (f->_mode == 0)
    _IO_fwide (f, 1);
  return _IO_OVERFLOW (f, wch); // <-- OUR INTEREST
}

when calls _IO_OVERFLOW ();

typedef int (*_IO_overflow_t) (FILE *, int);
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
#define _IO_WOVERFLOW(FP, CH) WJUMP1 (__overflow, FP, CH)

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

wint_t
_IO_wfile_overflow (FILE *f, wint_t wch)
{
  if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
    {
      f->_flags |= _IO_ERR_SEEN;
      __set_errno (EBADF);
      return WEOF;
    }
  /* If currently reading or no buffer allocated. */
  if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0) // <-- NEEDS TO BE SATISFIED
    {
      /* Allocate a buffer if needed. */
      if (f->_wide_data->_IO_write_base == 0) // <-- NEEDS TO BE SATISFIED
	{
	  _IO_wdoallocbuf (f);		      // <-- OUR INTEREST
	  // stuff
	  if (f->_IO_write_base == NULL)
	    {
	      // stuff
	    }
	}
     // ... not rlly relevant
}
libc_hidden_def (_IO_wfile_overflow)

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

void
_IO_wdoallocbuf (FILE *fp)
{
  if (fp->_wide_data->_IO_buf_base) // <-- NEEDS TO BE SATISFIED
    return;
  if (!(fp->_flags & _IO_UNBUFFERED)) // <-- NEEDS TO BE SATISFIED
    if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF) // <-- OUR INTEREST
      return;
  // stuff
}
libc_hidden_def (_IO_wdoallocbuf)

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

typedef int (*_IO_doallocate_t) (FILE *);
#define _IO_DOALLOCATE(FP) JUMP0 (__doallocate, FP)
#define _IO_WDOALLOCATE(FP) WJUMP0 (__doallocate, FP)

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


others

todo

PreviousArbitrary Read/WriteNextHeap Feng Shui

Last updated 9 months ago

🔍
LogolibioP.h - libio/libioP.h - Glibc source code (glibc-2.31) - Bootlin
Logowfileops.c - libio/wfileops.c - Glibc source code (glibc-2.31) - Bootlin
LogolibioP.h - libio/libioP.h - Glibc source code (glibc-2.31) - Bootlin
Logoiofwrite.c - libio/iofwrite.c - Glibc source code (glibc-2.31) - Bootlin
Logowgenops.c - libio/wgenops.c - Glibc source code (glibc-2.31) - Bootlin
Logowfileops.c - libio/wfileops.c - Glibc source code (glibc-2.31) - Bootlin
Logowgenops.c - libio/wgenops.c - Glibc source code (glibc-2.31) - Bootlin
LogolibioP.h - libio/libioP.h - Glibc source code (glibc-2.31) - Bootlin