diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index 708bc8f3c..0eadbedcc 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -8,6 +8,8 @@ class ScanStatus(Enum): SCANNED = 1 # Technique was attempted and succeeded USED = 2 + # Techique was disabled + DISABLED = 3 class UsageEnum(Enum): diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py index f5844e2c0..d8ee9de26 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -18,11 +18,17 @@ class T1003(AttackTechnique): @staticmethod def get_report_data(): + @T1003.is_status_disabled + def get_technique_status_and_data(): + if mongo.db.telemetry.count_documents(T1003.query): + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, []) + data = {'title': T1003.technique_title()} - if mongo.db.telemetry.count_documents(T1003.query): - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + status, _ = get_technique_status_and_data() + data.update(T1003.get_message_and_status(status)) data.update(T1003.get_mitigation_by_status(status)) data['stolen_creds'] = ReportService.get_stolen_creds() diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py index cfda065f1..a1162b109 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py @@ -27,8 +27,14 @@ class T1016(AttackTechnique): @staticmethod def get_report_data(): - network_info = list(mongo.db.telemetry.aggregate(T1016.query)) - status = ScanStatus.USED.value if network_info else ScanStatus.UNSCANNED.value + @T1016.is_status_disabled + def get_technique_status_and_data(): + network_info = list(mongo.db.telemetry.aggregate(T1016.query)) + status = ScanStatus.USED.value if network_info else ScanStatus.UNSCANNED.value + return (status, network_info) + + status, network_info = get_technique_status_and_data() + data = T1016.get_base_data_by_status(status) data.update({'network_info': network_info}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py index 65972265d..3ea49603c 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py @@ -28,11 +28,17 @@ class T1018(AttackTechnique): @staticmethod def get_report_data(): - scan_info = list(mongo.db.telemetry.aggregate(T1018.query)) - if scan_info: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + @T1018.is_status_disabled + def get_technique_status_and_data(): + scan_info = list(mongo.db.telemetry.aggregate(T1018.query)) + if scan_info: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, scan_info) + + status, scan_info = get_technique_status_and_data() + data = T1018.get_base_data_by_status(status) data.update({'scan_info': scan_info}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py index f197724dd..a43c76479 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py @@ -33,19 +33,25 @@ class T1021(AttackTechnique): @staticmethod def get_report_data(): - attempts = [] - if mongo.db.telemetry.count_documents(T1021.scanned_query): - attempts = list(mongo.db.telemetry.aggregate(T1021.query)) - if attempts: - status = ScanStatus.USED.value - for result in attempts: - result['successful_creds'] = [] - for attempt in result['attempts']: - result['successful_creds'].append(parse_creds(attempt)) + @T1021.is_status_disabled + def get_technique_status_and_data(): + attempts = [] + if mongo.db.telemetry.count_documents(T1021.scanned_query): + attempts = list(mongo.db.telemetry.aggregate(T1021.query)) + if attempts: + status = ScanStatus.USED.value + for result in attempts: + result['successful_creds'] = [] + for attempt in result['attempts']: + result['successful_creds'].append(parse_creds(attempt)) + else: + status = ScanStatus.SCANNED.value else: - status = ScanStatus.SCANNED.value - else: - status = ScanStatus.UNSCANNED.value + status = ScanStatus.UNSCANNED.value + return (status, attempts) + + status, attempts = get_technique_status_and_data() + data = T1021.get_base_data_by_status(status) data.update({'services': attempts}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py index df4ae4ca5..f56b3e23f 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py @@ -6,7 +6,7 @@ __author__ = "VakarisZ" class T1035(UsageTechnique): tech_id = "T1035" - unscanned_msg = "Monkey didn't try to interact with Windows services." + unscanned_msg = "Monkey didn't try to interact with Windows services since it didn't run on any Windows machines." scanned_msg = "Monkey tried to interact with Windows services, but failed." used_msg = "Monkey successfully interacted with Windows services." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py index 737004111..b4548dac8 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py @@ -13,14 +13,20 @@ class T1041(AttackTechnique): @staticmethod def get_report_data(): - monkeys = list(Monkey.objects()) - info = [{'src': monkey['command_control_channel']['src'], - 'dst': monkey['command_control_channel']['dst']} - for monkey in monkeys if monkey['command_control_channel']] - if info: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + @T1041.is_status_disabled + def get_technique_status_and_data(): + monkeys = list(Monkey.objects()) + info = [{'src': monkey['command_control_channel']['src'], + 'dst': monkey['command_control_channel']['dst']} + for monkey in monkeys if monkey['command_control_channel']] + if info: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, info) + + status, info = get_technique_status_and_data() + data = T1041.get_base_data_by_status(status) data.update({'command_control_channel': info}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py index 7af3978d5..511f819e3 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py @@ -1,34 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_JOB_SCHEDULING -from common.utils.attack_utils import ScanStatus -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1053(AttackTechnique): +class T1053(PostBreachTechnique): tech_id = "T1053" - unscanned_msg = "Monkey did not try scheduling a job on Windows." + unscanned_msg = "Monkey didn't try scheduling a job on Windows since it didn't run on any Windows machines." scanned_msg = "Monkey tried scheduling a job on the Windows system but failed." used_msg = "Monkey scheduled a job on the Windows system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_JOB_SCHEDULING, - 'data.command': {'$regex': 'schtasks'}}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': ['$data.ip']}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1053.technique_title()} - - job_scheduling_info = list(mongo.db.telemetry.aggregate(T1053.query)) - - status = (ScanStatus.USED.value if job_scheduling_info[0]['result'][1] - else ScanStatus.SCANNED.value) if job_scheduling_info else ScanStatus.UNSCANNED.value - - data.update(T1053.get_base_data_by_status(status)) - data.update({'info': job_scheduling_info}) - return data + pba_names = [POST_BREACH_JOB_SCHEDULING] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index 987c24d09..b702ddd58 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -23,12 +23,18 @@ class T1059(AttackTechnique): @staticmethod def get_report_data(): - cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) + @T1059.is_status_disabled + def get_technique_status_and_data(): + cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) + if cmd_data: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, cmd_data) + + status, cmd_data = get_technique_status_and_data() data = {'title': T1059.technique_title(), 'cmds': cmd_data} - if cmd_data: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + data.update(T1059.get_message_and_status(status)) data.update(T1059.get_mitigation_by_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py index 29bbc366c..5d3f270e7 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -7,7 +7,7 @@ __author__ = "VakarisZ" class T1075(AttackTechnique): tech_id = "T1075" - unscanned_msg = "Monkey didn't try to use pass the hash attack." + unscanned_msg = "Monkey didn't try to use pass the hash attack since it didn't run on any Windows machines." scanned_msg = "Monkey tried to use hashes while logging in but didn't succeed." used_msg = "Monkey successfully used hashed credentials." @@ -30,15 +30,21 @@ class T1075(AttackTechnique): @staticmethod def get_report_data(): + @T1075.is_status_disabled + def get_technique_status_and_data(): + successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) + if successful_logins: + status = ScanStatus.USED.value + elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): + status = ScanStatus.SCANNED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, successful_logins) + + status, successful_logins = get_technique_status_and_data() data = {'title': T1075.technique_title()} - successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) data.update({'successful_logins': successful_logins}) - if successful_logins: - status = ScanStatus.USED.value - elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): - status = ScanStatus.SCANNED.value - else: - status = ScanStatus.UNSCANNED.value + data.update(T1075.get_message_and_status(status)) data.update(T1075.get_mitigation_by_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py index 7e8801000..1a9ff94f8 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py @@ -38,13 +38,19 @@ class T1082(AttackTechnique): @staticmethod def get_report_data(): + @T1082.is_status_disabled + def get_technique_status_and_data(): + system_info = list(mongo.db.telemetry.aggregate(T1082.query)) + if system_info: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, system_info) + + status, system_info = get_technique_status_and_data() data = {'title': T1082.technique_title()} - system_info = list(mongo.db.telemetry.aggregate(T1082.query)) data.update({'system_info': system_info}) - if system_info: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + data.update(T1082.get_mitigation_by_status(status)) data.update(T1082.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py index ad5ddc974..d6237a3f7 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -7,7 +7,7 @@ __author__ = "VakarisZ" class T1086(AttackTechnique): tech_id = "T1086" - unscanned_msg = "Monkey didn't run powershell." + unscanned_msg = "Monkey didn't run powershell since it didn't run on any Windows machines." scanned_msg = "" used_msg = "Monkey successfully ran powershell commands on exploited machines in the network." @@ -25,12 +25,17 @@ class T1086(AttackTechnique): @staticmethod def get_report_data(): - cmd_data = list(mongo.db.telemetry.aggregate(T1086.query)) + @T1086.is_status_disabled + def get_technique_status_and_data(): + cmd_data = list(mongo.db.telemetry.aggregate(T1086.query)) + if cmd_data: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, cmd_data) + + status, cmd_data = get_technique_status_and_data() data = {'title': T1086.technique_title(), 'cmds': cmd_data} - if cmd_data: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value data.update(T1086.get_mitigation_by_status(status)) data.update(T1086.get_message_and_status(status)) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py index f0980637f..f68ab1166 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py @@ -13,9 +13,15 @@ class T1090(AttackTechnique): @staticmethod def get_report_data(): - monkeys = Monkey.get_tunneled_monkeys() - monkeys = [monkey.get_network_info() for monkey in monkeys] - status = ScanStatus.USED.value if monkeys else ScanStatus.UNSCANNED.value + @T1090.is_status_disabled + def get_technique_status_and_data(): + monkeys = Monkey.get_tunneled_monkeys() + monkeys = [monkey.get_network_info() for monkey in monkeys] + status = ScanStatus.USED.value if monkeys else ScanStatus.UNSCANNED.value + return (status, monkeys) + + status, monkeys = get_technique_status_and_data() + data = T1090.get_base_data_by_status(status) data.update({'proxies': monkeys}) 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 63ba68d6f..c2d6fc8d5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -26,21 +26,27 @@ class T1110(AttackTechnique): @staticmethod def get_report_data(): - attempts = list(mongo.db.telemetry.aggregate(T1110.query)) - succeeded = False + @T1110.is_status_disabled + def get_technique_status_and_data(): + attempts = list(mongo.db.telemetry.aggregate(T1110.query)) + succeeded = False - for result in attempts: - result['successful_creds'] = [] - for attempt in result['attempts']: - succeeded = True - result['successful_creds'].append(parse_creds(attempt)) + for result in attempts: + result['successful_creds'] = [] + for attempt in result['attempts']: + succeeded = True + result['successful_creds'].append(parse_creds(attempt)) + + if succeeded: + status = ScanStatus.USED.value + elif attempts: + status = ScanStatus.SCANNED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, attempts) + + status, attempts = get_technique_status_and_data() - if succeeded: - status = ScanStatus.USED.value - elif attempts: - status = ScanStatus.SCANNED.value - else: - status = ScanStatus.UNSCANNED.value data = T1110.get_base_data_by_status(status) # Remove data with no successful brute force attempts attempts = [attempt for attempt in attempts if attempt['attempts']] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py index fac76fb47..e84698058 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py @@ -6,9 +6,9 @@ __author__ = "VakarisZ" class T1129(UsageTechnique): tech_id = "T1129" - unscanned_msg = "Monkey didn't try to load any DLL's." - scanned_msg = "Monkey tried to load DLL's, but failed." - used_msg = "Monkey successfully loaded DLL's using Windows module loader." + unscanned_msg = "Monkey didn't try to load any DLLs since it didn't run on any Windows machines." + scanned_msg = "Monkey tried to load DLLs, but failed." + used_msg = "Monkey successfully loaded DLLs using Windows module loader." @staticmethod def get_report_data(): diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py index 042fd3c77..086a1c139 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py @@ -1,39 +1,14 @@ from common.data.post_breach_consts import ( POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER) -from common.utils.attack_utils import ScanStatus -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1136(AttackTechnique): +class T1136(PostBreachTechnique): tech_id = "T1136" unscanned_msg = "Monkey didn't try creating a new user on the network's systems." scanned_msg = "Monkey tried creating a new user on the network's systems, but failed." used_msg = "Monkey created a new user on the network's systems." - - query = [{'$match': {'telem_category': 'post_breach', - '$or': [{'data.name': POST_BREACH_BACKDOOR_USER}, - {'data.name': POST_BREACH_COMMUNICATE_AS_NEW_USER}]}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1136.technique_title()} - - create_user_info = list(mongo.db.telemetry.aggregate(T1136.query)) - - status = ScanStatus.UNSCANNED.value - if create_user_info: - successful_PBAs = mongo.db.telemetry.count({'$or': [{'data.name': POST_BREACH_BACKDOOR_USER}, - {'data.name': POST_BREACH_COMMUNICATE_AS_NEW_USER}], - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1136.get_base_data_by_status(status)) - data.update({'info': create_user_info}) - return data + pba_names = [POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py index 736192b1f..5d96d863e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py @@ -20,12 +20,17 @@ class T1145(AttackTechnique): @staticmethod def get_report_data(): - ssh_info = list(mongo.db.telemetry.aggregate(T1145.query)) + @T1145.is_status_disabled + def get_technique_status_and_data(): + ssh_info = list(mongo.db.telemetry.aggregate(T1145.query)) + if ssh_info: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, ssh_info) + + status, ssh_info = get_technique_status_and_data() - if ssh_info: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value data = T1145.get_base_data_by_status(status) data.update({'ssh_info': ssh_info}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py index a48f3ebbd..c905fc9ca 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py @@ -1,36 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_TRAP_COMMAND -from common.utils.attack_utils import ScanStatus -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1154(AttackTechnique): +class T1154(PostBreachTechnique): tech_id = "T1154" - unscanned_msg = "Monkey did not use the trap command." + unscanned_msg = "Monkey didn't use the trap command since it didn't run on any Linux machines." scanned_msg = "Monkey tried using the trap command but failed." used_msg = "Monkey used the trap command successfully." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_TRAP_COMMAND}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1154.technique_title(), 'info': []} - - trap_command_info = list(mongo.db.telemetry.aggregate(T1154.query)) - - status = ScanStatus.UNSCANNED.value - if trap_command_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_TRAP_COMMAND, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1154.get_base_data_by_status(status)) - data.update({'info': trap_command_info}) - return data + pba_names = [POST_BREACH_TRAP_COMMAND] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py index 64c401e8f..f09b70391 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py @@ -1,40 +1,14 @@ from common.data.post_breach_consts import \ POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION -from common.utils.attack_utils import ScanStatus -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1156(AttackTechnique): +class T1156(PostBreachTechnique): tech_id = "T1156" - unscanned_msg = "Monkey did not try modifying bash startup files on the system." - scanned_msg = "Monkey tried modifying bash startup files on the system but failed." - used_msg = "Monkey modified bash startup files on the system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION}}, - {'$project': {'_id': 0, - 'machine': {'hostname': {'$arrayElemAt': ['$data.hostname', 0]}, - 'ips': [{'$arrayElemAt': ['$data.ip', 0]}]}, - 'result': '$data.result'}}, - {'$unwind': '$result'}, - {'$match': {'$or': [{'result': {'$regex': r'\.bash'}}, - {'result': {'$regex': r'\.profile'}}]}}] - - @staticmethod - def get_report_data(): - data = {'title': T1156.technique_title(), 'info': []} - - bash_startup_modification_info = list(mongo.db.telemetry.aggregate(T1156.query)) - - status = ScanStatus.UNSCANNED.value - if bash_startup_modification_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1156.get_base_data_by_status(status)) - data.update({'info': bash_startup_modification_info}) - return data + unscanned_msg = "Monkey didn't try modifying bash startup files since it didn't run on any Linux machines." + scanned_msg = "Monkey tried modifying bash startup files but failed." + used_msg = "Monkey successfully modified bash startup files." + pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py index 6cf9faeb8..7b0f87358 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py @@ -1,36 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_HIDDEN_FILES -from common.utils.attack_utils import ScanStatus -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1158(AttackTechnique): +class T1158(PostBreachTechnique): tech_id = "T1158" - unscanned_msg = "Monkey did not try creating hidden files or folders." + unscanned_msg = "Monkey didn't try creating hidden files or folders." scanned_msg = "Monkey tried creating hidden files and folders on the system but failed." used_msg = "Monkey created hidden files and folders on the system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_HIDDEN_FILES}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1158.technique_title(), 'info': []} - - hidden_file_info = list(mongo.db.telemetry.aggregate(T1158.query)) - - status = ScanStatus.UNSCANNED.value - if hidden_file_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_HIDDEN_FILES, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1158.get_base_data_by_status(status)) - data.update({'info': hidden_file_info}) - return data + pba_names = [POST_BREACH_HIDDEN_FILES] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py index 84e213607..e3b74e5c5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py @@ -1,36 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_SETUID_SETGID -from common.utils.attack_utils import ScanStatus -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1166(AttackTechnique): +class T1166(PostBreachTechnique): tech_id = "T1166" - unscanned_msg = "Monkey did not try creating hidden files or folders." - scanned_msg = "Monkey tried creating hidden files and folders on the system but failed." - used_msg = "Monkey created hidden files and folders on the system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_SETUID_SETGID}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1166.technique_title(), 'info': []} - - setuid_setgid_info = list(mongo.db.telemetry.aggregate(T1166.query)) - - status = ScanStatus.UNSCANNED.value - if setuid_setgid_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_SETUID_SETGID, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1166.get_base_data_by_status(status)) - data.update({'info': setuid_setgid_info}) - return data + unscanned_msg = "Monkey didn't try setting the setuid or setgid bits since it didn't run on any Linux machines." + scanned_msg = "Monkey tried setting the setuid or setgid bits but failed." + used_msg = "Monkey successfully set the setuid or setgid bits." + pba_names = [POST_BREACH_SETUID_SETGID] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py index bda495845..76806806c 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py @@ -1,37 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_JOB_SCHEDULING -from common.utils.attack_utils import ScanStatus -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1168(AttackTechnique): +class T1168(PostBreachTechnique): tech_id = "T1168" - unscanned_msg = "Monkey did not try scheduling a job on Linux." + unscanned_msg = "Monkey didn't try scheduling a job on Linux since it didn't run on any Linux machines." scanned_msg = "Monkey tried scheduling a job on the Linux system but failed." used_msg = "Monkey scheduled a job on the Linux system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_JOB_SCHEDULING, - 'data.command': {'$regex': 'crontab'}}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1168.technique_title()} - - job_scheduling_info = list(mongo.db.telemetry.aggregate(T1168.query)) - - status = ScanStatus.UNSCANNED.value - if job_scheduling_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_JOB_SCHEDULING, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1168.get_base_data_by_status(status)) - data.update({'info': job_scheduling_info}) - return data + pba_names = [POST_BREACH_JOB_SCHEDULING] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py index 09e0edcdf..2dbf87638 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py @@ -13,19 +13,25 @@ class T1188(AttackTechnique): @staticmethod def get_report_data(): - monkeys = Monkey.get_tunneled_monkeys() - hops = [] - for monkey in monkeys: - proxy_count = 0 - proxy = initial = monkey - while proxy.tunnel: - proxy_count += 1 - proxy = proxy.tunnel - if proxy_count > 1: - hops.append({'from': initial.get_network_info(), - 'to': proxy.get_network_info(), - 'count': proxy_count}) - status = ScanStatus.USED.value if hops else ScanStatus.UNSCANNED.value + @T1188.is_status_disabled + def get_technique_status_and_data(): + monkeys = Monkey.get_tunneled_monkeys() + hops = [] + for monkey in monkeys: + proxy_count = 0 + proxy = initial = monkey + while proxy.tunnel: + proxy_count += 1 + proxy = proxy.tunnel + if proxy_count > 1: + hops.append({'from': initial.get_network_info(), + 'to': proxy.get_network_info(), + 'count': proxy_count}) + status = ScanStatus.USED.value if hops else ScanStatus.UNSCANNED.value + return (status, hops) + + status, hops = get_technique_status_and_data() + data = T1188.get_base_data_by_status(status) data.update({'hops': hops}) return data 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 b6bd316af..b87aeb275 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -6,7 +6,7 @@ __author__ = "VakarisZ" class T1197(AttackTechnique): tech_id = "T1197" - unscanned_msg = "Monkey didn't try to use any bits jobs." + unscanned_msg = "Monkey didn't try to use any bits jobs since it didn't run on any Windows machines." scanned_msg = "Monkey tried to use bits jobs but failed." used_msg = "Monkey successfully used bits jobs at least once in the network." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py index 8fe86ed61..baefcba8e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -13,15 +13,26 @@ class T1210(AttackTechnique): @staticmethod def get_report_data(): - data = {'title': T1210.technique_title()} - scanned_services = T1210.get_scanned_services() - exploited_services = T1210.get_exploited_services() - if exploited_services: - status = ScanStatus.USED.value - elif scanned_services: - status = ScanStatus.SCANNED.value + @T1210.is_status_disabled + def get_technique_status_and_data(): + scanned_services = T1210.get_scanned_services() + exploited_services = T1210.get_exploited_services() + if exploited_services: + status = ScanStatus.USED.value + elif scanned_services: + status = ScanStatus.SCANNED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, scanned_services, exploited_services) + + status_and_data = get_technique_status_and_data() + status = status_and_data[0] + if status == ScanStatus.DISABLED.value: + scanned_services, exploited_services = [], [] else: - status = ScanStatus.UNSCANNED.value + scanned_services, exploited_services = status_and_data[1], status_and_data[2] + data = {'title': T1210.technique_title()} + data.update(T1210.get_message_and_status(status)) data.update(T1210.get_mitigation_by_status(status)) data.update({'scanned_services': scanned_services, 'exploited_services': exploited_services}) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py index 05568fcb8..db1ea8aa5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py @@ -1,39 +1,14 @@ from common.data.post_breach_consts import \ POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION -from common.utils.attack_utils import ScanStatus -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1504(AttackTechnique): +class T1504(PostBreachTechnique): tech_id = "T1504" - unscanned_msg = "Monkey did not try modifying powershell startup files on the system." - scanned_msg = "Monkey tried modifying powershell startup files on the system but failed." - used_msg = "Monkey modified powershell startup files on the system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION}}, - {'$project': {'_id': 0, - 'machine': {'hostname': {'$arrayElemAt': ['$data.hostname', 0]}, - 'ips': [{'$arrayElemAt': ['$data.ip', 0]}]}, - 'result': '$data.result'}}, - {'$unwind': '$result'}, - {'$match': {'result': {'$regex': r'profile\.ps1'}}}] - - @staticmethod - def get_report_data(): - data = {'title': T1504.technique_title(), 'info': []} - - powershell_startup_modification_info = list(mongo.db.telemetry.aggregate(T1504.query)) - - status = ScanStatus.UNSCANNED.value - if powershell_startup_modification_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1504.get_base_data_by_status(status)) - data.update({'info': powershell_startup_modification_info}) - return data + unscanned_msg = "Monkey didn't try modifying powershell startup files since it didn't run on any Windows machines." + scanned_msg = "Monkey tried modifying powershell startup files but failed." + used_msg = "Monkey successfully modified powershell startup files." + pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index 9b39f30ef..61c1f89bd 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -10,6 +10,10 @@ from monkey_island.cc.services.attack.attack_config import AttackConfig logger = logging.getLogger(__name__) +disabled_msg = "This technique has been disabled. " +\ + "You can enable it from the [configuration page](../../configure)." + + class AttackTechnique(object, metaclass=abc.ABCMeta): """ Abstract class for ATT&CK report components """ @@ -59,9 +63,11 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): Gets the status of a certain attack technique. :return: ScanStatus numeric value """ - if mongo.db.telemetry.find_one({'telem_category': 'attack', - 'data.status': ScanStatus.USED.value, - 'data.technique': cls.tech_id}): + if not cls._is_enabled_in_config(): + return ScanStatus.DISABLED.value + elif mongo.db.telemetry.find_one({'telem_category': 'attack', + 'data.status': ScanStatus.USED.value, + 'data.technique': cls.tech_id}): return ScanStatus.USED.value elif mongo.db.telemetry.find_one({'telem_category': 'attack', 'data.status': ScanStatus.SCANNED.value, @@ -86,6 +92,8 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): :param status: Enum from common/attack_utils.py integer value :return: message string """ + if status == ScanStatus.DISABLED.value: + return disabled_msg if status == ScanStatus.UNSCANNED.value: return cls.unscanned_msg elif status == ScanStatus.SCANNED.value: @@ -129,3 +137,13 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): return {'mitigations': mitigation_document.to_mongo().to_dict()['mitigations']} else: return {} + + @classmethod + def is_status_disabled(cls, get_technique_status_and_data) -> bool: + def check_if_disabled_in_config(): + return (ScanStatus.DISABLED.value, []) if not cls._is_enabled_in_config() else get_technique_status_and_data() + return check_if_disabled_in_config + + @classmethod + def _is_enabled_in_config(cls) -> bool: + return AttackConfig.get_technique_values()[cls.tech_id] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py new file mode 100644 index 000000000..72188eb4e --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -0,0 +1,58 @@ +import abc +from typing import List + +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.database import mongo +from monkey_island.cc.services.attack.attack_config import AttackConfig +from monkey_island.cc.services.attack.technique_reports import AttackTechnique + + +class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): + """ Class for ATT&CK report components of post-breach actions """ + + @property + @abc.abstractmethod + def pba_names(self) -> List[str]: + """ + :return: names of post breach action + """ + pass + + @classmethod + def get_pba_query(cls, post_breach_action_names): + """ + :param post_breach_action_names: Names of post-breach actions with which the technique is associated + (example - `["Communicate as new user", "Backdoor user"]` for T1136) + :return: Mongo query that parses attack telemetries for a simple report component + (gets machines and post-breach action usage). + """ + return [{'$match': {'telem_category': 'post_breach', + '$or': [{'data.name': pba_name} for pba_name in post_breach_action_names]}}, + {'$project': {'_id': 0, + 'machine': {'hostname': '$data.hostname', + 'ips': ['$data.ip']}, + 'result': '$data.result'}}] + + @classmethod + def get_report_data(cls): + """ + :return: Technique's report data aggregated from the database + """ + @cls.is_status_disabled + def get_technique_status_and_data(): + info = list(mongo.db.telemetry.aggregate(cls.get_pba_query(cls.pba_names))) + status = ScanStatus.UNSCANNED.value + if info: + successful_PBAs = mongo.db.telemetry.count({ + '$or': [{'data.name': pba_name} for pba_name in cls.pba_names], + 'data.result.1': True + }) + status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value + return (status, info) + + data = {'title': cls.technique_title()} + status, info = get_technique_status_and_data() + + data.update(cls.get_base_data_by_status(status)) + data.update({'info': info}) + return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js index ebe12f25b..95820b82f 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js @@ -65,5 +65,6 @@ export function renderUsageFields(usages) { export const ScanStatus = { UNSCANNED: 0, SCANNED: 1, - USED: 2 + USED: 2, + DISABLED: 3 }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js index 5ab7e4f6e..97f3c1a18 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js @@ -6,6 +6,8 @@ import {faCircle} from '@fortawesome/free-solid-svg-icons/faCircle'; import {faRadiation} from '@fortawesome/free-solid-svg-icons/faRadiation'; import {faEye} from '@fortawesome/free-solid-svg-icons/faEye'; import {faEyeSlash} from '@fortawesome/free-solid-svg-icons/faEyeSlash'; +import {faToggleOff} from '@fortawesome/free-solid-svg-icons/faToggleOff'; +import marked from 'marked'; import ReportHeader, {ReportTypes} from './common/ReportHeader'; import {ScanStatus} from '../attack/techniques/Helpers'; @@ -37,14 +39,14 @@ class AttackReport extends React.Component { }; if (typeof this.props.report.schema !== 'undefined' && typeof this.props.report.techniques !== 'undefined'){ this.state['schema'] = this.props.report['schema']; - this.state['techniques'] = AttackReport.addLinksToTechniques(this.props.report['schema'], this.props.report['techniques']); + this.state['techniques'] = AttackReport.modifyTechniqueData(this.props.report['schema'], this.props.report['techniques']); } } componentDidUpdate(prevProps) { if (this.props.report !== prevProps.report) { this.setState({schema: this.props.report['schema'], - techniques: AttackReport.addLinksToTechniques(this.props.report['schema'], this.props.report['techniques'])}) + techniques: AttackReport.modifyTechniqueData(this.props.report['schema'], this.props.report['techniques'])}) } } @@ -62,6 +64,8 @@ class AttackReport extends React.Component { return 'collapse-warning'; case ScanStatus.USED: return 'collapse-danger'; + case ScanStatus.DISABLED: + return 'collapse-disabled'; default: return 'collapse-default'; } @@ -73,22 +77,28 @@ class AttackReport extends React.Component { return ; case ScanStatus.USED: return ; + case ScanStatus.DISABLED: + return ; default: return ; - } + } } renderLegend() { return (