From 40b99d5df905327bbe8d76eebc219bde4b846632 Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Fri, 26 Mar 2021 23:03:01 +0100 Subject: [PATCH] UncShareFile create --- red-teaming/C3-Client/README.md | 1 + red-teaming/C3-Client/c3-client.py | 156 ++++++++++++++++++++--------- 2 files changed, 107 insertions(+), 50 deletions(-) diff --git a/red-teaming/C3-Client/README.md b/red-teaming/C3-Client/README.md index cc0154e..d244276 100644 --- a/red-teaming/C3-Client/README.md +++ b/red-teaming/C3-Client/README.md @@ -109,6 +109,7 @@ Currently, following commands are supported: - `create` - Creates a MSSQL Negotiation Channel - `clear` - Clear DB Table entries to improve bandwidth - `uncsharefile` + - `create` - Creates UncShareFile Negotiation Channel - `clear` - Remove all message files to improve bandwidth - `dropbox` - `clear` - Remove All Files to improve bandwidth diff --git a/red-teaming/C3-Client/c3-client.py b/red-teaming/C3-Client/c3-client.py index 9f84e31..b86ef96 100644 --- a/red-teaming/C3-Client/c3-client.py +++ b/red-teaming/C3-Client/c3-client.py @@ -159,10 +159,13 @@ def postRequest(url, data=None, contentType = 'application/json', rawResp = Fals else: return '' - if contentType.endswith('/json'): - resp = requests.post(fullurl, json=data, headers=headers, auth=auth) - else: - resp = requests.post(fullurl, data=data, headers=headers, auth=auth) + for a in range(3): + if contentType.endswith('/json'): + resp = requests.post(fullurl, json=data, headers=headers, auth=auth) + else: + resp = requests.post(fullurl, data=data, headers=headers, auth=auth) + + if resp.status_code != 500: break if rawResp: return resp @@ -913,19 +916,6 @@ def shell(cmd, alternative = False, stdErrToStdout = False, surpressStderr = Fal outs, errs = proc.communicate() status = outs.decode(errors='ignore').strip() - - if len(errs) > 0 and not surpressStderr: - error = ''' -Running shell command ({}) failed: - ---------------------------------------------- -{} ---------------------------------------------- -'''.format(cmd, errs.decode(errors='ignore')) - - if stdErrToStdout: - return error - return status def onAlarmRelay(args): @@ -965,39 +955,45 @@ def onAlarmRelay(args): try: if args.execute != None and len(args.execute) > 0: - cmd = args.execute - cmd = cmd.replace("", newestRelay['hostInfo']['computerName']) - cmd = cmd.replace("", str(newestRelay['hostInfo']['isElevated'])) - cmd = cmd.replace("", newestRelay['hostInfo']['osVersion']) - cmd = cmd.replace("", newestRelay['hostInfo']['domain']) - cmd = cmd.replace("", newestRelay['hostInfo']['userName']) - cmd = cmd.replace("", str(newestRelay['hostInfo']['processId'])) - cmd = cmd.replace("", newestRelay['name']) - cmd = cmd.replace("", newestRelay['agentId']) - cmd = cmd.replace("", newestRelay['buildId']) - cmd = cmd.replace("", str(datetime.fromtimestamp(newestRelay['timestamp']))) - cmd = cmd.replace("", newestRelay['name']) + for command in args.execute: + cmd = command + cmd = cmd.replace("", newestRelay['hostInfo']['computerName']) + cmd = cmd.replace("", str(newestRelay['hostInfo']['isElevated'])) + cmd = cmd.replace("", newestRelay['hostInfo']['osVersion']) + cmd = cmd.replace("", newestRelay['hostInfo']['domain']) + cmd = cmd.replace("", newestRelay['hostInfo']['userName']) + cmd = cmd.replace("", str(newestRelay['hostInfo']['processId'])) + cmd = cmd.replace("", newestRelay['name']) + cmd = cmd.replace("", newestRelay['agentId']) + cmd = cmd.replace("", newestRelay['buildId']) + cmd = cmd.replace("", str(datetime.fromtimestamp(newestRelay['timestamp']))) + cmd = cmd.replace("", newestRelay['name']) - print(f'[.] Executing command: {cmd}') - shell(cmd) + print(f'[.] Executing command: {cmd}') + print(shell(cmd)) + + print('[.] Commands executed.') if args.webhook != None and len(args.webhook) > 0: - data = { - "", newestRelay['hostInfo']['computerName'], - "", newestRelay['hostInfo']['isElevated'], - "", newestRelay['hostInfo']['osVersion'], - "", newestRelay['hostInfo']['domain'], - "", newestRelay['hostInfo']['userName'], - "", newestRelay['hostInfo']['processId'], - "", newestRelay['name'], - "", newestRelay['agentId'], - "", newestRelay['buildId'], - "", datetime.fromtimestamp(newestRelay['timestamp']), - "", newestRelay['name'], - } + for webhook in args.webhook: + data = { + "", newestRelay['hostInfo']['computerName'], + "", newestRelay['hostInfo']['isElevated'], + "", newestRelay['hostInfo']['osVersion'], + "", newestRelay['hostInfo']['domain'], + "", newestRelay['hostInfo']['userName'], + "", newestRelay['hostInfo']['processId'], + "", newestRelay['name'], + "", newestRelay['agentId'], + "", newestRelay['buildId'], + "", datetime.fromtimestamp(newestRelay['timestamp']), + "", newestRelay['name'], + } - print(f'[.] Triggering a webhook: {args.webhook}') - requests.post(args.webhook, data = data, headers = headears) + print(f'[.] Triggering a webhook: {webhook}') + requests.post(webhook, data = data, headers = headears) + + print('[.] Webhooks triggered.') except Exception as e: print(f'[-] Exception occured during New-Relay alarm trigger: {e}') @@ -1019,6 +1015,7 @@ def findAgent(agentId): if r["agentId"].lower() == agentId.lower() or r["name"].lower() == agentId.lower(): return g, r + Logger.fatal('Could not find specified agent.') return None def getValueOrRandom(val, N = 6): @@ -1172,9 +1169,9 @@ def closeNetwork(gateway): ret = postRequest(f'/api/gateway/{gateway["agentId"]}/command', data, rawResp = True) if ret.status_code == 201: - print(f'[+] Gateway {gateway["name"]} (id: {gateway["agentId"]}) was closed.') + print(f'[+] Network on gateway {gateway["name"]} (id: {gateway["agentId"]}) was cleared.') else: - print(f'[-] Gateway {gateway["name"]} (id: {gateway["agentId"]}) was not closed: ({ret.status_code}) {ret.text}') + print(f'[-] Network on gateway {gateway["name"]} (id: {gateway["agentId"]}) was not cleared: ({ret.status_code}) {ret.text}') def onCloseNetwork(args): gateways = getRequest(f'/api/gateway') @@ -1361,6 +1358,58 @@ def onLDAPCreate(args): else: print(f'[-] Channel was not created: ({ret.status_code}) {ret.text}') +def onUncShareFileCreate(args): + gateway, relay = findAgent(args.agent_id) + if not relay and not gateway: + logger.fatal('Could not find agent (Gateway or Relay) which should be used to setup a channel.') + + url = f'/api/gateway/{gateway["agentId"]}/command' + + if relay != None: + url = f'/api/gateway/{gateway["agentId"]}/relay/{relay["agentId"]}/command' + print(f'[.] Will setup a UncShareFile channel on a Relay named {relay["name"]} ({relay["agentId"]})') + else: + print(f'[.] Will setup a UncShareFile channel on a Gateway named {gateway["name"]} ({gateway["agentId"]})') + + secondCommandId = getCommandIdMapping(gateway, 'AddNegotiationChannelUncShareFile') + commandId = getLastGatewayCommandID() + Logger.info(f'Issuing a command with ID = {commandId}') + + data = { + "data" : { + "arguments" : [ + { + "type" : "string", + "name" : "Negotiation Identifier", + "value" : getValueOrRandom(args.negotiation_id), + }, + { + "type" : "string", + "name" : "Filesystem path", + "value" : args.filesystem_path, + }, + { + "type" : "boolean", + "name" : "Clear", + "value" : args.clear, + } + ], + "command" : "AddNegotiationChannelUncShareFile", + "id" : secondCommandId, + "name" : "Command", + }, + 'id' : commandId, + 'name' : 'GatewayCommandGroup' + } + + Logger.info('Will create UncShareFile channel with following parameters:\n\n' + json.dumps(data, indent = 4)) + + ret = postRequest(url, data, rawResp = True) + + if ret.status_code == 201: + print('[+] Channel was created.') + else: + print(f'[-] Channel was not created: ({ret.status_code}) {ret.text}') def onMSSQLCreate(args): gateway, relay = findAgent(args.agent_id) @@ -1628,8 +1677,8 @@ def parseArgs(argv): alarm_sub = alarm.add_subparsers(help = 'Alarm on what?', required = True) alarm_relay = alarm_sub.add_parser('relay', help = 'Trigger an alarm whenever a new Relay checks-in.') - alarm_relay.add_argument('-e', '--execute', help = 'If new Relay checks in - execute this command. Use following placeholders in your command: , , , , , , , , , to customize executed command\'s parameters. Example: powershell -c "Add-Type -AssemblyName System.Speech; $synth = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer; $synth.Speak(\'New Relay just checked-in /@\')"') - alarm_relay.add_argument('-x', '--webhook', help = 'Trigger a Webhook (HTTP POST request) to this URL whenever a new Relay checks-in. The request will contain JSON message with all the fields available, mentioned in --execute option.') + alarm_relay.add_argument('-e', '--execute', action='append', help = 'If new Relay checks in - execute this command. Use following placeholders in your command: , , , , , , , , , to customize executed command\'s parameters. Example: powershell -c "Add-Type -AssemblyName System.Speech; $synth = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer; $synth.Speak(\'New Relay just checked-in /@\')"') + alarm_relay.add_argument('-x', '--webhook', action='append', help = 'Trigger a Webhook (HTTP POST request) to this URL whenever a new Relay checks-in. The request will contain JSON message with all the fields available, mentioned in --execute option.') alarm_relay.add_argument('-g', '--gateway-id', metavar='gateway_id', help = 'ID (or Name) of the Gateway which Relays should be returned. If not given, will result all relays from all gateways.') alarm_relay.set_defaults(func = onAlarmRelay) @@ -1834,6 +1883,13 @@ def parseArgs(argv): unc_clear = unc_parser.add_parser('clear', help = 'Clear all message files.') unc_clear.set_defaults(func = onUncShareFileClear) + unc_create = unc_parser.add_parser('create', help = 'Setup a Mattermost Negotiation channel.') + unc_create.add_argument('agent_id', metavar = 'agent_id', help = 'Gateway or Relay that will be used to setup a channel. Can be ID or Name.') + unc_create.add_argument('filesystem_path', metavar = 'filesystem_path', help = 'Filesystem path') + unc_create.add_argument('--clear', type=bool, metavar = 'clear', default = False, help = 'Clear previous messages') + unc_create.add_argument('--negotiation-id', metavar = 'ID', default='random', help = 'Negotiation Identifier. Will be picked at random if left empty.') + unc_create.set_defaults(func = onUncShareFileCreate) + ## Dropbox dropbox = parser_channel_sub.add_parser('dropbox', help = 'Dropbox channel specific commands.') dropbox_parser = dropbox.add_subparsers(help = 'Command to send', required = True)