From e2ef197ddf7f0c27dd946c609a3408f929318f7d Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Fri, 14 Jun 2024 15:53:33 +0200 Subject: [PATCH 01/11] Add MPLog parser to Defender plugin (DIS-2399) --- dissect/target/plugins/os/windows/defender.py | 216 +++++++++++++- .../os/windows/defender_helpers/__init__.py | 0 .../defender_helpers/defender_patterns.py | 280 ++++++++++++++++++ .../defender_helpers/defender_records.py | 176 +++++++++++ .../os/windows/defender/mplog/bmtelemetry.log | 3 + .../os/windows/defender/mplog/lines.log | 3 + .../windows/defender/mplog/resourcescan.log | 3 + .../plugins/os/windows/defender/mplog/rtp.log | 3 + .../windows/defender/mplog/threatactions.log | 3 + tests/plugins/os/windows/test_defender.py | 202 +++++++++++++ 10 files changed, 888 insertions(+), 1 deletion(-) create mode 100644 dissect/target/plugins/os/windows/defender_helpers/__init__.py create mode 100644 dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py create mode 100644 dissect/target/plugins/os/windows/defender_helpers/defender_records.py create mode 100644 tests/_data/plugins/os/windows/defender/mplog/bmtelemetry.log create mode 100644 tests/_data/plugins/os/windows/defender/mplog/lines.log create mode 100644 tests/_data/plugins/os/windows/defender/mplog/resourcescan.log create mode 100644 tests/_data/plugins/os/windows/defender/mplog/rtp.log create mode 100644 tests/_data/plugins/os/windows/defender/mplog/threatactions.log diff --git a/dissect/target/plugins/os/windows/defender.py b/dissect/target/plugins/os/windows/defender.py index 63f0b4b16b..2c9c9ea429 100644 --- a/dissect/target/plugins/os/windows/defender.py +++ b/dissect/target/plugins/os/windows/defender.py @@ -1,7 +1,8 @@ +import re from datetime import datetime, timezone from io import BytesIO from pathlib import Path -from typing import Any, BinaryIO, Generator, Iterable, Iterator, Union +from typing import Any, BinaryIO, Generator, Iterable, Iterator, TextIO, Union import dissect.util.ts as ts from dissect.cstruct import Structure, cstruct @@ -10,6 +11,27 @@ from dissect.target import plugin from dissect.target.exceptions import UnsupportedPluginError from dissect.target.helpers.record import TargetRecordDescriptor +from dissect.target.plugins.os.windows.defender_helpers.defender_patterns import ( + DEFENDER_MPLOG_BLOCK_PATTERNS, + DEFENDER_MPLOG_LINE, + DEFENDER_MPLOG_PATTERNS, +) +from dissect.target.plugins.os.windows.defender_helpers.defender_records import ( + DefenderMPLogBMTelemetryRecord, + DefenderMPLogDetectionAddRecord, + DefenderMPLogDetectionEventRecord, + DefenderMPLogEMSRecord, + DefenderMPLogExclusionRecord, + DefenderMPLogLowfiRecord, + DefenderMPLogMinFilBlockedFileRecord, + DefenderMPLogMinFilUSSRecord, + DefenderMPLogOriginalFileNameRecord, + DefenderMPLogProcessImageRecord, + DefenderMPLogResourceScanRecord, + DefenderMPLogRTPRecord, + DefenderMPLogThreatActionRecord, + DefenderMPLogThreatRecord, +) DEFENDER_EVTX_FIELDS = [ ("datetime", "ts"), @@ -73,6 +95,7 @@ EVTX_PROVIDER_NAME = "Microsoft-Windows-Windows Defender" DEFENDER_QUARANTINE_DIR = "sysvol/programdata/microsoft/windows defender/quarantine" +DEFENDER_MPLOG_DIR = "sysvol/programdata/microsoft/windows defender/support" DEFENDER_KNOWN_DETECTION_TYPES = [b"internalbehavior", b"regkey", b"runkey"] DEFENDER_EXCLUSION_KEY = "HKLM\\SOFTWARE\\Microsoft\\Windows Defender\\Exclusions" @@ -494,6 +517,197 @@ def exclusions(self) -> Iterator[DefenderExclusionRecord]: value=exclusion_value, ) + def _mplog_processimage(self, data: str) -> Iterator[DefenderMPLogProcessImageRecord]: + yield DefenderMPLogProcessImageRecord(_target=self.target, **data) + + def _mplog_minfiluss(self, data: str) -> Iterator[DefenderMPLogMinFilUSSRecord]: + yield DefenderMPLogMinFilUSSRecord(_target=self.target, **data) + + def _mplog_blockedfile(self, data: str) -> Iterator[DefenderMPLogMinFilBlockedFileRecord]: + yield DefenderMPLogMinFilBlockedFileRecord(_target=self.target, **data) + + def _mplog_bmtelemetry(self, data: str) -> Iterator[DefenderMPLogBMTelemetryRecord]: + data["ts"] = datetime.strptime(data["ts"], "%m-%d-%Y %H:%M:%S") + yield DefenderMPLogBMTelemetryRecord(_target=self.target, **data) + + def _mplog_ems(self, data: str) -> Iterator[DefenderMPLogEMSRecord]: + yield DefenderMPLogEMSRecord(_target=self.target, **data) + + def _mplog_originalfilename(self, data: str) -> Iterator[DefenderMPLogOriginalFileNameRecord]: + yield DefenderMPLogOriginalFileNameRecord(_target=self.target, **data) + + def _mplog_exclusion(self, data: str) -> Iterator[DefenderMPLogExclusionRecord]: + yield DefenderMPLogExclusionRecord(_target=self.target, **data) + + def _mplog_lowfi(self, data: str) -> Iterator[DefenderMPLogLowfiRecord]: + yield DefenderMPLogLowfiRecord(_target=self.target, **data) + + def _mplog_detectionadd(self, data: str) -> Iterator[DefenderMPLogDetectionAddRecord]: + yield DefenderMPLogDetectionAddRecord(_target=self.target, **data) + + def _mplog_threat(self, data: str) -> Iterator[DefenderMPLogThreatRecord]: + yield DefenderMPLogThreatRecord(_target=self.target, **data) + + def _mplog_resourcescan(self, data: str) -> Iterator[DefenderMPLogResourceScanRecord]: + data["start_time"] = datetime.strptime(data["start_time"], "%m-%d-%Y %H:%M:%S") + data["end_time"] = datetime.strptime(data["end_time"], "%m-%d-%Y %H:%M:%S") + data["ts"] = data["start_time"] + rest = data.pop("rest") + yield DefenderMPLogResourceScanRecord( + _target=self.target, + threats=re.findall("Threat Name:([^\n]+)", rest), + resources=re.findall("Resource Path:([^\n]+)", rest), + **data, + ) + + def _mplog_threataction(self, data: str) -> Iterator[DefenderMPLogThreatActionRecord]: + data["ts"] = datetime.strptime(data["ts"], "%m-%d-%Y %H:%M:%S") + rest = data.pop("rest") + yield DefenderMPLogThreatActionRecord( + _target=self.target, + threats=re.findall("Threat Name:([^\n]+)", rest), + resources=re.findall("(?:Path|File Name):([^\n]+)", rest), + actions=re.findall("Action:([^\n]+)", rest), + **data, + ) + + def _mplog_rtp(self, data: str) -> Iterator[DefenderMPLogRTPRecord]: + times = {} + for dtkey in ["ts", "last_perf", "first_rtp_scan"]: + try: + times[dtkey] = datetime.strptime(data[dtkey], "%m-%d-%Y %H:%M:%S") + except ValueError: + pass + + yield DefenderMPLogRTPRecord( + _target=self.target, + **times, + plugin_states=re.findall(r"^\s+(.*)$", data["plugin_states"])[0], + process_exclusions=re.findall(DEFENDER_MPLOG_LINE, data["process_exclusions"]), + path_exclusions=re.findall(DEFENDER_MPLOG_LINE, data["path_exclusions"]), + ext_exclusions=re.findall(DEFENDER_MPLOG_LINE, data["ext_exclusions"]), + ) + + def _mplog_detectionevent(self, data: str) -> Iterator[DefenderMPLogDetectionEventRecord]: + yield DefenderMPLogDetectionEventRecord(_target=self.target, **data) + + def _mplog_line( + self, mplog_line: str + ) -> Iterator[ + Union[ + DefenderMPLogProcessImageRecord, + DefenderMPLogMinFilUSSRecord, + DefenderMPLogMinFilBlockedFileRecord, + DefenderMPLogEMSRecord, + DefenderMPLogOriginalFileNameRecord, + DefenderMPLogExclusionRecord, + DefenderMPLogLowfiRecord, + DefenderMPLogDetectionAddRecord, + DefenderMPLogThreatRecord, + DefenderMPLogDetectionEventRecord, + ] + ]: + for pattern, record in DEFENDER_MPLOG_PATTERNS: + if match := pattern.match(mplog_line): + yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(match.groupdict()) + + def _mplog_block( + self, mplog_line: str, mplog: TextIO + ) -> Iterator[ + Union[ + DefenderMPLogBMTelemetryRecord, + DefenderMPLogResourceScanRecord, + DefenderMPLogThreatActionRecord, + DefenderMPLogRTPRecord, + ] + ]: + block = "" + for prefix, suffix, pattern, record in DEFENDER_MPLOG_BLOCK_PATTERNS: + if prefix.search(mplog_line): + block += mplog_line + break + if block: + while mplog_line := mplog.readline(): + block += mplog_line + if suffix.search(mplog_line): + break + match = pattern.match(block) + yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(match.groupdict()) + + def _mplog( + self, mplog: TextIO + ) -> Iterator[ + Union[ + DefenderMPLogProcessImageRecord, + DefenderMPLogMinFilUSSRecord, + DefenderMPLogMinFilBlockedFileRecord, + DefenderMPLogBMTelemetryRecord, + DefenderMPLogEMSRecord, + DefenderMPLogOriginalFileNameRecord, + DefenderMPLogExclusionRecord, + DefenderMPLogLowfiRecord, + DefenderMPLogDetectionAddRecord, + DefenderMPLogThreatRecord, + DefenderMPLogDetectionEventRecord, + DefenderMPLogResourceScanRecord, + DefenderMPLogThreatActionRecord, + DefenderMPLogRTPRecord, + ] + ]: + while mplog_line := mplog.readline(): + yield from self._mplog_line(mplog_line) + yield from self._mplog_block(mplog_line, mplog) + + @plugin.export( + record=[ + DefenderMPLogProcessImageRecord, + DefenderMPLogMinFilUSSRecord, + DefenderMPLogMinFilBlockedFileRecord, + DefenderMPLogBMTelemetryRecord, + DefenderMPLogEMSRecord, + DefenderMPLogOriginalFileNameRecord, + DefenderMPLogExclusionRecord, + DefenderMPLogLowfiRecord, + DefenderMPLogDetectionAddRecord, + DefenderMPLogThreatRecord, + DefenderMPLogDetectionEventRecord, + DefenderMPLogResourceScanRecord, + DefenderMPLogThreatActionRecord, + DefenderMPLogRTPRecord, + ] + ) + def mplog( + self, + ) -> Iterator[ + Union[ + DefenderMPLogProcessImageRecord, + DefenderMPLogMinFilUSSRecord, + DefenderMPLogMinFilBlockedFileRecord, + DefenderMPLogBMTelemetryRecord, + DefenderMPLogEMSRecord, + DefenderMPLogOriginalFileNameRecord, + DefenderMPLogExclusionRecord, + DefenderMPLogLowfiRecord, + DefenderMPLogDetectionAddRecord, + DefenderMPLogThreatRecord, + DefenderMPLogDetectionEventRecord, + DefenderMPLogResourceScanRecord, + DefenderMPLogThreatActionRecord, + DefenderMPLogRTPRecord, + ] + ]: + mplog_directory = self.target.fs.path(DEFENDER_MPLOG_DIR) + if mplog_directory.exists() and mplog_directory.is_dir(): + for mplog_file in mplog_directory.iterdir(): + if mplog_file.name.startswith("MPLog-"): + for encoding in ["UTF-16", "UTF-8"]: + try: + with mplog_file.open("rt", encoding=encoding) as mplog: + yield from self._mplog(mplog) + break + except UnicodeError: + continue + @plugin.arg( "--output", "-o", diff --git a/dissect/target/plugins/os/windows/defender_helpers/__init__.py b/dissect/target/plugins/os/windows/defender_helpers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py b/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py new file mode 100644 index 0000000000..68ab6e0a8f --- /dev/null +++ b/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py @@ -0,0 +1,280 @@ +import re + +from dissect.target.plugins.os.windows.defender_helpers.defender_records import ( + DefenderMPLogBMTelemetryRecord, + DefenderMPLogDetectionAddRecord, + DefenderMPLogDetectionEventRecord, + DefenderMPLogEMSRecord, + DefenderMPLogExclusionRecord, + DefenderMPLogLowfiRecord, + DefenderMPLogMinFilBlockedFileRecord, + DefenderMPLogMinFilUSSRecord, + DefenderMPLogOriginalFileNameRecord, + DefenderMPLogProcessImageRecord, + DefenderMPLogResourceScanRecord, + DefenderMPLogRTPRecord, + DefenderMPLogThreatActionRecord, + DefenderMPLogThreatRecord, +) + +DEFENDER_MPLOG_TS_PATTERN = r"(?P[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z) " + +DEFENDER_MPLOG_PATTERNS = [ + # Process Image + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r"ProcessImageName: (?P.*), ", + r"Pid: (?P\d*), ", + r"TotalTime: (?P\d*), ", + r"Count: (?P\d*), ", + r"MaxTime: (?P\d*), ", + r"MaxTimeFile: (?P.*), ", + r"EstimatedImpact: (?P\d*)", + ] + ) + ), + DefenderMPLogProcessImageRecord, + ), + # Mini-filter Unsuccessful scan status + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r"\[Mini-filter\] (Unsuccessful scan status)[^:]*: (.+) ", + r"Process: (?P.+), ", + r"Status: (?P.+), ", + r"State: (?P.+), ", + r"ScanRequest (?P.+), ", + r"FileId: (?P.+), ", + r"Reason: (?P.+), ", + r"IoStatusBlockForNewFile: (?P.+), ", + r"DesiredAccess:(?P.+), ", + r"FileAttributes:(?P.+), ", + r"ScanAttributes:(?P.+), ", + r"AccessStateFlags:(?P.+), ", + r"BackingFileInfo: (?P.+)", + ] + ) + ), + DefenderMPLogMinFilUSSRecord, + ), + # EMS Scan + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r".*", + r"process: (?P\w*) ", + r"pid: (?P\d*), ", + r"sigseq: (?P\w*), ", + r"sendMemoryScanReport: (?P\d*), ", + r"source: (?P\d*)", + ] + ) + ), + DefenderMPLogEMSRecord, + ), + # Original filename + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r".*", + r"original file name \"(?P.*)\" ", + r"for \"(?P.*)\", ", + r"hr=(?P
\w*)", + ] + ) + ), + DefenderMPLogOriginalFileNameRecord, + ), + # Mini-filter Blocked file + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r".*", + r"\[Mini-filter\] Blocked file: (?P.+) ", + r"Process: (?P.+), ", + r"Status: (?P.+), ", + r"State: (?P.+), ", + r"ScanRequest (?P.+), ", + r"FileId: (?P.+), ", + r"Reason: (?P.+), ", + r"IoStatusBlockForNewFile: (?P.+), ", + r"DesiredAccess:(?P.+), ", + r"FileAttributes:(?P.+), ", + r"ScanAttributes:(?P.+), ", + r"AccessStateFlags:(?P.+), ", + r"BackingFileInfo: (?P.+)", + ] + ) + ), + DefenderMPLogMinFilBlockedFileRecord, + ), + # Exclusion + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r"\[Exclusion\] (?P.+) ", + r"-> (?P.+)", + ] + ) + ), + DefenderMPLogExclusionRecord, + ), + # Lowfi + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r".*", + r"lowfi: (?P.+)", + ] + ) + ), + DefenderMPLogLowfiRecord, + ), + # Detection add + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r".*", + r"DETECTION_ADD\S* (?P.*)", + ] + ) + ), + DefenderMPLogDetectionAddRecord, + ), + # Threat + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r".*", + r"threat: (?P.*)", + ] + ) + ), + DefenderMPLogThreatRecord, + ), + # Detection event + ( + re.compile( + "".join( + [ + DEFENDER_MPLOG_TS_PATTERN, + r".*", + r"DETECTIONEVENT MPSOURCE_\S+ HackTool:(?P.*) file:(?P.*)", + ] + ) + ), + DefenderMPLogDetectionEventRecord, + ), +] + + +DEFENDER_MPLOG_BLOCK_PATTERNS = [ + ( + re.compile(r"Begin Resource Scan"), + re.compile(r"End Scan"), + re.compile( + "".join( + [ + r"Begin Resource Scan.*\n", + r"Scan ID:(?P[^\n]+)\n", + r"Scan Source:(?P\d+)\n", + r"Start Time:(?P[0-9\-\:\s]*)\n", + r"End Time:(?P[0-9\-\:\s]*)\n", + r".*", + r"Resource Schema:(?P[^\n]+)\n", + r"Resource Path:(?P[^\n]+)\n", + r"Result Count:(?P\d+)\n", + r"(?P.*)\n", + r"End Scan", + ] + ), + re.MULTILINE | re.DOTALL, + ), + DefenderMPLogResourceScanRecord, + ), + # Threat actions + ( + re.compile(r"Beginning threat actions"), + re.compile(r"Finished threat actions"), + re.compile( + "".join( + [ + r"Beginning threat actions\n", + r"Start time:(?P[0-9\-\:\s]*)\n", + r"(?P.*)\n", + r"Finished threat actions", + ] + ), + re.MULTILINE | re.DOTALL, + ), + DefenderMPLogThreatActionRecord, + ), + # RTP + ( + re.compile(r"\*\*RTP Perf Log\*\*"), + re.compile(r"\*\*END RTP Perf Log\*\*"), + re.compile( + "".join( + [ + r"\*+RTP Perf Log\*+\n", + r"RTP Start:(?P.*)\n", + r"Last Perf:(?P.*)\n", + r"First RTP Scan:(?P.*)\n", + r"Plugin States:(?P.*)\n", + r"Process Exclusions:\n(?P.*)", + r"Path Exclusions:\n(?P.*)", + r"Ext Exclusions:\n(?P.*)", + r"Worker Threads", + ] + ), + re.MULTILINE | re.DOTALL, + ), + DefenderMPLogRTPRecord, + ), + # BM Telemetry (block) + ( + re.compile(r"BEGIN BM telemetry"), + re.compile(r"END BM telemetry"), + re.compile( + "".join( + [ + r"BEGIN BM telemetry\n", + r"(GUID):(?P.+)\n", + r"(SignatureID):(?P.+)\n", + r"(SigSha):(?P.+)\n", + r"(ThreatLevel):(?P.+)\n", + r"(ProcessID):(?P.+)\n", + r"(ProcessCreationTime):(?P.+)\n", + r"(SessionID):(?P.+)\n", + r"(CreationTime):(?P.+)\n", + r"(ImagePath):(?P.+)\n", + r"(Taint Info):(?P.+)\n", + r"(Operations):(?P.+)\n", + r"END BM telemetry", + ] + ) + ), + DefenderMPLogBMTelemetryRecord, + ), +] + +DEFENDER_MPLOG_LINE = re.compile(r"^\s+(.*)$", re.MULTILINE) diff --git a/dissect/target/plugins/os/windows/defender_helpers/defender_records.py b/dissect/target/plugins/os/windows/defender_helpers/defender_records.py new file mode 100644 index 0000000000..01b74b85dc --- /dev/null +++ b/dissect/target/plugins/os/windows/defender_helpers/defender_records.py @@ -0,0 +1,176 @@ +from dissect.target.helpers.record import TargetRecordDescriptor + +DefenderMPLogProcessImageRecord = TargetRecordDescriptor( + "windows/defender/mplog/processimage", + [ + ("datetime", "ts"), + ("string", "process_image_name"), + ("varint", "pid"), + ("varint", "total_time"), + ("varint", "count"), + ("varint", "max_time"), + ("string", "max_time_file"), + ("varint", "estimated_impact"), + ], +) + +DefenderMPLogMinFilUSSRecord = TargetRecordDescriptor( + "windows/defender/mplog/minfiluss", + [ + ("datetime", "ts"), + ("string", "process"), + ("string", "status"), + ("string", "state"), + ("string", "scan_request"), + ("string", "file_id"), + ("string", "reason"), + ("string", "io_status_block_for_new_file"), + ("string", "desired_access"), + ("string", "file_attributes"), + ("string", "scan_attributes"), + ("string", "access_state_flags"), + ("string", "backing_file_info"), + ], +) + +DefenderMPLogMinFilBlockedFileRecord = TargetRecordDescriptor( + "windows/defender/mplog/blockedfile", + [ + ("datetime", "ts"), + ("string", "blocked_file"), + ("string", "process"), + ("string", "status"), + ("string", "state"), + ("string", "scan_request"), + ("string", "file_id"), + ("string", "reason"), + ("string", "io_status_block_for_new_file"), + ("string", "desired_access"), + ("string", "file_attributes"), + ("string", "scan_attributes"), + ("string", "access_state_flags"), + ("string", "backing_file_info"), + ], +) + + +DefenderMPLogBMTelemetryRecord = TargetRecordDescriptor( + "windows/defender/mplog/bmtelemetry", + [ + ("datetime", "ts"), + ("string", "guid"), + ("varint", "signature_id"), + ("string", "sigsha"), + ("varint", "threat_level"), + ("varint", "process_id"), + ("varint", "process_creation_time"), + ("varint", "session_id"), + ("path", "image_path"), + ("string", "taint_info"), + ("string", "operations"), + ], +) + +DefenderMPLogEMSRecord = TargetRecordDescriptor( + "windows/defender/mplog/ems", + [ + ("datetime", "ts"), + ("string", "process"), + ("varint", "pid"), + ("string", "sigseq"), + ("varint", "send_memory_scan_report"), + ("varint", "source"), + ], +) + +DefenderMPLogOriginalFileNameRecord = TargetRecordDescriptor( + "windows/defender/mplog/originalfilename", + [ + ("datetime", "ts"), + ("string", "original_file_name"), + ("path", "full_path"), + ("string", "hr"), + ], +) + +DefenderMPLogExclusionRecord = TargetRecordDescriptor( + "windows/defender/mplog/exclusion", + [ + ("datetime", "ts"), + ("path", "full_path_with_drive_letter"), + ("path", "full_path_with_device_path"), + ], +) + +DefenderMPLogLowfiRecord = TargetRecordDescriptor( + "windows/defender/mplog/lowfi", + [ + ("datetime", "ts"), + ("command", "lowfi"), + ], +) + +DefenderMPLogDetectionAddRecord = TargetRecordDescriptor( + "windows/defender/mplog/detectionadd", + [ + ("datetime", "ts"), + ("string", "detection"), + ], +) + + +DefenderMPLogThreatRecord = TargetRecordDescriptor( + "windows/defender/mplog/threat", + [ + ("datetime", "ts"), + ("command", "threat"), + ], +) + +DefenderMPLogDetectionEventRecord = TargetRecordDescriptor( + "windows/defender/mplog/detectionevent", + [ + ("datetime", "ts"), + ("string", "threat_type"), + ("command", "command"), + ], +) + +DefenderMPLogResourceScanRecord = TargetRecordDescriptor( + "windows/defender/mplog/resourcescan", + [ + ("datetime", "ts"), + ("string", "scan_id"), + ("varint", "scan_source"), + ("datetime", "start_time"), + ("datetime", "end_time"), + ("string", "resource_schema"), + ("path", "resource_path"), + ("varint", "result_count"), + ("string[]", "threats"), + ("path[]", "resources"), + ], +) + +DefenderMPLogThreatActionRecord = TargetRecordDescriptor( + "windows/defender/mplog/threataction", + [ + ("datetime", "ts"), + ("string[]", "threats"), + ("path[]", "resources"), + ("string[]", "actions"), + ], +) + +DefenderMPLogRTPRecord = TargetRecordDescriptor( + "windows/defender/mplog/rtp", + [ + ("datetime", "ts"), + ("datetime", "last_perf"), + ("datetime", "first_rtp_scan"), + ("string", "plugin_states"), + ("path[]", "process_exclusions"), + ("path[]", "path_exclusions"), + ("string[]", "ext_exclusions"), + ], +) diff --git a/tests/_data/plugins/os/windows/defender/mplog/bmtelemetry.log b/tests/_data/plugins/os/windows/defender/mplog/bmtelemetry.log new file mode 100644 index 0000000000..cd9ad5b829 --- /dev/null +++ b/tests/_data/plugins/os/windows/defender/mplog/bmtelemetry.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d41f17075d4708f967fba46f2f239124764c2e57b7246cda99bbf3c799f3381b +size 381 diff --git a/tests/_data/plugins/os/windows/defender/mplog/lines.log b/tests/_data/plugins/os/windows/defender/mplog/lines.log new file mode 100644 index 0000000000..770243d4a1 --- /dev/null +++ b/tests/_data/plugins/os/windows/defender/mplog/lines.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99df035c31653972710a9c9a7bc844e3c56aa62cd408b88dde4710d4797caa1a +size 2146 diff --git a/tests/_data/plugins/os/windows/defender/mplog/resourcescan.log b/tests/_data/plugins/os/windows/defender/mplog/resourcescan.log new file mode 100644 index 0000000000..1f37db17ce --- /dev/null +++ b/tests/_data/plugins/os/windows/defender/mplog/resourcescan.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:536fea1c5680bcbdd66dcb6e9704b366df9840b7a8347d02b6a39feaadf723bb +size 1549 diff --git a/tests/_data/plugins/os/windows/defender/mplog/rtp.log b/tests/_data/plugins/os/windows/defender/mplog/rtp.log new file mode 100644 index 0000000000..ae1334505f --- /dev/null +++ b/tests/_data/plugins/os/windows/defender/mplog/rtp.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76629edcb9f053e5a979a74381affccc2290857ff3fc2fa82f807da1c2bd668c +size 1948 diff --git a/tests/_data/plugins/os/windows/defender/mplog/threatactions.log b/tests/_data/plugins/os/windows/defender/mplog/threatactions.log new file mode 100644 index 0000000000..4eb48bce5f --- /dev/null +++ b/tests/_data/plugins/os/windows/defender/mplog/threatactions.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9caed751363d17e95f3fcec708b6c18d99f0871b5e23a3e4b35cd108a338d9b +size 1483 diff --git a/tests/plugins/os/windows/test_defender.py b/tests/plugins/os/windows/test_defender.py index 1830ae64a9..3fe4e44655 100644 --- a/tests/plugins/os/windows/test_defender.py +++ b/tests/plugins/os/windows/test_defender.py @@ -4,6 +4,8 @@ from pathlib import Path from dissect.ntfs.secure import ACL, SecurityDescriptor +from flow.record.fieldtypes import command +from flow.record.fieldtypes import datetime as dt from dissect.target.filesystem import VirtualFilesystem from dissect.target.helpers.regutil import VirtualHive, VirtualKey @@ -151,3 +153,203 @@ def test_defender_exclusions(target_win: Target, hive_hklm: VirtualHive) -> None assert len(path_exclusions) == 1 assert len(exclusion_records) == 6 + + +def _mplog_records(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path, log_filename: str): + # map default log location to pass EvtxPlugin's compatibility check + fs_win.map_dir("windows/system32/winevt/logs", tmp_path) + + log_file = absolute_path(f"_data/plugins/os/windows/defender/mplog/{log_filename}.log") + fs_win.map_file("ProgramData/Microsoft/Windows Defender/Support/MPLog-20240101-094808.log", log_file) + + target_win.add_plugin(defender.MicrosoftDefenderPlugin) + records = list(target_win.defender.mplog()) + return records + + +def test_defender_mplogs_rtp(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path) -> None: + record = _mplog_records(target_win, fs_win, tmp_path, "rtp").pop() + assert record.ts == dt("2021-05-04 09:54:06+00:00") + assert record.last_perf == dt("2021-05-04 09:54:06+00:00") + assert record.first_rtp_scan == dt("2021-05-04 09:54:06+00:00") + assert record.plugin_states == "AV:2 AS:2 RTP:2 OA:2 BM:2" + assert record.process_exclusions == ["C:\\ReportingServicesService.exe", "C:\\ReportingServicesService.exe"] + assert sorted(record.path_exclusions) == sorted( + ["C:\\Windows\\Security\\Database\\*.chk", "%windidr%\\SoftwareDistribution\\Datastore\\*.*"] + ) + assert sorted(record.ext_exclusions) == sorted([".DBF", ".NDF", ".RAR", ".XML", ".IDX", ".BAK", ".PDF", ".BKP"]) + + +def test_defender_mplogs_resource_scan(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path) -> None: + record = _mplog_records(target_win, fs_win, tmp_path, "resourcescan").pop() + assert record.ts == dt("2023-01-01 00:00:00+00:00") + assert record.scan_id == "{1A2B3C4D-5E6F-7A8B-9C0D-1E2F3A4B5C6D}" + assert record.scan_source == 4 + assert record.end_time == dt("2023-01-01 00:01:00+00:00") + assert record.resource_schema == "webfile" + assert ( + record.resource_path + == "C:\\Users\\user\\Downloads\\file.rar|https:/example.com/download?id=12345|browser_broker.exe" + ) + assert record.result_count == 2 + assert sorted(record.threats) == sorted(["Worm:VBS/Jenxcus.DN", "HackTool:MSIL/Mimikatz!MSR"]) + assert sorted(record.resources) == sorted( + [ + "".join( + [ + "C:\\ProgramData\\App\\Scans\\FilesStash\\9F8E7D6C-5A4B-3C2D-1E0F-A1B2C3D4E5F6_1d23456789abcdef|", + "C:\\Users\\user\\Downloads\\file.rar", + ] + ), + "C:\\Users\\user\\Downloads\\file.rar->file.WsF", + "C:\\ProgramData\\App\\Scans\\FilesStash\\9F8E7D6C-5A4B-3C2D-1E0F-A1B2C3D4E5F6_1d23456789abcdef", + "C:\\Users\\user\\Downloads\\file.rar|https:/example.com/download?id=67890|browser_broker.exe", + "".join( + [ + "C:\\ProgramData\\App\\Scans\\FilesStash\\9F8E7D6C-5A4B-3C2D-1E0F-A1B2C3D4E5F6_1d23456789abcdef|", + "https:/example.com/download?id=12345|browser_broker.exe", + ] + ), + "C:\\Users\\user\\Downloads\\file.rar", + "C:\\Users\\user\\Videos\\binary.exe", + ] + ) + + +def test_defender_mplogs_threat_actions(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path) -> None: + record = _mplog_records(target_win, fs_win, tmp_path, "threatactions").pop() + + assert record.ts == dt("2017-12-25 16:30:45+00:00") + assert record.threats == ["Worm:Win32/RandomName.X"] + for item in [ + "\\\\?\\C:\\OS\\Tasks\\RandomTask1.job", + "\\\\?\\C:\\OS\\system32\\randomfile.tmp->(UPX)", + "\\\\?\\C:\\OS\\Tasks\\RandomTask1.job", + "\\\\?\\C:\\OS\\system32\\randomfile.tmp", + "C:\\OS\\Tasks\\RandomTask1.job", + "\\\\?\\C:\\OS\\Tasks\\RandomTask1.job", + "C:\\OS\\system32\\randomfile.tmp->(UPX)", + "\\\\?\\C:\\OS\\system32\\randomfile.tmp->(UPX)", + "\\\\?\\C:\\OS\\Tasks\\RandomTask1.job", + ]: + assert item in list(record.resources) + + assert record.actions == ["quarantine"] + + +def test_defender_mplogs_bmtelemetry(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path) -> None: + record = _mplog_records(target_win, fs_win, tmp_path, "bmtelemetry").pop() + assert record.ts == dt("2024-07-15 11:45:22+00:00") + assert record.guid == "{1D3E4F07-89AB-45C2-923D-E5F6789A1B2C}" + assert record.signature_id == 123456789012345 + assert record.sigsha == "abcd1234ef567890abcd1234ef567890abcd1234" + assert record.threat_level == 0 + assert record.process_id == 8453 + assert record.process_creation_time == 153846236598765432 + assert record.image_path == "C:\\OS\\System32\\servicehost.exe" + assert record.taint_info == "Friendly: Y; Reason: ; Modules: ; Parents: " + assert record.operations == "None" + + +def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path) -> None: + records = _mplog_records(target_win, fs_win, tmp_path, "lines") + assert len(records) == 10 + + # Process Image + assert records[0].ts == dt("2024-07-13 14:42:19.659000+00:00") + assert records[0].process_image_name == "randomapp.exe" + assert records[0].pid == 5832 + assert records[0].total_time == 1398 + assert records[0].count == 22 + assert records[0].max_time == 398 + assert records[0].max_time_file == "\\Device\\HarddiskVolume2\\Users\\user123\\AppData\\Local\\Temp\\TEMP001.tmp" + assert records[0].estimated_impact == 4 + + # Lowfi + assert records[1].ts == dt("2023-01-20 08:45:40.321000+00:00") + assert records[1].lowfi == command( + "".join( + [ + "C:\\OS\\System32\\cfg.exe(reg add HKLM\\SYSTEM\\", + "OtherControlSet\\Control\\SecurityOptions\\SecurityModule /v RandomFlag /t REG_DWORD /d 0 /f)", + ] + ), + ) + + # Detection Add + assert records[2].ts == dt("2023-01-27 15:33:07.698000+00:00") + assert records[2].detection == "".join( + [ + "HackTool:MSIL/RndGen!MD5 file:C:\\Users\\user987\\Documents\\executable.exe ", + "PropBag [length: 0, data: (null)]", + ] + ) + + # Threat + assert records[3].ts == dt("2023-01-27 15:33:07.698000+00:00") + assert records[3].threat == command("C:\\Users\\user987\\Documents\\executable.exe") + + # Detection event + assert records[4].ts == dt("2023-01-27 15:33:07.698000+00:00") + assert records[4].threat_type == "MSIL/RndGen!MD5" + assert records[4].command == command("C:\\Users\\user987\\Documents\\executable.exe") + + # Exclusion + assert records[5].ts == dt("2024-08-17 17:35:22.614000+00:00") + assert records[5].full_path_with_drive_letter == "C:\\example.txt" + assert records[5].full_path_with_device_path == "example.txt" + + # Mini-filter unsuccesful scan + assert records[6].ts == dt("2024-07-13 14:38:15.272000+00:00") + assert records[6].process == "(unknown)" + assert records[6].status == "0xc000004a" + assert records[6].state == "0" + assert records[6].scan_request == "#16891" + assert records[6].file_id == "0x200000002c4b5" + assert records[6].reason == "OnAccess" + assert records[6].io_status_block_for_new_file == "0x3" + assert records[6].desired_access == "0x0" + assert records[6].file_attributes == "0x20" + assert records[6].scan_attributes == "0x10" + assert records[6].access_state_flags == "0x802" + assert records[6].backing_file_info == "0x0, 0x0, 0x0:0\\0x0:0" + + # Mini-filter blocked file + assert records[7].ts == dt("2024-07-13 14:38:15.272000+00:00") + assert records[7].blocked_file == "".join( + [ + "\\Device\\HarddiskVolume2\\Users\\userdefault\\AppData\\Local\\Packages\\", + "MicrosoftBrowser.Default_cw5n8h2txyuma\\LocalState\\ANWebView\\Default\\Popular URLs.", + ] + ) + assert records[7].process == "(unknown)" + assert records[7].status == "0xc000004a" + assert records[7].state == "0" + assert records[7].scan_request == "#16891" + assert records[7].file_id == "0x200000002c4b5" + assert records[7].reason == "OnAccess" + assert records[7].io_status_block_for_new_file == "0x3" + assert records[7].desired_access == "0x0" + assert records[7].file_attributes == "0x20" + assert records[7].scan_attributes == "0x10" + assert records[7].access_state_flags == "0x802" + assert records[7].backing_file_info == "0x0, 0x0, 0x0:0\\0x0:0" + + # EMS + assert records[8].ts == dt("2024-09-05 10:21:39.417000+00:00") + assert records[8].process == "sysproc" + assert records[8].pid == 2820 + assert records[8].sigseq == "0x1" + assert records[8].send_memory_scan_report == 1 + assert records[8].source == 4 + + # Original Filename + assert records[9].ts == dt("2024-09-03 18:12:05.364000+00:00") + assert records[9].original_file_name == "RandomData0123_static.dll" + assert records[9].full_path == "".join( + [ + "c:\\os\\winsxs\\x64_default-app-service_31bf3856ad364e35_10.0.29999.9999_none_fakef123456789a\\", + "randomdata0123.dll", + ] + ) + assert records[9].hr == "0x1" From 718fb24d87fc070543bfd88b5820fb6622cad7bf Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Mon, 17 Jun 2024 18:45:30 +0200 Subject: [PATCH 02/11] Update dissect/target/plugins/os/windows/defender.py Co-authored-by: Miauwkeru --- dissect/target/plugins/os/windows/defender.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/dissect/target/plugins/os/windows/defender.py b/dissect/target/plugins/os/windows/defender.py index 2c9c9ea429..f90f656bb0 100644 --- a/dissect/target/plugins/os/windows/defender.py +++ b/dissect/target/plugins/os/windows/defender.py @@ -697,16 +697,18 @@ def mplog( ] ]: mplog_directory = self.target.fs.path(DEFENDER_MPLOG_DIR) - if mplog_directory.exists() and mplog_directory.is_dir(): - for mplog_file in mplog_directory.iterdir(): - if mplog_file.name.startswith("MPLog-"): - for encoding in ["UTF-16", "UTF-8"]: - try: - with mplog_file.open("rt", encoding=encoding) as mplog: - yield from self._mplog(mplog) - break - except UnicodeError: - continue + + if not (mplog_directory.exists() and mplog_directory.is_dir()): + return + + for mplog_file in mplog_directory.glob("MPLog-*"): + for encoding in ["UTF-16", "UTF-8"]: + try: + with mplog_file.open("rt", encoding=encoding) as mplog: + yield from self._mplog(mplog) + break + except UnicodeError: + continue @plugin.arg( "--output", From a870ca3e7f3745975eed8dbb10541c5da1ac92b9 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Wed, 19 Jun 2024 10:01:59 +0200 Subject: [PATCH 03/11] apply suggestions --- dissect/target/plugins/os/windows/defender.py | 153 +++++++++--------- 1 file changed, 72 insertions(+), 81 deletions(-) diff --git a/dissect/target/plugins/os/windows/defender.py b/dissect/target/plugins/os/windows/defender.py index f90f656bb0..862db84271 100644 --- a/dissect/target/plugins/os/windows/defender.py +++ b/dissect/target/plugins/os/windows/defender.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import re from datetime import datetime, timezone from io import BytesIO @@ -517,61 +519,59 @@ def exclusions(self) -> Iterator[DefenderExclusionRecord]: value=exclusion_value, ) - def _mplog_processimage(self, data: str) -> Iterator[DefenderMPLogProcessImageRecord]: - yield DefenderMPLogProcessImageRecord(_target=self.target, **data) + def _mplog_processimage(self, data: dict) -> Iterator[DefenderMPLogProcessImageRecord]: + yield DefenderMPLogProcessImageRecord(**data) - def _mplog_minfiluss(self, data: str) -> Iterator[DefenderMPLogMinFilUSSRecord]: - yield DefenderMPLogMinFilUSSRecord(_target=self.target, **data) + def _mplog_minfiluss(self, data: dict) -> Iterator[DefenderMPLogMinFilUSSRecord]: + yield DefenderMPLogMinFilUSSRecord(**data) - def _mplog_blockedfile(self, data: str) -> Iterator[DefenderMPLogMinFilBlockedFileRecord]: - yield DefenderMPLogMinFilBlockedFileRecord(_target=self.target, **data) + def _mplog_blockedfile(self, data: dict) -> Iterator[DefenderMPLogMinFilBlockedFileRecord]: + yield DefenderMPLogMinFilBlockedFileRecord(**data) - def _mplog_bmtelemetry(self, data: str) -> Iterator[DefenderMPLogBMTelemetryRecord]: + def _mplog_bmtelemetry(self, data: dict) -> Iterator[DefenderMPLogBMTelemetryRecord]: data["ts"] = datetime.strptime(data["ts"], "%m-%d-%Y %H:%M:%S") - yield DefenderMPLogBMTelemetryRecord(_target=self.target, **data) + yield DefenderMPLogBMTelemetryRecord(**data) - def _mplog_ems(self, data: str) -> Iterator[DefenderMPLogEMSRecord]: - yield DefenderMPLogEMSRecord(_target=self.target, **data) + def _mplog_ems(self, data: dict) -> Iterator[DefenderMPLogEMSRecord]: + yield DefenderMPLogEMSRecord(**data) - def _mplog_originalfilename(self, data: str) -> Iterator[DefenderMPLogOriginalFileNameRecord]: - yield DefenderMPLogOriginalFileNameRecord(_target=self.target, **data) + def _mplog_originalfilename(self, data: dict) -> Iterator[DefenderMPLogOriginalFileNameRecord]: + yield DefenderMPLogOriginalFileNameRecord(**data) - def _mplog_exclusion(self, data: str) -> Iterator[DefenderMPLogExclusionRecord]: - yield DefenderMPLogExclusionRecord(_target=self.target, **data) + def _mplog_exclusion(self, data: dict) -> Iterator[DefenderMPLogExclusionRecord]: + yield DefenderMPLogExclusionRecord(**data) - def _mplog_lowfi(self, data: str) -> Iterator[DefenderMPLogLowfiRecord]: - yield DefenderMPLogLowfiRecord(_target=self.target, **data) + def _mplog_lowfi(self, data: dict) -> Iterator[DefenderMPLogLowfiRecord]: + yield DefenderMPLogLowfiRecord(**data) - def _mplog_detectionadd(self, data: str) -> Iterator[DefenderMPLogDetectionAddRecord]: - yield DefenderMPLogDetectionAddRecord(_target=self.target, **data) + def _mplog_detectionadd(self, data: dict) -> Iterator[DefenderMPLogDetectionAddRecord]: + yield DefenderMPLogDetectionAddRecord(**data) - def _mplog_threat(self, data: str) -> Iterator[DefenderMPLogThreatRecord]: - yield DefenderMPLogThreatRecord(_target=self.target, **data) + def _mplog_threat(self, data: dict) -> Iterator[DefenderMPLogThreatRecord]: + yield DefenderMPLogThreatRecord(**data) - def _mplog_resourcescan(self, data: str) -> Iterator[DefenderMPLogResourceScanRecord]: + def _mplog_resourcescan(self, data: dict) -> Iterator[DefenderMPLogResourceScanRecord]: data["start_time"] = datetime.strptime(data["start_time"], "%m-%d-%Y %H:%M:%S") data["end_time"] = datetime.strptime(data["end_time"], "%m-%d-%Y %H:%M:%S") data["ts"] = data["start_time"] rest = data.pop("rest") yield DefenderMPLogResourceScanRecord( - _target=self.target, threats=re.findall("Threat Name:([^\n]+)", rest), resources=re.findall("Resource Path:([^\n]+)", rest), **data, ) - def _mplog_threataction(self, data: str) -> Iterator[DefenderMPLogThreatActionRecord]: + def _mplog_threataction(self, data: dict) -> Iterator[DefenderMPLogThreatActionRecord]: data["ts"] = datetime.strptime(data["ts"], "%m-%d-%Y %H:%M:%S") rest = data.pop("rest") yield DefenderMPLogThreatActionRecord( - _target=self.target, threats=re.findall("Threat Name:([^\n]+)", rest), resources=re.findall("(?:Path|File Name):([^\n]+)", rest), actions=re.findall("Action:([^\n]+)", rest), **data, ) - def _mplog_rtp(self, data: str) -> Iterator[DefenderMPLogRTPRecord]: + def _mplog_rtp(self, data: dict) -> Iterator[DefenderMPLogRTPRecord]: times = {} for dtkey in ["ts", "last_perf", "first_rtp_scan"]: try: @@ -588,39 +588,32 @@ def _mplog_rtp(self, data: str) -> Iterator[DefenderMPLogRTPRecord]: ext_exclusions=re.findall(DEFENDER_MPLOG_LINE, data["ext_exclusions"]), ) - def _mplog_detectionevent(self, data: str) -> Iterator[DefenderMPLogDetectionEventRecord]: - yield DefenderMPLogDetectionEventRecord(_target=self.target, **data) + def _mplog_detectionevent(self, data: dict) -> Iterator[DefenderMPLogDetectionEventRecord]: + yield DefenderMPLogDetectionEventRecord(**data) def _mplog_line( self, mplog_line: str ) -> Iterator[ - Union[ - DefenderMPLogProcessImageRecord, - DefenderMPLogMinFilUSSRecord, - DefenderMPLogMinFilBlockedFileRecord, - DefenderMPLogEMSRecord, - DefenderMPLogOriginalFileNameRecord, - DefenderMPLogExclusionRecord, - DefenderMPLogLowfiRecord, - DefenderMPLogDetectionAddRecord, - DefenderMPLogThreatRecord, - DefenderMPLogDetectionEventRecord, - ] + DefenderMPLogProcessImageRecord + | DefenderMPLogMinFilUSSRecord + | DefenderMPLogMinFilBlockedFileRecord + | DefenderMPLogEMSRecord + | DefenderMPLogOriginalFileNameRecord + | DefenderMPLogExclusionRecord + | DefenderMPLogLowfiRecord + | DefenderMPLogDetectionAddRecord + | DefenderMPLogThreatRecord + | DefenderMPLogDetectionEventRecord ]: for pattern, record in DEFENDER_MPLOG_PATTERNS: if match := pattern.match(mplog_line): - yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(match.groupdict()) + data = match.groupdict() + data["_target"] = self.target + yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(data) def _mplog_block( self, mplog_line: str, mplog: TextIO - ) -> Iterator[ - Union[ - DefenderMPLogBMTelemetryRecord, - DefenderMPLogResourceScanRecord, - DefenderMPLogThreatActionRecord, - DefenderMPLogRTPRecord, - ] - ]: + ) -> Iterator[DefenderMPLogResourceScanRecord | DefenderMPLogThreatActionRecord | DefenderMPLogRTPRecord]: block = "" for prefix, suffix, pattern, record in DEFENDER_MPLOG_BLOCK_PATTERNS: if prefix.search(mplog_line): @@ -632,27 +625,27 @@ def _mplog_block( if suffix.search(mplog_line): break match = pattern.match(block) - yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(match.groupdict()) + data = match.groupdict() + data["_target"] = self.target + yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(data) def _mplog( self, mplog: TextIO ) -> Iterator[ - Union[ - DefenderMPLogProcessImageRecord, - DefenderMPLogMinFilUSSRecord, - DefenderMPLogMinFilBlockedFileRecord, - DefenderMPLogBMTelemetryRecord, - DefenderMPLogEMSRecord, - DefenderMPLogOriginalFileNameRecord, - DefenderMPLogExclusionRecord, - DefenderMPLogLowfiRecord, - DefenderMPLogDetectionAddRecord, - DefenderMPLogThreatRecord, - DefenderMPLogDetectionEventRecord, - DefenderMPLogResourceScanRecord, - DefenderMPLogThreatActionRecord, - DefenderMPLogRTPRecord, - ] + DefenderMPLogProcessImageRecord + | DefenderMPLogMinFilUSSRecord + | DefenderMPLogMinFilBlockedFileRecord + | DefenderMPLogBMTelemetryRecord + | DefenderMPLogEMSRecord + | DefenderMPLogOriginalFileNameRecord + | DefenderMPLogExclusionRecord + | DefenderMPLogLowfiRecord + | DefenderMPLogDetectionAddRecord + | DefenderMPLogThreatRecord + | DefenderMPLogDetectionEventRecord + | DefenderMPLogResourceScanRecord + | DefenderMPLogThreatActionRecord + | DefenderMPLogRTPRecord ]: while mplog_line := mplog.readline(): yield from self._mplog_line(mplog_line) @@ -679,22 +672,20 @@ def _mplog( def mplog( self, ) -> Iterator[ - Union[ - DefenderMPLogProcessImageRecord, - DefenderMPLogMinFilUSSRecord, - DefenderMPLogMinFilBlockedFileRecord, - DefenderMPLogBMTelemetryRecord, - DefenderMPLogEMSRecord, - DefenderMPLogOriginalFileNameRecord, - DefenderMPLogExclusionRecord, - DefenderMPLogLowfiRecord, - DefenderMPLogDetectionAddRecord, - DefenderMPLogThreatRecord, - DefenderMPLogDetectionEventRecord, - DefenderMPLogResourceScanRecord, - DefenderMPLogThreatActionRecord, - DefenderMPLogRTPRecord, - ] + DefenderMPLogProcessImageRecord + | DefenderMPLogMinFilUSSRecord + | DefenderMPLogMinFilBlockedFileRecord + | DefenderMPLogBMTelemetryRecord + | DefenderMPLogEMSRecord + | DefenderMPLogOriginalFileNameRecord + | DefenderMPLogExclusionRecord + | DefenderMPLogLowfiRecord + | DefenderMPLogDetectionAddRecord + | DefenderMPLogThreatRecord + | DefenderMPLogDetectionEventRecord + | DefenderMPLogResourceScanRecord + | DefenderMPLogThreatActionRecord + | DefenderMPLogRTPRecord ]: mplog_directory = self.target.fs.path(DEFENDER_MPLOG_DIR) From b74d39763351de1d6e4b84c5ff3a9e11798d6beb Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:53:28 +0200 Subject: [PATCH 04/11] Update dissect/target/plugins/os/windows/defender_helpers/defender_records.py Co-authored-by: Miauwkeru --- .../plugins/os/windows/defender_helpers/defender_records.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dissect/target/plugins/os/windows/defender_helpers/defender_records.py b/dissect/target/plugins/os/windows/defender_helpers/defender_records.py index 01b74b85dc..ef6866e3ad 100644 --- a/dissect/target/plugins/os/windows/defender_helpers/defender_records.py +++ b/dissect/target/plugins/os/windows/defender_helpers/defender_records.py @@ -163,7 +163,7 @@ ) DefenderMPLogRTPRecord = TargetRecordDescriptor( - "windows/defender/mplog/rtp", + "windows/defender/mplog/rtp_log", [ ("datetime", "ts"), ("datetime", "last_perf"), From 4fdb970d683d48f8fdc4c0f63e951f5c3cb0fbce Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:58:48 +0200 Subject: [PATCH 05/11] Fix naming --- dissect/target/plugins/os/windows/defender.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dissect/target/plugins/os/windows/defender.py b/dissect/target/plugins/os/windows/defender.py index 862db84271..29d95a5837 100644 --- a/dissect/target/plugins/os/windows/defender.py +++ b/dissect/target/plugins/os/windows/defender.py @@ -571,7 +571,7 @@ def _mplog_threataction(self, data: dict) -> Iterator[DefenderMPLogThreatActionR **data, ) - def _mplog_rtp(self, data: dict) -> Iterator[DefenderMPLogRTPRecord]: + def _mplog_rtp_log(self, data: dict) -> Iterator[DefenderMPLogRTPRecord]: times = {} for dtkey in ["ts", "last_perf", "first_rtp_scan"]: try: From 4684bfa0c4de24731141e8758f8cd2545cc56991 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:09:53 +0200 Subject: [PATCH 06/11] Extend minifilter --- .../plugins/os/windows/defender_helpers/defender_patterns.py | 2 +- .../plugins/os/windows/defender_helpers/defender_records.py | 1 + tests/plugins/os/windows/test_defender.py | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py b/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py index 68ab6e0a8f..4b2e3ca0cb 100644 --- a/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py +++ b/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py @@ -44,7 +44,7 @@ "".join( [ DEFENDER_MPLOG_TS_PATTERN, - r"\[Mini-filter\] (Unsuccessful scan status)[^:]*: (.+) ", + r"\[Mini-filter\] (Unsuccessful scan status)[^:]*: (?P.+) ", r"Process: (?P.+), ", r"Status: (?P.+), ", r"State: (?P.+), ", diff --git a/dissect/target/plugins/os/windows/defender_helpers/defender_records.py b/dissect/target/plugins/os/windows/defender_helpers/defender_records.py index ef6866e3ad..1b3c2e5c8b 100644 --- a/dissect/target/plugins/os/windows/defender_helpers/defender_records.py +++ b/dissect/target/plugins/os/windows/defender_helpers/defender_records.py @@ -18,6 +18,7 @@ "windows/defender/mplog/minfiluss", [ ("datetime", "ts"), + ("path", "path"), ("string", "process"), ("string", "status"), ("string", "state"), diff --git a/tests/plugins/os/windows/test_defender.py b/tests/plugins/os/windows/test_defender.py index 3fe4e44655..efe717dd84 100644 --- a/tests/plugins/os/windows/test_defender.py +++ b/tests/plugins/os/windows/test_defender.py @@ -300,6 +300,10 @@ def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tm assert records[5].full_path_with_device_path == "example.txt" # Mini-filter unsuccesful scan + assert ( + records[6].path + == "\\Device\\HarddiskVolume2\\Users\\userdefault\\AppData\\Local\\Packages\\MicrosoftBrowser.Default_cw5n8h2txyuma\\LocalState\\ANWebView\\Default\\Popular URLs." + ) assert records[6].ts == dt("2024-07-13 14:38:15.272000+00:00") assert records[6].process == "(unknown)" assert records[6].status == "0xc000004a" From ba9d0e3407c21494b35c42f66e1d98a2f0703c60 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:18:16 +0200 Subject: [PATCH 07/11] Add source of logs --- dissect/target/plugins/os/windows/defender.py | 14 ++++++++------ .../windows/defender_helpers/defender_records.py | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/dissect/target/plugins/os/windows/defender.py b/dissect/target/plugins/os/windows/defender.py index 29d95a5837..00bc047549 100644 --- a/dissect/target/plugins/os/windows/defender.py +++ b/dissect/target/plugins/os/windows/defender.py @@ -592,7 +592,7 @@ def _mplog_detectionevent(self, data: dict) -> Iterator[DefenderMPLogDetectionEv yield DefenderMPLogDetectionEventRecord(**data) def _mplog_line( - self, mplog_line: str + self, mplog_line: str, source: Path ) -> Iterator[ DefenderMPLogProcessImageRecord | DefenderMPLogMinFilUSSRecord @@ -609,10 +609,11 @@ def _mplog_line( if match := pattern.match(mplog_line): data = match.groupdict() data["_target"] = self.target + data["source_log"] = source yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(data) def _mplog_block( - self, mplog_line: str, mplog: TextIO + self, mplog_line: str, mplog: TextIO, source: Path ) -> Iterator[DefenderMPLogResourceScanRecord | DefenderMPLogThreatActionRecord | DefenderMPLogRTPRecord]: block = "" for prefix, suffix, pattern, record in DEFENDER_MPLOG_BLOCK_PATTERNS: @@ -627,10 +628,11 @@ def _mplog_block( match = pattern.match(block) data = match.groupdict() data["_target"] = self.target + data["source_log"] = source yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(data) def _mplog( - self, mplog: TextIO + self, mplog: TextIO, source: Path ) -> Iterator[ DefenderMPLogProcessImageRecord | DefenderMPLogMinFilUSSRecord @@ -648,8 +650,8 @@ def _mplog( | DefenderMPLogRTPRecord ]: while mplog_line := mplog.readline(): - yield from self._mplog_line(mplog_line) - yield from self._mplog_block(mplog_line, mplog) + yield from self._mplog_line(mplog_line, source) + yield from self._mplog_block(mplog_line, mplog, source) @plugin.export( record=[ @@ -696,7 +698,7 @@ def mplog( for encoding in ["UTF-16", "UTF-8"]: try: with mplog_file.open("rt", encoding=encoding) as mplog: - yield from self._mplog(mplog) + yield from self._mplog(mplog, Path(mplog_file)) break except UnicodeError: continue diff --git a/dissect/target/plugins/os/windows/defender_helpers/defender_records.py b/dissect/target/plugins/os/windows/defender_helpers/defender_records.py index 1b3c2e5c8b..8bfb36d45c 100644 --- a/dissect/target/plugins/os/windows/defender_helpers/defender_records.py +++ b/dissect/target/plugins/os/windows/defender_helpers/defender_records.py @@ -4,6 +4,7 @@ "windows/defender/mplog/processimage", [ ("datetime", "ts"), + ("path", "source_log"), ("string", "process_image_name"), ("varint", "pid"), ("varint", "total_time"), @@ -18,6 +19,7 @@ "windows/defender/mplog/minfiluss", [ ("datetime", "ts"), + ("path", "source_log"), ("path", "path"), ("string", "process"), ("string", "status"), @@ -38,6 +40,7 @@ "windows/defender/mplog/blockedfile", [ ("datetime", "ts"), + ("path", "source_log"), ("string", "blocked_file"), ("string", "process"), ("string", "status"), @@ -59,6 +62,7 @@ "windows/defender/mplog/bmtelemetry", [ ("datetime", "ts"), + ("path", "source_log"), ("string", "guid"), ("varint", "signature_id"), ("string", "sigsha"), @@ -76,6 +80,7 @@ "windows/defender/mplog/ems", [ ("datetime", "ts"), + ("path", "source_log"), ("string", "process"), ("varint", "pid"), ("string", "sigseq"), @@ -88,6 +93,7 @@ "windows/defender/mplog/originalfilename", [ ("datetime", "ts"), + ("path", "source_log"), ("string", "original_file_name"), ("path", "full_path"), ("string", "hr"), @@ -98,6 +104,7 @@ "windows/defender/mplog/exclusion", [ ("datetime", "ts"), + ("path", "source_log"), ("path", "full_path_with_drive_letter"), ("path", "full_path_with_device_path"), ], @@ -107,6 +114,7 @@ "windows/defender/mplog/lowfi", [ ("datetime", "ts"), + ("path", "source_log"), ("command", "lowfi"), ], ) @@ -115,6 +123,7 @@ "windows/defender/mplog/detectionadd", [ ("datetime", "ts"), + ("path", "source_log"), ("string", "detection"), ], ) @@ -124,6 +133,7 @@ "windows/defender/mplog/threat", [ ("datetime", "ts"), + ("path", "source_log"), ("command", "threat"), ], ) @@ -132,6 +142,7 @@ "windows/defender/mplog/detectionevent", [ ("datetime", "ts"), + ("path", "source_log"), ("string", "threat_type"), ("command", "command"), ], @@ -141,6 +152,7 @@ "windows/defender/mplog/resourcescan", [ ("datetime", "ts"), + ("path", "source_log"), ("string", "scan_id"), ("varint", "scan_source"), ("datetime", "start_time"), @@ -157,6 +169,7 @@ "windows/defender/mplog/threataction", [ ("datetime", "ts"), + ("path", "source_log"), ("string[]", "threats"), ("path[]", "resources"), ("string[]", "actions"), @@ -167,6 +180,7 @@ "windows/defender/mplog/rtp_log", [ ("datetime", "ts"), + ("path", "source_log"), ("datetime", "last_perf"), ("datetime", "first_rtp_scan"), ("string", "plugin_states"), From 43349236a240b9c350b62afadc40fe5fbf50bb1b Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Thu, 20 Jun 2024 11:04:49 +0200 Subject: [PATCH 08/11] Improvements --- tests/plugins/os/windows/test_defender.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/plugins/os/windows/test_defender.py b/tests/plugins/os/windows/test_defender.py index efe717dd84..29c69a5761 100644 --- a/tests/plugins/os/windows/test_defender.py +++ b/tests/plugins/os/windows/test_defender.py @@ -300,9 +300,12 @@ def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tm assert records[5].full_path_with_device_path == "example.txt" # Mini-filter unsuccesful scan - assert ( - records[6].path - == "\\Device\\HarddiskVolume2\\Users\\userdefault\\AppData\\Local\\Packages\\MicrosoftBrowser.Default_cw5n8h2txyuma\\LocalState\\ANWebView\\Default\\Popular URLs." + assert records[6].path == "".join( + [ + "\\Device\\HarddiskVolume2\\Users\\userdefault\\AppData\\Local\\", + "Packages\\MicrosoftBrowser.Default_cw5n8h2txyuma\\LocalState\\", + "ANWebView\\Default\\Popular URLs.", + ] ) assert records[6].ts == dt("2024-07-13 14:38:15.272000+00:00") assert records[6].process == "(unknown)" From 0fa8351868c4f9448306ad412635c77ccf7eadc4 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:00:44 +0200 Subject: [PATCH 09/11] Improvements --- dissect/target/plugins/os/windows/defender.py | 1 + .../windows/defender_helpers/defender_patterns.py | 2 ++ tests/plugins/os/windows/test_defender.py | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/dissect/target/plugins/os/windows/defender.py b/dissect/target/plugins/os/windows/defender.py index 00bc047549..5e0df7c75a 100644 --- a/dissect/target/plugins/os/windows/defender.py +++ b/dissect/target/plugins/os/windows/defender.py @@ -581,6 +581,7 @@ def _mplog_rtp_log(self, data: dict) -> Iterator[DefenderMPLogRTPRecord]: yield DefenderMPLogRTPRecord( _target=self.target, + source_log=data["source_log"], **times, plugin_states=re.findall(r"^\s+(.*)$", data["plugin_states"])[0], process_exclusions=re.findall(DEFENDER_MPLOG_LINE, data["process_exclusions"]), diff --git a/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py b/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py index 4b2e3ca0cb..4f6e034ce8 100644 --- a/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py +++ b/dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py @@ -19,6 +19,8 @@ DEFENDER_MPLOG_TS_PATTERN = r"(?P[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z) " +# Loosely based on https://github.com/Intrinsec/mplog_parser but feel free to add patterns + DEFENDER_MPLOG_PATTERNS = [ # Process Image ( diff --git a/tests/plugins/os/windows/test_defender.py b/tests/plugins/os/windows/test_defender.py index 29c69a5761..f22c3cb8ab 100644 --- a/tests/plugins/os/windows/test_defender.py +++ b/tests/plugins/os/windows/test_defender.py @@ -169,6 +169,7 @@ def _mplog_records(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path def test_defender_mplogs_rtp(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path) -> None: record = _mplog_records(target_win, fs_win, tmp_path, "rtp").pop() + assert record.source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert record.ts == dt("2021-05-04 09:54:06+00:00") assert record.last_perf == dt("2021-05-04 09:54:06+00:00") assert record.first_rtp_scan == dt("2021-05-04 09:54:06+00:00") @@ -182,6 +183,7 @@ def test_defender_mplogs_rtp(target_win: Target, fs_win: VirtualFilesystem, tmp_ def test_defender_mplogs_resource_scan(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path) -> None: record = _mplog_records(target_win, fs_win, tmp_path, "resourcescan").pop() + assert record.source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert record.ts == dt("2023-01-01 00:00:00+00:00") assert record.scan_id == "{1A2B3C4D-5E6F-7A8B-9C0D-1E2F3A4B5C6D}" assert record.scan_source == 4 @@ -218,7 +220,7 @@ def test_defender_mplogs_resource_scan(target_win: Target, fs_win: VirtualFilesy def test_defender_mplogs_threat_actions(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path) -> None: record = _mplog_records(target_win, fs_win, tmp_path, "threatactions").pop() - + assert record.source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert record.ts == dt("2017-12-25 16:30:45+00:00") assert record.threats == ["Worm:Win32/RandomName.X"] for item in [ @@ -239,6 +241,7 @@ def test_defender_mplogs_threat_actions(target_win: Target, fs_win: VirtualFiles def test_defender_mplogs_bmtelemetry(target_win: Target, fs_win: VirtualFilesystem, tmp_path: Path) -> None: record = _mplog_records(target_win, fs_win, tmp_path, "bmtelemetry").pop() + assert record.source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert record.ts == dt("2024-07-15 11:45:22+00:00") assert record.guid == "{1D3E4F07-89AB-45C2-923D-E5F6789A1B2C}" assert record.signature_id == 123456789012345 @@ -256,6 +259,7 @@ def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tm assert len(records) == 10 # Process Image + assert records[0].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[0].ts == dt("2024-07-13 14:42:19.659000+00:00") assert records[0].process_image_name == "randomapp.exe" assert records[0].pid == 5832 @@ -266,6 +270,7 @@ def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tm assert records[0].estimated_impact == 4 # Lowfi + assert records[1].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[1].ts == dt("2023-01-20 08:45:40.321000+00:00") assert records[1].lowfi == command( "".join( @@ -277,6 +282,7 @@ def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tm ) # Detection Add + assert records[2].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[2].ts == dt("2023-01-27 15:33:07.698000+00:00") assert records[2].detection == "".join( [ @@ -286,20 +292,24 @@ def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tm ) # Threat + assert records[3].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[3].ts == dt("2023-01-27 15:33:07.698000+00:00") assert records[3].threat == command("C:\\Users\\user987\\Documents\\executable.exe") # Detection event + assert records[4].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[4].ts == dt("2023-01-27 15:33:07.698000+00:00") assert records[4].threat_type == "MSIL/RndGen!MD5" assert records[4].command == command("C:\\Users\\user987\\Documents\\executable.exe") # Exclusion + assert records[5].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[5].ts == dt("2024-08-17 17:35:22.614000+00:00") assert records[5].full_path_with_drive_letter == "C:\\example.txt" assert records[5].full_path_with_device_path == "example.txt" # Mini-filter unsuccesful scan + assert records[6].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[6].path == "".join( [ "\\Device\\HarddiskVolume2\\Users\\userdefault\\AppData\\Local\\", @@ -322,6 +332,7 @@ def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tm assert records[6].backing_file_info == "0x0, 0x0, 0x0:0\\0x0:0" # Mini-filter blocked file + assert records[7].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[7].ts == dt("2024-07-13 14:38:15.272000+00:00") assert records[7].blocked_file == "".join( [ @@ -343,6 +354,7 @@ def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tm assert records[7].backing_file_info == "0x0, 0x0, 0x0:0\\0x0:0" # EMS + assert records[8].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[8].ts == dt("2024-09-05 10:21:39.417000+00:00") assert records[8].process == "sysproc" assert records[8].pid == 2820 @@ -351,6 +363,7 @@ def test_defender_mplogs_lines(target_win: Target, fs_win: VirtualFilesystem, tm assert records[8].source == 4 # Original Filename + assert records[9].source_log == "sysvol/programdata/microsoft/windows defender/support/MPLog-20240101-094808.log" assert records[9].ts == dt("2024-09-03 18:12:05.364000+00:00") assert records[9].original_file_name == "RandomData0123_static.dll" assert records[9].full_path == "".join( From ac6b6546ab5e7976954f1573c8eb795a4ba24d1d Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:13:09 +0200 Subject: [PATCH 10/11] Improvements --- dissect/target/plugins/os/windows/defender.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dissect/target/plugins/os/windows/defender.py b/dissect/target/plugins/os/windows/defender.py index 5e0df7c75a..bf0fa23ca2 100644 --- a/dissect/target/plugins/os/windows/defender.py +++ b/dissect/target/plugins/os/windows/defender.py @@ -690,6 +690,13 @@ def mplog( | DefenderMPLogThreatActionRecord | DefenderMPLogRTPRecord ]: + """Return the contents of the Defender MPLog file. + + References: + - https://www.crowdstrike.com/blog/how-to-use-microsoft-protection-logging-for-forensic-investigations/ + - https://www.intrinsec.com/hunt-mplogs/ + - https://github.com/Intrinsec/mplog_parser + """ mplog_directory = self.target.fs.path(DEFENDER_MPLOG_DIR) if not (mplog_directory.exists() and mplog_directory.is_dir()): From 05f885feb3bc982b9b93cfff7925d277cca399cb Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:21:00 +0200 Subject: [PATCH 11/11] Improvements --- dissect/target/plugins/os/windows/defender.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dissect/target/plugins/os/windows/defender.py b/dissect/target/plugins/os/windows/defender.py index bf0fa23ca2..1a433cceba 100644 --- a/dissect/target/plugins/os/windows/defender.py +++ b/dissect/target/plugins/os/windows/defender.py @@ -706,7 +706,7 @@ def mplog( for encoding in ["UTF-16", "UTF-8"]: try: with mplog_file.open("rt", encoding=encoding) as mplog: - yield from self._mplog(mplog, Path(mplog_file)) + yield from self._mplog(mplog, self.target.fs.path(mplog_file)) break except UnicodeError: continue