Scanning MongoDB

ちょっとしたことでネットワークサービスのスキャンはうまくいかなくなります。MongoDB を例に解説します。

はじめに

サイバーディフェンス研究所 アドベントカレンダー 2022 の 21 日目(15 記事目)です。

短縮 URL 狩り 記事に続き、社内で共有していた雑メモを公開してみたいと思います。
Nmap とネットワークスキャンのお勉強に役立つかもしれません。
ちょうど1年前の2021年12月頃に書いたもので、2022年現在の最新版だとどうなるのか追加検証し直しました。

いつものようにペネトレーションテストをしていたある日、Nmap が MongoDB サーバーをうまくスキャンできていないことに気づきました。

MongoDB サーバーは HTTP でアクセスすると「こちら MongoDB。アクセスする方法を間違えてますよ。」と親切に教えてくれます。

Nmap は HTTP レスポンスを返してくれるサービスを検知するシグネチャをたくさん持っていますし、見つけるのはあまり難しくなさそうに思えるのですが、どうしたことでしょう?

新進気鋭の Nuclei はうまく検出できています。

条件を変えた MongoDB サーバーを Docker でまとめて起動して、Nmap と Nuclei でスキャンして考察してみることにしました。

環境

Docker Compose で環境を作成しました。
手元でも試してみたい方向けの説明になりますので、読み飛ばして構いません。

次のファイルをダウンロードして適当なディレクトリに置いてください。

次の条件(設定)で MongoDB サーバーを起動します。

  • 認証有無
    • docker-compose.yml の MONGO_INITDB_ROOT_USERNAME/MONGO_INITDB_ROOT_PASSWORD 指定で認証あり
  • TLS 利用の有無
    • docker-compose.yml の --tlsMode requireTLS --tlsCertificateKeyFile /mongodb.pem で TLS 有効
  • 非標準ポート利用の有無
    • MongoDB は標準で 27017/tcp を使用(Default MongoDB Port
    • docker-compose.yml の --port 27011 で非標準ポート番号の 27011/tcp で起動
    • nmap-service データベースによって 27017-27019,28017/tcp ポート番号から mongod と判別されるため、それ以外のポートを利用するのがよい

docker compose up または docker-compose up を実行すると、IP アドレス 198.18.1.10〜17 の 8 つの Docker コンテナが起動します。
環境変数 TAG で MongoDB サーバーのバージョンをまとめて切り替えるようにしています。
1年前はバージョン 5.0.4 で検証していたので、次のように実行します。

TAG=5.0.4 docker compose up

次のように変更することで現在最新の MongoDB サーバーを対象にします。

TAG=6.0.3 docker compose up

もちろん .env ファイルを使う方法でも構いません。

スキャン方法

Nmap(version 7.93)を次のように実行してスキャンします。

  • nmap(9): nmap --privileged -n -p 27017,27011 --open 198.18.1.10-17 -sV --version-intensity=9
    • サービス/バージョンスキャンを強度 MAX(9)で実行
  • nmap(7): nmap --privileged -n -p 27017,27011 --open 198.18.1.10-17 -sV --version-intensity=7
    • サービス/バージョンスキャンを強度デフォルト(7)で実行
    • スキャン強度の明示を意図しているだけで --version-intensity=7 は省略可能

Nuclei(nuclei 2.8.3/nuclei-templates 9.3.2)を次のように実行してスキャンします。

  • nuclei: nuclei -disable-update-check -no-interactsh -tags tech -tags mongodb -target http://198.18.1.10:27017 -target https://198.18.1.11:27017 -target http://198.18.1.12:27011 -target https://198.18.1.13:27011 -target http://198.18.1.14:27017 -target https://198.18.1.15:27017 -target http://198.18.1.16:27011 -target https://198.18.1.17:27011

MongoDB 5.0.4 の結果

設定 nmap (9) nmap (7) nuclei
設定名 認証 TLS port service versions service versions detect unauth
mongo_normal - - - mongodb 5.0.4 mongodb 5.0.4 ok yes
mongo_tls - yes - ssl/mongodb 5.0.4 ssl/mongod? ok
mongo_port - - yes mongodb 5.0.4 unknown ok yes
mongo_tls_port - yes yes ssl/mongodb 5.0.4 ssl/unknown ok
mongo_auth yes - - mongod? mongod? ok
mongo_auth_tls yes yes - ssl/mongod? ssl/mongod? ok
mongo_auth_port yes - yes unknown unknown ok
mongo_auth_tls_port yes yes yes ssl/unknown ssl/unknown ok
  • 設定の yes は次を意味します
    • 認証: 認証あり
    • TLS: TLS 有効
    • port: 非標準 port 番号使用
  • nmap (9) および nmap(7) の意味は スキャン方法 も参照ください
    • service: 検出したサービス名。? が付いているのはポート番号からの推測。
    • versions: 検出したバージョン
  • nuclei
    • detect: MongoDB サービスを検出
    • unauth: MongoDB が認証無しであることを検出

Nmap の結果考察

まずはサマリ。

  • 認証がかかっていると、ポート番号からの推測しか出来ていない
  • 非標準ポートの場合、バージョンスキャン強度がデフォルトだとサービスを識別出来ない
  • TLS 有無は問題なし

以下、もう少し詳しく。

  • nmap-service 上は、27017-27019,28017/tcp が mongod と判別されるポート
  • nmap-service-probes から、MongoDB probe でプロービングするポートは 9001,27017,49153
  • それ以外のポートだと、rarity 8 となっていることからバージョンスキャン強度(--version-intensity)を 8 以上に指定していなければ MongoDB probe によるプロービングは行われない
  • Nmap にも HTTP レスポンスで mongodb を検出するシグネチャーは存在する
    • softmatch mongodb m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 84\r\n\r\nIt looks like you are trying to access MongoDB over HTTP on the native driver port\.\n| p/MongoDB/ v/2.5.1 or later/ cpe:/a:mongodb:mongodb/
    • MongoDB 5.0.4 が返すレスポンスは Content-Length: 85 で、最後に \n だけでなく \r もついている、というだけの理由で取りこぼしている
    • もう少し緩いシグネチャーに修正すると、いずれの設定の mongodb であっても version-intensity=1 で mongodb であることが検出可能になる

NSE(Nmap スクリプト)の mongodb-info を使用したスキャンも追加で実施。

  • 認証有かつ TLS なしの状態でしか動作しなかった
  • いつものようにバグっていて例外吐いている

※ 1年前、Nmap version 7.91 で検証した際の話です
※ Nmap version 7.93 では、TLS なしであれば正常に動作していました

Nuclei の結果考察

まずはサマリ。

  • いずれの設定の mongodb も検出可能であった
    • ただし、http/https プロトコルは正しく指定する必要がある
  • 認証の有無は TLS 有りだと判別出来ていなかった

以下、もう少し詳しく。 mongodb 関連のテンプレートは 3 つある。

  • network/mongodb-detect.yaml
    • mongo wire protocol でマスターノードか問い合わせるクエリを投げ、レスポンスに含まれる文字列で mongodb であるか判定
    • ペイロードは hex ベタ書きで TLS は非対応
    • 認証有りでも検出可能
    • フィンガープリント検出の方が優れているので、こちらはスキャン結果には未記載
  • network/mongodb-unauth.yaml
    • 認証機能が有効である場合は認証が要求される、getLog 管理コマンドを投げることで認証の有無を検出
    • ペイロードは hex ベタ書きで TLS は非対応
    • 非標準ポートでも OK
  • technologies/fingerprinthub-web-fingerprints.yaml
    • HTTP/HTTPS アクセス時のレスポンスに含まれる文字列で検知
    • It looks like you are trying to access MongoDB over HTTP on the native driver port.
    • スキャン結果に記載しているのはこちら

MongoDB 6.0.3 の結果

設定 nmap (9) nmap (7) nuclei
設定名 認証 TLS port service versions service versions detect unauth
mongo_normal - - - mongod? mongod? ok
mongo_tls - yes - ssl/mongod? ssl/mongod? ok
mongo_port - - yes unknown unknown ok
mongo_tls_port - yes yes ssl/unknown ssl/unknown ok
mongo_auth yes - - mongod? mongod? ok
mongo_auth_tls yes yes - ssl/mongod? ssl/mongod? ok
mongo_auth_port yes - yes unknown unknown ok
mongo_auth_tls_port yes yes yes ssl/unknown ssl/unknown ok
  • 設定の yes は次を意味します
    • 認証: 認証あり
    • TLS: TLS 有効
    • port: 非標準 port 番号使用
  • nmap (9) および nmap(7) の意味は スキャン方法 も参照ください
    • service: 検出したサービス名。? が付いているのはポート番号からの推測。
    • versions: 検出したバージョン
  • nuclei
    • detect: MongoDB サービスを検出
    • unauth: MongoDB が認証無しであることを検出

Nmap の結果考察

まさかの大敗です。ポート番号からの推測しかできませんでした。

MongoDB サーバーのレスポンスの内容に変化があり MongoDB probe によるプロービングでマッチしなくなっていました。
Nmap がサービス/バージョン検出に使っていた serverStatus コマンドが非サポートとなったことが原因のようです。

MongoDB 6.0.3 の MongoDB probe レスポンス

Nuclei の結果考察

mongodb サービスの検出は可能なままでしたが、認証の有無を検出できなくなっていました。
Nmap と同様、getLog コマンドが非サポートとなったが原因のようです。

サイバーディフェンスではどうしているか

内製のネットワークペネトレーションテストツールを使用してスキャンしています。
MongoDB サーバーに対しては PyMongo を使用したプロービングおよび認証試行が可能です。
ライブラリの力によるもので特に自慢話にはなりませんが、今回検証したどの条件の MongoDB サーバーを対象にしても、バージョンや認証有無などの情報が安定して取得できています。

Nmap のプロービングをカスタマイズしてみる

一体誰が読んでくれるのか、ニッチ過ぎる当記事をここまで読み進めて頂いたみなさまに、ささやかな贈り物をしたいと思います🎁

PyMongo が送信するペイロードをコピーしただけの、適当な nmap-service-probes ファイルです。
nmap-service-probes をダウンロードして適当なディレクトリに置いてください。
そして、そのディレクトリで nmap を実行します。
nmap の実行オプションに --versiondb=./nmap-service-probes をつけてください。

サービス/バージョンスキャンで TLS 検出と PyMongo ペイロードによる MongoDB 検出 のみ 実行する PoC ですので、そのまま普段使いにするは避けてください(他のスキャンをやらなくなります)。

実行例(クリックで表示)
$ nmap --privileged -n -p 27017,27011 --open 198.18.1.10-17 -sV --version-intensity=9 --versiondb=./nmap-service-probes
Starting Nmap 7.93 ( https://nmap.org ) at 2022-12-20 18:30 JST
Nmap scan report for 198.18.1.10
Host is up (0.000036s latency).
Not shown: 1 closed tcp port (reset)
PORT      STATE SERVICE VERSION
27017/tcp open  mongodb MongoDB 6.0.3
MAC Address: 02:42:C6:12:01:0A (Unknown)

Nmap scan report for 198.18.1.11
Host is up (0.00011s latency).
Not shown: 1 closed tcp port (reset)
PORT      STATE SERVICE     VERSION
27017/tcp open  ssl/mongodb MongoDB 6.0.3
MAC Address: 02:42:C6:12:01:0B (Unknown)

Nmap scan report for 198.18.1.12
Host is up (0.000042s latency).
Not shown: 1 closed tcp port (reset)
PORT      STATE SERVICE VERSION
27011/tcp open  mongodb MongoDB 6.0.3
MAC Address: 02:42:C6:12:01:0C (Unknown)

Nmap scan report for 198.18.1.13
Host is up (0.000039s latency).
Not shown: 1 closed tcp port (reset)
PORT      STATE SERVICE     VERSION
27011/tcp open  ssl/mongodb MongoDB 6.0.3
MAC Address: 02:42:C6:12:01:0D (Unknown)

Nmap scan report for 198.18.1.14
Host is up (0.000037s latency).
Not shown: 1 closed tcp port (reset)
PORT      STATE SERVICE VERSION
27017/tcp open  mongodb MongoDB 6.0.3
MAC Address: 02:42:C6:12:01:0E (Unknown)

Nmap scan report for 198.18.1.15
Host is up (0.000048s latency).
Not shown: 1 closed tcp port (reset)
PORT      STATE SERVICE     VERSION
27017/tcp open  ssl/mongodb MongoDB 6.0.3
MAC Address: 02:42:C6:12:01:0F (Unknown)

Nmap scan report for 198.18.1.16
Host is up (0.000041s latency).
Not shown: 1 closed tcp port (reset)
PORT      STATE SERVICE VERSION
27011/tcp open  mongodb MongoDB 6.0.3
MAC Address: 02:42:C6:12:01:10 (Unknown)

Nmap scan report for 198.18.1.17
Host is up (0.000037s latency).
Not shown: 1 closed tcp port (reset)
PORT      STATE SERVICE     VERSION
27011/tcp open  ssl/mongodb MongoDB 6.0.3
MAC Address: 02:42:C6:12:01:11 (Unknown)

Read from .: nmap-service-probes.
Read from /usr/bin/../share/nmap: nmap-mac-prefixes nmap-services.
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 8 IP addresses (8 hosts up) scanned in 5.79 seconds

おわりに

MongoDB サーバーのネットワークサービススキャンについて解説しました。

ちょっとしたことでスキャンがうまくいかなくなってしまうことを感じとって頂けたら幸いです。
Nmap を中心に解説しましたが、こうした問題は Nmap に限った話ではなくあらゆるスキャンツールで起こりうる話です。

私個人はスキャナーによる自動型の脆弱性診断に肯定的です。
しかし、エキスパートによる手動のネットワークペネトレーションテストや脆弱性診断もやりましょう。現実の攻撃者も突いてくるであろう、細やかな問題のキャッチが期待できます。という宣伝で〆させて頂きます。

P.S. ブログに書いてないでオープンソースに貢献しなさい!という声が聞こえる気がします。頑張ります。

© 2016 - 2022 DARK MATTER / Built with Hugo / Theme Stack designed by Jimmy