diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 6aa77733c..804a5a2fc 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -24,7 +24,8 @@ class HostExploiter(object): 'started': '', 'finished': '', 'vulnerable_urls': [], - 'vulnerable_ports': []} + 'vulnerable_ports': [], + 'executed_cmds': []} self._exploit_attempts = [] self.host = host @@ -70,6 +71,14 @@ class HostExploiter(object): def add_vuln_port(self, port): self._exploit_info['vulnerable_ports'].append(port) + def add_executed_cmd(self, cmd): + """ + Appends command to exploiter's info. + :param cmd: String of executed command. e.g. 'echo Example' + """ + command = {'cmd': cmd} + self._exploit_info['executed_cmds'].append(command) + from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter from infection_monkey.exploit.wmiexec import WmiExploiter diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 43336420c..958bab7eb 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -49,6 +49,7 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() + self.add_executed_cmd(command) return True def exploit(self, url, command): diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 86555f546..e94be15eb 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -77,7 +77,7 @@ class MSSQLExploiter(HostExploiter): commands.extend(monkey_args) MSSQLExploiter.execute_command(cursor, commands) MSSQLExploiter.run_file(cursor, tmp_file_path) - + self.add_executed_cmd(commands[-1]) return True @staticmethod diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index a3e24e33a..0db63e86d 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -343,5 +343,5 @@ class RdpExploiter(HostExploiter): LOG.info("Executed monkey '%s' on remote victim %r", os.path.basename(src_path), self.host) - + self.add_executed_cmd(command) return True diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index 337e0ec03..d65733d03 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -144,6 +144,7 @@ class ShellShockExploiter(HostExploiter): if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") continue + self.add_executed_cmd(cmdline) return True return False diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index e8b933589..c7cf030c1 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -178,6 +178,7 @@ class SSHExploiter(HostExploiter): self._config.dropper_target_path_linux, self.host, cmdline) ssh.close() + self.add_executed_cmd(cmdline) return True except Exception as exc: diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index d60c336ca..498c09eea 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -138,6 +138,7 @@ class VSFTPDExploiter(HostExploiter): if backdoor_socket.send(run_monkey): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, run_monkey) + self.add_executed_cmd(run_monkey) return True else: return False diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 8e1717f74..97358fe53 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -408,6 +408,7 @@ class WebRCE(HostExploiter): # If exploiter returns True / False if type(resp) is bool: LOG.info("Execution attempt successfully finished") + self.add_executed_cmd(command) return resp # If exploiter returns command output, we can check for execution errors if 'is not recognized' in resp or 'command not found' in resp: @@ -420,6 +421,8 @@ class WebRCE(HostExploiter): LOG.error("Something went wrong when trying to execute remote monkey: %s" % e) return False LOG.info("Execution attempt finished") + + self.add_executed_cmd(command) return resp def get_monkey_upload_path(self, url_to_monkey): diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index feff1df70..9439d7414 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -114,7 +114,7 @@ class WmiExploiter(HostExploiter): result.RemRelease() wmi_connection.close() - + self.add_executed_cmd(cmdline) return success return False diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 43bdeb411..fac55a038 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,5 +1,5 @@ import logging -from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075 +from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059 from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo @@ -11,7 +11,9 @@ LOG = logging.getLogger(__name__) TECHNIQUES = {'T1210': T1210.T1210, 'T1197': T1197.T1197, 'T1110': T1110.T1110, - 'T1075': T1075.T1075} + 'T1075': T1075.T1075, + 'T1003': T1003.T1003, + 'T1059': T1059.T1059} REPORT_NAME = 'new_report' diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index 2a59103ad..a79b57a87 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -84,5 +84,19 @@ SCHEMA = { } } }, + "execution": { + "title": "Execution", + "type": "object", + "properties": { + "T1059": { + "title": "T1059 Command line interface", + "type": "bool", + "value": True, + "necessary": True, + "description": "Adversaries may use command-line interfaces to interact with systems " + "and execute other software during the course of an operation.", + } + } + }, } } diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py new file mode 100644 index 000000000..cd1a538cb --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -0,0 +1,27 @@ +from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.database import mongo + +__author__ = "VakarisZ" + + +class T1003(AttackTechnique): + + tech_id = "T1003" + unscanned_msg = "Monkey tried to obtain credentials from systems in the network but didn't find any or failed." + scanned_msg = "" + used_msg = "Monkey successfully obtained some credentials from systems on the network." + + query = {'telem_category': 'system_info_collection', '$and': [{'data.credentials': {'$exists': True}}, + # $gt: {} checks if field is not an empty object + {'data.credentials': {'$gt': {}}}]} + + @staticmethod + def get_report_data(): + data = {'title': T1003.technique_title()} + if mongo.db.telemetry.count_documents(T1003.query): + status = ScanStatus.USED + else: + status = ScanStatus.UNSCANNED + data.update(T1003.get_message_and_status(status)) + return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py new file mode 100644 index 000000000..488a8f547 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -0,0 +1,31 @@ +from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.database import mongo + +__author__ = "VakarisZ" + + +class T1059(AttackTechnique): + + tech_id = "T1059" + unscanned_msg = "Monkey didn't exploit any machines to run commands at." + scanned_msg = "" + used_msg = "Monkey successfully ran commands on exploited machines in the network." + + query = [{'$match': {'telem_category': 'exploit', + 'data.info.executed_cmds': {'$exists': True, '$ne': []}}}, + {'$project': {'_id': 0, + 'machine': '$data.machine', + 'info': '$data.info'}}, + {'$group': {'_id': '$machine', 'data': {'$push': '$$ROOT'}}}] + + @staticmethod + def get_report_data(): + cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) + data = {'title': T1059.technique_title(), 'cmds': cmd_data} + if cmd_data: + status = ScanStatus.USED + else: + status = ScanStatus.UNSCANNED + data.update(T1059.get_message_and_status(status)) + return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index 0f09fb0fe..60ae14c0b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -13,7 +13,7 @@ class T1110(AttackTechnique): used_msg = "Monkey successfully used brute force in the network." # Gets data about brute force attempts - query = [{'$match': {'telem_type': 'exploit', + query = [{'$match': {'telem_category': 'exploit', 'data.attempts': {'$not': {'$size': 0}}}}, {'$project': {'_id': 0, 'machine': '$data.machine', diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py index 9a968e998..b6bd316af 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -13,13 +13,15 @@ class T1197(AttackTechnique): @staticmethod def get_report_data(): data = T1197.get_tech_base_data() - bits_results = mongo.db.telemetry.aggregate([{'$match': {'telem_category': 'attack', 'data.technique': T1197.tech_id}}, - {'$group': {'_id': {'ip_addr': '$data.machine.ip_addr', 'usage': '$data.usage'}, - 'ip_addr': {'$first': '$data.machine.ip_addr'}, - 'domain_name': {'$first': '$data.machine.domain_name'}, - 'usage': {'$first': '$data.usage'}, - 'time': {'$first': '$timestamp'}} - }]) + bits_results = mongo.db.telemetry.aggregate([{'$match': {'telem_category': 'attack', + 'data.technique': T1197.tech_id}}, + {'$group': {'_id': {'ip_addr': '$data.machine.ip_addr', + 'usage': '$data.usage'}, + 'ip_addr': {'$first': '$data.machine.ip_addr'}, + 'domain_name': {'$first': '$data.machine.domain_name'}, + 'usage': {'$first': '$data.usage'}, + 'time': {'$first': '$timestamp'}} + }]) bits_results = list(bits_results) data.update({'bits_jobs': bits_results}) return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js new file mode 100644 index 000000000..d7783714a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js @@ -0,0 +1,26 @@ +import React from 'react'; +import '../../../styles/Collapse.scss' +import '../../report-components/StolenPasswords' +import StolenPasswordsComponent from "../../report-components/StolenPasswords"; + + +class T1003 extends React.Component { + + constructor(props) { + super(props); + } + + render() { + return ( +