dissect.target 3.17.dev25__py3-none-any.whl → 3.17.dev26__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|