免责声明
本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者及本公众号不为此承担任何责任。
CVE-2019-10744
漏洞CVE-2019-10744指出,JS库lodash中lodash.mergeWith、lodash.merge、lodash.set等方法存在原型链污染的问题,我们以lodash.set为例,研究一下在实际情况下原型链污染是怎么发生的。
写一个函数测试的脚本:
var lodash = require('lodash');
var result = lodash.set({}, "__proto__.evil", "evil!!!!");
console.log({}.evil);
运行代码,可以看到原型链已经遭到污染,任何一个对象都有了evil属性:
我们跟踪set()函数,看看漏洞如何发生:
-
进入set函数,随后进入baseSet
function set(object, path, value) {
return object == null ? object : baseSet(object, path, value);
}
2. 进入baseSet,关键在这一行assignValue(nested, key, newValue);,其中,nested就是我们输入的object,key就是我们输入的key路径中的每一条路径,newValue就是我们输入的value。在我们构造了payload:"__proto__.evil", "evil!!!!"后,path = castPath(path, object);会把我们的key变为[’__proto__’,’evil’]的Array,然后遍历这个Array,分别对__proto__和evil执行两次assignValue,当没有遍历到数组最后一个值,也就是evil时,会直接复制为object原本的值。
function baseSet(object, path, value, customizer) {
if (!isObject(object)) {
return object;
}
path = castPath(path, object);
var index = -1,
length = path.length,
lastIndex = length - 1,
nested = object;
while (nested != null && ++index < length) {
var key = toKey(path[index]),
newValue = value;
if (index != lastIndex) {
var objValue = nested[key];
newValue = customizer ? customizer(objValue, key, nested) : undefined;
if (newValue === undefined) {
newValue = isObject(objValue)
? objValue
: (isIndex(path[index + 1]) ? [] : {});
}
}
assignValue(nested, key, newValue);
nested = nested[key];
}
return object;
}
3. 进入assignValue,随后进入baseAssignValue
function assignValue(object, key, value) {
var objValue = object[key];
if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
(value === undefined && !(key in object))) {
baseAssignValue(object, key, value);
}
}
4. 进入最关键的函数baseAssignValue,注意看代码object[key] = value;,是不是就是文章开头给出的obj[a][b] = value;样式?只是在baseSet中,把obj[a][b] = value分解了: obj[a] = obj[a],obj[a][b] = value两次执行
function baseAssignValue(object, key, value) {
if (key == '__proto__' && defineProperty) {
defineProperty(object, key, {
'configurable': true,
'enumerable': true,
'value': value,
'writable': true
});
} else {
object[key] = value;
}
}
5. 因此,在我们构造了payload:"__proto__.evil", "evil!!!!"后,会执行两次baseAssignValue,第一次是baseAssignValue(object, __proto__, object.__proto__),第二次是baseAssignValue(object, __proto__.evil, evil!!!)
总结
本文介绍了JavaScript原型链污染中有名的漏洞CVE-2019-10744,让我们对JavaScript原型链污染有了直观的认识。下一章我们将介绍如何自己写代码批量刷原型链污染漏洞,自动化刷洞的方法已经放在Github,点击原文链接可以直达。
原文始发于微信公众号(赛博安全狗):原型链污染:从原理分析到批量刷洞(三)—— CVE-2019-10744
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论