_IO_mem_jumps¶
Bir
open_memstreamFILE'ı (_IO_mem_jumpsvtable) 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ğı:
vtable = &_IO_mem_jumps(in range) ile bir memstream FILE forge et.bufloc/sizeloc'ı target adreslerine ayarla;_IO_write_base/ptr'yi, hesaplanan buffer pointer/length yazdırmak istediğin değerler olacak şekilde ayarla.fclose'u tetikle →_IO_mem_finishkontrollü değerleri yazar ve_IO_write_baseüzerindekireallockontrollü 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.