Orz 最近碰到的几道题都是用到了它,出师不利2333

IO_FILE相关结构

覆盖 _IO_list_all (全局变量) 指向伪造的结构,然后改写 vtable 指针来 getshell
FILE 结构在程序执行 fopen 的时候动态创建并且分配在堆中

_IO_FILE 结构体

  1. struct _IO_FILE {
  2. int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
  3. #define _IO_file_flags _flags
  4. /* The following pointers correspond to the C++ streambuf protocol. */
  5. /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  6. char* _IO_read_ptr; /* Current read pointer */
  7. char* _IO_read_end; /* End of get area. */
  8. char* _IO_read_base; /* Start of putback+get area. */
  9. char* _IO_write_base; /* Start of put area. */
  10. char* _IO_write_ptr; /* Current put pointer. */
  11. char* _IO_write_end; /* End of put area. */
  12. char* _IO_buf_base; /* Start of reserve area. */
  13. char* _IO_buf_end; /* End of reserve area. */
  14. /* The following fields are used to support backing up and undo. */
  15. char *_IO_save_base; /* Pointer to start of non-current get area. */
  16. char *_IO_backup_base; /* Pointer to first valid character of backup area */
  17. char *_IO_save_end; /* Pointer to end of non-current get area. */
  18. struct _IO_marker *_markers;
  19. struct _IO_FILE *_chain;
  20. int _fileno;
  21. #if 0
  22. int _blksize;
  23. #else
  24. int _flags2;
  25. #endif
  26. _IO_off_t _old_offset; /* This used to be _offset but it's too small. */
  27. #define __HAVE_COLUMN /* temporary */
  28. /* 1+column number of pbase(); 0 is unknown. */
  29. unsigned short _cur_column;
  30. signed char _vtable_offset;
  31. char _shortbuf[1];
  32. /* char* _save_gptr; char* _save_egptr; */
  33. _IO_lock_t *_lock;
  34. #ifdef _IO_USE_OLD_IO_FILE
  35. };

然后通过 _chain 连接成一个链表,全局变量 _IO_list_all 指向链表头部

IO_FILE相关 - 图1

实际上在 IO_FILE 结构体是包含在一个叫 _IO_FILE_plus 中的

  1. struct _IO_FILE_plus
  2. {
  3. _IO_FILE file;
  4. IO_jump_t *vtable;
  5. }

里面还有个 IO_jump_t 类型的指针 vtable

  1. struct _IO_jump_t
  2. {
  3. JUMP_FIELD(size_t, __dummy);
  4. JUMP_FIELD(size_t, __dummy2);
  5. JUMP_FIELD(_IO_finish_t, __finish);
  6. JUMP_FIELD(_IO_overflow_t, __overflow);
  7. JUMP_FIELD(_IO_underflow_t, __underflow);
  8. JUMP_FIELD(_IO_underflow_t, __uflow);
  9. JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
  10. /* showmany */
  11. JUMP_FIELD(_IO_xsputn_t, __xsputn);
  12. JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
  13. JUMP_FIELD(_IO_seekoff_t, __seekoff);
  14. JUMP_FIELD(_IO_seekpos_t, __seekpos);
  15. JUMP_FIELD(_IO_setbuf_t, __setbuf);
  16. JUMP_FIELD(_IO_sync_t, __sync);
  17. JUMP_FIELD(_IO_doallocate_t, __doallocate);
  18. JUMP_FIELD(_IO_read_t, __read);
  19. JUMP_FIELD(_IO_write_t, __write);
  20. JUMP_FIELD(_IO_seek_t, __seek);
  21. JUMP_FIELD(_IO_close_t, __close);
  22. JUMP_FIELD(_IO_stat_t, __stat);
  23. JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
  24. JUMP_FIELD(_IO_imbue_t, __imbue);
  25. #if 0
  26. get_column;
  27. set_column;
  28. #endif
  29. };

伪造 vtable 劫持程序流程

linux 中常见的 IO 函数都需要经过 FILE 结构体进行处理,尤其是 IO_FILE_plus 结构体中的 vtable,一些函数会取出 vtable 中的指针进行调用

所以这种攻击方法的核心思想是:对 _IO_FILE_plus 的 vtable 动手脚,通过把 vtable 指向我们控制的内存,并在其中布置函数指针来实现

攻击方法有两种:一种是通过任意地址写改写 vtable 中的函数指针
一种是覆盖 vtable 的指针指向我们控制的内存,然后在上面布置函数指针

FSOP

前面的 house of orange 用的就是这种方法来 getshell 的
语雀内容