Merge pull request #1514 from guardicore/pba-attack-telemetry

Fix ATT&CK report bug: showed a different technique's results under a technique if the PBA behind them was the same
This commit is contained in:
Mike Salvatore 2021-10-06 12:12:28 -04:00 committed by GitHub
commit c3ea714977
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 31 additions and 78 deletions

View File

@ -48,6 +48,10 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
the config successfully now.) #1490 the config successfully now.) #1490
- Mimikatz collector no longer fails if Azure credential collector is disabled. #1512 #1493 - Mimikatz collector no longer fails if Azure credential collector is disabled. #1512 #1493
- Unhandled error when "modify shell startup files PBA" is unable to find regular users. #1507 - Unhandled error when "modify shell startup files PBA" is unable to find regular users. #1507
- ATT&CK report bug that showed different techniques' results under a technique if the PBA behind
them was the same. #1514
- ATT&CK report bug that said that the technique "`.bash_profile` and `.bashrc`" was not attempted
when it actually was attempted but failed. #1511
### Security ### Security

View File

@ -2,6 +2,7 @@ import socket
from common.common_consts.telem_categories import TelemCategoryEnum from common.common_consts.telem_categories import TelemCategoryEnum
from infection_monkey.telemetry.base_telem import BaseTelem from infection_monkey.telemetry.base_telem import BaseTelem
from infection_monkey.utils.environment import is_windows_os
class PostBreachTelem(BaseTelem): class PostBreachTelem(BaseTelem):
@ -25,6 +26,7 @@ class PostBreachTelem(BaseTelem):
"name": self.pba.name, "name": self.pba.name,
"hostname": self.hostname, "hostname": self.hostname,
"ip": self.ip, "ip": self.ip,
"os": PostBreachTelem._get_os(),
} }
@staticmethod @staticmethod
@ -36,3 +38,7 @@ class PostBreachTelem(BaseTelem):
hostname = "Unknown" hostname = "Unknown"
ip = "Unknown" ip = "Unknown"
return hostname, ip return hostname, ip
@staticmethod
def _get_os():
return "Windows" if is_windows_os() else "Linux"

View File

@ -12,24 +12,3 @@ class T1146(PostBreachTechnique):
"restored it back)." "restored it back)."
) )
pba_names = [POST_BREACH_CLEAR_CMD_HISTORY] pba_names = [POST_BREACH_CLEAR_CMD_HISTORY]
@staticmethod
def get_pba_query(*args):
return [
{
"$match": {
"telem_category": "post_breach",
"data.name": POST_BREACH_CLEAR_CMD_HISTORY,
}
},
{
"$project": {
"_id": 0,
"machine": {
"hostname": {"$arrayElemAt": ["$data.hostname", 0]},
"ips": [{"$arrayElemAt": ["$data.ip", 0]}],
},
"result": "$data.result",
}
},
]

View File

@ -9,30 +9,3 @@ class T1156(PostBreachTechnique):
scanned_msg = "Monkey tried modifying bash startup files but failed." scanned_msg = "Monkey tried modifying bash startup files but failed."
used_msg = "Monkey successfully modified bash startup files." used_msg = "Monkey successfully modified bash startup files."
pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION]
@staticmethod
def get_pba_query(*args):
return [
{
"$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"}}]
}
},
]

View File

@ -9,26 +9,3 @@ class T1504(PostBreachTechnique):
scanned_msg = "Monkey tried modifying PowerShell startup files but failed." scanned_msg = "Monkey tried modifying PowerShell startup files but failed."
used_msg = "Monkey successfully modified PowerShell startup files." used_msg = "Monkey successfully modified PowerShell startup files."
pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION]
@staticmethod
def get_pba_query(*args):
return [
{
"$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"}}},
]

View File

@ -18,7 +18,7 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta):
... ...
@classmethod @classmethod
def get_pba_query(cls, post_breach_action_names): def get_pba_query(cls, post_breach_action_names, relevant_systems):
""" """
:param post_breach_action_names: Names of post-breach actions with which the technique is :param post_breach_action_names: Names of post-breach actions with which the technique is
associated associated
@ -29,14 +29,20 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta):
return [ return [
{ {
"$match": { "$match": {
"telem_category": "post_breach", "$and": [
"$or": [{"data.name": pba_name} for pba_name in post_breach_action_names], {"telem_category": "post_breach"},
{"$or": [{"data.name": pba_name} for pba_name in post_breach_action_names]},
{"$or": [{"data.os": os} for os in relevant_systems]},
]
} }
}, },
{ {
"$project": { "$project": {
"_id": 0, "_id": 0,
"machine": {"hostname": "$data.hostname", "ips": ["$data.ip"]}, "machine": {
"hostname": {"$arrayElemAt": ["$data.hostname", 0]},
"ips": [{"$arrayElemAt": ["$data.ip", 0]}],
},
"result": "$data.result", "result": "$data.result",
} }
}, },
@ -50,13 +56,18 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta):
@cls.is_status_disabled @cls.is_status_disabled
def get_technique_status_and_data(): def get_technique_status_and_data():
info = list(mongo.db.telemetry.aggregate(cls.get_pba_query(cls.pba_names))) info = list(
mongo.db.telemetry.aggregate(cls.get_pba_query(cls.pba_names, cls.relevant_systems))
)
status = ScanStatus.UNSCANNED.value status = ScanStatus.UNSCANNED.value
if info: if info:
successful_PBAs = mongo.db.telemetry.count( successful_PBAs = mongo.db.telemetry.count(
{ {
"$or": [{"data.name": pba_name} for pba_name in cls.pba_names], "$and": [
"data.result.1": True, {"$or": [{"data.name": pba_name} for pba_name in cls.pba_names]},
{"$or": [{"data.os": os} for os in cls.relevant_systems]},
{"data.result.1": True},
]
} }
) )
status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value

View File

@ -6,6 +6,7 @@ from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
HOSTNAME = "hostname" HOSTNAME = "hostname"
IP = "0.0.0.0" IP = "0.0.0.0"
OS = "operating system"
PBA_COMMAND = "run some pba" PBA_COMMAND = "run some pba"
PBA_NAME = "some pba" PBA_NAME = "some pba"
RESULT = False RESULT = False
@ -21,6 +22,7 @@ class StubSomePBA:
def post_breach_telem_test_instance(monkeypatch): def post_breach_telem_test_instance(monkeypatch):
PBA = StubSomePBA() PBA = StubSomePBA()
monkeypatch.setattr(PostBreachTelem, "_get_hostname_and_ip", lambda: (HOSTNAME, IP)) monkeypatch.setattr(PostBreachTelem, "_get_hostname_and_ip", lambda: (HOSTNAME, IP))
monkeypatch.setattr(PostBreachTelem, "_get_os", lambda: OS)
return PostBreachTelem(PBA, RESULT) return PostBreachTelem(PBA, RESULT)
@ -32,6 +34,7 @@ def test_post_breach_telem_send(post_breach_telem_test_instance, spy_send_teleme
"name": PBA_NAME, "name": PBA_NAME,
"hostname": HOSTNAME, "hostname": HOSTNAME,
"ip": IP, "ip": IP,
"os": OS,
} }
expected_data = json.dumps(expected_data, cls=post_breach_telem_test_instance.json_encoder) expected_data = json.dumps(expected_data, cls=post_breach_telem_test_instance.json_encoder)
assert spy_send_telemetry.data == expected_data assert spy_send_telemetry.data == expected_data