NOI 2025 游记
AzusaShirasu · · 科技·工程
没去参加笔试,Day 1 早上睡过头了(早上十一点醒的),洗漱完一看群里面有好多消息,才想起来今天是 NOI。
原来我不是 NOI 选手啊,不急了。打开电脑补一补题。
SJTU 2025,Web,女娲补胎
有附件,看看代码,其实只是把正常的 JS 库给取了一些神话名字而已,有趣,不过专心做题就行了。
最先引起注意的是这个:
//神兽「玄武」以甲壳御侮、以鳞角擅战
function Xuan_Wu(req) {
if (req.header['admin_key'] != undefined)
if (Gui_Xu(req.header['admin_key']) == "81cb271f0e52999ba6a0fb11fa6dd9fd")
return "pass"; return "fail";
}
// ... 省略部分代码
function Triple_Secure(req, res, next) {
if (!Xuan_Wu(req)) { // 这里
res.redirect('/');
}
else if (!Double_Pupil(req)) {
res.redirect('/');
}
else if (!Kui_Dragon(req)) {
res.redirect('/');
}
else {
next();
}
}
问题来了,Xuan_Wu 返回的值是个非空字符串,所以必定能够通过检验……因此第一层验证就是唬人用的。
先试着登录,根据代码,密码是 12345678910。但是不对,试半天进不去。难道改密码了?
确实改密码了。找了半天才发现 start.sh 里有私货:
这说明,它通过修改源代码文件,把密码变成了一个随机字符串。那怎么办?
然后发现居然没做访问控制,可以通过访问 /app.js 把源代码扒出来……然后就找到修改后的密码了。
然后登录,尝试访问 /flag,当然进不去,因为没权限。看看代码里是怎么判断用户有没有权限的?
//独脚神兽「夔」借雷声震慑天下
function Kui_Dragon(req) {
return req.cookies['role'] == "admin";
}
居然是用 Cookies 判断,那就是纸老虎了。本人用的是 Firefox 浏览器,按 F12,转到存储就可以编辑 Cookies 的值。编辑为 admin 之后就能访问 /flag 拿到答案了。
SJTU 2025,Web,EzLogin
下发了附件,看看。首先是 index.php,呃……
这年头还敢用 extract 是真狠人。往下审,遇到了 preg_match,经典之参数传入数组绕过 preg_match。这样就可以传入特殊符号去搞 SQL 注入之类的事情了。
找注入点,先找 common.php 看验证逻辑,指向了 internal/db_class.php 里的内容,在里面找到了如下验证代码:
有注入点,随便注入就行了。1 号用户被禁用了,用 2 号就能登录,不需要知道密码。注意,这里直接往输入框注入是不够的,因为要把键名 username 变为 username[],用 JS 执行(Python 也行):
f=new FormData();
f.set('username[]','aaaa');
f.set('password[]',"' or id=2 or '");
fetch('/',{body:f,method:'POST'});
接着访问 /admin.php,不出所料进不去,因为需要 admin 身份。再回到代码里去看看,在哪里判断用户身份?
用的是 _SESSION['role'] 判断。它的值在登录时就被赋值了。但是……代码里用过 extract 函数能够覆盖赋值……
f=new FormData();
f.set('_SESSION[id]',2);
f.set('_SESSION[role]','admin');
fetch('/',{body:f,method:'POST'});
再访问 /admin.php 就看到 flag 了。
SJTU 2025,Web,realLibraryManager
谁才是真正的“图书馆管理系统”管理员?\ 注意:登录页面验证码无法加载是正常现象(后端没装图形库)
只给网站,不给代码?
先看看前端的代码吧,前端的 JS 没混淆(注释都还在)。而且还可以看出,登录成功后的跳转页面也写出来了。不过直接访问会因为没有权限被拒绝。
尝试简单的注入发现没有什么用。哪里能找到代码呢?
随便复制 JS 代码中的一小段,到 github 上搜一下,结果就发现源代码了。这估计是某个大学生写的大作业。
发现 readme 里的截图一模一样,确定网站后端大概率是这个。readme 里还提供了测试账号,原文如下:
默认管理员账号为10086,密码为admin\ 默认一般用户的密码为123456,初始的账号有10000、10001、10002、10010,其中10010默认被挂失
直接登录会有验证码的问题,看看验证码逻辑,在 Common/Controller/LoginController.class.php 里,一眼就看到问题所在:
//先验证验证码,正确再验证账号密码,减小数据库压力
if($code != $rightCode){
$this->sendJsonMessage("验证码错误",1);
}
不等关系用 != 作比较?要是没有验证码参数(空参数),不就炸了吗?
手动模拟一下 JS 发 FormData 的过程,然后手动跳转,成功登录了。
也就 10000 可以登录,可惜管理员 10086 登不上(其他都登不上)。不着急,先到系统里面看看有什么。
大致翻了一下有哪些书,都是不错的文学著作,242 本书基本都读过,不过似乎并没有什么用。
接下来就是要找突破口了。有输入框?试着注入看看,输入一个单引号 ' 作为查询字符串看看,一次就成功了:
注入点确定存在。用经典的 xml 错误注入:
' or updatexml(1,concat(0x7e,(select id from user limit 4,1),0x7e),1) or '
改变 limit 参数一直到返回空,可以知道有 4 个用户,其中最后一个用户的 id 最特别,是 1145141920。然后查密码,表名是 pwd:
' or updatexml(1,concat(0x7e,(select pwd from user limit 4,1),0x7e),1) or '
发现返回的信息太长被截断了,没关系,截取一下获得后半段:
' or updatexml(1,concat(0x7e,mid(concat(0x7e,(select pwd from user limit 4,1),0x7e),9,30),0x7e),1) or '
然后得到管理员的密码 MD5 是 9cfbe247708a79cd184d832de35504d6。稍微爆破一下得到密码 adMin1,登录。
有个数据备份功能,发现数据备份可以写文件?
随手打了个备份到 test.php,没想到还成功了,接着访问 /test.php 居然也成功了,显示出了数据库的内容。说明这里可以任意写文件!
能够控制写在何处还不够,还得控制能写什么内容。去其他地方逛逛,发现有添加图书的功能。这就好办了,添加一个含 php 标签的书进去就行了。
写个一句话木马,其他的随便填点东西进去(因为能任意写东西的只有简介部分)。
保存到 a.php,访问 /a.php?a=ls > ans.php,再访问 /ans.php 就能看到 ls 的结果了,有个 F1aaaAagG 文件,里面就是 flag 了。
P.S:本人中间各种尝试(比如试表名、试注入点)其实完全不必要……后端代码都给出了,直接审计找漏洞就好了……
后记
BA 真好玩!先不补了,上线。