From 58a7f2467ff37d4322e5a83358c918de0b13a231 Mon Sep 17 00:00:00 2001 From: Schamper <1254028+Schamper@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:53:05 +0100 Subject: [PATCH 1/3] Mount volumes to drive letters in Velociraptor loader --- dissect/target/loaders/dir.py | 14 ++++++++++---- dissect/target/loaders/velociraptor.py | 8 ++++++++ tests/loaders/test_velociraptor.py | 6 ++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/dissect/target/loaders/dir.py b/dissect/target/loaders/dir.py index 4dda9fabd..a86a540b0 100644 --- a/dissect/target/loaders/dir.py +++ b/dissect/target/loaders/dir.py @@ -34,12 +34,12 @@ def find_entry_path(path: Path) -> str | None: return prefix -def map_dirs(target: Target, dirs: list[Path], os_type: str, **kwargs) -> None: +def map_dirs(target: Target, dirs: list[Path | tuple[str, Path]], os_type: str, **kwargs) -> None: """Map directories as filesystems into the given target. Args: target: The target to map into. - dirs: The directories to map as filesystems. + dirs: The directories to map as filesystems. If a list member is a tuple, the first element is the drive letter. os_type: The operating system type, used to determine how the filesystem should be mounted. """ alt_separator = "" @@ -49,6 +49,12 @@ def map_dirs(target: Target, dirs: list[Path], os_type: str, **kwargs) -> None: case_sensitive = False for path in dirs: + drive_letter = None + if isinstance(path, tuple): + drive_letter, path = path + elif is_drive_letter_path(path): + drive_letter = path.name[0] + if isinstance(path, zipfile.Path): dfs = ZipFilesystem(path.root.fp, path.at, alt_separator=alt_separator, case_sensitive=case_sensitive) else: @@ -58,8 +64,8 @@ def map_dirs(target: Target, dirs: list[Path], os_type: str, **kwargs) -> None: if os_type == OperatingSystem.WINDOWS: loaderutil.add_virtual_ntfs_filesystem(target, dfs, **kwargs) - if is_drive_letter_path(path): - target.fs.mount(path.name[0] + ":", dfs) + if drive_letter is not None: + target.fs.mount(drive_letter.lower() + ":", dfs) def find_and_map_dirs(target: Target, path: Path, **kwargs) -> None: diff --git a/dissect/target/loaders/velociraptor.py b/dissect/target/loaders/velociraptor.py index 0eaf77b25..dab2ea390 100644 --- a/dissect/target/loaders/velociraptor.py +++ b/dissect/target/loaders/velociraptor.py @@ -46,6 +46,8 @@ def find_fs_directories(path: Path) -> tuple[Optional[OperatingSystem], Optional # https://github.com/Velocidex/velociraptor/blob/87368e7cc678144592a1614bb3bbd0a0f900ded9/accessors/ntfs/vss.go#L82 if "HarddiskVolumeShadowCopy" in volume.name: vss_volumes.add(volume) + elif (drive_letter := extract_drive_letter(volume.name)) is not None: + volumes.add((drive_letter, volume)) else: volumes.add(volume) @@ -57,6 +59,12 @@ def find_fs_directories(path: Path) -> tuple[Optional[OperatingSystem], Optional return None, None +def extract_drive_letter(name: str) -> Optional[str]: + # \\.\X: in URL encoding + if len(name) == 14 and name.startswith("%5C%5C.%5C") and name.endswith("%3A"): + return name[10].lower() + + class VelociraptorLoader(DirLoader): """Load Rapid7 Velociraptor forensic image files. diff --git a/tests/loaders/test_velociraptor.py b/tests/loaders/test_velociraptor.py index f0b299c38..bf2318401 100644 --- a/tests/loaders/test_velociraptor.py +++ b/tests/loaders/test_velociraptor.py @@ -55,6 +55,9 @@ def test_velociraptor_loader_windows_ntfs(sub_dir: str, target_bare: Target, tmp loader.map(target_bare) target_bare.apply() + assert "sysvol" in target_bare.fs.mounts + assert "c:" in target_bare.fs.mounts + usnjrnl_records = 0 for fs in target_bare.filesystems: if isinstance(fs, NtfsFilesystem): @@ -80,6 +83,9 @@ def test_velociraptor_loader_windows_ntfs_zip(sub_dir: str, target_bare: Target, loader.map(target_bare) target_bare.apply() + assert "sysvol" in target_bare.fs.mounts + assert "c:" in target_bare.fs.mounts + usnjrnl_records = 0 for fs in target_bare.filesystems: if isinstance(fs, NtfsFilesystem): From cfc240fd43f5e83c9bba24dc90268e4e10dd32e7 Mon Sep 17 00:00:00 2001 From: Schamper <1254028+Schamper@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:58:47 +0100 Subject: [PATCH 2/3] Fix kape and tanium tests --- tests/loaders/test_kape.py | 5 ++++- tests/loaders/test_tanium.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/loaders/test_kape.py b/tests/loaders/test_kape.py index 15c7fffc6..8c6397205 100644 --- a/tests/loaders/test_kape.py +++ b/tests/loaders/test_kape.py @@ -26,7 +26,10 @@ def test_kape_loader(target_bare, tmp_path): loader = KapeLoader(root) loader.map(target_bare) + assert "sysvol" in target_bare.fs.mounts + assert "c:" in target_bare.fs.mounts + # The 3 found drive letter directories + the fake NTFS filesystem assert len(target_bare.filesystems) == 4 assert len(target_bare.fs.mounts) == 3 - assert len(list(target_bare.fs.mounts["C:"].ntfs.usnjrnl.records())) == 1 + assert len(list(target_bare.fs.mounts["c:"].ntfs.usnjrnl.records())) == 1 diff --git a/tests/loaders/test_tanium.py b/tests/loaders/test_tanium.py index 05d017be2..c1f62b433 100644 --- a/tests/loaders/test_tanium.py +++ b/tests/loaders/test_tanium.py @@ -26,7 +26,10 @@ def test_tanium_loader(target_bare, tmp_path): loader = TaniumLoader(root) loader.map(target_bare) + assert "sysvol" in target_bare.fs.mounts + assert "c:" in target_bare.fs.mounts + # The 3 found drive letter directories + the fake NTFS filesystem assert len(target_bare.filesystems) == 4 assert len(target_bare.fs.mounts) == 3 - assert len(list(target_bare.fs.mounts["C:"].ntfs.usnjrnl.records())) == 1 + assert len(list(target_bare.fs.mounts["c:"].ntfs.usnjrnl.records())) == 1 From 8a6b2a8a5b94e891fe075a92ed05c61aa414438b Mon Sep 17 00:00:00 2001 From: Schamper <1254028+Schamper@users.noreply.github.com> Date: Fri, 1 Mar 2024 15:21:40 +0100 Subject: [PATCH 3/3] Actually pay attention and fix the unit tests --- tests/loaders/test_kape.py | 6 +++++- tests/loaders/test_tanium.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/loaders/test_kape.py b/tests/loaders/test_kape.py index 8c6397205..fb224fdd8 100644 --- a/tests/loaders/test_kape.py +++ b/tests/loaders/test_kape.py @@ -25,11 +25,15 @@ def test_kape_loader(target_bare, tmp_path): loader = KapeLoader(root) loader.map(target_bare) + target_bare.apply() assert "sysvol" in target_bare.fs.mounts assert "c:" in target_bare.fs.mounts + assert "d:" in target_bare.fs.mounts + assert "e:" in target_bare.fs.mounts # The 3 found drive letter directories + the fake NTFS filesystem assert len(target_bare.filesystems) == 4 - assert len(target_bare.fs.mounts) == 3 + # The 3 found drive letters + sysvol + the fake NTFS filesystem at /$fs$ + assert len(target_bare.fs.mounts) == 5 assert len(list(target_bare.fs.mounts["c:"].ntfs.usnjrnl.records())) == 1 diff --git a/tests/loaders/test_tanium.py b/tests/loaders/test_tanium.py index c1f62b433..ee1ae0c42 100644 --- a/tests/loaders/test_tanium.py +++ b/tests/loaders/test_tanium.py @@ -25,11 +25,15 @@ def test_tanium_loader(target_bare, tmp_path): loader = TaniumLoader(root) loader.map(target_bare) + target_bare.apply() assert "sysvol" in target_bare.fs.mounts assert "c:" in target_bare.fs.mounts + assert "d:" in target_bare.fs.mounts + assert "e:" in target_bare.fs.mounts # The 3 found drive letter directories + the fake NTFS filesystem assert len(target_bare.filesystems) == 4 - assert len(target_bare.fs.mounts) == 3 + # The 3 found drive letters + sysvol + the fake NTFS filesystem at /$fs$ + assert len(target_bare.fs.mounts) == 5 assert len(list(target_bare.fs.mounts["c:"].ntfs.usnjrnl.records())) == 1