原型污染是一种 JavaScript 漏洞,攻击者可借此向全局对象原型添加任意属性,这些属性随后可能被用户自定义对象继承。原型污染源是任何可由用户控制的输入,使你能够向原型对象添加任意属性。最常见的来源如下: URL JSON Web 消息
对于 tough-cookies 包(v2.5.0),更相关的是第一种,即通过 URL。其发生过程如下:
这里有一个请求:
https://vulnerable-website.com/?__proto__[badProperty]=payload
当将查询字符串分解为键值对时,URL 解析器可能会将 __proto__ 解释为任意字符串。在某个时刻,递归合并操作可能会使用类似于以下语句的方式为 evilProperty 赋值:
targetObject.__proto__.badProperty = 'payload';
__proto__ 在 JavaScript 中有特殊用途。众所周知,如果某个对象的某个属性 P1 未被找到,将会在该对象的原型中查找。
示例
function myClass() {
this.P1 = 'value';
}
let mc = new myClass();
console.log(mc.P1); //Value
console.log(mc.P2); //Undefined
mc.__proto__.P2 = 'value2';
let mc2 = new myClass();
console.log(mc.P2); //value2
更改 __proto__ 后,myClass 的所有已创建实例都将具有属性 P2。
对于 tough-cookies 包(v2.5.0),可以从以下代码片段中看到此原型污染漏洞:
await new Promise((resolve, reject) => {
cookiejar.setCookie(
"Slonser=polluted; Domain=__proto__; Path=/notauth",
"https://__proto__/admin",
{ loose: true },
(err, cookie) => {
if (err) {
reject(err);
} else {
resolve(cookie);
}
}
);
});
发现 CookieJar 类默认使用 MemoryCookieStore 作为存储。在 MemoryCookieStore 的若干方法中,对象初始化如下:
this.idx = {};
这意味着可以通过 __proto__ 向 this.idx 的原型添加带有值的属性(这些值可能是有问题的值)。为了防止这种情况,在若干处已将初始化改为如下形式:
this.idx = Object.create(null);
这将创建一个没有原型的空对象。随后添加了一个测试 memstore_vulnerability_fix_test.js 来验证该修复。