_IO_wstr_overflow¶
_IO_str_overflow'un wide-string analogu: bir wide string-stream (_IO_wstr_jumps) forge et ki grow/finish path'i attacker-controlled bir buffer function pointer'ı çağırsın.
Mechanism¶
Note
_IO_wstr_overflow bir wide string-stream buffer'ını büyütür; _IO_wstr_finish onu
free eder (_IO_FLAGS2_USER_WBUF flag'iyle gate edilir) ve _IO_wdefault_finish'e
chain'ler. Narrow string stream gibi, grow (overflow) path'i tarihsel glibc'de
(<= ~2.23) strfile'da saklanan bir function pointer'a (_s._allocate_buffer) ulaşır ve
onu wide buffer bounds'tan türetilen bir size ile çağırır; finish path'i ise bir function
pointer değil, _wide_data->_IO_buf_base üzerinde doğrudan free() çağırır (yukarıdaki
koda bkz.). Modern glibc bu allocate-call'ı düz malloc'la değiştirdi (aşağıdaki
warning'e bkz.). _IO_wstr_jumps __io_vtables içinde olduğu
için ona yönelen forge edilmiş bir FILE glibc 2.24 range check'ini geçer; kırılan
invariant narrow durumla aynıdır — range-valid bir vtable artı attacker-controlled
buffer field'ları wide-character code path'inde kontrollü bir call/free verir.
Walkthrough¶
Wide str slot'ları ve finish (glibc libio/wstrops.c):
const struct _IO_jump_t _IO_wstr_jumps = {
JUMP_INIT(finish, _IO_wstr_finish),
JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstr_overflow),
JUMP_INIT(underflow,(_IO_underflow_t) _IO_wstr_underflow),
...
};
void _IO_wstr_finish (FILE *fp, int dummy) {
if (fp->_wide_data->_IO_buf_base && !(fp->_flags2 & _IO_FLAGS2_USER_WBUF))
free (fp->_wide_data->_IO_buf_base); /* controlled free if forged */
_IO_wdefault_finish (fp, 0);
}
Grow (overflow) path'inin allocate satırı — tarihsel glibc'de (<= ~2.23) strfile'da
saklanan bir function pointer üzerinden; modern glibc bunu düz malloc'la değiştirdi:
/* _IO_wstr_overflow (historical glibc, libio/wstrops.c): enlarge path */
wchar_t *old_buf = fp->_wide_data->_IO_buf_base;
size_t old_wblen = _IO_wblen (fp);
size_t new_size = 2 * old_wblen + 100;
/* buffer-function field'ı üzerinden allocate (modern glibc: düz malloc) */
new_buf = (wchar_t *)
(*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size * sizeof (wchar_t));
if (old_buf) {
__wmemcpy (new_buf, old_buf, old_wblen); /* eski içeriği kopyala */
(*((_IO_strfile *) fp)->_s._free_buffer) (old_buf); /* eski buffer'ı bırak */
fp->_wide_data->_IO_buf_base = NULL;
}
/* wide read/write pointer'ları new_buf'a göre yeniden hesapla */
fp->_wide_data->_IO_write_ptr = new_buf + (fp->_wide_data->_IO_write_ptr - old_buf);
_IO_wstr_overflow yeni bir wide buffer allocate eder (malloc/buffer-function), eski
içeriği wmemcpy'ler ve wide write pointer'larını günceller. Forge reçetesi narrow durumu
yansıtır:
vtable = &_IO_wstr_jumps(in range, bkz. io-vtable-check-bypass.md).- Kontrollü bir call için wide buffer bounds'unu / strfile buffer-function field'ını ayarla
(ya da kontrollü bir
freeiçin_wide_data->_IO_buf_base'i set edip_IO_FLAGS2_USER_WBUF'u temizle). _IO_flush_all_lockpüzerinden tetikle (fsop-via-io-list-all-io-flush-all-lockp.md).
Warning
Modern glibc'de buffer-function field'ı static init'te yeniden adlandırılmış/kullanılmıyor,
dolayısıyla doğrudan allocate-call formu körelmiş durumda; wide path daha güvenilir şekilde
_IO_wfile_jumps / House of Apple 2 üzerinden exploit edilir
(io-wfile-jumps.md). Offset'leri target libc'ye karşı doğrula.
Detection¶
- Hiçbir zaman wide string stream olarak yaratılmamış objelerde
_IO_wstr_overflow/_IO_wstr_finish'ten gelen call'lar/free.
Mitigation¶
- glibc 2.24+ range check; sonradan indirect buffer call'larının kaldırılması; yönlendirilen
freeüzerinde hardened malloc; full RELRO.