From ab91f2486683442b3f8f4bdab771a69b0c92ab77 Mon Sep 17 00:00:00 2001 From: Shreya Date: Sat, 11 Jul 2020 01:08:41 +0530 Subject: [PATCH] Add DISABLED status for attack techniques --- monkey/common/utils/attack_utils.py | 2 ++ .../attack/technique_reports/T1136.py | 32 ++++--------------- .../attack/technique_reports/T1156.py | 29 +++-------------- .../attack/technique_reports/T1158.py | 2 +- .../attack/technique_reports/T1504.py | 29 +++-------------- .../attack/technique_reports/__init__.py | 18 ++++++++++- .../attack/technique_reports/pba_technique.py | 13 +++++--- .../components/attack/techniques/Helpers.js | 3 +- .../report-components/AttackReport.js | 16 +++++++--- .../cc/ui/src/styles/components/Collapse.scss | 11 +++++-- .../pages/report/ReportAttackMatrix.scss | 10 ++++++ 11 files changed, 74 insertions(+), 91 deletions(-) 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/T1136.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py index cb68d3a4f..1bd3ba212 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py @@ -2,38 +2,18 @@ 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.attack_config import AttackConfig from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports.pba_technique import \ + PostBreachTechnique +from monkey_island.cc.services.reporting.report import ReportService __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/T1156.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py index 4c8f0d11f..48f12f5e3 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py @@ -3,36 +3,15 @@ from common.data.post_breach_consts import \ 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, - 'data.command': {'$regex': 'bash'}}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': ['$data.ip']}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1156.technique_title(), 'info': []} - - bash_modification_info = list(mongo.db.telemetry.aggregate(T1156.query)) - - status = [] - for pba_node in bash_modification_info: - status.append(pba_node['result'][1]) - status = (ScanStatus.USED.value if any(status) else ScanStatus.SCANNED.value)\ - if status else ScanStatus.UNSCANNED.value - - data.update(T1156.get_base_data_by_status(status)) - data.update({'info': bash_modification_info}) - return data + 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 382fa6489..f83d818c4 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py @@ -11,4 +11,4 @@ class T1158(PostBreachTechnique): 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." - pba_name = POST_BREACH_HIDDEN_FILES + pba_names = [POST_BREACH_HIDDEN_FILES] 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 a9aeb38b7..9f6a0fb83 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py @@ -3,36 +3,15 @@ from common.data.post_breach_consts import \ 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, - 'data.command': {'$regex': 'powershell'}}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': ['$data.ip']}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1504.technique_title(), 'info': []} - - powershell_profile_modification_info = list(mongo.db.telemetry.aggregate(T1504.query)) - - status = [] - for pba_node in powershell_profile_modification_info: - status.append(pba_node['result'][1]) - status = (ScanStatus.USED.value if any(status) else ScanStatus.SCANNED.value)\ - if status else ScanStatus.UNSCANNED.value - - data.update(T1504.get_base_data_by_status(status)) - data.update({'info': powershell_profile_modification_info}) - return data + 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..b93e15997 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,9 @@ 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." + + class AttackTechnique(object, metaclass=abc.ABCMeta): """ Abstract class for ATT&CK report components """ @@ -68,7 +71,8 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): 'data.technique': cls.tech_id}): return ScanStatus.SCANNED.value else: - return ScanStatus.UNSCANNED.value + return ScanStatus.DISABLED.value if not AttackConfig.get_technique_values()[cls.tech_id]\ + else ScanStatus.UNSCANNED.value @classmethod def get_message_and_status(cls, status): @@ -77,6 +81,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): :param status: Enum from common/attack_utils.py integer value :return: Dict with message and status """ + status = cls._check_status(status) return {'message': cls.get_message_by_status(status), 'status': status} @classmethod @@ -86,6 +91,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: @@ -117,6 +124,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): @classmethod def get_base_data_by_status(cls, status): + status = cls._check_status(status) data = cls.get_message_and_status(status) data.update({'title': cls.technique_title()}) data.update(cls.get_mitigation_by_status(status)) @@ -124,8 +132,16 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): @classmethod def get_mitigation_by_status(cls, status: ScanStatus) -> dict: + status = cls._check_status(status) if status == ScanStatus.USED.value: mitigation_document = AttackMitigations.get_mitigation_by_technique_id(str(cls.tech_id)) return {'mitigations': mitigation_document.to_mongo().to_dict()['mitigations']} else: return {} + + @classmethod + def _check_status(cls, status): + if status == ScanStatus.UNSCANNED.value: + return ScanStatus.DISABLED.value if not AttackConfig.get_technique_values()[cls.tech_id]\ + else ScanStatus.UNSCANNED.value + return status 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 index fce4edf70..c588bb3c9 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -9,16 +9,17 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): @property @abc.abstractmethod - def pba_name(self): + def pba_names(self): """ :return: name of post breach action """ pass @classmethod - def get_pba_query(cls, post_breach_action_name): + def get_pba_query(cls, post_breach_action_names): return [{'$match': {'telem_category': 'post_breach', - 'data.name': post_breach_action_name}}, + # 'data.name': post_breach_action_name}}, + '$or': [{'data.name': pba_name} for pba_name in post_breach_action_names]}}, {'$project': {'_id': 0, 'machine': {'hostname': '$data.hostname', 'ips': ['$data.ip']}, @@ -28,7 +29,7 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): def get_report_data(cls): data = {'title': cls.technique_title(), 'info': []} - info = list(mongo.db.telemetry.aggregate(cls.get_pba_query(cls.pba_name))) + info = list(mongo.db.telemetry.aggregate(cls.get_pba_query(cls.pba_names))) status = [] for pba_node in info: @@ -36,6 +37,10 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): status = (ScanStatus.USED.value if any(status) else ScanStatus.SCANNED.value)\ if status else ScanStatus.UNSCANNED.value + if status == ScanStatus.UNSCANNED.value and\ + not AttackConfig.get_technique_values()[cls.tech_id]: + status = ScanStatus.DISABLED.value + 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..ccc755687 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 @@ -62,8 +62,10 @@ class AttackReport extends React.Component { return 'collapse-warning'; case ScanStatus.USED: return 'collapse-danger'; - default: - return 'collapse-default'; + case ScanStatus.UNSCANNED: + return 'collapse-unscanned'; + case ScanStatus.DISABLED: + return 'collapse-disabled'; } } @@ -80,15 +82,19 @@ class AttackReport extends React.Component { renderLegend() { return (