0%

原型链污染

1. 基础概念

JavaScript是一种基于原型的语言,每个对象都有一个原型对象(__proto__属性),对象从原型继承属性和方法。原型链是JavaScript实现继承的主要方式。

在 JavaScript 中,没有引入类这个概念,通常需要定义一个构造函数然后通过 new 操作符调用该函数来创建实例。而 JavaScript 中的继承关系则是靠一种叫做 “原型链” 的模式来实现的。

谈到继承,JavaScript 只有一种结构:对象。每个函数对象有 prototype 属性,而实例对象没有,但所有的实例对象(函数,数组,对象)都会初始化一个私有属性 __proto__ 指向它的 构造函数的原型对象 prototype。该原型对象也有一个自己的原型对象 __proto__,层层向上直到一个对象的原型对象为 null

1
2
3
4
5
6
7
8
function Person(name) {
this.name = name;
}

let p = new Person('Alice');
// p.__proto__ === Person.prototype
// Person.prototype.__proto__ === Object.prototype
// Object.prototype.__proto__ === null

原型链污染是指攻击者通过修改对象的原型属性,从而影响所有基于该原型的对象。当对象属性查找时,如果对象本身没有该属性,JavaScript会沿着原型链向上查找。

2. 常见场景

2.1 合并操作

1
2
3
4
5
6
7
8
9
10
11
12
13
function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
merge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}

let obj1 = {};
let obj2 = JSON.parse('{"__proto__":{"isAdmin":true}}');
merge(obj1, obj2); // 污染了Object.prototype

2.2 复制/克隆

1
2
3
4
5
6
function clone(obj) {
return JSON.parse(JSON.stringify(obj));
}

let malicious = '{"__proto__":{"polluted":true}}';
let obj = clone(JSON.parse(malicious)); // 在某些环境下可能导致污染

2.3 属性赋值漏洞

1
2
let obj = {};
obj['__proto__']['polluted'] = true; // 直接通过__proto__修改原型

3. 常见运用

3.1 修改全局对象属性

1
2
3
4
// 污染后,所有对象都会继承污染属性
Object.prototype.polluted = true;
let obj = {};
console.log(obj.polluted); // true

3.2 覆盖内置方法

1
2
3
4
5
// 覆盖toString方法
Object.prototype.toString = function() {
return 'Hacked!';
};
console.log({}.toString()); // 输出"Hacked!"

3.3 影响模板引擎

许多模板引擎(如Handlebars, EJS等)会使用原型链查找属性,污染后可导致XSS或代码执行。

1
2
3
4
// 以EJS为例
Object.prototype.client = true;
Object.prototype.eval = function(x) { return eval(x); };
// 可能导致模板中的代码执行

3.4 影响Express等框架

1
2
3
4
// Express中的路由处理可能受到原型污染影响
Object.prototype.status = 404;
Object.prototype.body = 'Hacked';
// 可能导致所有路由返回404和'Hacked'

4. 防御措施

4.1 使用Object.create(null)

创建没有原型的对象:

1
2
let safeObj = Object.create(null);
safeObj.__proto__ = { polluted: true }; // 无效,因为safeObj没有原型链

4.2 避免使用__proto__

使用Object.getPrototypeOfObject.setPrototypeOf替代直接访问__proto__

4.3 安全的对象合并

1
2
3
4
5
6
7
function safeMerge(target, source) {
for (let key in source) {
if (key !== '__proto__') { // 过滤__proto__属性
target[key] = source[key];
}
}
}

4.4 使用Map代替Object

Map不会受到原型链污染影响:

1
2
let map = new Map();
map.set('__proto__', { polluted: true }); // 安全

5. 实际案例分析

Lodash CVE-2018-3721

Lodash的defaultsDeep函数存在原型污染漏洞:

1
2
const _ = require('lodash');
_.defaultsDeep({}, JSON.parse('{"__proto__":{"polluted":true}}'));

jQuery CVE-2019-11358

jQuery的extend函数在特定条件下可被用于原型污染:

1
jQuery.extend(true, {}, JSON.parse('{"__proto__": {"polluted": true}}'));

6. 总结

JavaScript原型链污染是CTF Web题目中常见的高级漏洞类型,理解原型链机制是关键。防御措施主要包括:

  1. 避免不安全的对象操作
  2. 使用无原型对象(Object.create(null))
  3. 对用户输入进行严格过滤
  4. 使用现代JavaScript特性替代危险操作

在CTF比赛中,遇到对象合并、克隆等操作时要特别警惕原型链污染的可能性,尝试通过污染全局原型来影响程序行为。

-------------结束啦感谢阅读-------------
Coffee = Code Fuel. Want to donate a cup?