acquire 3.14.dev7__tar.gz → 3.14.dev9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {acquire-3.14.dev7/acquire.egg-info → acquire-3.14.dev9}/PKG-INFO +1 -1
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/acquire.py +117 -199
- acquire-3.14.dev9/acquire/collector.py +786 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/utils.py +40 -48
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/version.py +2 -2
- {acquire-3.14.dev7 → acquire-3.14.dev9/acquire.egg-info}/PKG-INFO +1 -1
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/conftest.py +4 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_acquire_command.py +6 -1
- acquire-3.14.dev9/tests/test_collector.py +659 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_utils.py +128 -87
- acquire-3.14.dev7/acquire/collector.py +0 -600
- acquire-3.14.dev7/tests/test_collector.py +0 -392
- {acquire-3.14.dev7 → acquire-3.14.dev9}/COPYRIGHT +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/LICENSE +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/MANIFEST.in +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/README.md +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/__init__.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/crypt.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/__init__.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/__init__.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/collect.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/exceptions.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/handles.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/named_objects.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/ntdll.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/types.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/esxi.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/gui/__init__.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/gui/base.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/gui/win32.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/hashes.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/log.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/__init__.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/base.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/dir.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/tar.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/zip.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/tools/__init__.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/tools/decrypter.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/uploaders/__init__.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/uploaders/minio.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/uploaders/plugin.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/uploaders/plugin_registry.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/volatilestream.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/SOURCES.txt +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/dependency_links.txt +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/entry_points.txt +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/requires.txt +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/top_level.txt +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/pyproject.toml +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/setup.cfg +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/__init__.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/docs/Makefile +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/docs/conf.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/docs/index.rst +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_acquire_modules.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_decryptor_funcs.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_esxi_memory.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_file_sorting.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_minio_uploader.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_misc_users.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_outputs_dir.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_outputs_tar.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_plugin.py +0 -0
- {acquire-3.14.dev7 → acquire-3.14.dev9}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: acquire
|
|
3
|
-
Version: 3.14.
|
|
3
|
+
Version: 3.14.dev9
|
|
4
4
|
Summary: A tool to quickly gather forensic artifacts from disk images or a live system into a lightweight container
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Affero General Public License v3
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import argparse
|
|
2
4
|
import enum
|
|
3
5
|
import functools
|
|
4
6
|
import io
|
|
5
7
|
import itertools
|
|
6
|
-
import json
|
|
7
8
|
import logging
|
|
8
9
|
import os
|
|
9
10
|
import platform
|
|
@@ -16,16 +17,15 @@ import urllib.request
|
|
|
16
17
|
from collections import defaultdict, namedtuple
|
|
17
18
|
from itertools import product
|
|
18
19
|
from pathlib import Path
|
|
19
|
-
from typing import Iterator, Optional, Union
|
|
20
|
+
from typing import BinaryIO, Callable, Iterator, Optional, Union
|
|
20
21
|
|
|
21
|
-
from dissect.target import Target
|
|
22
|
+
from dissect.target import Target
|
|
22
23
|
from dissect.target.filesystem import Filesystem
|
|
23
24
|
from dissect.target.filesystems import ntfs
|
|
24
25
|
from dissect.target.helpers import fsutil
|
|
25
|
-
from dissect.target.loaders.remote import RemoteStreamConnection
|
|
26
|
-
from dissect.target.loaders.targetd import TargetdLoader
|
|
27
26
|
from dissect.target.plugins.apps.webserver import iis
|
|
28
27
|
from dissect.target.plugins.os.windows.log import evt, evtx
|
|
28
|
+
from dissect.target.tools.utils import args_to_uri
|
|
29
29
|
from dissect.util.stream import RunlistStream
|
|
30
30
|
|
|
31
31
|
from acquire.collector import Collector, get_full_formatted_report, get_report_summary
|
|
@@ -92,7 +92,6 @@ MODULE_LOOKUP = {}
|
|
|
92
92
|
|
|
93
93
|
CLI_ARGS_MODULE = "cli-args"
|
|
94
94
|
|
|
95
|
-
|
|
96
95
|
log = logging.getLogger("acquire")
|
|
97
96
|
log.propagate = 0
|
|
98
97
|
log_file_handler = None
|
|
@@ -149,46 +148,48 @@ MISC_MAPPING = {
|
|
|
149
148
|
def from_user_home(target: Target, path: str) -> Iterator[str]:
|
|
150
149
|
try:
|
|
151
150
|
for user_details in target.user_details.all_with_home():
|
|
152
|
-
yield
|
|
151
|
+
yield user_details.home_path.joinpath(path).as_posix()
|
|
153
152
|
except Exception as e:
|
|
154
153
|
log.warning("Error occurred when requesting all user homes")
|
|
155
154
|
log.debug("", exc_info=e)
|
|
156
155
|
|
|
157
156
|
misc_user_homes = MISC_MAPPING.get(target.os, misc_unix_user_homes)
|
|
158
157
|
for user_dir in misc_user_homes(target):
|
|
159
|
-
yield
|
|
158
|
+
yield user_dir.joinpath(path).as_posix()
|
|
160
159
|
|
|
161
160
|
|
|
162
|
-
def iter_ntfs_filesystems(target: Target) -> Iterator[tuple[ntfs.NtfsFilesystem, str, str]]:
|
|
161
|
+
def iter_ntfs_filesystems(target: Target) -> Iterator[tuple[ntfs.NtfsFilesystem, Optional[str], str, str]]:
|
|
163
162
|
mount_lookup = defaultdict(list)
|
|
164
163
|
for mount, fs in target.fs.mounts.items():
|
|
165
164
|
mount_lookup[fs].append(mount)
|
|
166
165
|
|
|
167
|
-
sysvol = target.fs.mounts["sysvol"]
|
|
168
166
|
for fs in target.filesystems:
|
|
169
|
-
if fs in mount_lookup:
|
|
170
|
-
mountpoints = ", ".join(mount_lookup[fs])
|
|
171
|
-
else:
|
|
172
|
-
mountpoints = "No mounts"
|
|
173
|
-
|
|
174
167
|
# The attr check is needed to correctly collect fake NTFS filesystems
|
|
175
168
|
# where the MFT etc. are added to a VirtualFilesystem. This happens for
|
|
176
169
|
# instance when the target is an acquired tar target.
|
|
177
170
|
if not isinstance(fs, ntfs.NtfsFilesystem) and not hasattr(fs, "ntfs"):
|
|
178
|
-
log.warning("Skipping %s
|
|
171
|
+
log.warning("Skipping %s - not an NTFS filesystem", fs)
|
|
179
172
|
continue
|
|
180
173
|
|
|
181
|
-
if fs
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
174
|
+
if fs in mount_lookup:
|
|
175
|
+
mountpoints = mount_lookup[fs]
|
|
176
|
+
|
|
177
|
+
for main_mountpoint in mountpoints:
|
|
178
|
+
if main_mountpoint != "sysvol":
|
|
179
|
+
break
|
|
180
|
+
|
|
181
|
+
name = main_mountpoint
|
|
182
|
+
mountpoints = ", ".join(mountpoints)
|
|
185
183
|
else:
|
|
184
|
+
main_mountpoint = None
|
|
186
185
|
name = f"vol-{fs.ntfs.serial:x}"
|
|
186
|
+
mountpoints = "No mounts"
|
|
187
|
+
log.warning("Unmounted NTFS filesystem found %s (%s)", fs, name)
|
|
187
188
|
|
|
188
|
-
yield fs, name, mountpoints
|
|
189
|
+
yield fs, main_mountpoint, name, mountpoints
|
|
189
190
|
|
|
190
191
|
|
|
191
|
-
def iter_esxi_filesystems(target: Target) -> Iterator[tuple[str, str,
|
|
192
|
+
def iter_esxi_filesystems(target: Target) -> Iterator[tuple[Filesystem, str, str, Optional[str]]]:
|
|
192
193
|
for mount, fs in target.fs.mounts.items():
|
|
193
194
|
if not mount.startswith("/vmfs/volumes/"):
|
|
194
195
|
continue
|
|
@@ -200,11 +201,11 @@ def iter_esxi_filesystems(target: Target) -> Iterator[tuple[str, str, Filesystem
|
|
|
200
201
|
elif fs.__type__ == "vmfs":
|
|
201
202
|
name = fs.vmfs.label
|
|
202
203
|
|
|
203
|
-
yield uuid, name
|
|
204
|
+
yield fs, mount, uuid, name
|
|
204
205
|
|
|
205
206
|
|
|
206
|
-
def register_module(*args, **kwargs):
|
|
207
|
-
def wrapper(module_cls):
|
|
207
|
+
def register_module(*args, **kwargs) -> Callable[[type[Module]], type[Module]]:
|
|
208
|
+
def wrapper(module_cls: type[Module]) -> type[Module]:
|
|
208
209
|
name = module_cls.__name__
|
|
209
210
|
|
|
210
211
|
if name in MODULES:
|
|
@@ -228,8 +229,8 @@ def register_module(*args, **kwargs):
|
|
|
228
229
|
return wrapper
|
|
229
230
|
|
|
230
231
|
|
|
231
|
-
def module_arg(*args, **kwargs):
|
|
232
|
-
def wrapper(module_cls):
|
|
232
|
+
def module_arg(*args, **kwargs) -> Callable[[type[Module]], type[Module]]:
|
|
233
|
+
def wrapper(module_cls: type[Module]) -> type[Module]:
|
|
233
234
|
if not hasattr(module_cls, "__cli_args__"):
|
|
234
235
|
module_cls.__cli_args__ = []
|
|
235
236
|
module_cls.__cli_args__.append((args, kwargs))
|
|
@@ -238,7 +239,7 @@ def module_arg(*args, **kwargs):
|
|
|
238
239
|
return wrapper
|
|
239
240
|
|
|
240
241
|
|
|
241
|
-
def local_module(cls):
|
|
242
|
+
def local_module(cls: type[object]) -> object:
|
|
242
243
|
"""A decorator that sets property `__local__` on a module class to mark it for local target only"""
|
|
243
244
|
cls.__local__ = True
|
|
244
245
|
return cls
|
|
@@ -308,22 +309,25 @@ class NTFS(Module):
|
|
|
308
309
|
|
|
309
310
|
@classmethod
|
|
310
311
|
def _run(cls, target: Target, cli_args: argparse.Namespace, collector: Collector) -> None:
|
|
311
|
-
for fs, name, mountpoints in iter_ntfs_filesystems(target):
|
|
312
|
-
log.info("Acquiring %s (%s)", fs, mountpoints)
|
|
312
|
+
for fs, main_mountpoint, name, mountpoints in iter_ntfs_filesystems(target):
|
|
313
|
+
log.info("Acquiring from %s as %s (%s)", fs, name, mountpoints)
|
|
314
|
+
|
|
315
|
+
for filename in ("$MFT", "$Boot", "$Secure:$SDS"):
|
|
316
|
+
if main_mountpoint is not None:
|
|
317
|
+
path = fsutil.join(main_mountpoint, filename)
|
|
318
|
+
collector.collect_path(path)
|
|
313
319
|
|
|
314
|
-
|
|
315
|
-
|
|
320
|
+
else:
|
|
321
|
+
# In case the NTFS filesystem is not mounted, which should not occur but
|
|
322
|
+
# iter_ntfs_filesystems allows for the possibility, we fall back to raw file
|
|
323
|
+
# collection.
|
|
324
|
+
collector.collect_file_raw(filename, fs, name)
|
|
316
325
|
|
|
317
326
|
cls.collect_usnjrnl(collector, fs, name)
|
|
318
|
-
cls.collect_ntfs_secure(collector, fs, name)
|
|
319
327
|
|
|
320
328
|
@classmethod
|
|
321
329
|
def collect_usnjrnl(cls, collector: Collector, fs: Filesystem, name: str) -> None:
|
|
322
|
-
|
|
323
|
-
usnjrnl_path = fs.path("$Extend/$Usnjrnl:$J")
|
|
324
|
-
entry = usnjrnl_path.get()
|
|
325
|
-
journal = entry.open()
|
|
326
|
-
|
|
330
|
+
def usnjrnl_accessor(journal: BinaryIO) -> tuple[BinaryIO, int]:
|
|
327
331
|
# If the filesystem is a virtual NTFS filesystem, journal will be
|
|
328
332
|
# plain BinaryIO, not a RunlistStream.
|
|
329
333
|
if isinstance(journal, RunlistStream):
|
|
@@ -331,57 +335,18 @@ class NTFS(Module):
|
|
|
331
335
|
while journal.runlist[i][0] is None:
|
|
332
336
|
journal.seek(journal.runlist[i][1] * journal.block_size, io.SEEK_CUR)
|
|
333
337
|
i += 1
|
|
338
|
+
size = journal.size - journal.tell()
|
|
339
|
+
else:
|
|
340
|
+
size = journal.size
|
|
334
341
|
|
|
335
|
-
|
|
336
|
-
# collector.collect_file()
|
|
337
|
-
outpath = collector._output_path(f"{name}/$Extend/$Usnjrnl:$J")
|
|
338
|
-
|
|
339
|
-
collector.output.write(
|
|
340
|
-
outpath,
|
|
341
|
-
journal,
|
|
342
|
-
size=journal.size - journal.tell(),
|
|
343
|
-
entry=entry,
|
|
344
|
-
)
|
|
345
|
-
collector.report.add_file_collected(cls.__name__, usnjrnl_path)
|
|
346
|
-
result = "OK"
|
|
347
|
-
except exceptions.FileNotFoundError:
|
|
348
|
-
collector.report.add_file_missing(cls.__name__, usnjrnl_path)
|
|
349
|
-
result = "File not found"
|
|
350
|
-
except Exception as err:
|
|
351
|
-
log.debug("Failed to acquire UsnJrnl", exc_info=True)
|
|
352
|
-
collector.report.add_file_failed(cls.__name__, usnjrnl_path)
|
|
353
|
-
result = repr(err)
|
|
354
|
-
|
|
355
|
-
log.info("- Collecting file $Extend/$Usnjrnl:$J: %s", result)
|
|
356
|
-
|
|
357
|
-
@classmethod
|
|
358
|
-
def collect_ntfs_secure(cls, collector: Collector, fs: Filesystem, name: str) -> None:
|
|
359
|
-
try:
|
|
360
|
-
secure_path = fs.path("$Secure:$SDS")
|
|
361
|
-
entry = secure_path.get()
|
|
362
|
-
sds = entry.open()
|
|
363
|
-
|
|
364
|
-
# Use the same method to construct the output path as is used in
|
|
365
|
-
# collector.collect_file()
|
|
366
|
-
outpath = collector._output_path(f"{name}/$Secure:$SDS")
|
|
367
|
-
|
|
368
|
-
collector.output.write(
|
|
369
|
-
outpath,
|
|
370
|
-
sds,
|
|
371
|
-
size=sds.size,
|
|
372
|
-
entry=entry,
|
|
373
|
-
)
|
|
374
|
-
collector.report.add_file_collected(cls.__name__, secure_path)
|
|
375
|
-
result = "OK"
|
|
376
|
-
except FileNotFoundError:
|
|
377
|
-
collector.report.add_file_missing(cls.__name__, secure_path)
|
|
378
|
-
result = "File not found"
|
|
379
|
-
except Exception as err:
|
|
380
|
-
log.debug("Failed to acquire SDS", exc_info=True)
|
|
381
|
-
collector.report.add_file_failed(cls.__name__, secure_path)
|
|
382
|
-
result = repr(err)
|
|
342
|
+
return (journal, size)
|
|
383
343
|
|
|
384
|
-
|
|
344
|
+
collector.collect_file_raw(
|
|
345
|
+
"$Extend/$Usnjrnl:$J",
|
|
346
|
+
fs,
|
|
347
|
+
name,
|
|
348
|
+
file_accessor=usnjrnl_accessor,
|
|
349
|
+
)
|
|
385
350
|
|
|
386
351
|
|
|
387
352
|
@register_module("-r", "--registry")
|
|
@@ -722,13 +687,20 @@ class RecycleBin(Module):
|
|
|
722
687
|
patterns.extend(["$Recycle.Bin/$R*", "$Recycle.Bin/*/$R*", "RECYCLE*/D*"])
|
|
723
688
|
|
|
724
689
|
with collector.file_filter(large_files_filter):
|
|
725
|
-
for fs, name, mountpoints in iter_ntfs_filesystems(target):
|
|
726
|
-
log.info("Acquiring recycle bin from %s (%s)", fs, mountpoints)
|
|
690
|
+
for fs, main_mountpoint, name, mountpoints in iter_ntfs_filesystems(target):
|
|
691
|
+
log.info("Acquiring recycle bin from %s as %s (%s)", fs, name, mountpoints)
|
|
727
692
|
|
|
728
693
|
for pattern in patterns:
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
694
|
+
if main_mountpoint is not None:
|
|
695
|
+
pattern = fsutil.join(main_mountpoint, pattern)
|
|
696
|
+
collector.collect_glob(pattern)
|
|
697
|
+
else:
|
|
698
|
+
# In case the NTFS filesystem is not mounted, which should not occur but
|
|
699
|
+
# iter_ntfs_filesystems allows for the possibility, we fall back to raw file
|
|
700
|
+
# collection.
|
|
701
|
+
for entry in fs.path().glob(pattern):
|
|
702
|
+
if entry.is_file():
|
|
703
|
+
collector.collect_file_raw(fs, entry, name)
|
|
732
704
|
|
|
733
705
|
|
|
734
706
|
@register_module("--drivers")
|
|
@@ -1294,8 +1266,9 @@ class Boot(Module):
|
|
|
1294
1266
|
|
|
1295
1267
|
|
|
1296
1268
|
def private_key_filter(path: fsutil.TargetPath) -> bool:
|
|
1297
|
-
|
|
1298
|
-
|
|
1269
|
+
if path.is_file() and not path.is_symlink():
|
|
1270
|
+
with path.open("rt") as file:
|
|
1271
|
+
return "PRIVATE KEY" in file.readline()
|
|
1299
1272
|
|
|
1300
1273
|
|
|
1301
1274
|
@register_module("--home")
|
|
@@ -1441,21 +1414,24 @@ class Bootbanks(Module):
|
|
|
1441
1414
|
"bootbank": "BOOTBANK1",
|
|
1442
1415
|
"altbootbank": "BOOTBANK2",
|
|
1443
1416
|
}
|
|
1444
|
-
boot_fs =
|
|
1417
|
+
boot_fs = {}
|
|
1445
1418
|
|
|
1446
1419
|
for boot_dir, boot_vol in boot_dirs.items():
|
|
1447
1420
|
dir_path = target.fs.path(boot_dir)
|
|
1448
1421
|
if dir_path.is_symlink() and dir_path.exists():
|
|
1449
1422
|
dst = dir_path.readlink()
|
|
1450
|
-
|
|
1423
|
+
fs = dst.get().top.fs
|
|
1424
|
+
boot_fs[fs] = boot_vol
|
|
1451
1425
|
|
|
1452
|
-
for
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1426
|
+
for fs, mountpoint, uuid, _ in iter_esxi_filesystems(target):
|
|
1427
|
+
if fs in boot_fs:
|
|
1428
|
+
name = boot_fs[fs]
|
|
1429
|
+
log.info("Acquiring %s (%s)", mountpoint, name)
|
|
1430
|
+
mountpoint_len = len(mountpoint)
|
|
1431
|
+
base = f"fs/{uuid}:{name}"
|
|
1432
|
+
for path in target.fs.path(mountpoint).rglob("*"):
|
|
1433
|
+
outpath = path.as_posix()[mountpoint_len:]
|
|
1434
|
+
collector.collect_path(path, outpath=outpath, base=base)
|
|
1459
1435
|
|
|
1460
1436
|
|
|
1461
1437
|
@register_module("--esxi")
|
|
@@ -1478,16 +1454,16 @@ class VMFS(Module):
|
|
|
1478
1454
|
|
|
1479
1455
|
@classmethod
|
|
1480
1456
|
def _run(cls, target: Target, cli_args: argparse.Namespace, collector: Collector) -> None:
|
|
1481
|
-
for uuid, name
|
|
1457
|
+
for fs, mountpoint, uuid, name in iter_esxi_filesystems(target):
|
|
1482
1458
|
if not fs.__type__ == "vmfs":
|
|
1483
1459
|
continue
|
|
1484
1460
|
|
|
1485
|
-
log.info("Acquiring
|
|
1461
|
+
log.info("Acquiring %s (%s)", mountpoint, name)
|
|
1462
|
+
mountpoint_len = len(mountpoint)
|
|
1486
1463
|
base = f"fs/{uuid}:{name}"
|
|
1487
|
-
for path in fs.path(
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
collector.collect_file(path, outpath=path, base=base)
|
|
1464
|
+
for path in target.fs.path(mountpoint).glob("*.sf"):
|
|
1465
|
+
outpath = path.as_posix()[mountpoint_len:]
|
|
1466
|
+
collector.collect_path(path, outpath=outpath, base=base)
|
|
1491
1467
|
|
|
1492
1468
|
|
|
1493
1469
|
@register_module("--activities-cache")
|
|
@@ -1658,45 +1634,6 @@ def print_acquire_warning(target: Target) -> None:
|
|
|
1658
1634
|
log.warning("========================================== WARNING ==========================================")
|
|
1659
1635
|
|
|
1660
1636
|
|
|
1661
|
-
def modargs2json(args: argparse.Namespace) -> dict:
|
|
1662
|
-
json_opts = {}
|
|
1663
|
-
for module in MODULES.values():
|
|
1664
|
-
cli_arg = module.__cli_args__[-1:][0][1]
|
|
1665
|
-
if opt := cli_arg.get("dest"):
|
|
1666
|
-
json_opts[opt] = getattr(args, opt)
|
|
1667
|
-
return json_opts
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
def acquire_target(target: Target, *args, **kwargs) -> list[str]:
|
|
1671
|
-
if isinstance(target._loader, TargetdLoader):
|
|
1672
|
-
files = acquire_target_targetd(target, *args, **kwargs)
|
|
1673
|
-
else:
|
|
1674
|
-
files = acquire_target_regular(target, *args, **kwargs)
|
|
1675
|
-
return files
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
def acquire_target_targetd(target: Target, args: argparse.Namespace, output_ts: Optional[str] = None) -> list[str]:
|
|
1679
|
-
files = []
|
|
1680
|
-
# debug logs contain references to flow objects and will give errors
|
|
1681
|
-
logging.getLogger().setLevel(logging.CRITICAL)
|
|
1682
|
-
if not len(target.hostname()):
|
|
1683
|
-
log.error("Unable to initialize targetd.")
|
|
1684
|
-
return files
|
|
1685
|
-
json_opts = modargs2json(args)
|
|
1686
|
-
json_opts["profile"] = args.profile
|
|
1687
|
-
json_opts["file"] = args.file
|
|
1688
|
-
json_opts["directory"] = args.directory
|
|
1689
|
-
json_opts["glob"] = args.glob
|
|
1690
|
-
m = {"targetd-meta": "acquire", "args": json_opts}
|
|
1691
|
-
json_str = json.dumps(m)
|
|
1692
|
-
targetd = target._loader.instance.client
|
|
1693
|
-
targetd.send_message(json_str.encode("utf-8"))
|
|
1694
|
-
targetd.sync()
|
|
1695
|
-
for stream in targetd.streams:
|
|
1696
|
-
files.append(stream.out_file)
|
|
1697
|
-
return files
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
1637
|
def _add_modules_for_profile(choice: str, operating_system: str, profile: dict, msg: str) -> Optional[dict]:
|
|
1701
1638
|
modules_selected = dict()
|
|
1702
1639
|
|
|
@@ -1712,7 +1649,7 @@ def _add_modules_for_profile(choice: str, operating_system: str, profile: dict,
|
|
|
1712
1649
|
return modules_selected
|
|
1713
1650
|
|
|
1714
1651
|
|
|
1715
|
-
def
|
|
1652
|
+
def acquire_target(target: Target, args: argparse.Namespace, output_ts: Optional[str] = None) -> list[str]:
|
|
1716
1653
|
acquire_gui = GUI()
|
|
1717
1654
|
files = []
|
|
1718
1655
|
output_ts = output_ts or get_utc_now_str()
|
|
@@ -1727,7 +1664,7 @@ def acquire_target_regular(target: Target, args: argparse.Namespace, output_ts:
|
|
|
1727
1664
|
if log_file:
|
|
1728
1665
|
files.append(log_file)
|
|
1729
1666
|
if target.path.name == "local":
|
|
1730
|
-
skip_list.add(normalize_path(target, log_file,
|
|
1667
|
+
skip_list.add(normalize_path(target, log_file, resolve_parents=True, preserve_case=False))
|
|
1731
1668
|
|
|
1732
1669
|
print_disks_overview(target)
|
|
1733
1670
|
print_volumes_overview(target)
|
|
@@ -1817,7 +1754,7 @@ def acquire_target_regular(target: Target, args: argparse.Namespace, output_ts:
|
|
|
1817
1754
|
log.info("Logging to file %s", log_path)
|
|
1818
1755
|
files = [log_file_handler.baseFilename]
|
|
1819
1756
|
if target.path.name == "local":
|
|
1820
|
-
skip_list = {normalize_path(target, log_path,
|
|
1757
|
+
skip_list = {normalize_path(target, log_path, resolve_parents=True, preserve_case=False)}
|
|
1821
1758
|
|
|
1822
1759
|
output_path = args.output or args.output_file
|
|
1823
1760
|
if output_path.is_dir():
|
|
@@ -1833,7 +1770,7 @@ def acquire_target_regular(target: Target, args: argparse.Namespace, output_ts:
|
|
|
1833
1770
|
)
|
|
1834
1771
|
files.append(output.path)
|
|
1835
1772
|
if target.path.name == "local":
|
|
1836
|
-
skip_list.add(normalize_path(target, output.path,
|
|
1773
|
+
skip_list.add(normalize_path(target, output.path, resolve_parents=True, preserve_case=False))
|
|
1837
1774
|
|
|
1838
1775
|
log.info("Writing output to %s", output.path)
|
|
1839
1776
|
if skip_list:
|
|
@@ -2092,7 +2029,7 @@ VOLATILE = {
|
|
|
2092
2029
|
|
|
2093
2030
|
def main() -> None:
|
|
2094
2031
|
parser = create_argument_parser(PROFILES, VOLATILE, MODULES)
|
|
2095
|
-
args = parse_acquire_args(parser, config=CONFIG)
|
|
2032
|
+
args, rest = parse_acquire_args(parser, config=CONFIG)
|
|
2096
2033
|
|
|
2097
2034
|
# start GUI if requested through CLI / config
|
|
2098
2035
|
flavour = None
|
|
@@ -2144,26 +2081,6 @@ def main() -> None:
|
|
|
2144
2081
|
log.exception(err)
|
|
2145
2082
|
parser.exit(1)
|
|
2146
2083
|
|
|
2147
|
-
if args.targetd:
|
|
2148
|
-
from targetd.tools.targetd import start_client
|
|
2149
|
-
|
|
2150
|
-
# set @auto hostname to real hostname
|
|
2151
|
-
if args.targetd_hostname == "@auto":
|
|
2152
|
-
args.targetd_hostname = f"/host/{Target.open('local').hostname}"
|
|
2153
|
-
|
|
2154
|
-
config = {
|
|
2155
|
-
"function": args.targetd_func,
|
|
2156
|
-
"topics": [args.targetd_hostname, args.targetd_groupname, args.targetd_globalname],
|
|
2157
|
-
"link": args.targetd_link,
|
|
2158
|
-
"address": args.targetd_ip,
|
|
2159
|
-
"port": args.targetd_port,
|
|
2160
|
-
"cacert_str": args.targetd_cacert,
|
|
2161
|
-
"service": args.targetd_func == "agent",
|
|
2162
|
-
"cacert": None,
|
|
2163
|
-
}
|
|
2164
|
-
start_client(args, presets=config)
|
|
2165
|
-
return
|
|
2166
|
-
|
|
2167
2084
|
if args.upload:
|
|
2168
2085
|
try:
|
|
2169
2086
|
upload_files(args.upload, args.upload_plugin, args.no_proxy)
|
|
@@ -2171,43 +2088,44 @@ def main() -> None:
|
|
|
2171
2088
|
log.exception("Failed to upload files")
|
|
2172
2089
|
return
|
|
2173
2090
|
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
target_query.update({"force-directory-fs": 1})
|
|
2091
|
+
target_paths = []
|
|
2092
|
+
for target_path in args.targets:
|
|
2093
|
+
target_path = args_to_uri([target_path], args.loader, rest)[0] if args.loader else target_path
|
|
2094
|
+
if target_path == "local":
|
|
2095
|
+
target_query = {}
|
|
2096
|
+
if args.force_fallback:
|
|
2097
|
+
target_query.update({"force-directory-fs": 1})
|
|
2182
2098
|
|
|
2183
|
-
|
|
2184
|
-
|
|
2099
|
+
if args.fallback:
|
|
2100
|
+
target_query.update({"fallback-to-directory-fs": 1})
|
|
2185
2101
|
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
log.info("Loading target %s", target_path)
|
|
2102
|
+
target_query = urllib.parse.urlencode(target_query)
|
|
2103
|
+
target_path = f"{target_path}?{target_query}"
|
|
2104
|
+
target_paths.append(target_path)
|
|
2190
2105
|
|
|
2191
2106
|
try:
|
|
2192
|
-
|
|
2193
|
-
|
|
2107
|
+
target_name = "Unknown" # just in case open_all already fails
|
|
2108
|
+
for target in Target.open_all(target_paths):
|
|
2109
|
+
target_name = "Unknown" # overwrite previous target name
|
|
2110
|
+
target_name = target.name
|
|
2111
|
+
log.info("Loading target %s", target_name)
|
|
2112
|
+
log.info(target)
|
|
2113
|
+
if target.os == "esxi" and target.name == "local":
|
|
2114
|
+
# Loader found that we are running on an esxi host
|
|
2115
|
+
# Perform operations to "enhance" memory
|
|
2116
|
+
with esxi_memory_context_manager():
|
|
2117
|
+
acquire_children_and_targets(target, args)
|
|
2118
|
+
else:
|
|
2119
|
+
acquire_children_and_targets(target, args)
|
|
2194
2120
|
except Exception:
|
|
2195
2121
|
if not is_user_admin():
|
|
2196
|
-
log.error("Failed to load target, try re-running as administrator/root
|
|
2122
|
+
log.error("Failed to load target: %s, try re-running as administrator/root", target_name)
|
|
2197
2123
|
acquire_gui.message("This application must be run as administrator.")
|
|
2198
2124
|
acquire_gui.wait_for_quit()
|
|
2199
2125
|
parser.exit(1)
|
|
2200
|
-
log.exception("Failed to load target")
|
|
2126
|
+
log.exception("Failed to load target: %s", target_name)
|
|
2201
2127
|
raise
|
|
2202
2128
|
|
|
2203
|
-
if target.os == "esxi" and target.name == "local":
|
|
2204
|
-
# Loader found that we are running on an esxi host
|
|
2205
|
-
# Perform operations to "enhance" memory
|
|
2206
|
-
with esxi_memory_context_manager():
|
|
2207
|
-
acquire_children_and_targets(target, args)
|
|
2208
|
-
else:
|
|
2209
|
-
acquire_children_and_targets(target, args)
|
|
2210
|
-
|
|
2211
2129
|
|
|
2212
2130
|
def load_child(target: Target, child_path: Path) -> None:
|
|
2213
2131
|
log.info("")
|