dissect.target 3.20.dev48__py3-none-any.whl → 3.20.dev49__py3-none-any.whl
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.
- dissect/target/plugins/os/unix/_os.py +43 -32
- dissect/target/plugins/os/unix/linux/fortios/_os.py +68 -64
- dissect/target/plugins/os/windows/_os.py +9 -9
- {dissect.target-3.20.dev48.dist-info → dissect.target-3.20.dev49.dist-info}/METADATA +1 -1
- {dissect.target-3.20.dev48.dist-info → dissect.target-3.20.dev49.dist-info}/RECORD +10 -10
- {dissect.target-3.20.dev48.dist-info → dissect.target-3.20.dev49.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev48.dist-info → dissect.target-3.20.dev49.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev48.dist-info → dissect.target-3.20.dev49.dist-info}/WHEEL +0 -0
- {dissect.target-3.20.dev48.dist-info → dissect.target-3.20.dev49.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.20.dev48.dist-info → dissect.target-3.20.dev49.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,6 @@ import logging
|
|
4
4
|
import re
|
5
5
|
import uuid
|
6
6
|
from pathlib import Path
|
7
|
-
from struct import unpack
|
8
7
|
from typing import Iterator
|
9
8
|
|
10
9
|
from flow.record.fieldtypes import posix_path
|
@@ -19,6 +18,25 @@ from dissect.target.target import Target
|
|
19
18
|
log = logging.getLogger(__name__)
|
20
19
|
|
21
20
|
|
21
|
+
# https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#ISA
|
22
|
+
ARCH_MAP = {
|
23
|
+
0x00: "unknown",
|
24
|
+
0x02: "sparc",
|
25
|
+
0x03: "x86",
|
26
|
+
0x08: "mips",
|
27
|
+
0x14: "powerpc32",
|
28
|
+
0x15: "powerpc64",
|
29
|
+
0x16: "s390", # and s390x
|
30
|
+
0x28: "aarch32", # armv7
|
31
|
+
0x2A: "superh",
|
32
|
+
0x32: "ia-64",
|
33
|
+
0x3E: "x86_64",
|
34
|
+
0xB7: "aarch64", # armv8
|
35
|
+
0xF3: "riscv64",
|
36
|
+
0xF7: "bpf",
|
37
|
+
}
|
38
|
+
|
39
|
+
|
22
40
|
class UnixPlugin(OSPlugin):
|
23
41
|
def __init__(self, target: Target):
|
24
42
|
super().__init__(target)
|
@@ -301,37 +319,30 @@ class UnixPlugin(OSPlugin):
|
|
301
319
|
continue
|
302
320
|
return os_release
|
303
321
|
|
304
|
-
def _get_architecture(self, os: str = "unix", path: str = "/bin/ls") -> str | None:
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
arch = unpack("H", fh.read(2))[0]
|
329
|
-
arch = arch_strings.get(arch)
|
330
|
-
|
331
|
-
if bits == 1: # 32 bit system
|
332
|
-
return f"{arch}_32-{os}"
|
333
|
-
else:
|
334
|
-
return f"{arch}-{os}"
|
322
|
+
def _get_architecture(self, os: str = "unix", path: Path | str = "/bin/ls") -> str | None:
|
323
|
+
"""Determine architecture by reading an ELF header of a binary on the target.
|
324
|
+
|
325
|
+
Resources:
|
326
|
+
- https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#ISA
|
327
|
+
"""
|
328
|
+
|
329
|
+
if not isinstance(path, TargetPath):
|
330
|
+
for fs in [self.target.fs, *self.target.filesystems]:
|
331
|
+
if (path := fs.path(path)).exists():
|
332
|
+
break
|
333
|
+
|
334
|
+
if not path.exists():
|
335
|
+
return
|
336
|
+
|
337
|
+
fh = path.open("rb")
|
338
|
+
fh.seek(4) # ELF - e_ident[EI_CLASS]
|
339
|
+
bits = fh.read(1)[0]
|
340
|
+
|
341
|
+
fh.seek(18) # ELF - e_machine
|
342
|
+
e_machine = int.from_bytes(fh.read(2), "little")
|
343
|
+
arch = ARCH_MAP.get(e_machine, "unknown")
|
344
|
+
|
345
|
+
return f"{arch}_32-{os}" if bits == 1 and not arch[-2:] == "32" else f"{arch}-{os}"
|
335
346
|
|
336
347
|
|
337
348
|
def parse_fstab(
|
@@ -6,7 +6,7 @@ from base64 import b64decode
|
|
6
6
|
from datetime import datetime
|
7
7
|
from io import BytesIO
|
8
8
|
from tarfile import ReadError
|
9
|
-
from typing import BinaryIO, Iterator,
|
9
|
+
from typing import BinaryIO, Iterator, TextIO
|
10
10
|
|
11
11
|
from dissect.util import cpio
|
12
12
|
from dissect.util.compression import xz
|
@@ -73,10 +73,11 @@ class FortiOSPlugin(LinuxPlugin):
|
|
73
73
|
return config
|
74
74
|
|
75
75
|
@classmethod
|
76
|
-
def detect(cls, target: Target) ->
|
76
|
+
def detect(cls, target: Target) -> Filesystem | None:
|
77
77
|
for fs in target.filesystems:
|
78
|
-
# Tested on FortiGate
|
79
|
-
|
78
|
+
# Tested on FortiGate, FortiAnalyzer and FortiManager.
|
79
|
+
# Other Fortinet devices may look different.
|
80
|
+
if fs.exists("/rootfs.gz") and (any(map(fs.exists, (".fgtsum", ".fmg_sign", "flatkc", "system.conf")))):
|
80
81
|
return fs
|
81
82
|
|
82
83
|
@classmethod
|
@@ -212,7 +213,7 @@ class FortiOSPlugin(LinuxPlugin):
|
|
212
213
|
return "FortiOS Unknown"
|
213
214
|
|
214
215
|
@export(record=FortiOSUserRecord)
|
215
|
-
def users(self) -> Iterator[
|
216
|
+
def users(self) -> Iterator[FortiOSUserRecord | UnixUserRecord]:
|
216
217
|
"""Return local users of the FortiOS system."""
|
217
218
|
|
218
219
|
# Possible unix-like users
|
@@ -224,7 +225,7 @@ class FortiOSPlugin(LinuxPlugin):
|
|
224
225
|
yield FortiOSUserRecord(
|
225
226
|
name=username,
|
226
227
|
password=":".join(entry.get("password", [])),
|
227
|
-
groups=
|
228
|
+
groups=list(entry.get("accprofile", [])),
|
228
229
|
home="/root",
|
229
230
|
_target=self.target,
|
230
231
|
)
|
@@ -233,69 +234,72 @@ class FortiOSPlugin(LinuxPlugin):
|
|
233
234
|
self.target.log.debug("", exc_info=e)
|
234
235
|
|
235
236
|
# FortiManager administrative users
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
237
|
+
if self._config.get("global-config", {}).get("system", {}).get("admin", {}).get("user"):
|
238
|
+
try:
|
239
|
+
for username, entry in self._config["global-config"]["system"]["admin"]["user"].items():
|
240
|
+
yield FortiOSUserRecord(
|
241
|
+
name=username,
|
242
|
+
password=":".join(entry.get("password", [])),
|
243
|
+
groups=list(entry.get("profileid", [])),
|
244
|
+
home="/root",
|
245
|
+
_target=self.target,
|
246
|
+
)
|
247
|
+
except KeyError as e:
|
248
|
+
self.target.log.warning("Exception while parsing FortiManager admin users")
|
249
|
+
self.target.log.debug("", exc_info=e)
|
250
|
+
|
251
|
+
if self._config.get("root-config"):
|
252
|
+
# Local users
|
253
|
+
try:
|
254
|
+
local_groups = local_groups_to_users(self._config["root-config"]["user"]["group"])
|
255
|
+
for username, entry in self._config["root-config"]["user"].get("local", {}).items():
|
256
|
+
try:
|
257
|
+
password = decrypt_password(entry["passwd"][-1])
|
258
|
+
except (ValueError, RuntimeError):
|
259
|
+
password = ":".join(entry.get("passwd", []))
|
260
|
+
|
261
|
+
yield FortiOSUserRecord(
|
262
|
+
name=username,
|
263
|
+
password=password,
|
264
|
+
groups=local_groups.get(username, []),
|
265
|
+
home=None,
|
266
|
+
_target=self.target,
|
267
|
+
)
|
268
|
+
except KeyError as e:
|
269
|
+
self.target.log.warning("Exception while parsing FortiOS local users")
|
270
|
+
self.target.log.debug("", exc_info=e)
|
271
|
+
|
272
|
+
# Temporary guest users
|
273
|
+
try:
|
274
|
+
for _, entry in (
|
275
|
+
self._config["root-config"]["user"]["group"].get("guestgroup", {}).get("guest", {}).items()
|
276
|
+
):
|
277
|
+
try:
|
278
|
+
password = decrypt_password(entry.get("password")[-1])
|
279
|
+
except (ValueError, RuntimeError):
|
280
|
+
password = ":".join(entry.get("password"))
|
281
|
+
|
282
|
+
yield FortiOSUserRecord(
|
283
|
+
name=entry["user-id"][0],
|
284
|
+
password=password,
|
285
|
+
groups=["guestgroup"],
|
286
|
+
home=None,
|
287
|
+
_target=self.target,
|
288
|
+
)
|
289
|
+
except KeyError as e:
|
290
|
+
self.target.log.warning("Exception while parsing FortiOS temporary guest users")
|
291
|
+
self.target.log.debug("", exc_info=e)
|
287
292
|
|
288
293
|
@export(property=True)
|
289
294
|
def os(self) -> str:
|
290
295
|
return OperatingSystem.FORTIOS.value
|
291
296
|
|
292
297
|
@export(property=True)
|
293
|
-
def architecture(self) ->
|
298
|
+
def architecture(self) -> str | None:
|
294
299
|
"""Return architecture FortiOS runs on."""
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
return self._get_architecture(path=path)
|
300
|
+
for path in ["/lib/libav.so", "/bin/ctr", "/bin/grep"]:
|
301
|
+
if (bin := self.target.fs.path(path)).exists():
|
302
|
+
return self._get_architecture(path=bin)
|
299
303
|
|
300
304
|
|
301
305
|
class ConfigNode(dict):
|
@@ -528,7 +532,7 @@ def decrypt_rootfs(fh: BinaryIO, key: bytes, iv: bytes) -> BinaryIO:
|
|
528
532
|
return BytesIO(result)
|
529
533
|
|
530
534
|
|
531
|
-
def _kdf_7_4_x(key_data:
|
535
|
+
def _kdf_7_4_x(key_data: str | bytes) -> tuple[bytes, bytes]:
|
532
536
|
"""Derive 32 byte key and 16 byte IV from 32 byte seed.
|
533
537
|
|
534
538
|
As the IV needs to be 16 bytes, we return the first 16 bytes of the sha256 hash.
|
@@ -542,7 +546,7 @@ def _kdf_7_4_x(key_data: Union[str, bytes]) -> tuple[bytes, bytes]:
|
|
542
546
|
return key, iv
|
543
547
|
|
544
548
|
|
545
|
-
def get_kernel_hash(sysvol: Filesystem) ->
|
549
|
+
def get_kernel_hash(sysvol: Filesystem) -> str | None:
|
546
550
|
"""Return the SHA256 hash of the (compressed) kernel."""
|
547
551
|
kernel_files = ["flatkc", "vmlinuz", "vmlinux"]
|
548
552
|
for k in kernel_files:
|
@@ -12,6 +12,14 @@ from dissect.target.helpers.record import WindowsUserRecord
|
|
12
12
|
from dissect.target.plugin import OperatingSystem, OSPlugin, export
|
13
13
|
from dissect.target.target import Target
|
14
14
|
|
15
|
+
ARCH_MAP = {
|
16
|
+
"x86": 32,
|
17
|
+
"IA64": 64,
|
18
|
+
"ARM64": 64,
|
19
|
+
"EM64T": 64,
|
20
|
+
"AMD64": 64,
|
21
|
+
}
|
22
|
+
|
15
23
|
|
16
24
|
class WindowsPlugin(OSPlugin):
|
17
25
|
CURRENT_VERSION_KEY = "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion"
|
@@ -265,19 +273,11 @@ class WindowsPlugin(OSPlugin):
|
|
265
273
|
Dict: arch: architecture, bitness: bits
|
266
274
|
"""
|
267
275
|
|
268
|
-
arch_strings = {
|
269
|
-
"x86": 32,
|
270
|
-
"IA64": 64,
|
271
|
-
"ARM64": 64,
|
272
|
-
"EM64T": 64,
|
273
|
-
"AMD64": 64,
|
274
|
-
}
|
275
|
-
|
276
276
|
key = "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"
|
277
277
|
|
278
278
|
try:
|
279
279
|
arch = self.target.registry.key(key).value("PROCESSOR_ARCHITECTURE").value
|
280
|
-
bits =
|
280
|
+
bits = ARCH_MAP.get(arch)
|
281
281
|
|
282
282
|
# return {"arch": arch, "bitness": bits}
|
283
283
|
if bits == 64:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.20.
|
3
|
+
Version: 3.20.dev49
|
4
4
|
Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
6
6
|
License: Affero General Public License v3
|
@@ -196,7 +196,7 @@ dissect/target/plugins/general/scrape.py,sha256=Fz7BNXflvuxlnVulyyDhLpyU8D_hJdH6
|
|
196
196
|
dissect/target/plugins/general/users.py,sha256=yy9gvRXfN9BT71v4Xqo5hpwfgN9he9Otu8TBPZ_Tegs,3009
|
197
197
|
dissect/target/plugins/os/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
198
198
|
dissect/target/plugins/os/unix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
199
|
-
dissect/target/plugins/os/unix/_os.py,sha256=
|
199
|
+
dissect/target/plugins/os/unix/_os.py,sha256=mk2yxWGqdZMzdr8hyYT5nyjSIEN3F5JuoPaaiz0Rvf8,15311
|
200
200
|
dissect/target/plugins/os/unix/applications.py,sha256=AUgZRP35FzswGyFyChj2o4dfGO34Amc6nqHgiMEaqdI,3129
|
201
201
|
dissect/target/plugins/os/unix/cronjobs.py,sha256=tgWQ3BUZpfyvRzodMwGtwFUdPjZ17k7ZRbZ9Q8wmXPk,3393
|
202
202
|
dissect/target/plugins/os/unix/datetime.py,sha256=gKfBdPyUirt3qmVYfOJ1oZXRPn8wRzssbZxR_ARrtk8,1518
|
@@ -251,7 +251,7 @@ dissect/target/plugins/os/unix/linux/debian/vyos/__init__.py,sha256=47DEQpj8HBSa
|
|
251
251
|
dissect/target/plugins/os/unix/linux/debian/vyos/_os.py,sha256=TPjcfv1n68RCe3Er4aCVQwQDCZwJT-NLvje3kPjDfhk,1744
|
252
252
|
dissect/target/plugins/os/unix/linux/fortios/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
253
253
|
dissect/target/plugins/os/unix/linux/fortios/_keys.py,sha256=jDDHObfsUn9BGoIir9p4J_-rg9rI1rgoOfnL3R3lg4o,123358
|
254
|
-
dissect/target/plugins/os/unix/linux/fortios/_os.py,sha256=
|
254
|
+
dissect/target/plugins/os/unix/linux/fortios/_os.py,sha256=381VI9TDMR2-XPwLsvCU8hcRgTz1H5yJ-q_sCNQzSiM,19790
|
255
255
|
dissect/target/plugins/os/unix/linux/fortios/generic.py,sha256=dc6YTDLV-VZq9k8IWmY_PE0sTGkkp3yamR-cYNUCtes,1265
|
256
256
|
dissect/target/plugins/os/unix/linux/fortios/locale.py,sha256=Pe7Bdj8UemCiktLeQnQ50TpY_skARAzRJA0ewAB4710,5243
|
257
257
|
dissect/target/plugins/os/unix/linux/redhat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -274,7 +274,7 @@ dissect/target/plugins/os/unix/log/lastlog.py,sha256=Wr3-2n1-GwckN9mSx-yM55N6_L0
|
|
274
274
|
dissect/target/plugins/os/unix/log/messages.py,sha256=XtjZ0a2budgQm_K5JT3fMf7JcjuD0AelcD3zOFN2xpI,5732
|
275
275
|
dissect/target/plugins/os/unix/log/utmp.py,sha256=k2A69s2qUT2JunJrH8GO6nQ0zMDotXMTaj8OzQ7ljj8,7336
|
276
276
|
dissect/target/plugins/os/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
277
|
-
dissect/target/plugins/os/windows/_os.py,sha256
|
277
|
+
dissect/target/plugins/os/windows/_os.py,sha256=WoXSq-HVOKqI9p3CMXrOFAjsi9MlVmxE2JVHzN7RH0s,12441
|
278
278
|
dissect/target/plugins/os/windows/activitiescache.py,sha256=BbGD-vETHm1IRMoazVer_vqSJIoQxxhWcJ_xlBeOMds,6899
|
279
279
|
dissect/target/plugins/os/windows/adpolicy.py,sha256=ul8lKlG9ExABnd6yVLMPFFgVxN74CG4T3MvcRuBLHJc,7158
|
280
280
|
dissect/target/plugins/os/windows/amcache.py,sha256=1jq-S80_FIzGegrqQ6HqrjmaAPTyxyn69HxnbRBlaUc,27608
|
@@ -378,10 +378,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
378
378
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
379
379
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
380
380
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
381
|
-
dissect.target-3.20.
|
382
|
-
dissect.target-3.20.
|
383
|
-
dissect.target-3.20.
|
384
|
-
dissect.target-3.20.
|
385
|
-
dissect.target-3.20.
|
386
|
-
dissect.target-3.20.
|
387
|
-
dissect.target-3.20.
|
381
|
+
dissect.target-3.20.dev49.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
382
|
+
dissect.target-3.20.dev49.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
383
|
+
dissect.target-3.20.dev49.dist-info/METADATA,sha256=cyNr191yNPZoEE1uGY1DqaExAYn8H9NCuOL2OUWn1oM,12897
|
384
|
+
dissect.target-3.20.dev49.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
385
|
+
dissect.target-3.20.dev49.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
386
|
+
dissect.target-3.20.dev49.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
387
|
+
dissect.target-3.20.dev49.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.20.dev48.dist-info → dissect.target-3.20.dev49.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|