0x01 题目描述
我们打开题目,是一个简单的登录注册页面
0x02 解题过程
首先我们随便注册一个账户试试
我们直接F12查看源码,我们可以看见图片的src
1、我们这儿便联想到ssrf
,我们试试看可不可以读取源码,通过测试,我们发现用户名必须和图片的名称相对应,否则会报错,于是我们便可以通过?绕过这个限制,根据题目提示,此网站由js的express搭建,我们知道一般js
框架都有package.json
文件,于是我们注册用户名为../package.json?
,这样便可以读取package.json
文件了,由于这个文件在img标签里,所以图片无法显示,我们可以先把图片下载下来,然后用记事本打开就可以,根据提示,flag在根目录/flag中。
2、package.json
显示主入口为mainapp.js
,所以继续注册读取mainapp.js
文件, 发现路由在 /routers/index.js文件 继续读取 ,读取 /routers/index.js 可以看到有个 VerYs3cretWwWb4ck4p33441122.zip
路由 直接在web
上访问即可下载源代码。接下来的便是审计代码了。
3、然后就是白盒审计,可以发现注册登录功能采用了jwt
认证,每个人拥有自己独立的jwt secret
并且存在于服务端一个列表中,并且不同用户secret列表对应的id
存储在了jwt
中,登陆的时候会直接从jwt token
中读取id
然后通过列表获取secret
进行解密,这里有个trick
,node
的jsonwebtoken
有个bug,当jwt secret
为空时 jsonwebtoken
会采用algorithm none
进行解密 又因为服务端 通过
var secret = global.secretlist[id];
jwt.verify(req.cookies.token,secret);
解密,我可以通过传入不存在的id
,让secret
为undefined
,导致algorithm
为none
,然后就可以通过伪造jwt
来成为admin
import jwt
token = jwt.encode({"id":-1,"username":"admin","password":"123456"},algorithm="none",key="").decode(encoding='utf-8')
print(token)
4、成为admin
后,就可以访问admin23333_interface
接口 审计可以发现,这是一个读取文件的接口 这里用到了express
的特性,当传入?a[b]=1
的时候,变量a
会自动变成一个对象 a = {"b":1}
所以可以通过传入name
为一个对象,避开进入if
判断 从而绕过第一层
if(!/^key$/im.test(req.query.name.filename))return res.sendStatus(500);
的白名单过滤 第二个过滤是 判断filename
不能大于3
,否者会过滤.
和/
,而读取flag
需要先目录穿越到根目录 而../
就已经占了3个字符,再加上flag
肯定超过限制 这时候可以换个思路,length
不仅可以取字符串长度还可以取数组长度,把filename
设数组,再配合下面的循环 即可完美绕过过滤 而express
中当碰到两个同名变量时,会把这个变量设置为数组,例如a=123&a=456
解析后 a = [123,456]
,所以最终组合成
/admin23333_interface?name[filename]=../&name[filename]=f&name[filename]=l&name[filename]=a&name[filename]=g
此时 name={"filename":["../", "f", "l", "a", "g"]}
完美绕过 if(c !== "/" && c!==".")