티스토리 뷰
Introduction
file struct 구조를 분석하여 File Stream Oriented Programming
에 대해 공부한 내용을 정리하려 한다.
Data Structure in File
먼저, 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
};
libc에서, __IO_FILE_
구조체들은 모두 단일 연결 리스트
로 연결 되어있다.
포인터 *_chain
은 리스트의 다음 __IO_FILE
의 주소를 가지고 있다.
연결 리스트의 꼭대기는 __IO_list_all_
에 저장된다.
일반적으로 링크된 layout은 다음과 같다.
0x7f0cc0434500 <_IO_list_all>: 0x00007f0cc0434520 0x0000000000000000
0x7f0cc0434510: 0x0000000000000000 0x0000000000000000
0x7f0cc0434520 <_IO_2_1_stderr_>: 0x00000000fbad2087 0x00007f0cc04345a3
0x7f0cc0434530 <_IO_2_1_stderr_+16>: 0x00007f0cc04345a3 0x00007f0cc04345a3
0x7f0cc0434540 <_IO_2_1_stderr_+32>: 0x00007f0cc04345a3 0x00007f0cc04345a3
0x7f0cc0434550 <_IO_2_1_stderr_+48>: 0x00007f0cc04345a3 0x00007f0cc04345a3
0x7f0cc0434560 <_IO_2_1_stderr_+64>: 0x00007f0cc04345a4 0x0000000000000000
0x7f0cc0434570 <_IO_2_1_stderr_+80>: 0x0000000000000000 0x0000000000000000
0x7f0cc0434580 <_IO_2_1_stderr_+96>: 0x0000000000000000 0x00007f0cc0434600
0x7f0cc0434590 <_IO_2_1_stderr_+112>: 0x0000000000000002 0xffffffffffffffff
0x7f0cc04345a0 <_IO_2_1_stderr_+128>: 0x0000000000000000 0x00007f0cc0435750
0x7f0cc04345b0 <_IO_2_1_stderr_+144>: 0xffffffffffffffff 0x0000000000000000
0x7f0cc04345c0 <_IO_2_1_stderr_+160>: 0x00007f0cc0433640 0x0000000000000000
0x7f0cc04345d0 <_IO_2_1_stderr_+176>: 0x0000000000000000 0x0000000000000000
0x7f0cc04345e0 <_IO_2_1_stderr_+192>: 0x0000000000000000 0x0000000000000000
0x7f0cc04345f0 <_IO_2_1_stderr_+208>: 0x0000000000000000 0x00007f0cc0430400
0x7f0cc0434600 <_IO_2_1_stdout_>: 0x00000000fbad2887 0x00007f0cc0434683
//And the data of _IO_2_1_stderr_ can be interpreted as:
_flags = 0xfbad2087,
_IO_read_ptr = 0x7f0cc04345a3,
_IO_read_end = 0x7f0cc04345a3,
_IO_read_base = 0x7f0cc04345a3,
_IO_write_base = 0x7f0cc04345a3,
_IO_write_ptr = 0x7f0cc04345a3,
_IO_write_end = 0x7f0cc04345a3,
_IO_buf_base = 0x7f0cc04345a3,
_IO_buf_end = 0x7f0cc04345a4,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7f0cc0434600, //point to _IO_2_1_stdout_
_fileno = 0x2,
_flags2 = 0x0,
_old_offset = 0xffffffffffffffff,
_cur_column = 0x0,
_vtable_offset = 0x0,
_shortbuf = {0x0},
_lock = 0x7f0cc0435750,
_offset = 0xffffffffffffffff,
_codecvt = 0x0,
_wide_data = 0x7f0cc0433640,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0x0,
_mode = 0x0,
_unused2 = {0x0 <repeats 20 times>}
_IO_FILE
의 또 다른 중요한 구조체는 __IO_FILE_plus_
이다.
이것은 vtable(구조체 __IO_jump_t_
와 같은) 을 유지시킨다.
struct _IO_FILE_plus
{
_IO_FILE file;
const struct _IO_jump_t *vtable;
};
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
};
Exploitation techniques
기본적인 부분만 정리를 해보려고 한다.
(심화 된 내용은 아래 레퍼런스를 참조.)
fp를 이용한 fsop
File *fp;
의 값을 overwrite할 수 있다면, fake fp를 만들어 포인터 값을 옮기고 fake vtable으로 위치시켜 file stream 함수를 이용할때 프로그램의 실행 흐름을 control할 수 있다.
ba0bab➤ x/50gx 0x602010
0x602010: 0x00000000fbad2488 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000000
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x00007ffff7dd2540
0x602080: 0x0000000000000003 0x0000000000000000
0x602090: 0x0000000000000000 0x00000000006020f0
0x6020a0: 0xffffffffffffffff 0x0000000000000000
0x6020b0: 0x0000000000602100 0x0000000000000000
0x6020c0: 0x0000000000000000 0x0000000000000000
0x6020d0: 0x0000000000000000 0x0000000000000000
0x6020e0: 0x0000000000000000 0x00007ffff7dd06e0
0x6020f0: 0x0000000000000000 0x0000000000000000
0x602100: 0x0000000000000000 0x0000000000000000
0x602110: 0x0000000000000000 0x0000000000000000
0x602120: 0x0000000000000000 0x0000000000000000
0x602130: 0x0000000000000000 0x0000000000000000
0x602140: 0x0000000000000000 0x0000000000000000
0x602150: 0x0000000000000000 0x0000000000000000
0x602160: 0x0000000000000000 0x0000000000000000
0x602170: 0x0000000000000000 0x0000000000000000
0x602180: 0x0000000000000000 0x0000000000000000
0x602190: 0x0000000000000000 0x0000000000000000
ba0bab➤
fake fp구조체에서 0x602098
위치 (구조체에서 _cur_column
)에 0을 가르키고 있는 주소로 세팅해주고
0x6020e8
위치(_IO_file_jumps
)에 fake vtable의 주소를 세팅해준다.
그리고 이제 fake vtable(== fake _IO_file_jumps )에서 원하는 부분을 원하는 걸로 바꿔주면 된다.
예를 들어서, 만약 _IO_file_jumps
구조체 에서 __GI__IO_file_close
를 원샷이나 system으로 덮고 fclose(fp)를 콜하면 실행 될 것이다!
Reference
문서 좋은 것 같다! 더 공부해봐야겠다.
'pwnable > 정리' 카테고리의 다른 글
_IO_FILE Arbitrary Read & Write (0) | 2020.03.09 |
---|---|
glibc exit.c analysis (0) | 2019.11.12 |
64bit fsb 정리 (0) | 2019.11.06 |
libc_start_main leak (0) | 2018.09.13 |
kail tool pattern_create / offset (0) | 2018.09.06 |
- glibc
- rt_sigreturn
- hacking
- shellcoding
- fsop
- heap
- srop
- overflow
- tcache
- codegate
- FSB
- ebp change
- SQLi
- stack reusing
- oob
- pwable
- 해킹
- HackCTF
- fastbindup
- fastbin
- 본선가고싶다
- pwnable.tw
- TLS
- exit
- pwnable
- Total
- Today
- Yesterday