From 577f5a0641a438aa285ebf2c4d595309fb7aaf1e Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Fri, 26 Mar 2021 20:05:36 +0100 Subject: [PATCH] Added more commands. --- .../UserImages/Cari Drummelle.jpg | Bin 52061 -> 52061 bytes .../UserImages/Diane Tibbot.jpg | Bin 47199 -> 47199 bytes .../UserImages/Gareth Chan.jpg | Bin 23947 -> 23947 bytes .../Properties/AssemblyInfo.cs | Bin 1526 -> 1562 bytes red-teaming/C3-Client/README.md | 30 +- red-teaming/C3-Client/c3-client.py | 640 +++++++++++++++++- 6 files changed, 639 insertions(+), 31 deletions(-) diff --git a/others/Contoso-AD-Structure/UserImages/Cari Drummelle.jpg b/others/Contoso-AD-Structure/UserImages/Cari Drummelle.jpg index 3eeb04e7d1cf47947a83764edbd4cdf988940a5a..49e8ce97b1460ba4911c93bef6e05f8df9367dd5 100644 GIT binary patch delta 11197 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 b2h;#NRaWYLr}@zq?)B*($Z*~IeyIQ1Eu_=L diff --git a/others/Contoso-AD-Structure/UserImages/Diane Tibbot.jpg b/others/Contoso-AD-Structure/UserImages/Diane Tibbot.jpg index bee8fe0c442443295cd7ec681f166eace82bcd63..a4b40ed8869e0f9384cc4ba2cc87267ab41c61ff 100644 GIT binary patch delta 2168 ycmccrf$9DSrUe`783q=N+CLip0~rFN>2DxIpMhaC{71uoAVXlJr@wqU2R;BqE)7xu delta 2168 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 WfB^#r3>YwAz<>b*1`HT5a5perivR%t literal 23947 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

xRI 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 py c3-client.py --help +PS> py .\c3-client.py --help :: F-Secure's C3 Client - a lightweight automated companion with C3 voyages Mariusz B. / mgeeky, @@ -22,12 +22,14 @@ Usage: ./c3-client.py [options] [...] positional arguments: host C3 Web API host:port - {alarm,list,get,ping,channel} + {alarm,list,get,ping,connector,close,channel} command help alarm Alarm options list List options get Get options ping Ping Relays + connector Connector options + close Close command. channel Send Channel-specific command optional arguments: @@ -36,6 +38,7 @@ optional arguments: -d, --debug Display debug output. -f {json,text}, --format {json,text} Output format. Can be JSON or text (default). + -n, --dry-run Do not send any HTTP POST request that could introduce changes in C3 network. -A user:pass, --httpauth user:pass HTTP Basic Authentication (user:pass) ``` @@ -78,16 +81,32 @@ Currently, following commands are supported: - `alarm` - `relay` - trigger an alarm whenever a new Relay checks-in on a gateway +- `connector` + - `turnon` + - `teamserver` - allows to establish connection with a Teamserver + - `turnoff` - closes connection with Connector specified by connector_id + +- `close` + - `network` - sends `ClearNetwork` command to specified Gateway + - `channel` - closes selected channel + - `relay` - closes selected Relay(s) and all its bound peripherals, channels and Gateway-Return Channel + +- `download` + - `gateway` - downloads gateway executable + - `ping` - ping selected Relays - `channel` - channel-specific commands - `all` - `clear` - Clear message queue of every supported channel at once - `mattermost` + - `create`- Creates a Mattermost Negotiation channel - `clear` - Clear Mattermost's channel messages to improve bandwidth - `ldap` + - `create` - Creates a LDAP Negotiation Channel - `clear` - Clear LDAP attribute to improve bandwidth - `mssql` + - `create` - Creates a MSSQL Negotiation Channel - `clear` - Clear DB Table entries to improve bandwidth - `uncsharefile` - `clear` - Remove all message files to improve bandwidth @@ -224,3 +243,10 @@ PS D:\> py c3-client.py http://192.168.0.200:52935 alarm relay -g gate4 --execut ``` + +### Author + +``` +Mariusz B. / mgeeky, '21 + +``` \ No newline at end of file diff --git a/red-teaming/C3-Client/c3-client.py b/red-teaming/C3-Client/c3-client.py index 41255a0..ba67380 100644 --- a/red-teaming/C3-Client/c3-client.py +++ b/red-teaming/C3-Client/c3-client.py @@ -2,6 +2,7 @@ import os import sys +import io import re import time import json @@ -10,6 +11,7 @@ import subprocess import argparse import random import string +import zipfile from datetime import datetime @@ -17,6 +19,7 @@ config = { 'verbose' : False, 'debug' : False, 'host' : '', + 'dry_run' : False, 'command' : '', 'format' : 'text', 'httpauth' : '', @@ -88,7 +91,7 @@ class Logger: def printJson(data): print(json.dumps(data, sort_keys=True, indent=4)) -def getRequest(url, rawResp = False): +def getRequest(url, rawResp = False, stream = False): auth = None if config['httpauth']: user, _pass = config['httpauth'].split(':') @@ -98,7 +101,13 @@ def getRequest(url, rawResp = False): fullurl = config["host"] + url Logger.info(f'GET Request: {fullurl}') - resp = requests.get(fullurl, headers=headers, auth=auth) + try: + resp = requests.get(fullurl, headers=headers, auth=auth, stream = stream, timeout = 5) + except requests.exceptions.ConnectTimeout as e: + Logger.fatal(f'Connection with {config["host"]} timed-out.') + except Exception as e: + Logger.fatal(f'GET request failed ({url}): {e}') + if rawResp: return resp @@ -121,6 +130,18 @@ def postRequest(url, data=None, contentType = 'application/json', rawResp = Fals resp = None + if config['dry_run']: + print(f'[?] Dry-run mode: Skipping post request ({url})') + if rawResp: + class MockResponse(): + def __init__(self, status_code, text): + self.status_code = status_code + self.text = text + + return MockResponse(201, '') + else: + return '' + if contentType.endswith('/json'): resp = requests.post(fullurl, json=data, headers=headers, auth=auth) else: @@ -163,12 +184,11 @@ def printFullGateway(gatewayId): for d in c['propertiesText']['arguments']: if d['type'] == 'ip': addr = d['value'] - break elif d['type'] == 'uint16': port = d['value'] - break - print(f'{indent} Host: {addr}:{port}\n') + print(f'{indent} Connector ID: {c["iid"]}') + print(f'{indent} Host: {addr}:{port}\n') num = 0 print(f'{indent}Channels:') @@ -465,6 +485,42 @@ def collectRelays(args): return relays +def processCapability(gateway): + caps = getRequest(f'/api/gateway/{gateway["agentId"]}/capability') + + commandIds = {} + channels = {} + peripherals = {} + + for gatewayVal in caps['gateway']: + for commandVal in gatewayVal['commands']: + commandIds[commandVal['name'].lower()] = commandVal['id'] + + Logger.dbg(f'Gateway capability: commands: {commandVal["name"]} = {commandVal["id"]}') + + for channel in caps['channels']: + channels[channel['name']] = channel['type'] + + for peri in caps['peripherals']: + peripherals[peri['name']] = peri['type'] + + Logger.dbg('Gateway supports following channels: ' + str(', '.join(channels.keys()))) + Logger.dbg('Gateway supports following peripherals: ' + str(', '.join(peripherals.keys()))) + + capability = { + 'raw' : caps, + 'commandIds' : commandIds, + 'channels' : channels, + 'peripherals' : peripherals, + } + + return capability + +def getCommandIdMapping(gateway, command): + capability = processCapability(gateway) + + return capability['commandIds'][command.lower()] + def onPing(args): if args.keep_pinging > 0: while True: @@ -506,20 +562,17 @@ def _onPing(args): else: print(f'[+] Pinged {pinged} active relays.\n') -def getLastGatewayCommandID(gateway, secondOrder = True): +def getLastGatewayCommandID(): lastId = 0 - commands = getRequest(f'/api/gateway/{gateway["agentId"]}/command') - for comm in commands: - if secondOrder: - if 'data' in comm.keys(): - if 'id' in comm['data'].keys(): - if comm['data']['id'] > lastId: - lastId = comm['data']['id'] - else: + gateways = getRequest(f'/api/gateway') + + for gateway in gateways: + commands = getRequest(f'/api/gateway/{gateway["agentId"]}/command') + for comm in commands: if comm['id'] > lastId: lastId = comm['id'] - return lastId + return lastId + random.randint(5, 25) def onAllChannelsClear(args): channels = { @@ -873,6 +926,8 @@ def onAlarmRelay(args): try: while True: + time.sleep(2) + currRelays = collectRelays(args) currRelayIds = set() currLastTimestamp = 0 @@ -955,6 +1010,174 @@ def getValueOrRandom(val, N = 6): return val +def closeRelay(gateway, relay): + gateway = getRequest(f'/api/gateway/{gateway["agentId"]}') + relayMeta = getRequest(f'/api/gateway/{gateway["agentId"]}/relay/{relay["agentId"]}') + capability = processCapability(gateway) + + print('\n[.] step 1: Closing bound Peripherals') + for peri in relayMeta['peripherals']: + name = list(capability['peripherals'].keys())[list(capability['peripherals'].values()).index(peri['type'])] + Logger.info(f'Closing relay\'s peripheral {name} id:{peri["iid"]}') + closePeripheral(gateway, relay, name, peri['iid']) + + print('\n[.] step 2: Closing attached channels') + grcChannel = None + + for chan in relayMeta['channels']: + if 'isReturnChannel' in chan.keys(): + chan['url'] = f'/api/gateway/{gateway["agentId"]}/relay/{relay["agentId"]}/channel/{chan["iid"]}/command' + grcChannel = chan + continue + + chanName = list(capability['channels'].keys())[list(capability['channels'].values()).index(chan['type'])] + Logger.info(f'Closing relay\'s channel {chanName} id:{chan["iid"]}') + + chan['url'] = f'/api/gateway/{gateway["agentId"]}/relay/{relay["agentId"]}/channel/{chan["iid"]}/command' + closeChannel(chan, chanName) + + if not grcChannel: + Logger.fatal(f'Could not determine Gateway-Return Channel of the specified Relay {relay["name"]} / {relay["agentId"]}. \n Probably its unreachable or already closed.') + + closeChannel(grcChannel, list(capability['channels'].keys())[list(capability['channels'].values()).index(grcChannel['type'])]) + + print('\n[.] step 3: closing Relay itself') + closeRelay(gateway, relay) + + print('\n[.] step 4: closing a channel being a neighbour for Relay\'s GRC') + closed = False + for relayNode in gateway['relays'] + [gateway,]: + for route in relayNode['routes']: + if route['receivingInterface'] == grcChannel['iid']: + for chan in relayNode['channels']: + if chan['iid'] == route['outgoingInterface']: + if relayNode["agentId"] == gateway['agentId']: + chan['url'] = f'/api/gateway/{gateway["agentId"]}/channel/{chan["iid"]}/command' + else: + chan['url'] = f'/api/gateway/{gateway["agentId"]}/relay/{relayNode["agentId"]}/channel/{chan["iid"]}/command' + + closeChannel(chan, list(capability['channels'].keys())[list(capability['channels'].values()).index(chan['type'])]) + closed = True + break + if closed: break + if closed: break + if closed: break + + if closed: + print('[+] Non-Negotiation channel linked to Relay\'s Gateway-Return Channel was closed.') + +def onCloseRelay(args): + relays = collectRelays(args) + if len(relays) == 0: + Logger.fatal('Could not find agent (Gateway or Relay) which should be used to setup a channel.') + + for gateway, relay in relays: + print(f'[.] Closing relay {relay["name"]} (in gateway: {gateway["name"]}).') + closeRelay(gateway, relay) + +def closePeripheral(gateway, relay, peripheralName, peripheralId): + data = { + "name" : "PeripheralCommandGroup", + "data" : { + "id" : commandsMap['Close'], + "name" : peripheralName, + "command" : "Close", + "arguments" : [] + } + } + + Logger.info(f'Closing peripheral {peripheralName} (id: {peripheralId}). with following parameters:\n\n' + json.dumps(data, indent = 4)) + + ret = postRequest(f'/api/gateway/{gateway["agentId"]}/relay/{relay["agentId"]}/peripheral/{peripheralId}/command', data, rawResp = True) + if ret.status_code == 201: + print(f'[+] Peripheral {peripheralName} id:{peripheralId} was closed.') + else: + print(f'[-] Peripheral {peripheralName} id:{peripheralId} was not closed: ({ret.status_code}) {ret.text}') + +def closeChannel(channel, channelToClose): + chanId = '' + if 'channelId' in channel.keys(): chanId = channel['channelId'] + elif 'channel_id' in channel.keys(): chanId = channel['channel_id'] + elif 'iid' in channel.keys(): chanId = channel['iid'] + + data = { + "name" : "ChannelCommandGroup", + "data" : { + "id" : commandsMap['Close'], + "name" : channelToClose, + "command" : "Close", + "arguments" : [] + } + } + + Logger.info(f'Closing {channelToClose} channel (id: {chanId}). with following parameters:\n\n' + json.dumps(data, indent = 4)) + + ret = postRequest(channel["url"], data, rawResp = True) + if ret.status_code == 201: + print(f'[+] Channel {channelToClose} (id: {chanId}) was closed.') + else: + print(f'[-] Channel {channelToClose} (id: {chanId}) was not closed: ({ret.status_code}) {ret.text}') + +def closeNetwork(gateway): + data = { + "name":"GatewayCommandGroup", + "data":{ + "id":commandsMap['ClearNetwork'], + "name":"Command", + "command":"ClearNetwork", + "arguments": [ + { + "type":"boolean", + "name":"Are you sure?", + "value": True + } + ] + } + } + + Logger.info(f'Closing gateway {gateway["name"]} with following parameters:\n\n' + json.dumps(data, indent = 4)) + + 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.') + else: + print(f'[-] Gateway {gateway["name"]} (id: {gateway["agentId"]}) was not closed: ({ret.status_code}) {ret.text}') + +def onCloseNetwork(args): + gateways = getRequest(f'/api/gateway') + + for _gateway in gateways: + gateway = getRequest(f'/api/gateway/{_gateway["agentId"]}') + if gateway['name'].lower() == args.gateway_id.lower() or gateway['agentId'] == args.gateway_id.lower(): + closeNetwork(gateway) + +def onCloseChannel(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.') + + channelToClose = '' + + capability = processCapability(gateway) + for chan in capability['channels'].keys(): + for a in sys.argv: + if a.lower() == chan.lower(): + channelToClose = a + break + + if len(channelToClose) > 0: break + + if len(channelToClose) == 0: + Logger.fatal('Couldnt identify which channel is to be closed. Specify your channel name in script parameters') + + channels = collectChannelsToSendCommand(args, channelToClose) + + if len(channels) == 0: + Logger.fatal("Could not find channel to be close. Adjust your agent ID/Name setting and try again.") + + for channel in channels: + closeChannel(channel, channelToClose) + def onMattermostCreate(args): server_url = args.server_url if server_url.endswith('/'): server_url = server_url[:-1] @@ -971,8 +1194,8 @@ def onMattermostCreate(args): else: print(f'[.] Will setup a Mattermost channel on a Gateway named {gateway["name"]} ({gateway["agentId"]})') - secondCommandId = getLastGatewayCommandID(gateway) + 1 - commandId = getLastGatewayCommandID(gateway, False) + 1 + secondCommandId = getCommandIdMapping(gateway, 'AddNegotiationChannelMattermost') + commandId = getLastGatewayCommandID() Logger.info(f'Issuing a command with ID = {commandId}') data = { @@ -1025,8 +1248,281 @@ def onMattermostCreate(args): if ret.status_code == 201: print('[+] Channel was created.') else: - print(f'[-] Channel was not created: {ret.text}') + print(f'[-] Channel was not created: ({ret.status_code}) {ret.text}') +def onLDAPCreate(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 LDAP channel on a Relay named {relay["name"]} ({relay["agentId"]})') + else: + print(f'[.] Will setup a LDAP channel on a Gateway named {gateway["name"]} ({gateway["agentId"]})') + + secondCommandId = getCommandIdMapping(gateway, 'AddNegotiationChannelLDAP') + commandId = getLastGatewayCommandID() + Logger.info(f'Issuing a command with ID = {commandId}') + + data = { + "name" : "GatewayCommandGroup", + "data" : { + "arguments" : [ + { + "type" : "string", + "name" : "Negotiation Identifier", + "value" : getValueOrRandom(args.negotiation_id), + }, + { + "type" : "string", + "name" : "Data LDAP Attribute", + "value" : args.data_attribute, + }, + { + "type" : "string", + "name" : "Lock LDAP Attribute", + "value" : args.lock_attribute + }, + { + "type" : "uint32", + "name" : "Max Packet Size", + "value" : args.max_size, + }, + { + "type" : "string", + "name" : "Domain Controller", + "value" : args.domain_controller, + }, + { + "type" : "string", + "name" : "Username", + "value" : args.username, + }, + { + "type" : "string", + "name" : "Password", + "value" : args.password, + }, + { + "type" : "string", + "name" : "User DN", + "value" : args.user_dn, + } + ], + "command" : "AddNegotiationChannelLDAP", + "id" : secondCommandId, + "name" : "Command", + }, + 'id' : commandId, + 'name' : 'GatewayCommandGroup' + } + + Logger.info('Will create LDAP 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) + 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 MSSQL channel on a Relay named {relay["name"]} ({relay["agentId"]})') + else: + print(f'[.] Will setup a MSSQL channel on a Gateway named {gateway["name"]} ({gateway["agentId"]})') + + secondCommandId = getCommandIdMapping(gateway, 'AddNegotiationChannelMSSQL') + commandId = getLastGatewayCommandID() + Logger.info(f'Issuing a command with ID = {commandId}') + + data = { + "name" : "GatewayCommandGroup", + "data" : { + "arguments" : [ + { + "type" : "string", + "name" : "Negotiation Identifier", + "value" : getValueOrRandom(args.negotiation_id), + }, + { + "type" : "string", + "name" : "Server Name", + "value" : args.server_name, + }, + { + "type" : "string", + "name" : "Database Name", + "value" : args.database_name + }, + { + "type" : "string", + "name" : "Table Name", + "value" : args.table_name, + }, + { + "type" : "string", + "name" : "Username", + "value" : args.username, + }, + { + "type" : "string", + "name" : "Password", + "value" : args.password, + }, + { + "type" : "boolean", + "name" : "Use Integrated Security (SSPI) - use for domain joined accounts", + "value" : args.sspi, + } + ], + "command" : "AddNegotiationChannelMSSQL", + "id" : secondCommandId, + "name" : "Command", + }, + 'id' : commandId, + 'name' : 'GatewayCommandGroup' + } + + Logger.info('Will create MSSQL 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 onTurnOnTeamserver(args): + gateways = getRequest(f'/api/gateway') + gateway = None + + for _gateway in gateways: + g = getRequest(f'/api/gateway/{_gateway["agentId"]}') + if g['name'].lower() == args.gateway_id.lower() or g['agentId'] == args.gateway_id.lower(): + gateway = g + break + + if not gateway: + Logger.fatal(f'Could not find Gateway with specified gateway_id: {args.gateway_id}') + + commandId = getCommandIdMapping(gateway, "TurnOnConnectorTeamServer") + data = { + "name":"GatewayCommandGroup", + "data": { + "id":commandId, + "name":"Command", + "command":"TurnOnConnectorTeamServer", + "arguments": [ + { + "type":"ip", + "name":"Address", + "value":args.address + }, + { + "type":"uint16", + "name":"Port", + "value":args.port + } + ] + } + } + + Logger.info(f'Will Turn On connector TeamServer on gateway {gateway["name"]} with following parameters:\n\n' + json.dumps(data, indent = 4)) + + ret = postRequest(f'/api/gateway/{gateway["agentId"]}/command', data, rawResp = True) + + if ret.status_code == 201: + print('[+] Connection with Teamserver established.') + else: + print(f'[-] Could not establish connection with Teamserver: ({ret.status_code}) {ret.text}') + +def onTurnOffConnector(args): + gateways = getRequest(f'/api/gateway') + gateway = None + + for _gateway in gateways: + g = getRequest(f'/api/gateway/{_gateway["agentId"]}') + if g['name'].lower() == args.gateway_id.lower() or g['agentId'] == args.gateway_id.lower(): + gateway = g + break + + if not gateway: + Logger.fatal(f'Could not find Gateway with specified gateway_id: {args.gateway_id}') + + data = { + "name":"PeripheralCommandGroup", + "data": { + "id":commandsMap['Close'], + "name":"TeamServer", + "command":"TurnOff", + "arguments": [] + } + } + + Logger.info(f'Will Turn Off connector TeamServer on gateway {gateway["name"]} with following parameters:\n\n' + json.dumps(data, indent = 4)) + + ret = postRequest(f'/api/gateway/{gateway["agentId"]}/connector/{args.connector_id}/command', data, rawResp = True) + + if ret.status_code == 201: + print('[+] Closed connection with Connector.') + else: + print(f'[-] Could not close connection with connector: ({ret.status_code}) {ret.text}') + + +def onDownloadGateway(args): + gateway_name = getValueOrRandom(args.gateway_name) + _format = 'exe' + arch = 'x64' + + if args.format.lower().startswith('dll'): _format = 'dll' + if args.format.lower().endswith('86'): _format = 'x86' + + print(f'[.] Downloading gateway executable in format {args.format} with name: {gateway_name}') + url = f'/api/gateway/{_format}/{arch}?name={gateway_name}' + + output = getRequest(url, True, stream = True) + data = output.content + + if len(args.override_ip) > 0: + data2 = io.BytesIO() + with zipfile.ZipFile(io.BytesIO(data), 'r') as f: + with zipfile.ZipFile(data2, 'w') as g: + for i in f.infolist(): + buf = f.read(i.filename) + if i.filename.lower().endswith('.json'): + conf = json.loads(buf) + conf['API Bridge IP'] = args.override_ip + buf = json.dumps(conf, indent=4) + print(f'[.] Overidden stored in JSON configuration IP address to: {args.override_ip}') + g.writestr(i.filename, buf) + + data = data2.getvalue() + + if args.extract: + with zipfile.ZipFile(io.BytesIO(data), 'r') as f: + for i in f.infolist(): + outp = os.path.join(args.outfile, os.path.basename(i.filename)) + with open(outp, 'wb') as g: + g.write(f.read(i.filename)) + + print('[+] Gateway ZIP package downloaded & extracted.') + else: + with open(args.outfile, 'wb') as f: + f.write(data) + + print('[+] Gateway ZIP package downloaded.') def parseArgs(argv): global config @@ -1042,6 +1538,7 @@ def parseArgs(argv): opts.add_argument('-v', '--verbose', action='store_true', help='Display verbose output.') opts.add_argument('-d', '--debug', action='store_true', help='Display debug output.') opts.add_argument('-f', '--format', choices=['json', 'text'], default='text', help='Output format. Can be JSON or text (default).') + opts.add_argument('-n', '--dry-run', action='store_true', help='Do not send any HTTP POST request that could introduce changes in C3 network.') opts.add_argument('-A', '--httpauth', metavar = 'user:pass', help = 'HTTP Basic Authentication (user:pass)') subparsers = opts.add_subparsers(help = 'command help', required = True) @@ -1058,6 +1555,20 @@ def parseArgs(argv): 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) + # + # Download + # + download = subparsers.add_parser('download', help = 'Download options') + download_sub = download.add_subparsers(help = 'Download what?', required = True) + + download_gateway = download_sub.add_parser('gateway', help = 'Download gateway') + download_gateway.add_argument('-x', '--extract', action='store_true', help = 'Consider outfile as directory path. Then extract downloaded ZIP file with gateway into that directory.') + download_gateway.add_argument('-F', '--format', choices=['exe86', 'exe64', 'dll86', 'dll64'], default='exe64', help = 'Gateway executable format. . Formats: exe, dll. Archs: 86, 64. Default: exe64') + download_gateway.add_argument('-G', '--gateway-name', metavar='GATEWAY_NAME', default='random', help = 'Name of the Gateway. Default: random name') + download_gateway.add_argument('-O', '--override-ip', metavar='IP', help = 'Override gateway configuration IP stored in JSON. By default will use 0.0.0.0') + download_gateway.add_argument('outfile', metavar='outfile', help = 'Where to save output file.') + download_gateway.set_defaults(func = onDownloadGateway) + # # List # @@ -1097,6 +1608,52 @@ def parseArgs(argv): parser_ping.add_argument('-k', '--keep-pinging', metavar='delay', type=int, default=0, help = 'Keep pinging choosen Relays. Will send a ping every "delay" number of seconds. Default: sends ping only once.') parser_ping.set_defaults(func = onPing) + # + # Connector + # + parser_connector = subparsers.add_parser('connector', help = 'Connector options') + parser_connector.add_argument('gateway_id', metavar = 'gateway_id', help = 'Gateway which should be used to manage its connectors.') + parser_connector_sub = parser_connector.add_subparsers(help = 'What to do about that Connector?', required = True) + + ## turnon + connector_turnon = parser_connector_sub.add_parser('turnon', help = 'Turn on connector (connects to a Teamserver, Covenant, etc).') + connector_turnon_sub = connector_turnon.add_subparsers(help = 'What kind of connector?', required = True) + + ### Teamserver + turnon_connector_teamserver = connector_turnon_sub.add_parser('teamserver', help = 'Teamserver connector specific options.') + turnon_connector_teamserver.add_argument('address', metavar = 'address', help = 'Teamserver externalC2 address') + turnon_connector_teamserver.add_argument('port', metavar = 'port', help = 'Teamserver externalC2 port') + turnon_connector_teamserver.set_defaults(func = onTurnOnTeamserver) + + ## turnoff + connector_turnoff = parser_connector_sub.add_parser('turnoff', help = 'Turn off connector (connects to a Teamserver, Covenant, etc).') + connector_turnoff.add_argument('connector_id', metavar = 'connector_id', help = 'Connector\'s ID that should be closed') + connector_turnoff.set_defaults(func = onTurnOffConnector) + + # + # Close + # + parser_close = subparsers.add_parser('close', help = 'Close command.') + parser_close_sub = parser_close.add_subparsers(help = 'Close what?', required = True) + + ## Network + close_channel = parser_close_sub.add_parser('network', help = 'Close Network / ClearNetwork.') + close_channel.add_argument('gateway_id', metavar = 'gateway_id', help = 'Gateway which network is to be closed. Can be ID or Name.') + close_channel.set_defaults(func = onCloseNetwork) + + ## Channel + close_channel = parser_close_sub.add_parser('channel', help = 'Close a channel.') + close_channel.add_argument('agent_id', metavar = 'agent_id', help = 'Gateway or Relay that will be used to find a channel to close. Can be ID or Name.') + close_channel.add_argument('-c', '--channel-id', help = 'Specifies ID of the channel to commander. If not given - will issue specified command to all channels in a Gateway/Relay.') + close_channel.set_defaults(func = onCloseChannel) + + ## Relay + close_channel = parser_close_sub.add_parser('relay', help = 'Close a Relay.') + close_channel.add_argument('relay_id', metavar = 'relay_id', help = 'Relay to be closed. Can be ID or Name.') + close_channel.add_argument('-g', '--gateway-id', metavar='gateway_id', help = 'ID (or Name) of the Gateway runs specified Relay. If not given, will return all relays matching criteria from all gateways.') + close_channel.set_defaults(func = onCloseRelay) + + # # Channel # @@ -1114,22 +1671,22 @@ def parseArgs(argv): ### clear all_channels_clear = all_channels_parser.add_parser('clear', help = 'Clear every channel\'s message queue.') all_channels_clear.set_defaults(func = onAllChannelsClear) - + ## Mattermost mattermost = parser_channel_sub.add_parser('mattermost', help = 'Mattermost channel specific commands.') mattermost_parser = mattermost.add_subparsers(help = 'Command to send', required = True) ### Create - #mattermost_create = mattermost_parser.add_parser('create', help = 'Setup a Mattermost channel.') - #mattermost_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.') - #mattermost_create.add_argument('server_url', metavar = 'server_url', help = 'Mattermost Server URL, example: http://192.168.0.100:8888') - #mattermost_create.add_argument('team_name', metavar = 'team_name', help = 'Mattermost Team name where to create channels.') - #mattermost_create.add_argument('access_token', metavar = 'access_token', help = 'Personal Access Token value.') - #mattermost_create.add_argument('--negotiation-id', metavar = 'ID', default='random', help = 'Negotiation Identifier. Will be picked at random if left empty.') - #mattermost_create.add_argument('--channel-name', metavar = 'CHANNEL', default='random', help = 'Channel name to create. Will be picked at random if left empty.') - #mattermost_create.add_argument('--user-agent', metavar = 'USERAGENT', default='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36', - # help = 'User-Agent string to use in HTTP requests.') - #mattermost_create.set_defaults(func = onMattermostCreate) + mattermost_create = mattermost_parser.add_parser('create', help = 'Setup a Mattermost Negotiation channel.') + mattermost_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.') + mattermost_create.add_argument('server_url', metavar = 'server_url', help = 'Mattermost Server URL, example: http://192.168.0.100:8888') + mattermost_create.add_argument('team_name', metavar = 'team_name', help = 'Mattermost Team name where to create channels.') + mattermost_create.add_argument('access_token', metavar = 'access_token', help = 'Personal Access Token value.') + mattermost_create.add_argument('--negotiation-id', metavar = 'ID', default='random', help = 'Negotiation Identifier. Will be picked at random if left empty.') + mattermost_create.add_argument('--channel-name', metavar = 'CHANNEL', default='random', help = 'Channel name to create. Will be picked at random if left empty.') + mattermost_create.add_argument('--user-agent', metavar = 'USERAGENT', default='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36', + help = 'User-Agent string to use in HTTP requests.') + mattermost_create.set_defaults(func = onMattermostCreate) ### Purge mattermost_purge = mattermost_parser.add_parser('clear', help = 'Purge all dangling posts/messages from Mattermost channel.') @@ -1143,6 +1700,19 @@ def parseArgs(argv): ldap_clear = ldap_parser.add_parser('clear', help = 'Clear LDAP attribute associated with that channel.') ldap_clear.set_defaults(func = onLDAPClear) + ### Create + ldap_create = ldap_parser.add_parser('create', help = 'Setup a LDAP Negotiation channel.') + ldap_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.') + ldap_create.add_argument('--data-attribute', metavar = 'data_attribute', default = 'mSMQSignCertificates', help = 'Data LDAP Attribute. Default: mSMQSignCertificates') + ldap_create.add_argument('--lock-attribute', metavar = 'lock_attribute', default = 'primaryInternationalISDNNumber', help = 'Lock LDAP Attribute. Default: primaryInternationalISDNNumber') + ldap_create.add_argument('--max-size', metavar = 'max_size', default = 1047552, type = int, help = 'Max Packet Size. Default: 1047552') + ldap_create.add_argument('domain_controller', metavar = 'domain_controller', help = 'Domain Controller.') + ldap_create.add_argument('username', metavar = 'username', help = 'LDAP username.') + ldap_create.add_argument('password', metavar = 'password', help = 'LDAP password.') + ldap_create.add_argument('user_dn', metavar = 'user_dn', help = 'User Distinguished Name, example: CN=Jeff Smith,CN=users,DC=fabrikam,DC=com') + ldap_create.add_argument('--negotiation-id', metavar = 'ID', default='random', help = 'Negotiation Identifier. Will be picked at random if left empty.') + ldap_create.set_defaults(func = onLDAPCreate) + ## MSSQL mssql = parser_channel_sub.add_parser('mssql', help = 'MSSQL channel specific commands.') mssql_parser = mssql.add_subparsers(help = 'Command to send', required = True) @@ -1151,6 +1721,18 @@ def parseArgs(argv): mssql_clear = mssql_parser.add_parser('clear', help = 'Clear channel\'s DB Table.') mssql_clear.set_defaults(func = onMSSQLClearTable) + ### Create + mssql_create = mssql_parser.add_parser('create', help = 'Setup a MSSQL Negotiation channel.') + mssql_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.') + mssql_create.add_argument('server_name', metavar = 'server_name', help = 'MSSQL Server name') + mssql_create.add_argument('database_name', metavar = 'database_name', help = 'Database Name.') + mssql_create.add_argument('table_name', metavar = 'table_name', help = 'Table Name.') + mssql_create.add_argument('username', metavar = 'username', help = 'Database username.') + mssql_create.add_argument('password', metavar = 'password', help = 'Database password.') + mssql_create.add_argument('sspi', metavar = 'sspi', type=bool, help = 'Use Integrated Security (SSPI) - use for domain joined accounts. Default: false.') + mssql_create.add_argument('--negotiation-id', metavar = 'ID', default='random', help = 'Negotiation Identifier. Will be picked at random if left empty.') + mssql_create.set_defaults(func = onMSSQLCreate) + ## UncShareFile unc = parser_channel_sub.add_parser('uncsharefile', help = 'UncShareFile channel specific commands.') unc_parser = unc.add_subparsers(help = 'Command to send', required = True)