dissect.target 3.20.dev48__py3-none-any.whl → 3.20.dev49__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- arch_strings = {
306
- 0x00: "Unknown",
307
- 0x02: "SPARC",
308
- 0x03: "x86",
309
- 0x08: "MIPS",
310
- 0x14: "PowerPC",
311
- 0x16: "S390",
312
- 0x28: "ARM",
313
- 0x2A: "SuperH",
314
- 0x32: "IA-64",
315
- 0x3E: "x86_64",
316
- 0xB7: "AArch64",
317
- 0xF3: "RISC-V",
318
- }
319
-
320
- for fs in self.target.filesystems:
321
- if fs.exists(path):
322
- fh = fs.open(path)
323
- fh.seek(4)
324
- # ELF - e_ident[EI_CLASS]
325
- bits = unpack("B", fh.read(1))[0]
326
- fh.seek(18)
327
- # ELF - e_machine
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, Optional, TextIO, Union
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) -> Optional[Filesystem]:
76
+ def detect(cls, target: Target) -> Filesystem | None:
77
77
  for fs in target.filesystems:
78
- # Tested on FortiGate and FortiAnalyzer, other Fortinet devices may look different.
79
- if fs.exists("/rootfs.gz") and (fs.exists("/.fgtsum") or fs.exists("/.fmg_sign") or fs.exists("/flatkc")):
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[Union[FortiOSUserRecord, UnixUserRecord]]:
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=[entry["accprofile"][0]],
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
- try:
237
- for username, entry in self._config["global-config"]["system"]["admin"]["user"].items():
238
- yield FortiOSUserRecord(
239
- name=username,
240
- password=":".join(entry.get("password", [])),
241
- groups=[entry["profileid"][0]],
242
- home="/root",
243
- _target=self.target,
244
- )
245
- except KeyError as e:
246
- self.target.log.warning("Exception while parsing FortiManager admin users")
247
- self.target.log.debug("", exc_info=e)
248
-
249
- # Local users
250
- try:
251
- local_groups = local_groups_to_users(self._config["root-config"]["user"]["group"])
252
- for username, entry in self._config["root-config"]["user"].get("local", {}).items():
253
- try:
254
- password = decrypt_password(entry["passwd"][-1])
255
- except (ValueError, RuntimeError):
256
- password = ":".join(entry.get("passwd", []))
257
-
258
- yield FortiOSUserRecord(
259
- name=username,
260
- password=password,
261
- groups=local_groups.get(username, []),
262
- home=None,
263
- _target=self.target,
264
- )
265
- except KeyError as e:
266
- self.target.log.warning("Exception while parsing FortiOS local users")
267
- self.target.log.debug("", exc_info=e)
268
-
269
- # Temporary guest users
270
- try:
271
- for _, entry in self._config["root-config"]["user"]["group"].get("guestgroup", {}).get("guest", {}).items():
272
- try:
273
- password = decrypt_password(entry.get("password")[-1])
274
- except (ValueError, RuntimeError):
275
- password = ":".join(entry.get("password"))
276
-
277
- yield FortiOSUserRecord(
278
- name=entry["user-id"][0],
279
- password=password,
280
- groups=["guestgroup"],
281
- home=None,
282
- _target=self.target,
283
- )
284
- except KeyError as e:
285
- self.target.log.warning("Exception while parsing FortiOS temporary guest users")
286
- self.target.log.debug("", exc_info=e)
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) -> Optional[str]:
298
+ def architecture(self) -> str | None:
294
299
  """Return architecture FortiOS runs on."""
295
- paths = ["/lib/libav.so", "/bin/ctr"]
296
- for path in paths:
297
- if self.target.fs.path(path).exists():
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: Union[str, bytes]) -> tuple[bytes, bytes]:
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) -> Optional[str]:
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 = arch_strings.get(arch)
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.dev48
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=prvcuIkJ1jmbb_g1CtgHmmJUcGjFaZn3yU6VPNMJ27k,15061
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=Cyw6KyGNc-uZn2WDlD-7G9K7swe_ofxwykIZeQRGYKU,19416
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=-Bsp9696JqU7luh_AbqojzG9BxVdYIFl5Ma-LiFBQBo,12505
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.dev48.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
382
- dissect.target-3.20.dev48.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
383
- dissect.target-3.20.dev48.dist-info/METADATA,sha256=coXjiskalhbUYyedrn0Fe49ZmJvIbeAaMYawTB3Q9QQ,12897
384
- dissect.target-3.20.dev48.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
385
- dissect.target-3.20.dev48.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
386
- dissect.target-3.20.dev48.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
387
- dissect.target-3.20.dev48.dist-info/RECORD,,
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,,