Skip to content

_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:

  1. vtable = &_IO_wstr_jumps (in range, bkz. io-vtable-check-bypass.md).
  2. Kontrollü bir call için wide buffer bounds'unu / strfile buffer-function field'ını ayarla (ya da kontrollü bir free için _wide_data->_IO_buf_base'i set edip _IO_FLAGS2_USER_WBUF'u temizle).
  3. _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.

References