Lodash这个前端框架的历史漏洞大多均为原型链污染,在渗透中如果碰到存在漏洞的版本,可以测试一下。
这里通过js加载不同版本的lodash来进行测试,需要哪个版本修改src中的版本即可
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lodash原型链污染</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.12/lodash.min.js"></script>
<script>
console.log('Lodash版本:', _.VERSION);
console.log('页面已加载,可以进行功能测试');
</script>
</body>
</html>
CVE-2018-3721
版本影响:lodash < 4.17.5
漏洞描述:攻击者可以通过 merge
、defaultsDeep
、mergeWith
等函数将恶意属性注入到对象的原型上,导致全局对象被篡改。
poc
以merge为例
_.merge({}, JSON.parse('{"__proto__": {"polluted": "yes"}}'));
console.log({}.polluted);
原理
merge
函数的作用是递归地把源对象的属性赋值给目标对象,poc中将该属性__proto__.polluted===yes
赋给了目标对象{},即{}.__proto__.polluted===yes
,成功将顶层对象Object
污染了polluted
属性。
CVE-2019-10744
- 版本影响:lodash < 4.17.12
- 漏洞描述:
defaultsDeep
函数未正确校验prototype
以及constructor
,可以被利用污染原型。
poc
_.defaultsDeep({}, JSON.parse('{"constructor": {"prototype": {"vuln": true}}}'))
console.log({}.vuln)
如果输出true说明污染成功,存在漏洞。查看Object.prototype
可以看到的确存在vuln属性
原理
{"constructor": {"prototype": {"vuln": true}}}
这段json很容易就能看出来是一个原型链污染的payload,就是给constructor.prototype
对象加上一个属性值"vuln": true
_.defaultsDeep
函数的作用同样是递归地把源对象的属性赋值给目标对象,但不会赋值目标对象已经存在的属性。_.defaultsDeep(目标对象, 源对象)
poc中的目标对象为Object
空对象,源对象经过JSON.prase
解析后变为constructor.prototype.vuln===true
,赋值后即Object.constructor.prototype.vuln===true
,
首先Object.constructor
拿到Object
对象的构造函数Object(),Object.prototype
意为构造函数的原型,即顶层Object
对象,最后给顶层Object
对象赋属性vuln===true
,从而污染了Object
对象。此时任意对象访问vuln属性都会向上查找,直到找到Object.vuln
属性。
CVE-2020-8203
- 版本影响:lodash < 4.17.20
- 漏洞描述:
zipObjectDeep
存在原型链污染风险。
poc
const paths = ['({}).constructor.prototype.polluted'];
const values = ['yes!'];
_.zipObjectDeep(paths, values);
console.log({}.polluted);
NVD以及其他很多漏洞通报都说的是小于4.17.20存在,但是我经过测试发现只有<4.17.15才能成功复现。
chatgpt回答的是现代v8的安全防御机制,只有在特定老版本 Node 或浏览器里才生效。但我使用ie浏览器同样无法复现,就很疑惑。
原理
漏洞出在zipObjectDeep
函数,该函数的作用是给数组路径赋值,zipObjectDeep(paths, value)
,支持对象解析,例如
_.zipObjectDeep(['a.b.c'], [1])
{
a: { b: { c: 1 } }
}
poc中({}).constructor.prototype.polluted
将顶层Object
对象污染了polluted
属性