Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mount volumes to drive letters in Velociraptor loader #560

Merged
merged 5 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions dissect/target/loaders/dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ""
Expand All @@ -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:
Expand All @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions dissect/target/loaders/velociraptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,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)

Expand All @@ -54,6 +56,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.

Expand Down
11 changes: 9 additions & 2 deletions tests/loaders/test_kape.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +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
assert len(list(target_bare.fs.mounts["C:"].ntfs.usnjrnl.records())) == 1
# 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
11 changes: 9 additions & 2 deletions tests/loaders/test_tanium.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +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
assert len(list(target_bare.fs.mounts["C:"].ntfs.usnjrnl.records())) == 1
# 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
6 changes: 6 additions & 0 deletions tests/loaders/test_velociraptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand Down
Loading