浅谈原型污染
字数 1453 2025-08-29 08:30:30

JavaScript原型污染漏洞详解

1. 什么是原型污染

原型污染(Prototype Pollution)是一种JavaScript安全漏洞,它允许攻击者向全局对象原型中添加任意属性,这些属性随后可能会被用户定义的对象继承。

关键特性:

  • 通常不能作为独立漏洞被利用,但可以与其他漏洞串联
  • 在客户端可能导致DOM型XSS攻击
  • 在服务器端可能引发远程代码执行(RCE)
  • 主要影响内置的全局Object.prototype

2. 漏洞产生原理

原型污染漏洞通常发生在JavaScript函数将包含用户可控属性的对象递归合并到现有对象中时,而没有事先对键进行清理。

攻击流程:

  1. 攻击者注入一个键名为__proto__的属性
  2. 合并操作将嵌套属性分配到对象的原型上而非目标对象本身
  3. 所有继承该原型的对象都会获得这些恶意属性

关键要素:

  1. 污染源:能够注入任意属性的输入点
  2. 污染接收点(Sink):能导致代码执行的函数或DOM元素
  3. 可利用的工具(Gadget):被传递到接收点且未经适当过滤的属性

3. 污染源分析

3.1 通过URL的原型污染

示例URL:

https://vulnerable-website.com/?__proto__[evilProperty]=payload

解析过程:

  1. URL解析器将__proto__视为普通字符串
  2. 键值对被合并到现有对象中
  3. JavaScript引擎将__proto__视为原型访问器
  4. evilProperty被赋值给原型对象而非目标对象

3.2 通过JSON输入的原型污染

恶意JSON示例:

{
    "__proto__": {
        "evilProperty": "payload"
    }
}

关键点:

  • JSON.parse()__proto__视为普通字符串键
  • 生成的对象具有__proto__自有属性
  • 后续合并操作可能导致原型污染

4. 污染接收点(Sink)

接收点是能够执行任意代码的JavaScript函数或DOM元素,包括但不限于:

  • eval()及相关函数
  • DOM操作函数
  • 动态脚本加载
  • 模板引擎
  • 反序列化函数

5. 可利用的工具(Gadget)

工具特征:

  1. 被应用程序以不安全方式使用
  2. 可通过原型污染被攻击者控制
  3. 不是对象的自有属性

典型示例:

let transport_url = config.transport_url || defaults.transport_url;
let script = document.createElement('script');
script.src = transport_url;

攻击方式:

  1. 污染Object.prototype添加transport_url属性
  2. 库代码使用被污染的默认值
  3. 加载恶意脚本

6. 防御措施

6.1 输入验证与过滤

  • 禁止合并包含__proto__constructorprototype键的对象
  • 使用对象白名单策略

6.2 安全合并函数

实现安全的深度合并函数,例如:

function safeMerge(target, source) {
    for (const key in source) {
        if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
            continue;
        }
        if (typeof source[key] === 'object' && source[key] !== null) {
            if (!target[key]) Object.assign(target, {[key]: {}});
            safeMerge(target[key], source[key]);
        } else {
            target[key] = source[key];
        }
    }
    return target;
}

6.3 使用Object.create(null)

创建无原型的对象:

const safeObject = Object.create(null);

6.4 冻结原型

防止原型被修改:

Object.freeze(Object.prototype);

6.5 使用Map替代普通对象

对于键值存储,使用Map更安全:

const safeMap = new Map();

7. 检测方法

7.1 手动测试

尝试注入以下payload并观察行为:

{"__proto__":{"testProperty":"testValue"}}

7.2 自动化工具

  • 使用专门的扫描工具如PPFuzz
  • 结合DAST工具进行检测

8. 实际案例分析

8.1 Lodash漏洞(CVE-2018-3721)

  • 影响版本:< 4.17.5
  • 问题函数:_.defaultsDeep
  • 修复方式:添加原型键检查

8.2 jQuery漏洞

  • 影响版本:< 3.4.0
  • 问题函数:$.extend
  • 修复方式:深度复制时跳过__proto__

9. 总结

原型污染是一种危险的JavaScript漏洞,其危害程度取决于:

  1. 应用程序中存在的可污染属性
  2. 这些属性被如何使用
  3. 是否有合适的接收点

防御的关键在于:

  • 严格控制对象合并操作
  • 避免信任原型链上的属性
  • 实施深度防御策略
JavaScript原型污染漏洞详解 1. 什么是原型污染 原型污染(Prototype Pollution)是一种JavaScript安全漏洞,它允许攻击者向全局对象原型中添加任意属性,这些属性随后可能会被用户定义的对象继承。 关键特性: 通常不能作为独立漏洞被利用,但可以与其他漏洞串联 在客户端可能导致DOM型XSS攻击 在服务器端可能引发远程代码执行(RCE) 主要影响内置的全局Object.prototype 2. 漏洞产生原理 原型污染漏洞通常发生在JavaScript函数将包含用户可控属性的对象递归合并到现有对象中时,而没有事先对键进行清理。 攻击流程: 攻击者注入一个键名为 __proto__ 的属性 合并操作将嵌套属性分配到对象的原型上而非目标对象本身 所有继承该原型的对象都会获得这些恶意属性 关键要素: 污染源 :能够注入任意属性的输入点 污染接收点(Sink) :能导致代码执行的函数或DOM元素 可利用的工具(Gadget) :被传递到接收点且未经适当过滤的属性 3. 污染源分析 3.1 通过URL的原型污染 示例URL: 解析过程: URL解析器将 __proto__ 视为普通字符串 键值对被合并到现有对象中 JavaScript引擎将 __proto__ 视为原型访问器 evilProperty 被赋值给原型对象而非目标对象 3.2 通过JSON输入的原型污染 恶意JSON示例: 关键点: JSON.parse() 将 __proto__ 视为普通字符串键 生成的对象具有 __proto__ 自有属性 后续合并操作可能导致原型污染 4. 污染接收点(Sink) 接收点是能够执行任意代码的JavaScript函数或DOM元素,包括但不限于: eval() 及相关函数 DOM操作函数 动态脚本加载 模板引擎 反序列化函数 5. 可利用的工具(Gadget) 工具特征: 被应用程序以不安全方式使用 可通过原型污染被攻击者控制 不是对象的自有属性 典型示例: 攻击方式: 污染 Object.prototype 添加 transport_url 属性 库代码使用被污染的默认值 加载恶意脚本 6. 防御措施 6.1 输入验证与过滤 禁止合并包含 __proto__ 、 constructor 或 prototype 键的对象 使用对象白名单策略 6.2 安全合并函数 实现安全的深度合并函数,例如: 6.3 使用Object.create(null) 创建无原型的对象: 6.4 冻结原型 防止原型被修改: 6.5 使用Map替代普通对象 对于键值存储,使用Map更安全: 7. 检测方法 7.1 手动测试 尝试注入以下payload并观察行为: 7.2 自动化工具 使用专门的扫描工具如PPFuzz 结合DAST工具进行检测 8. 实际案例分析 8.1 Lodash漏洞(CVE-2018-3721) 影响版本: < 4.17.5 问题函数: _.defaultsDeep 修复方式:添加原型键检查 8.2 jQuery漏洞 影响版本: < 3.4.0 问题函数: $.extend 修复方式:深度复制时跳过 __proto__ 9. 总结 原型污染是一种危险的JavaScript漏洞,其危害程度取决于: 应用程序中存在的可污染属性 这些属性被如何使用 是否有合适的接收点 防御的关键在于: 严格控制对象合并操作 避免信任原型链上的属性 实施深度防御策略