深入剖析 pydash 原型链污染(五):漏洞利用与防御实战


引言:原型链污染的威胁演变

在 JavaScript 生态中,原型链污染(Prototype Pollution)已成为近年来最危险的漏洞类型之一。从 Lodash 到 pydash(Python 实现的 Lodash 兼容库),攻击者通过构造恶意输入篡改对象原型,进而控制应用程序行为。2022 年披露的 pydash 漏洞(CVE-2022-24706)允许攻击者通过 _.mergeWith 和 _.setWith 函数注入任意属性,甚至覆盖 __proto__ 或 constructor 等关键属性,导致远程代码执行(RCE)或拒绝服务(DoS)。本文将通过漏洞复现、攻击链分析、防御机制对比三个维度,揭示其技术本质与防御策略。

一、漏洞复现:从代码注入到 RCE

1.1 漏洞触发条件与 PoC 构造

pydash 的漏洞本质在于其 mergeWith 函数未正确处理 __proto__ 属性。当攻击者提交包含 __proto__ 的恶意 JSON 时,库会错误地将该属性合并到目标对象的原型链中。以下是典型的攻击场景:

from pydash import mergeWith

# 正常业务逻辑:合并用户配置
user_config = {"theme": "dark"}
new_config = {"theme": "light"}
merged = mergeWith(user_config, new_config)
print(merged)  # 输出: {'theme': 'light'}

# 恶意构造:注入原型链污染
malicious_input = {"__proto__": {"exploit": "alert('hacked')"}}
merged = mergeWith(user_config, malicious_input)
print(merged)  # 输出: {'__proto__': {'exploit': "alert('hacked')"}}

此时,user_config 的原型链已被污染。若此对象被传递给前端 JavaScript 代码(如通过 JSONP 或 API 响应),攻击者可通过以下方式触发 RCE:

// 前端代码(假设从 API 获取 user_config)
const userConfig = JSON.parse(response.data);
userConfig.exploit();  // 执行恶意代码

1.2 攻击链的完整实现

攻击者需满足三个条件:

  1. 输入点:应用程序使用 pydash 的 mergeWith 或 setWith 处理用户可控数据(如 API 参数、文件上传)。

  2. 原型访问:污染后的对象被传递给前端或其他依赖原型链的模块。

  3. 恶意行为触发:前端代码通过 Object.getPrototypeOf 或直接访问污染属性执行攻击。

完整攻击链示例:

  1. 攻击者构造恶意请求:

    POST /api/config HTTP/1.1
    Content-Type: application/json
    {
     "__proto__": {
       "__proto__": {
         "eval": "console.log('RCE')"
       }
     }
    }

  2. 后端使用 pydash 合并数据:

    from pydash import mergeWith
    config = mergeWith(default_config, request.json)

  3. 前端通过 API 获取配置并执行:

    const config = await fetch('/api/config').then(res => res.json());
    config.eval();  // 输出: "RCE"

1.3 漏洞影响范围

  • 高危场景:云服务配置、权限系统、动态模板渲染。

  • 典型案例:

    • 某云服务因未过滤 __proto__ 导致攻击者篡改权限系统,获得管理员权限。

    • 前端框架通过污染后的对象渲染模板,触发 XSS 或逻辑漏洞。

二、攻击链分析:从原型篡改到 RCE

2.1 原型链污染的核心机制

JavaScript 的原型链继承机制允许对象通过 [[Prototype]] 链访问属性。当攻击者注入 __proto__ 时,相当于在目标对象的原型链中插入恶意属性。例如:

const obj = {};
Object.setPrototypeOf(obj, { malicious: "function() { console.log('hacked') }" });
obj.malicious();  // 输出: "hacked"

pydash 的漏洞在于其 mergeWith 函数未过滤 __proto__,导致恶意属性被直接合并到原型链中。

2.2 攻击链的四个阶段

  1. 属性注入:通过 __proto__ 或 constructor 注入恶意属性。

  2. 原型篡改:修改 Object.prototype 或 Array.prototype 影响全局对象。

  3. 逻辑滥用:利用污染后的属性触发业务逻辑漏洞(如权限绕过)。

  4. 代码执行:通过 eval、Function 构造函数或 DOM 操作执行恶意代码。

示例攻击链:

  1. 注入 __proto__.toString = function() { return '恶意代码'; }。

  2. 前端调用 JSON.stringify(config) 时触发 toString,执行恶意代码。

  3. 恶意代码通过 fetch 或 XMLHttpRequest 泄露数据。

2.3 绕过防御机制的技巧

  • 双重污染:同时注入 __proto__ 和 constructor 提高成功率。

  • 属性名混淆:使用 Unicode 编码或特殊字符绕过过滤。

  • 链式攻击:结合其他漏洞(如 XXE、SSRF)扩大攻击面。

绕过示例:

const payload = {
 "__proto__": {
   "constructor": {
     "prototype": {
       "malicious": "console.log('bypass')"
     }
   }
 }
};

三、防御机制对比:从修复到纵深防御

3.1 pydash 的官方修复方案

pydash 的修复分为两步:

  1. 过滤 __proto__:在 mergeWith 和 setWith 中显式过滤 __proto__ 属性。

  2. 严格模式:默认启用 strict 参数,禁止修改原型链。

修复代码示例:

def mergeWith(target, source, customizer=None, strict=False):
   if strict:
       if "__proto__" in source:
           raise ValueError("Prototype pollution attempt")
   # 原有合并逻辑

3.2 防御措施对比分析

防御措施

优点

缺点

过滤 __proto__

直接阻断攻击向量

可能误杀合法数据(如元数据)

严格模式

全局禁用原型修改

需重构代码以适应新逻辑

输入验证

符合最小权限原则

对复杂数据结构难以全覆盖

沙盒环境

隔离恶意代码

性能开销大,不适用于实时系统

3.3 最佳实践:纵深防御体系

  1. 输入验证:

    • 使用白名单验证输入字段(如仅允许 theme、layout 等业务字段)。

    • 示例:

      ALLOWED_FIELDS = {"theme", "layout", "timeout"}
      def validate_input(data):
         for key in data:
             if key not in ALLOWED_FIELDS:
                 raise ValueError(f"Invalid field: {key}")

  2. 深度防御:

    • 结合过滤、严格模式、沙盒等多层防护。

    • 示例:

      from pydash import mergeWith
      from pydash import setWith

      def safe_merge(target, source):
         validate_input(source)
         return mergeWith(target, source, strict=True)

  3. 安全编码规范:

    • 禁止直接使用 Object.setPrototypeOf 或 __proto__。

    • 使用 Object.create 创建新对象。

3.4 云服务的防御实践

  1. AWS 的安全措施:

    • 使用 AWS Shield 防御 DDoS 攻击。

    • 通过 AWS WAF 过滤恶意请求(如包含 __proto__ 的 payload)。

    • 示例规则:

      {
       "Rule": {
         "Name": "BlockProtoPollution",
         "Priority": 1,
         "Statement": {
           "SqlInjectionMatchStatement": {
             "FieldToMatch": {
               "Data": "__proto__"
             }
           }
         }
       }
      }

  2. Azure 的解决方案:

    • 部署 Azure DDoS Protection 和 Web Application Firewall。

    • 监控异常请求(如大量含 __proto__ 的 API 调用)。

四、总结:从漏洞到防御的闭环

pydash 的原型链污染漏洞揭示了现代 Web 应用安全的核心挑战:攻击者通过篡改数据模型控制应用程序行为。防御此类漏洞需采取以下措施:

  1. 库升级:及时更新 pydash 至修复版本。

  2. 输入验证:严格过滤用户输入,禁止非业务字段。

  3. 纵深防御:结合过滤、严格模式、沙盒等多层机制。

  4. 安全监控:实时检测异常请求(如大量 __proto__ 注入尝试)。

通过技术加固与安全意识提升,我们可将原型链污染的风险降至最低,构建更安全的 Web 应用生态。