IO_FILE leak
_IO_FILE_plus
的结构如下(glibc-2.33 libioP.h)
struct _IO_FILE_plus
{
FILE file;
const struct _IO_jump_t *vtable;
};
在libc-2.23版本中,有个全局变量_IO_list_all
,该变量指向了FILE链表的头部,我们可以通过gdb动态调试来实际观察一下。首先认识一个结构体_IO_FILE_plus
_IO_list_all
的结构如下(glibc-2.23 libioP.h)
extern struct _IO_FILE_plus *_IO_list_all;
_IO_FILE_plus
的结构如下(glibc-2.23 libioP.h)
struct _IO_FILE_plus
{
_IO_FILE file;
const struct _IO_jump_t *vtable;
};
_IO_jump_t
的结构如下(glibc-2.23 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);
在glibc-2.23版本下libio.h
中_IO_FILE
的结构如下(glibc-233中找了好几圈没找到)
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
IO_FILE leak|利用IO_FILE进行leak
在pwn题中,当遇到没有可以用来泄露的功能时,我们一般将堆块分配到stdout
指针处存储的_IO_2_1_stdout_
该IO_FILE
结构体处,修改其_flags
为0xfbad1800,将后面三个read指针置空,将_IO_write_base
处的第一个字节改为0x58,后面的_IO_write_ptr
和_IO_write_end
保持不变。之后当程序遇到puts
函数时就会打印_IO_write_base
到_IO_write_ptr
之间的内容,按照上面步骤改动的话,我们泄露出的第一个libc地址是_IO_file_jumps
。 常用的payload如下所示,至于为啥需要flags=0xfbad1800(flags也可以是0xfbad3887),这里分析起来十分复杂,可以参见puts源码,只能说为了达到输出效果需要这样设置,另外该flags这样设置只是针对puts函数,其余打印函数略有不同。
payload = p64(0xfbad1800)+p64(0)*3+b"\x58"
另外payload也可以这样写,这样泄露出来的第一个地址将会是_IO_2_1_stdin_
。
payload = p64(0xfbad3887)+p64(0)*3+p8(0)