dissect.target 3.16.dev43__py3-none-any.whl → 3.16.dev44__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/linux/fortios/_keys.py +1250 -0
- dissect/target/plugins/os/unix/linux/fortios/_os.py +45 -40
- {dissect.target-3.16.dev43.dist-info → dissect.target-3.16.dev44.dist-info}/METADATA +1 -1
- {dissect.target-3.16.dev43.dist-info → dissect.target-3.16.dev44.dist-info}/RECORD +9 -8
- {dissect.target-3.16.dev43.dist-info → dissect.target-3.16.dev44.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.16.dev43.dist-info → dissect.target-3.16.dev44.dist-info}/LICENSE +0 -0
- {dissect.target-3.16.dev43.dist-info → dissect.target-3.16.dev44.dist-info}/WHEEL +0 -0
- {dissect.target-3.16.dev43.dist-info → dissect.target-3.16.dev44.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.16.dev43.dist-info → dissect.target-3.16.dev44.dist-info}/top_level.txt +0 -0
@@ -17,6 +17,7 @@ from dissect.target.helpers.fsutil import open_decompress
|
|
17
17
|
from dissect.target.helpers.record import TargetRecordDescriptor, UnixUserRecord
|
18
18
|
from dissect.target.plugin import OperatingSystem, export
|
19
19
|
from dissect.target.plugins.os.unix.linux._os import LinuxPlugin
|
20
|
+
from dissect.target.plugins.os.unix.linux.fortios._keys import KERNEL_KEY_MAP
|
20
21
|
from dissect.target.target import Target
|
21
22
|
|
22
23
|
try:
|
@@ -92,12 +93,14 @@ class FortiOSPlugin(LinuxPlugin):
|
|
92
93
|
except ReadError:
|
93
94
|
# The rootfs.gz file could be encrypted.
|
94
95
|
try:
|
95
|
-
|
96
|
+
kernel_hash = get_kernel_hash(sysvol)
|
97
|
+
key, iv = key_iv_for_kernel_hash(kernel_hash)
|
98
|
+
rfs_fh = decrypt_rootfs(rootfs.open(), key, iv)
|
96
99
|
vfs = TarFilesystem(rfs_fh, tarinfo=cpio.CpioInfo)
|
97
100
|
except RuntimeError:
|
98
101
|
target.log.warning("Could not decrypt rootfs.gz. Missing `pycryptodome` dependency.")
|
99
102
|
except ValueError as e:
|
100
|
-
target.log.warning("Could not decrypt rootfs.gz.
|
103
|
+
target.log.warning("Could not decrypt rootfs.gz. Unknown kernel hash (%s).", kernel_hash)
|
101
104
|
target.log.debug("", exc_info=e)
|
102
105
|
except ReadError as e:
|
103
106
|
target.log.warning("Could not mount rootfs.gz. It could be corrupt.")
|
@@ -457,58 +460,60 @@ def decrypt_password(input: str) -> str:
|
|
457
460
|
return "ENC:" + input
|
458
461
|
|
459
462
|
|
460
|
-
def
|
461
|
-
"""
|
463
|
+
def key_iv_for_kernel_hash(kernel_hash: str) -> tuple[bytes, bytes]:
|
464
|
+
"""Return decryption key and IV for a specific sha256 kernel hash.
|
465
|
+
|
466
|
+
The decryption key and IV are used to decrypt the ``rootfs.gz`` file.
|
467
|
+
|
468
|
+
Args:
|
469
|
+
kernel_hash: SHA256 hash of the kernel file.
|
470
|
+
|
471
|
+
Returns:
|
472
|
+
Tuple with decryption key and IV.
|
473
|
+
|
474
|
+
Raises:
|
475
|
+
ValueError: When no decryption keys are available for the given kernel hash.
|
476
|
+
"""
|
477
|
+
|
478
|
+
key = bytes.fromhex(KERNEL_KEY_MAP.get(kernel_hash, ""))
|
479
|
+
if len(key) == 32:
|
480
|
+
# FortiOS 7.4.x uses a KDF to derive the key and IV
|
481
|
+
return _kdf_7_4_x(key)
|
482
|
+
elif len(key) == 48:
|
483
|
+
# FortiOS 7.0.13 and 7.0.14 uses a static key and IV
|
484
|
+
return key[:32], key[32:]
|
485
|
+
raise ValueError(f"No known decryption keys for kernel hash: {kernel_hash}")
|
486
|
+
|
487
|
+
|
488
|
+
def decrypt_rootfs(fh: BinaryIO, key: bytes, iv: bytes) -> BinaryIO:
|
489
|
+
"""Attempt to decrypt an encrypted ``rootfs.gz`` file with given key and IV.
|
462
490
|
|
463
491
|
FortiOS releases as of 7.4.1 / 2023-08-31, have ChaCha20 encrypted ``rootfs.gz`` files.
|
464
492
|
This function attempts to decrypt a ``rootfs.gz`` file using a static key and IV
|
465
493
|
which can be found in the kernel.
|
466
494
|
|
467
|
-
|
468
|
-
- FortiGate VM 7.0.13
|
469
|
-
- FortiGate VM 7.0.14
|
470
|
-
- FortiGate VM 7.4.1
|
471
|
-
- FortiGate VM 7.4.2
|
472
|
-
- FortiGate VM 7.4.3
|
495
|
+
Known keys can be found in the ``_keys.py`` file.
|
473
496
|
|
474
497
|
Resources:
|
475
498
|
- https://docs.fortinet.com/document/fortimanager/7.4.2/release-notes/519207/special-notices
|
476
499
|
- Reversing kernel (fgt_verifier_iv, fgt_verifier_decrypt, fgt_verifier_initrd)
|
500
|
+
|
501
|
+
Args:
|
502
|
+
fh: File-like object to the encrypted rootfs.gz file.
|
503
|
+
key: ChaCha20 key.
|
504
|
+
iv: ChaCha20 iv.
|
505
|
+
|
506
|
+
Returns:
|
507
|
+
File-like object to the decrypted rootfs.gz file.
|
508
|
+
|
509
|
+
Raises:
|
510
|
+
ValueError: When decryption failed.
|
511
|
+
RuntimeError: When PyCryptodome is not available.
|
477
512
|
"""
|
478
513
|
|
479
514
|
if not HAS_PYCRYPTODOME:
|
480
515
|
raise RuntimeError("PyCryptodome module not available")
|
481
516
|
|
482
|
-
# SHA256 hashes of kernel files
|
483
|
-
KERNEL_KEY_MAP = {
|
484
|
-
# FortiGate VM 7.0.13
|
485
|
-
"25cb2c8a419cde1f42d38fc6cbc95cf8b53db41096d0648015674d8220eba6bf": (
|
486
|
-
bytes.fromhex("c87e13e1f7d21c1aca81dc13329c3a948d6e420d3a859f3958bd098747873d08"),
|
487
|
-
bytes.fromhex("87486a24637e9a66f09ec182eee25594"),
|
488
|
-
),
|
489
|
-
# FortiGate VM 7.0.14
|
490
|
-
"67d4c913b1ceb7a62e2076ca835ebfdc67e65c7716fc604caa7552512f171197": (
|
491
|
-
bytes.fromhex("9ba00c035bcaa97717d936f8268a973eb1dd64d19388153fad5f7849b8fdf0d8"),
|
492
|
-
bytes.fromhex("9df4ba40dbddcf5ec9d2983681eb1940"),
|
493
|
-
),
|
494
|
-
# FortiGate VM 7.4.1
|
495
|
-
"a008b47327293e48502a121ee8709f243ad5da4e63d6f663c253db27bd01ea28": _kdf_7_4_x(
|
496
|
-
"366486c0f2c6322ec23e4f33a98caa1b19d41c74bb4f25f6e8e2087b0655b30f"
|
497
|
-
),
|
498
|
-
# FortiGate VM 7.4.2
|
499
|
-
"c392cf83ab484e0b2419b2711b02cdc88a73db35634c10340037243394a586eb": _kdf_7_4_x(
|
500
|
-
"480767be539de28ee773497fa731dd6368adc9946df61da8e1253fa402ba0302"
|
501
|
-
),
|
502
|
-
# FortiGate VM 7.4.3
|
503
|
-
"ba0450947e51844588b29bd302d2a1a3802f7718cf6840011c1b34f1c1f0bb89": _kdf_7_4_x(
|
504
|
-
"4cf7a950b99cf29b0343e7ba6c609e49d9766f16c6d2f075f72ad400542f0765"
|
505
|
-
),
|
506
|
-
}
|
507
|
-
|
508
|
-
if not (key_data := KERNEL_KEY_MAP.get(kernel_hash)):
|
509
|
-
raise ValueError("Failed to decrypt: Unknown kernel hash.")
|
510
|
-
|
511
|
-
key, iv = key_data
|
512
517
|
# First 8 bytes = counter, last 8 bytes = nonce
|
513
518
|
# PyCryptodome interally divides this seek by 64 to get a (position, offset) tuple
|
514
519
|
# We're interested in updating the position in the ChaCha20 internal state, so to make
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.16.
|
3
|
+
Version: 3.16.dev44
|
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
|
@@ -223,7 +223,8 @@ dissect/target/plugins/os/unix/linux/debian/dpkg.py,sha256=DPBLQiHAF7ZS8IorRsGAi
|
|
223
223
|
dissect/target/plugins/os/unix/linux/debian/vyos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
224
224
|
dissect/target/plugins/os/unix/linux/debian/vyos/_os.py,sha256=q8qG2FLJhUbpjfwlNCmWAhFdTWMzSWUh7s7H8m4x7Fw,1741
|
225
225
|
dissect/target/plugins/os/unix/linux/fortios/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
226
|
-
dissect/target/plugins/os/unix/linux/fortios/
|
226
|
+
dissect/target/plugins/os/unix/linux/fortios/_keys.py,sha256=jDDHObfsUn9BGoIir9p4J_-rg9rI1rgoOfnL3R3lg4o,123358
|
227
|
+
dissect/target/plugins/os/unix/linux/fortios/_os.py,sha256=gFFzByku_3qpSrHpnqJv6xIbIe3V4iGXdUxxGD_-EFA,19435
|
227
228
|
dissect/target/plugins/os/unix/linux/fortios/generic.py,sha256=tT4-lE0Z_DeDIN3zHrQbE8JB3cRJop1_TiEst-Au0bs,1230
|
228
229
|
dissect/target/plugins/os/unix/linux/fortios/locale.py,sha256=VDdk60sqe2JTfftssO05C667-_BpI3kcqKOTVzO3ueU,5209
|
229
230
|
dissect/target/plugins/os/unix/linux/redhat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -332,10 +333,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
332
333
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
333
334
|
dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
|
334
335
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
335
|
-
dissect.target-3.16.
|
336
|
-
dissect.target-3.16.
|
337
|
-
dissect.target-3.16.
|
338
|
-
dissect.target-3.16.
|
339
|
-
dissect.target-3.16.
|
340
|
-
dissect.target-3.16.
|
341
|
-
dissect.target-3.16.
|
336
|
+
dissect.target-3.16.dev44.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
337
|
+
dissect.target-3.16.dev44.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
338
|
+
dissect.target-3.16.dev44.dist-info/METADATA,sha256=FVsQADibmaOBO3JcCbWI9r2XQ0uXQLKL-yeCztWemQM,11100
|
339
|
+
dissect.target-3.16.dev44.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
340
|
+
dissect.target-3.16.dev44.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
|
341
|
+
dissect.target-3.16.dev44.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
342
|
+
dissect.target-3.16.dev44.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.16.dev43.dist-info → dissect.target-3.16.dev44.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|