From e40922f3148e73b24e8888598b52e98fbcaf985c Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Tue, 20 Feb 2024 16:51:05 +0100 Subject: [PATCH 1/9] IIS plugin does not process logs in default dir without ApplicationHost config file (DIS-2795) --- dissect/target/plugins/apps/webserver/iis.py | 27 +++++++++++++++++--- tests/plugins/apps/webserver/test_iis.py | 27 ++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index 9ff028cb8..6b5d1d817 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -54,17 +54,26 @@ class IISLogsPlugin(WebserverPlugin): APPLICATION_HOST_CONFIG = "sysvol/windows/system32/inetsrv/config/applicationHost.config" + DEFAULT_LOG_PATHS = [ + "sysvol\\Windows\\System32\\LogFiles\\W3SVC*\\*.log", + "sysvol\\Windows.old\\Windows\\System32\\LogFiles\\W3SVC*\\*.log", + "sysvol\\inetpub\\logs\\LogFiles\\*.log", + "sysvol\\inetpub\\logs\\LogFiles\\W3SVC*\\*.log", + "sysvol\\Resources\\Directory\\*\\LogFiles\\Web\\W3SVC*\\*.log", + ] + __namespace__ = "iis" def __init__(self, target): super().__init__(target) self.config = self.target.fs.path(self.APPLICATION_HOST_CONFIG) + self.log_dirs = self.get_log_dirs() self._create_extended_descriptor = lru_cache(4096)(self._create_extended_descriptor) def check_compatible(self) -> None: - if not self.config.exists() and not self.target.fs.path("sysvol/files").exists(): - raise UnsupportedPluginError("No ApplicationHost config file found") + if not self.log_dirs: + raise UnsupportedPluginError("No IIS log files found") @plugin.internal def get_log_dirs(self) -> list[tuple[str, Path]]: @@ -84,11 +93,23 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: except (ElementTree.ParseError, DissectFileNotFoundError) as e: self.target.log.warning(f"Error while parsing {self.config}: {e}") + for log_path in self.DEFAULT_LOG_PATHS: + for log_file in self.target.fs.glob(log_path): + try: + log_dir = self.target.fs.path(log_file).parents[1] + if ( + "auto", + log_dir, + ) not in log_paths: + log_paths.append(("auto", log_dir)) + except KeyError: + pass + return log_paths @plugin.internal def iter_log_format_path_pairs(self) -> list[tuple[str, str]]: - for log_format, log_dir_path in self.get_log_dirs(): + for log_format, log_dir_path in self.log_dirs: for log_file in log_dir_path.glob("*/*.log"): yield (log_format, log_file) diff --git a/tests/plugins/apps/webserver/test_iis.py b/tests/plugins/apps/webserver/test_iis.py index fe24d4d8b..cfe66921e 100644 --- a/tests/plugins/apps/webserver/test_iis.py +++ b/tests/plugins/apps/webserver/test_iis.py @@ -183,3 +183,30 @@ def test_plugins_apps_webservers_iis_access_w3c_format(target_win: Target, fs_wi == "Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/93.0.4577.82+Safari/537.36+Edg/93.0.961.52" # noqa: E501 ) assert w3c_record_3.source == "C:/Users/John/w3c-logs/W3SVC1/u_ex211001_x.log" + + +@pytest.mark.parametrize( + "map_dir", + [ + ("inetpub/logs/LogFiles/W3SVC1"), + ("inetpub/logs/LogFiles"), + ("Windows/System32/LogFiles/W3SVC1"), + ("Windows.old/Windows/System32/LogFiles/W3SVC2"), + ("Resources/Directory/aaa/LogFiles/Web/W3SVC1"), + ], +) +@pytest.mark.parametrize( + "log_format", + [ + ("iis"), + ("w3c"), + ], +) +def test_plugins_apps_webservers_iis_access_iis_format_noconfig( + target_win_tzinfo: Target, fs_win: VirtualFilesystem, map_dir: str, log_format: str +) -> None: + data_dir = absolute_path(f"_data/plugins/apps/webserver/iis/iis-logs-{log_format}/W3SVC1") + fs_win.map_dir(map_dir, data_dir) + target_win_tzinfo.add_plugin(iis.IISLogsPlugin) + results = list(target_win_tzinfo.iis.access()) + assert len(results) > 0 From 6508308babc98d14194ab9060dcfc1f64ffa5579 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:03:47 +0100 Subject: [PATCH 2/9] Improvements. --- dissect/target/plugins/apps/webserver/iis.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index 6b5d1d817..cde39e096 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -77,10 +77,10 @@ def check_compatible(self) -> None: @plugin.internal def get_log_dirs(self) -> list[tuple[str, Path]]: - log_paths = [] + log_paths = set() if (sysvol_files := self.target.fs.path("sysvol/files")).exists(): - log_paths.append(("auto", sysvol_files)) + log_paths.add(("auto", sysvol_files)) try: xml_data = ElementTree.fromstring(self.config.open().read(), forbid_dtd=True) @@ -88,7 +88,7 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: log_format = log_file_element.get("logFormat") or "W3C" if log_dir := log_file_element.get("directory"): log_dir = self.target.resolve(log_dir) - log_paths.append((log_format, log_dir)) + log_paths.add((log_format, log_dir)) except (ElementTree.ParseError, DissectFileNotFoundError) as e: self.target.log.warning(f"Error while parsing {self.config}: {e}") @@ -97,15 +97,11 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: for log_file in self.target.fs.glob(log_path): try: log_dir = self.target.fs.path(log_file).parents[1] - if ( - "auto", - log_dir, - ) not in log_paths: - log_paths.append(("auto", log_dir)) + log_paths.add(("auto", log_dir)) except KeyError: pass - return log_paths + return list(log_paths) @plugin.internal def iter_log_format_path_pairs(self) -> list[tuple[str, str]]: From db4e4479b126993e4cb9284ff42dfa1606fa0169 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Fri, 23 Feb 2024 09:30:03 +0100 Subject: [PATCH 3/9] Improvements. --- dissect/target/helpers/fsutil.py | 1 + dissect/target/plugins/apps/webserver/iis.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/dissect/target/helpers/fsutil.py b/dissect/target/helpers/fsutil.py index 53a4a85a7..b299b9dd4 100644 --- a/dissect/target/helpers/fsutil.py +++ b/dissect/target/helpers/fsutil.py @@ -78,6 +78,7 @@ "generate_addr", "glob_ext", "glob_split", + "has_glob_magic", "isabs", "join", "normalize", diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index cde39e096..056b56ffe 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -10,6 +10,7 @@ from dissect.target import plugin from dissect.target.exceptions import FileNotFoundError as DissectFileNotFoundError from dissect.target.exceptions import PluginError, UnsupportedPluginError +from dissect.target.helpers.fsutil import has_glob_magic from dissect.target.helpers.record import TargetRecordDescriptor from dissect.target.plugins.apps.webserver.webserver import ( WebserverAccessLogRecord, @@ -94,11 +95,20 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: self.target.log.warning(f"Error while parsing {self.config}: {e}") for log_path in self.DEFAULT_LOG_PATHS: + try: + log_dir = self.target.fs.path(log_path).parents[1] + except IndexError: + continue + + if not has_glob_magic(str(log_dir)) and log_dir.exists(): + log_paths.add(("auto", log_dir)) + continue + for log_file in self.target.fs.glob(log_path): try: log_dir = self.target.fs.path(log_file).parents[1] log_paths.add(("auto", log_dir)) - except KeyError: + except IndexError: pass return list(log_paths) From 36dfa65c69ce60ebbe9b4a6c882e14662f4b7199 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:07:38 +0100 Subject: [PATCH 4/9] Update dissect/target/plugins/apps/webserver/iis.py Co-authored-by: Paul M <22234727+Poeloe@users.noreply.github.com> --- dissect/target/plugins/apps/webserver/iis.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index 056b56ffe..e607659fa 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -104,12 +104,10 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: log_paths.add(("auto", log_dir)) continue - for log_file in self.target.fs.glob(log_path): - try: - log_dir = self.target.fs.path(log_file).parents[1] - log_paths.add(("auto", log_dir)) - except IndexError: - pass + for _log_dir in self.target.fs.glob(log_dir): + if not (_log_dir := self.target.fs.path(_log_dir)).is_dir(): + continue + log_paths.add(("auto", _log_dir)) return list(log_paths) From 61b8a835ff816beb5aacf5352b35b496f3bc231a Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:07:15 +0100 Subject: [PATCH 5/9] Improvements --- dissect/target/plugins/apps/webserver/iis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index e607659fa..a90e40425 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -104,7 +104,7 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: log_paths.add(("auto", log_dir)) continue - for _log_dir in self.target.fs.glob(log_dir): + for _log_dir in self.target.fs.glob(str(log_dir)): if not (_log_dir := self.target.fs.path(_log_dir)).is_dir(): continue log_paths.add(("auto", _log_dir)) From 9289257469438bced621aeeb136871c12a2e6ce9 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:08:32 +0100 Subject: [PATCH 6/9] Improvements --- dissect/target/plugins/apps/webserver/iis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index a90e40425..7acc935c7 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -96,6 +96,7 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: for log_path in self.DEFAULT_LOG_PATHS: try: + # later on we use */*.log to collect the files, so we need to move up 2 levels log_dir = self.target.fs.path(log_path).parents[1] except IndexError: continue From 1bc9521abfad0697a78308f4800296758dd22707 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:13:42 +0100 Subject: [PATCH 7/9] Improvements --- dissect/target/plugins/apps/webserver/iis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index 7acc935c7..ff1431fb7 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -92,13 +92,14 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: log_paths.add((log_format, log_dir)) except (ElementTree.ParseError, DissectFileNotFoundError) as e: - self.target.log.warning(f"Error while parsing {self.config}: {e}") + self.target.log.warning("Error while parsing %s:%s", self.config, e) for log_path in self.DEFAULT_LOG_PATHS: try: # later on we use */*.log to collect the files, so we need to move up 2 levels log_dir = self.target.fs.path(log_path).parents[1] except IndexError: + self.target.log.error("Incompatible path found: %s", log_path) continue if not has_glob_magic(str(log_dir)) and log_dir.exists(): From 9186b474c38093df87f6c831fccfe104f22e5cf8 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:25:33 +0100 Subject: [PATCH 8/9] Update dissect/target/plugins/apps/webserver/iis.py Co-authored-by: Paul M <22234727+Poeloe@users.noreply.github.com> --- dissect/target/plugins/apps/webserver/iis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index ff1431fb7..23f410788 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -107,7 +107,7 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: continue for _log_dir in self.target.fs.glob(str(log_dir)): - if not (_log_dir := self.target.fs.path(_log_dir)).is_dir(): + if not _log_dir.is_dir(): continue log_paths.add(("auto", _log_dir)) From 5cf54234691b35ab47a5cf897e9124816bd79206 Mon Sep 17 00:00:00 2001 From: cecinestpasunepipe <110607403+cecinestpasunepipe@users.noreply.github.com> Date: Tue, 27 Feb 2024 12:01:11 +0100 Subject: [PATCH 9/9] Improvements --- dissect/target/plugins/apps/webserver/iis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dissect/target/plugins/apps/webserver/iis.py b/dissect/target/plugins/apps/webserver/iis.py index 23f410788..5c37ffcf1 100644 --- a/dissect/target/plugins/apps/webserver/iis.py +++ b/dissect/target/plugins/apps/webserver/iis.py @@ -106,8 +106,8 @@ def get_log_dirs(self) -> list[tuple[str, Path]]: log_paths.add(("auto", log_dir)) continue - for _log_dir in self.target.fs.glob(str(log_dir)): - if not _log_dir.is_dir(): + for _log_dir_str in self.target.fs.glob(str(log_dir)): + if not (_log_dir := self.target.fs.path(_log_dir_str)).is_dir(): continue log_paths.add(("auto", _log_dir))