dissect.target 3.17.dev25__py3-none-any.whl → 3.17.dev26__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/apps/ssh/openssh.py +4 -0
- dissect/target/plugins/apps/ssh/putty.py +35 -13
- dissect/target/plugins/apps/ssh/ssh.py +40 -0
- {dissect.target-3.17.dev25.dist-info → dissect.target-3.17.dev26.dist-info}/METADATA +1 -1
- {dissect.target-3.17.dev25.dist-info → dissect.target-3.17.dev26.dist-info}/RECORD +10 -10
- {dissect.target-3.17.dev25.dist-info → dissect.target-3.17.dev26.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.17.dev25.dist-info → dissect.target-3.17.dev26.dist-info}/LICENSE +0 -0
- {dissect.target-3.17.dev25.dist-info → dissect.target-3.17.dev26.dist-info}/WHEEL +0 -0
- {dissect.target-3.17.dev25.dist-info → dissect.target-3.17.dev26.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.17.dev25.dist-info → dissect.target-3.17.dev26.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
import base64
|
1
2
|
import re
|
2
3
|
from itertools import product
|
3
4
|
from pathlib import Path
|
@@ -14,6 +15,7 @@ from dissect.target.plugins.apps.ssh.ssh import (
|
|
14
15
|
PrivateKeyRecord,
|
15
16
|
PublicKeyRecord,
|
16
17
|
SSHPlugin,
|
18
|
+
calculate_fingerprints,
|
17
19
|
)
|
18
20
|
|
19
21
|
|
@@ -143,12 +145,14 @@ class OpenSSHPlugin(SSHPlugin):
|
|
143
145
|
continue
|
144
146
|
|
145
147
|
key_type, public_key, comment = parse_ssh_public_key_file(file_path)
|
148
|
+
fingerprints = calculate_fingerprints(base64.b64decode(public_key))
|
146
149
|
|
147
150
|
yield PublicKeyRecord(
|
148
151
|
mtime_ts=file_path.stat().st_mtime,
|
149
152
|
key_type=key_type,
|
150
153
|
public_key=public_key,
|
151
154
|
comment=comment,
|
155
|
+
fingerprint=fingerprints,
|
152
156
|
path=file_path,
|
153
157
|
_target=self.target,
|
154
158
|
_user=user,
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
from base64 import b64decode
|
2
3
|
from datetime import datetime
|
3
4
|
from pathlib import Path
|
4
5
|
from typing import Iterator, Optional, Union
|
@@ -12,7 +13,11 @@ from dissect.target.helpers.fsutil import TargetPath, open_decompress
|
|
12
13
|
from dissect.target.helpers.record import create_extended_descriptor
|
13
14
|
from dissect.target.helpers.regutil import RegistryKey
|
14
15
|
from dissect.target.plugin import export
|
15
|
-
from dissect.target.plugins.apps.ssh.ssh import
|
16
|
+
from dissect.target.plugins.apps.ssh.ssh import (
|
17
|
+
KnownHostRecord,
|
18
|
+
SSHPlugin,
|
19
|
+
calculate_fingerprints,
|
20
|
+
)
|
16
21
|
from dissect.target.plugins.general.users import UserDetails
|
17
22
|
|
18
23
|
log = logging.getLogger(__name__)
|
@@ -96,12 +101,15 @@ class PuTTYPlugin(SSHPlugin):
|
|
96
101
|
key_type, host = entry.name.split("@")
|
97
102
|
port, host = host.split(":")
|
98
103
|
|
104
|
+
public_key, fingerprints = construct_public_key(key_type, entry.value)
|
105
|
+
|
99
106
|
yield KnownHostRecord(
|
100
107
|
mtime_ts=ssh_host_keys.ts,
|
101
108
|
host=host,
|
102
109
|
port=port,
|
103
110
|
key_type=key_type,
|
104
|
-
public_key=
|
111
|
+
public_key=public_key,
|
112
|
+
fingerprint=fingerprints,
|
105
113
|
comment="",
|
106
114
|
marker=None,
|
107
115
|
path=windows_path(ssh_host_keys.path),
|
@@ -121,12 +129,15 @@ class PuTTYPlugin(SSHPlugin):
|
|
121
129
|
key_type, host = parts[0].split("@")
|
122
130
|
port, host = host.split(":")
|
123
131
|
|
132
|
+
public_key, fingerprints = construct_public_key(key_type, parts[1])
|
133
|
+
|
124
134
|
yield KnownHostRecord(
|
125
135
|
mtime_ts=ts,
|
126
136
|
host=host,
|
127
137
|
port=port,
|
128
138
|
key_type=key_type,
|
129
|
-
public_key=
|
139
|
+
public_key=public_key,
|
140
|
+
fingerprint=fingerprints,
|
130
141
|
comment="",
|
131
142
|
marker=None,
|
132
143
|
path=posix_path(ssh_host_keys_path),
|
@@ -197,8 +208,8 @@ def parse_host_user(host: str, user: str) -> tuple[str, str]:
|
|
197
208
|
return host, user
|
198
209
|
|
199
210
|
|
200
|
-
def construct_public_key(key_type: str, iv: str) -> str:
|
201
|
-
"""Returns OpenSSH format public key calculated from PuTTY SshHostKeys format.
|
211
|
+
def construct_public_key(key_type: str, iv: str) -> tuple[str, tuple[str, str, str]]:
|
212
|
+
"""Returns OpenSSH format public key calculated from PuTTY SshHostKeys format and set of fingerprints.
|
202
213
|
|
203
214
|
PuTTY stores raw public key components instead of OpenSSH-formatted public keys
|
204
215
|
or fingerprints. With RSA public keys the exponent and modulus are stored.
|
@@ -206,9 +217,7 @@ def construct_public_key(key_type: str, iv: str) -> str:
|
|
206
217
|
|
207
218
|
Currently supports ``ssh-ed25519``, ``ecdsa-sha2-nistp256`` and ``rsa2`` key types.
|
208
219
|
|
209
|
-
NOTE:
|
210
|
-
- Sha256 fingerprints of the reconstructed public keys are currently not generated.
|
211
|
-
- More key types could be supported in the future.
|
220
|
+
NOTE: More key types could be supported in the future.
|
212
221
|
|
213
222
|
Resources:
|
214
223
|
- https://github.com/github/putty/blob/master/contrib/kh2reg.py
|
@@ -217,20 +226,33 @@ def construct_public_key(key_type: str, iv: str) -> str:
|
|
217
226
|
- https://github.com/mkorthof/reg2kh
|
218
227
|
"""
|
219
228
|
|
229
|
+
if not isinstance(key_type, str) or not isinstance(iv, str):
|
230
|
+
raise ValueError("Invalid key_type or iv")
|
231
|
+
|
232
|
+
key = None
|
233
|
+
|
220
234
|
if key_type == "ssh-ed25519":
|
221
235
|
x, y = iv.split(",")
|
222
236
|
key = ECC.construct(curve="ed25519", point_x=int(x, 16), point_y=int(y, 16))
|
223
|
-
return key.public_key().export_key(format="OpenSSH").split()[-1]
|
224
237
|
|
225
238
|
if key_type == "ecdsa-sha2-nistp256":
|
226
239
|
_, x, y = iv.split(",")
|
227
240
|
key = ECC.construct(curve="NIST P-256", point_x=int(x, 16), point_y=int(y, 16))
|
228
|
-
return key.public_key().export_key(format="OpenSSH").split()[-1]
|
229
241
|
|
230
242
|
if key_type == "rsa2":
|
231
243
|
exponent, modulus = iv.split(",")
|
232
244
|
key = RSA.construct((int(modulus, 16), int(exponent, 16)))
|
233
|
-
return key.public_key().export_key(format="OpenSSH").decode("utf-8").split()[-1]
|
234
245
|
|
235
|
-
|
236
|
-
|
246
|
+
if key is None:
|
247
|
+
log.warning("Could not reconstruct public key: type %s not implemented", key_type)
|
248
|
+
return iv, (None, None, None)
|
249
|
+
|
250
|
+
openssh_public_key = key.public_key().export_key(format="OpenSSH")
|
251
|
+
|
252
|
+
if isinstance(openssh_public_key, bytes):
|
253
|
+
# RSA's export_key() returns bytes
|
254
|
+
openssh_public_key = openssh_public_key.decode()
|
255
|
+
|
256
|
+
key_part = openssh_public_key.split()[-1]
|
257
|
+
fingerprints = calculate_fingerprints(b64decode(key_part))
|
258
|
+
return key_part, fingerprints
|
@@ -1,3 +1,6 @@
|
|
1
|
+
import base64
|
2
|
+
from hashlib import md5, sha1, sha256
|
3
|
+
|
1
4
|
from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
|
2
5
|
from dissect.target.helpers.record import create_extended_descriptor
|
3
6
|
from dissect.target.plugin import NamespacePlugin
|
@@ -29,6 +32,7 @@ KnownHostRecord = OpenSSHUserRecordDescriptor(
|
|
29
32
|
("varint", "port"),
|
30
33
|
("string", "public_key"),
|
31
34
|
("string", "marker"),
|
35
|
+
("digest", "fingerprint"),
|
32
36
|
],
|
33
37
|
)
|
34
38
|
|
@@ -50,9 +54,45 @@ PublicKeyRecord = OpenSSHUserRecordDescriptor(
|
|
50
54
|
("datetime", "mtime_ts"),
|
51
55
|
*COMMON_ELLEMENTS,
|
52
56
|
("string", "public_key"),
|
57
|
+
("digest", "fingerprint"),
|
53
58
|
],
|
54
59
|
)
|
55
60
|
|
56
61
|
|
57
62
|
class SSHPlugin(NamespacePlugin):
|
58
63
|
__namespace__ = "ssh"
|
64
|
+
|
65
|
+
|
66
|
+
def calculate_fingerprints(public_key_decoded: bytes, ssh_keygen_format: bool = False) -> tuple[str, str, str]:
|
67
|
+
"""Calculate the MD5, SHA1 and SHA256 digest of the given decoded public key.
|
68
|
+
|
69
|
+
Adheres as much as possible to the output provided by ssh-keygen when ``ssh_keygen_format``
|
70
|
+
parameter is set to ``True``. When set to ``False`` (default) hexdigests are calculated
|
71
|
+
instead for ``sha1``and ``sha256``.
|
72
|
+
|
73
|
+
Resources:
|
74
|
+
- https://en.wikipedia.org/wiki/Public_key_fingerprint
|
75
|
+
- https://man7.org/linux/man-pages/man1/ssh-keygen.1.html
|
76
|
+
- ``ssh-keygen -l -E <alg> -f key.pub``
|
77
|
+
"""
|
78
|
+
if not public_key_decoded:
|
79
|
+
raise ValueError("No decoded public key provided")
|
80
|
+
|
81
|
+
if not isinstance(public_key_decoded, bytes):
|
82
|
+
raise ValueError("Provided public key should be bytes")
|
83
|
+
|
84
|
+
if public_key_decoded[0:3] != b"\x00\x00\x00":
|
85
|
+
raise ValueError("Provided value does not look like a public key")
|
86
|
+
|
87
|
+
digest_md5 = md5(public_key_decoded).digest()
|
88
|
+
digest_sha1 = sha1(public_key_decoded).digest()
|
89
|
+
digest_sha256 = sha256(public_key_decoded).digest()
|
90
|
+
|
91
|
+
if ssh_keygen_format:
|
92
|
+
fingerprint_sha1 = base64.b64encode(digest_sha1).rstrip(b"=").decode()
|
93
|
+
fingerprint_sha256 = base64.b64encode(digest_sha256).rstrip(b"=").decode()
|
94
|
+
else:
|
95
|
+
fingerprint_sha1 = digest_sha1.hex()
|
96
|
+
fingerprint_sha256 = digest_sha256.hex()
|
97
|
+
|
98
|
+
return digest_md5.hex(), fingerprint_sha1, fingerprint_sha256
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.17.
|
3
|
+
Version: 3.17.dev26
|
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
|
@@ -133,10 +133,10 @@ dissect/target/plugins/apps/remoteaccess/teamviewer.py,sha256=SiEH36HM2NvdPuCjfL
|
|
133
133
|
dissect/target/plugins/apps/shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
134
134
|
dissect/target/plugins/apps/shell/powershell.py,sha256=biPSMRWxPI6kRqP0-75yMtrw0Ti2Bzfl_xI3xbmmF48,2641
|
135
135
|
dissect/target/plugins/apps/ssh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
136
|
-
dissect/target/plugins/apps/ssh/openssh.py,sha256=
|
136
|
+
dissect/target/plugins/apps/ssh/openssh.py,sha256=yt3bX93Q9wfF25_vG9APMwfZWUUqCPyLlVJdhu20syI,7250
|
137
137
|
dissect/target/plugins/apps/ssh/opensshd.py,sha256=DaXKdgGF3GYHHA4buEvphcm6FF4C8YFjgD96Dv6rRnM,5510
|
138
|
-
dissect/target/plugins/apps/ssh/putty.py,sha256=
|
139
|
-
dissect/target/plugins/apps/ssh/ssh.py,sha256=
|
138
|
+
dissect/target/plugins/apps/ssh/putty.py,sha256=bTX6ZU6zsc78zBdsBGUotc_ek_E1a7HSowoqD1y4PzA,9927
|
139
|
+
dissect/target/plugins/apps/ssh/ssh.py,sha256=tTA87u0B8yY1yVCPV0VJdRUct6ggkir_pIziP-eKnVo,3009
|
140
140
|
dissect/target/plugins/apps/vpn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
141
141
|
dissect/target/plugins/apps/vpn/openvpn.py,sha256=d-DGINTIHP_bvv3T09ZwbezHXGctvCyAhJ482m2_-a0,7654
|
142
142
|
dissect/target/plugins/apps/vpn/wireguard.py,sha256=SoAMED_bwWJQ3nci5qEY-qV4wJKSSDZQ8K7DoJRYq0k,6521
|
@@ -336,10 +336,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
336
336
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
337
337
|
dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
|
338
338
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
339
|
-
dissect.target-3.17.
|
340
|
-
dissect.target-3.17.
|
341
|
-
dissect.target-3.17.
|
342
|
-
dissect.target-3.17.
|
343
|
-
dissect.target-3.17.
|
344
|
-
dissect.target-3.17.
|
345
|
-
dissect.target-3.17.
|
339
|
+
dissect.target-3.17.dev26.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
340
|
+
dissect.target-3.17.dev26.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
341
|
+
dissect.target-3.17.dev26.dist-info/METADATA,sha256=fyuJSNpaOXUDx5rJFQYpsaxFKwa7VqFttG1XIvZxXco,11300
|
342
|
+
dissect.target-3.17.dev26.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
343
|
+
dissect.target-3.17.dev26.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
|
344
|
+
dissect.target-3.17.dev26.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
345
|
+
dissect.target-3.17.dev26.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.17.dev25.dist-info → dissect.target-3.17.dev26.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|