티스토리 뷰

libc leak with stdout struct


도저히 libc leak을 할 벡터가 없을 때, libc leak을 하기 위한 여러가지 테크닉을 익히기 위해서

관련된 문서자료들을 읽어보고, 관련함수들을 분석해 보고, 해당 내용들을 정리하기 위해서 한번 작성해봤당.


관련 문제

hitcon - baby_tcache
pwnable.tw - heap paradise

flow

해당 libc leak은 _IO_FILE struct의 flag값을 사용자가 임의의 값으로 바꾸고, _IO_write_base 의 일부를 NULL로 변조함에 따라, puts와 같은 stdout을 사용하는 함수가 사용될 때, 내부 루틴이 꼬이면서

libc가 leak이 되도록 한다.

file struct _flag define

#define _IO_MAGIC         0xFBAD0000 /* Magic number */
#define _IO_MAGIC_MASK   0xFFFF0000
#define _IO_USER_BUF         0x0001 /* Don't deallocate buffer on close. */
#define _IO_UNBUFFERED       0x0002
#define _IO_NO_READS         0x0004 /* Reading not allowed. */
#define _IO_NO_WRITES         0x0008 /* Writing not allowed. */
#define _IO_EOF_SEEN         0x0010
#define _IO_ERR_SEEN         0x0020
#define _IO_DELETE_DONT_CLOSE 0x0040 /* Don't call close(_fileno) on close. */
#define _IO_LINKED           0x0080 /* In the list of all open files. */
#define _IO_IN_BACKUP         0x0100
#define _IO_LINE_BUF         0x0200
#define _IO_TIED_PUT_GET     0x0400 /* Put and get pointer move in unison. */
#define _IO_CURRENTLY_PUTTING 0x0800
#define _IO_IS_APPENDING     0x1000
#define _IO_IS_FILEBUF       0x2000
                          /* 0x4000 No longer used, reserved for compat. */
#define _IO_USER_LOCK         0x8000

FILE struct에 정의된 flag들은 다음과 같이 지정되어있으며,

_IO_MAGIC
_IO_IS_FILEBUF
_IO_CURRENTLY_PUTTING
_IO_LINKED
_IO_NO_READS | _IO_UNBUFFERED | _IO_USER_BUF

일반적으로는 해당 flag들로 0xfbad28~~이 file struct의 flag값으로 존재하게 된다.

우리는 flag를 0xfbad1800 으로 변조할 것이다.

이로써 우리는 puts 의 내부루틴속 조작된 flag값에의해 libc leak이 될것을 유도한다.

puts routine

int
_IO_puts (const char *str)
{
 int result = EOF;
 size_t len = strlen (str);
 _IO_acquire_lock (_IO_stdout);
 if ((_IO_vtable_offset (_IO_stdout) != 0
      || _IO_fwide (_IO_stdout, -1) == -1)
     && _IO_sputn (_IO_stdout, str, len) == len
     && _IO_putc_unlocked ('\n', _IO_stdout) != EOF)
   result = MIN (INT_MAX, len + 1);
 _IO_release_lock (_IO_stdout);
 return result;
}
#define _IO_sputn(__fp, __s, __n) _IO_XSPUTN (__fp, __s, __n)

puts의 호출 과정이다.

여기서 포인트는

_IO_sputn이호출되며 _IO_sputn_IO_xsputn를 호출한다는 내용이다.

glibc/libio/libioP.h
extern unsigned _IO_adjust_column (unsigned, const char *, int) __THROW;
libc_hidden_proto (_IO_adjust_column)

#define _IO_sputn(__fp, __s, __n) _IO_XSPUTN (__fp, __s, __n)
ssize_t _IO_least_wmarker (FILE *, wchar_t *) __THROW;
libc_hidden_proto (_IO_least_wmarker)

glibc/libio/libioP.h 헤더속의 _IO_sputn정의

_IO_xsputn의 루틴을 살펴본다.

_IO_xsputn

size_t
_IO_new_file_xsputn (FILE *f, const void *data, size_t n)
{
 const char *s = (const char *) data;
 size_t to_do = n;
 int must_flush = 0;
 size_t count = 0;
 if (n <= 0)
   return 0;
   
 if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
  {
     count = f->_IO_buf_end - f->_IO_write_ptr;
     if (count >= n)
      {
         const char *p;
         for (p = s + n; p > s; )
          {
             if (*--p == '\n')
              {
                 count = p - s + 1;
                 must_flush = 1;
                 break;
              }
          }
      }
  }
 else if (f->_IO_write_end > f->_IO_write_ptr)
   count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */
 /* Then fill the buffer. */
 if (count > 0)
  {
     if (count > to_do)
       count = to_do;
     f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
     s += count;
     to_do -= count;
  }
 if (to_do + must_flush > 0)
  {
     size_t block_size, do_write;
     /* Next flush the (full) buffer. */
     if (_IO_OVERFLOW (f, EOF) == EOF)
       /* If nothing else has to be written we must not signal the
          caller that everything has been written. */
       return to_do == 0 ? EOF : n - to_do;
     /* Try to maintain alignment: write a whole number of blocks. */
     block_size = f->_IO_buf_end - f->_IO_buf_base;
     do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);
     if (do_write)
      {
         count = new_do_write (f, s, do_write);
         to_do -= count;
         if (count < do_write)
           return n - to_do;
      }
     /* Now write out the remainder. Normally, this will fit in the
        buffer, but it's somewhat messier for line-buffered files,
        so we let _IO_default_xsputn handle the general case. */
     if (to_do)
       to_do -= _IO_default_xsputn (f, s+do_write, to_do);
  }
 return n - to_do;
}
libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn)

해당 _IO_xsputn_IO_OVERFLOW를 호출하고,

_IO_OVERFLOW_IO_new_file_overflow를 가지고 있으며

_IO_new_file_overflow_IO_do_write를 호출한다.

_IO_new_file_overflow

int
_IO_new_file_overflow (FILE *f, int ch)
{
 if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
  {
     f->_flags |= _IO_ERR_SEEN;
     __set_errno (EBADF);
     return EOF;
  }
 /* If currently reading or no buffer allocated. */
 if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL)
  {
     /* Allocate a buffer if needed. */
     if (f->_IO_write_base == NULL)
      {
         _IO_doallocbuf (f);
         _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
      }
     /* Otherwise must be currently reading.
        If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end,
        logically slide the buffer forwards one block (by setting the
        read pointers to all point at the beginning of the block). This
        makes room for subsequent output.
        Otherwise, set the read pointers to _IO_read_end (leaving that
        alone, so it can continue to correspond to the external position). */
     if (__glibc_unlikely (_IO_in_backup (f)))
      {
         size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;
         _IO_free_backup_area (f);
         f->_IO_read_base -= MIN (nbackup,
                                  f->_IO_read_base - f->_IO_buf_base);
         f->_IO_read_ptr = f->_IO_read_base;
      }
     if (f->_IO_read_ptr == f->_IO_buf_end)
       f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
     f->_IO_write_ptr = f->_IO_read_ptr;
     f->_IO_write_base = f->_IO_write_ptr;
     f->_IO_write_end = f->_IO_buf_end;
     f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
     f->_flags |= _IO_CURRENTLY_PUTTING;
     if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
       f->_IO_write_end = f->_IO_write_ptr;
  }
 if (ch == EOF)
   return _IO_do_write (f, f->_IO_write_base,
                        f->_IO_write_ptr - f->_IO_write_base);
 if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */
   if (_IO_do_flush (f) == EOF)
     return EOF;
 *f->_IO_write_ptr++ = ch;
 if ((f->_flags & _IO_UNBUFFERED)
     || ((f->_flags & _IO_LINE_BUF) && ch == '\n'))
   if (_IO_do_write (f, f->_IO_write_base,
                     f->_IO_write_ptr - f->_IO_write_base) == EOF)
     return EOF;
 return (unsigned char) ch;
}
libc_hidden_ver (_IO_new_file_overflow, _IO_file_overflow)

puts -> _IO_sputn -> _IO_xsputn -> _IO_OVERFLOW -> _IO_new_file_overflow -> _IO_do_write

이때, _IO_do_write는 다음과 같은 인자를 가진다.

_IO_do_write(f, f->_IO_write_base, f->_IO_write_ptr - f -> _IO_write_base)

일반적인 stdout의 상태는 다음과 같다.

0x7ffff7dd0760 <_IO_2_1_stdout_>:   0x00000000fbad2887  0x00007ffff7dd07e3
0x7ffff7dd0770 <_IO_2_1_stdout_+16>: 0x00007ffff7dd07e3 0x00007ffff7dd07e3
0x7ffff7dd0780 <_IO_2_1_stdout_+32>: 0x00007ffff7dd07e3 0x00007ffff7dd07e3
0x7ffff7dd0790 <_IO_2_1_stdout_+48>: 0x00007ffff7dd07e3 0x00007ffff7dd07e3
0x7ffff7dd07a0 <_IO_2_1_stdout_+64>: 0x00007ffff7dd07e4 0x0000000000000000

하지만 우리는 NULL을 25byte정도 넣어주었으므로

0x7ffff7dd0760 <_IO_2_1_stdout_>:   0x00000000fbad2887  0x0000000000000000
0x7ffff7dd0770 <_IO_2_1_stdout_+16>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd0780 <_IO_2_1_stdout_+32>: 0x00007ffff7dd0700 0x00007ffff7dd07e3
0x7ffff7dd0790 <_IO_2_1_stdout_+48>: 0x00007ffff7dd07e3 0x00007ffff7dd07e3
0x7ffff7dd07a0 <_IO_2_1_stdout_+64>: 0x00007ffff7dd07e4 0x0000000000000000

다음과 같은 상태를 가질 태고,

이를 바탕으로 내부 루틴에서 _IO_do_write가 실행되면,

_IO_do_write(fp, 0x00007ffff7dd0700, 0x00007ffff7dd07e3- 0x00007ffff7dd0700)

_IO_do_write(fp, 0x00007ffff7dd0700, 0xe3)

가 실행이 되어서 leak 이 된다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함