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.
@@ -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 KnownHostRecord, SSHPlugin
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=construct_public_key(key_type, entry.value),
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=construct_public_key(key_type, parts[1]),
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
- log.warning("Could not reconstruct public key: type %s not implemented.", key_type)
236
- return iv
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.dev25
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=jDNP8aq9JHivosexPlxWRUgeJo1MHclb336dzO1zRJc,7086
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=N8ssjutUVN50JNA5fEIVISbP5sJ7bGTFidRbX3uNG5Y,9404
139
- dissect/target/plugins/apps/ssh/ssh.py,sha256=uCaoWlT2bgKLUHA1aL6XymJDWJ8JmLsN8PB1C66eidY,1409
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.dev25.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
340
- dissect.target-3.17.dev25.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
341
- dissect.target-3.17.dev25.dist-info/METADATA,sha256=9gF0AAh1PZsU75RQtErozfTPL0q2jyWQo1i76L34lR0,11300
342
- dissect.target-3.17.dev25.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
- dissect.target-3.17.dev25.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
344
- dissect.target-3.17.dev25.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
345
- dissect.target-3.17.dev25.dist-info/RECORD,,
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,,