fflush() can arbitrary readable by FSOP
해당 문제를 풀면서 얻은 지식을 간단하게 정리한다.
물론 libc가 2.27이라 상당히 구식이다.
이 문제는 stdout에 FSOP가 가능한 취약점이 존재하고
fflush(stdout) 을 하게 된다.
_IO_fflush 함수에는
C
int
_IO_fflush (_IO_FILE *fp)
{
if (fp == NULL)
return_IO_flush_all ();
else
{
int result;
CHECK_FILE (fp,EOF);
_IO_acquire_lock (fp);
result =_IO_SYNC (fp) ?EOF : 0;
_IO_release_lock (fp);
return result;
}
}다음과 같이 _IO_SYNC 를 하게 되는데
C
int
_IO_new_file_sync (_IO_FILE *fp)
{
_IO_ssize_t delta;
int retval = 0;
/* char* ptr = cur_ptr(); */
if (fp->_IO_write_ptr > fp->_IO_write_base)
if (_IO_do_flush(fp)) return EOF;
delta = fp->_IO_read_ptr - fp->_IO_read_end;
if (delta != 0)
{
#ifdef TODO
if (_IO_in_backup (fp))
delta -= eGptr () - Gbase ();
#endif
_IO_off64_t new_pos = _IO_SYSSEEK (fp, delta, 1);
if (new_pos != (_IO_off64_t) EOF)
fp->_IO_read_end = fp->_IO_read_ptr;
else if (errno == ESPIPE)
; /* Ignore error from unseekable devices. */
else
retval = EOF;
}
if (retval != EOF)
fp->_offset = _IO_pos_BAD;
/* FIXME: Cleanup - can this be shared? */
/* setg(base(), ptr, ptr); */
return retval;
}여기서 FSOP 조작을 통해 _IO_do_flush(fp) 를 실행하게 하고
C
#define _IO_do_flush(_f) \
((_f)->_mode <= 0 \
? _IO_do_write(_f, (_f)->_IO_write_base, \
(_f)->_IO_write_ptr-(_f)->_IO_write_base) \
: _IO_wdo_write(_f, (_f)->_wide_data->_IO_write_base, \
((_f)->_wide_data->_IO_write_ptr \
- (_f)->_wide_data->_IO_write_base)))_IO_do_flush 매크로에서 _mode 값 조작으로 _IO_do_write 함수를 실행하게 한다
C
int
_IO_new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{
return (to_do == 0
|| (_IO_size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF;
}C
static
_IO_size_t
new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{
_IO_size_t count;
if (fp->_flags & _IO_IS_APPENDING)
/* On a system without a proper O_APPEND implementation,
you would need to sys_seek(0, SEEK_END) here, but is
not needed nor desirable for Unix- or Posix-like systems.
Instead, just indicate that offset (before and after) is
unpredictable. */
fp->_offset = _IO_pos_BAD;
else if (fp->_IO_read_end != fp->_IO_write_base)
{
_IO_off64_t new_pos
= _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);
if (new_pos == _IO_pos_BAD)
return 0;
fp->_offset = new_pos;
}
count = _IO_SYSWRITE (fp, data, to_do);
if (fp->_cur_column && count)
fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1;
_IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;
fp->_IO_write_end = (fp->_mode <= 0
&& (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
? fp->_IO_buf_base : fp->_IO_buf_end);
return count;
}
그럼 이제 다음과 같은데 _IO_SYSWRITE (fp, data, to_do); 을 실행시키면 arbitrary readalbe이 가능하게 된다.