Skip to content

_IO_mem_jumps

Bir open_memstream FILE'ı (_IO_mem_jumps vtable) forge et; böylece kapanışta _IO_mem_finish, attacker-etkili değerleri kendi user buffer/size pointer'ları üzerinden yazar.

Mechanism

Note

open_memstream(), vtable'ı _IO_mem_jumps olan bir struct _IO_FILE_memstream döndürür. İki user pointer taşır, bufloc (char**) ve sizeloc (size_t*); kütüphane büyüyen buffer'ı bunlar üzerinden caller'a geri raporlar. Abuse target'ı, fclose'da erişilen __finish slot'u olan _IO_mem_finish'tir: write buffer'ı realloc eder ve sonra *mp->bufloc = buffer ile *mp->sizeloc = length yazar — yani attacker-kontrol edilebilir pointer field'ları üzerinden write'lar gerçekleştirir. _IO_mem_sync benzer şekilde büyümek için _IO_str_overflow çağırır ve sonra aynı field'lar üzerinden store eder — bu meşrudur çünkü _IO_FILE_memstream bir _IO_strfile embed eder, dolayısıyla _IO_mem_jumps vtable'ı _IO_str_* operasyonlarını yeniden kullanır (overflow slot'u doğrudan _IO_str_overflow'dur; glibc libio/memstream.c ile doğrulandı). O field'ları forge et ve stream'i kapatmak bir controlled write'a (ve _IO_write_base'in kontrollü bir realloc/free'sine) dönüşür. Not: fmemopen() modern glibc'de bunun yerine cookie-tabanlı (_IO_cookie_jumps) bir mekanizma kullanır, _IO_mem_jumps değil — bu teknik open_memstream'e özgüdür.

Walkthrough

Yapı ve finish (glibc libio/memstream.c):

struct _IO_FILE_memstream {
  _IO_strfile _sf;
  char  **bufloc;
  size_t *sizeloc;
};

static int _IO_mem_finish (FILE *fp, int dummy) {
  struct _IO_FILE_memstream *mp = (struct _IO_FILE_memstream *) fp;
  char *buf = realloc (fp->_IO_write_base, fp->_IO_write_ptr - fp->_IO_write_base + 1);
  if (buf != NULL) {
    *mp->bufloc = buf;                                   /* write through bufloc  */
    *mp->sizeloc = fp->_IO_write_ptr - fp->_IO_write_base;/* write through sizeloc */
  }
  ...
}

Silahlandırma taslağı:

  1. vtable = &_IO_mem_jumps (in range) ile bir memstream FILE forge et.
  2. bufloc/sizeloc'ı target adreslerine ayarla; _IO_write_base/ptr'yi, hesaplanan buffer pointer/length yazdırmak istediğin değerler olacak şekilde ayarla.
  3. fclose'u tetikle → _IO_mem_finish kontrollü değerleri yazar ve _IO_write_base üzerindeki realloc kontrollü bir free'ye yönlendirilebilir.

Warning

Bu, doğrudan bir çağrı değil, bir write/free primitive'idir; code execution için onu zincirle (ör. sahte bir _IO_list_all ya da başka bir vtable target yaz).

Detection

  • _IO_mem_finish/_IO_mem_sync'ten stack-olmayan/caller-olmayan adreslere write'lar.
  • glibc check build'leri; yönlendirilen realloc/free üzerinde heap-corruption dedektörleri.

Mitigation

  • glibc 2.24+ vtable range check'i; takip eden free'yi körleştirmek için RELRO ve hardened malloc.

References