dploot 3.2.0__py2.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.
Files changed (50) hide show
  1. dploot/__init__.py +0 -0
  2. dploot/action/__init__.py +0 -0
  3. dploot/action/backupkey.py +102 -0
  4. dploot/action/blob.py +167 -0
  5. dploot/action/browser.py +192 -0
  6. dploot/action/certificates.py +146 -0
  7. dploot/action/cng.py +129 -0
  8. dploot/action/credentials.py +132 -0
  9. dploot/action/machinecertificates.py +136 -0
  10. dploot/action/machinecng.py +120 -0
  11. dploot/action/machinecredentials.py +120 -0
  12. dploot/action/machinemasterkeys.py +117 -0
  13. dploot/action/machinetriage.py +158 -0
  14. dploot/action/machinevaults.py +127 -0
  15. dploot/action/masterkeys.py +200 -0
  16. dploot/action/mobaxterm.py +138 -0
  17. dploot/action/rdg.py +132 -0
  18. dploot/action/sccm.py +125 -0
  19. dploot/action/triage.py +187 -0
  20. dploot/action/vaults.py +132 -0
  21. dploot/action/wam.py +133 -0
  22. dploot/action/wifi.py +137 -0
  23. dploot/entry.py +98 -0
  24. dploot/lib/__init__.py +0 -0
  25. dploot/lib/consts.py +30 -0
  26. dploot/lib/crypto.py +415 -0
  27. dploot/lib/dpapi.py +302 -0
  28. dploot/lib/masterkey.py +75 -0
  29. dploot/lib/smb.py +517 -0
  30. dploot/lib/target.py +219 -0
  31. dploot/lib/utils.py +99 -0
  32. dploot/lib/wmi.py +61 -0
  33. dploot/triage/__init__.py +30 -0
  34. dploot/triage/backupkey.py +81 -0
  35. dploot/triage/browser.py +441 -0
  36. dploot/triage/certificates.py +444 -0
  37. dploot/triage/cng.py +165 -0
  38. dploot/triage/credentials.py +163 -0
  39. dploot/triage/masterkeys.py +279 -0
  40. dploot/triage/mobaxterm.py +653 -0
  41. dploot/triage/rdg.py +249 -0
  42. dploot/triage/sccm.py +302 -0
  43. dploot/triage/vaults.py +307 -0
  44. dploot/triage/wam.py +251 -0
  45. dploot/triage/wifi.py +424 -0
  46. dploot-3.2.0.dist-info/METADATA +865 -0
  47. dploot-3.2.0.dist-info/RECORD +50 -0
  48. dploot-3.2.0.dist-info/WHEEL +4 -0
  49. dploot-3.2.0.dist-info/entry_points.txt +3 -0
  50. dploot-3.2.0.dist-info/licenses/LICENSE +21 -0
dploot/lib/dpapi.py ADDED
@@ -0,0 +1,302 @@
1
+ import logging
2
+ from typing import Any, Dict, List, Optional
3
+ from Cryptodome.Cipher import AES, PKCS1_v1_5
4
+ from Cryptodome.PublicKey import RSA
5
+ from binascii import hexlify, unhexlify
6
+
7
+ from Cryptodome.Util.Padding import unpad
8
+ from Cryptodome.Hash import HMAC
9
+
10
+ from impacket.uuid import bin_to_string
11
+ from impacket.dpapi import (
12
+ MasterKeyFile,
13
+ MasterKey,
14
+ DomainKey,
15
+ DPAPI_BLOB,
16
+ CREDENTIAL_BLOB,
17
+ VAULT_VCRD,
18
+ VAULT_VPOL,
19
+ VAULT_KNOWN_SCHEMAS,
20
+ VAULT_VPOL_KEYS,
21
+ PVK_FILE_HDR,
22
+ PRIVATE_KEY_BLOB,
23
+ ALGORITHMS_DATA,
24
+ privatekeyblob_to_pkcs1,
25
+ DPAPI_DOMAIN_RSA_MASTER_KEY,
26
+ CredentialFile,
27
+ )
28
+
29
+ from dploot.lib.crypto import (
30
+ PRIVATE_KEY_RSA,
31
+ PVKFile,
32
+ PVKFile_SIG,
33
+ PVKHeader,
34
+ deriveKeysFromUser,
35
+ deriveKeysFromUserkey,
36
+ pvkblob_to_pkcs1,
37
+ )
38
+
39
+
40
+ def decrypt_masterkey(
41
+ masterkey: bytes,
42
+ domain_backupkey: Optional[bytes] = None,
43
+ dpapi_systemkey: Optional[Dict] = None,
44
+ sid: str = "",
45
+ password: Optional[str] = None,
46
+ nthash: Optional[str] = None,
47
+ ) -> Any:
48
+ if (
49
+ domain_backupkey is None
50
+ and password is None
51
+ and nthash is None
52
+ and dpapi_systemkey is None
53
+ ):
54
+ return None
55
+ data = masterkey
56
+ mkf = MasterKeyFile(data)
57
+ dk = mk = bkmk = None
58
+ data = data[len(mkf) :]
59
+ if mkf["MasterKeyLen"] > 0:
60
+ mk = MasterKey(data[: mkf["MasterKeyLen"]])
61
+ data = data[len(mk) :]
62
+
63
+ if mkf["BackupKeyLen"] > 0:
64
+ bkmk = MasterKey(data[: mkf["BackupKeyLen"]])
65
+ data = data[len(bkmk) :]
66
+
67
+ if mkf["DomainKeyLen"] > 0:
68
+ dk = DomainKey(data[: mkf["DomainKeyLen"]])
69
+ data = data[len(dk) :]
70
+
71
+ if domain_backupkey is not None and dk is not None:
72
+ key = PRIVATE_KEY_BLOB(domain_backupkey[len(PVK_FILE_HDR()) :])
73
+ private = privatekeyblob_to_pkcs1(key)
74
+ cipher = PKCS1_v1_5.new(private)
75
+
76
+ decryptedKey = cipher.decrypt(dk["SecretData"][::-1], None)
77
+ if decryptedKey:
78
+ domain_master_key = DPAPI_DOMAIN_RSA_MASTER_KEY(decryptedKey)
79
+ return domain_master_key["buffer"][: domain_master_key["cbMasterKey"]]
80
+
81
+ if sid != "":
82
+ if nthash is not None:
83
+ nthash = unhexlify(nthash)
84
+ key1, key2 = deriveKeysFromUserkey(sid, nthash)
85
+ decryptedKey = mk.decrypt(key2) or mk.decrypt(key1)
86
+ if decryptedKey:
87
+ return decryptedKey
88
+
89
+ if password is not None:
90
+ key1, key2, key3 = deriveKeysFromUser(sid, password)
91
+ decryptedKey = mk.decrypt(key3) or mk.decrypt(key2) or mk.decrypt(key1)
92
+ if decryptedKey:
93
+ return decryptedKey
94
+
95
+ if dpapi_systemkey is not None:
96
+ decryptedKey = (
97
+ mk.decrypt(dpapi_systemkey["UserKey"])
98
+ or mk.decrypt(dpapi_systemkey["MachineKey"])
99
+ or bkmk.decrypt(dpapi_systemkey["UserKey"])
100
+ or bkmk.decrypt(dpapi_systemkey["MachineKey"])
101
+ )
102
+ if decryptedKey:
103
+ return decryptedKey
104
+
105
+ if sid != "":
106
+ test = dpapi_systemkey["UserKey"]
107
+ key1, key2 = deriveKeysFromUserkey(sid, test)
108
+ if key2 is not None:
109
+ decryptedKey = mk.decrypt(key2)
110
+ if decryptedKey:
111
+ return decryptedKey
112
+
113
+ decryptedKey = mk.decrypt(key1)
114
+ if decryptedKey:
115
+ return decryptedKey
116
+
117
+ if key2 is not None:
118
+ decryptedKey = bkmk.decrypt(key2)
119
+ if decryptedKey:
120
+ return decryptedKey
121
+
122
+ decryptedKey = bkmk.decrypt(key1)
123
+ if decryptedKey:
124
+ return decryptedKey
125
+ return None
126
+
127
+
128
+ def decrypt_credential(credential_bytes: bytes, masterkey: MasterKey) -> Any:
129
+ cred = CredentialFile(credential_bytes)
130
+ decrypted = decrypt_blob(cred["Data"], masterkey)
131
+ if decrypted:
132
+ return CREDENTIAL_BLOB(decrypted)
133
+ return None
134
+
135
+
136
+ def find_masterkey_for_credential_blob(
137
+ credential_bytes: bytes, masterkeys: Any
138
+ ) -> "Any | None":
139
+ cred = CredentialFile(credential_bytes)
140
+ return find_masterkey_for_blob(cred["Data"], masterkeys=masterkeys)
141
+
142
+
143
+ def decrypt_privatekey(
144
+ privatekey_bytes: bytes, masterkey: Any
145
+ ) -> RSA.RsaKey:
146
+ blob = PVKHeader(privatekey_bytes)
147
+ blob = PVKFile_SIG(privatekey_bytes) if blob["SigHeadLen"] > 0 else PVKFile(privatekey_bytes)
148
+ key = unhexlify(masterkey.sha1)
149
+ decrypted = decrypt(blob["Blob"], key)
150
+ rsa_temp = PRIVATE_KEY_RSA(decrypted)
151
+ return pvkblob_to_pkcs1(rsa_temp)
152
+
153
+
154
+ def find_masterkey_for_privatekey_blob(
155
+ privatekey_bytes: bytes, masterkeys: List[Any]
156
+ ) -> "Any | None":
157
+ blob = PVKHeader(privatekey_bytes)
158
+ if len(blob["Remaining"]) == 0:
159
+ return None
160
+ blob = PVKFile_SIG(privatekey_bytes) if blob["SigHeadLen"] > 0 else PVKFile(privatekey_bytes)
161
+
162
+ masterkey = bin_to_string(blob["Blob"]["GuidMasterKey"])
163
+ return find_masterkey(masterkey=masterkey, masterkeys=masterkeys)
164
+
165
+
166
+ def decrypt_vpol(vpol_bytes: bytes, masterkey: Any) -> "VAULT_VPOL_KEYS | None":
167
+ vpol = VAULT_VPOL(vpol_bytes)
168
+ blob = vpol["Blob"]
169
+
170
+ key = unhexlify(masterkey.sha1)
171
+ decrypted = decrypt(blob, key)
172
+ if decrypted:
173
+ return VAULT_VPOL_KEYS(decrypted)
174
+ return None
175
+
176
+
177
+ def decrypt_vcrd(vcrd_bytes: bytes, vpol_keys: List[bytes]) -> Any:
178
+ blob = VAULT_VCRD(vcrd_bytes)
179
+
180
+ for key in vpol_keys:
181
+ key = unhexlify(key)
182
+ cleartext = None
183
+ for i, entry in enumerate(blob.attributesLen):
184
+ try:
185
+ if entry > 28:
186
+ attribute = blob.attributes[i]
187
+ cipher = AES.new(key, AES.MODE_CBC, iv=attribute["IV"]) if "IV" in attribute.fields and len(attribute["IV"]) == 16 else AES.new(key, AES.MODE_CBC)
188
+ cleartext = cipher.decrypt(attribute["Data"])
189
+ if cleartext is not None:
190
+ # Lookup schema Friendly Name and print if we find one
191
+ if (
192
+ blob["FriendlyName"].decode("utf-16le")[:-1]
193
+ in VAULT_KNOWN_SCHEMAS
194
+ ):
195
+ # Found one. Cast it and print
196
+ return VAULT_KNOWN_SCHEMAS[
197
+ blob["FriendlyName"].decode("utf-16le")[:-1]
198
+ ](cleartext)
199
+ else:
200
+ # otherwise
201
+ return cleartext
202
+ except Exception as e:
203
+ if (
204
+ str(e)
205
+ != "('unpack requires a buffer of 4 bytes', \"When unpacking field 'Id2 | <L=0 | b''[:4]'\")"
206
+ ):
207
+ logging.debug(e)
208
+ return None
209
+
210
+
211
+ def find_masterkey_for_vpol_blob(vault_bytes: bytes, masterkeys: Any) -> "Any | None":
212
+ vault = VAULT_VPOL(vault_bytes)
213
+ blob = vault["Blob"]
214
+ masterkey = bin_to_string(blob["GuidMasterKey"])
215
+ return find_masterkey(masterkey=masterkey, masterkeys=masterkeys)
216
+
217
+
218
+ def decrypt_blob(blob_bytes: bytes, masterkey: Any, entropy=None) -> "bytes | None":
219
+ blob = DPAPI_BLOB(blob_bytes)
220
+ # Ugly fix below:
221
+ # if blob_bytes was too long, strip blob.rawData so its len matches len(blob)
222
+ if len(blob.rawData) > len(blob):
223
+ logging.debug(
224
+ f"{__name__}.decrypt_blob(): rawData too long. Stripping {len(blob.rawData) - len(blob)} bytes."
225
+ )
226
+ blob.rawData = blob.rawData[0 : len(blob)]
227
+ key = unhexlify(masterkey.sha1)
228
+ return decrypt(blob, key, entropy=entropy) if entropy is not None else decrypt(blob, key)
229
+
230
+
231
+ def decrypt(blob: DPAPI_BLOB, keyHash, entropy=None) -> "bytes | None":
232
+ hash_algo = ALGORITHMS_DATA[blob["HashAlgo"]][1]
233
+ block_size = hash_algo.block_size
234
+ for algo in [compute_sessionKey_1, compute_sessionKey_2]:
235
+ sessionKey = algo(keyHash, blob["Salt"], hash_algo, block_size, entropy)
236
+ sessionKey = sessionKey.digest()
237
+ derivedKey = blob.deriveKey(sessionKey)
238
+ crypto = ALGORITHMS_DATA[blob["CryptAlgo"]]
239
+ cipher = crypto[1].new(
240
+ derivedKey[: crypto[0]], mode=crypto[2], iv=b"\x00" * crypto[3]
241
+ )
242
+ cleartext = cipher.decrypt(blob["Data"])
243
+ try:
244
+ cleartext = unpad(cleartext, crypto[1].block_size)
245
+ except ValueError as e:
246
+ if "Padding is incorrect" in str(e):
247
+ pass
248
+ # Now check the signature
249
+ # ToDo Fix this, it's just ugly, more testing so we can remove one
250
+ toSign = blob.rawData[20 : len(blob) - len(blob["Sign"]) - 4]
251
+ if toSign[-4:] != blob["Data"][-4:]:
252
+ logging.debug(f"{__name__}.decrypt(): toSign is wrong!")
253
+ logging.debug("toSign : %s" % (hexlify(toSign)))
254
+ logging.debug(
255
+ "Sign (%2d) : %s" % (len(blob["Sign"]), hexlify(blob["Sign"]))
256
+ )
257
+
258
+ hmac_calculated = algo(keyHash, blob["HMac"], hash_algo, block_size, entropy)
259
+ hmac_calculated.update(toSign)
260
+ if blob["Sign"] == hmac_calculated.digest():
261
+ return cleartext
262
+
263
+ return None
264
+
265
+
266
+ def compute_sessionKey_1(
267
+ key_hash: bytes, salt: bytes, hash_algo: object, block_size: int, entropy: bytes
268
+ ):
269
+ pad_block = key_hash.ljust(block_size, b"\x00")
270
+ ipad = bytearray(i ^ 0x36 for i in pad_block)
271
+ opad = bytearray(i ^ 0x5C for i in pad_block)
272
+
273
+ a = hash_algo.new(ipad)
274
+ a.update(salt)
275
+
276
+ computed_key = hash_algo.new(opad)
277
+ computed_key.update(a.digest())
278
+
279
+ if entropy is not None:
280
+ computed_key.update(entropy)
281
+ return computed_key
282
+
283
+
284
+ def compute_sessionKey_2(
285
+ key_hash: bytes, salt: bytes, hash_algo: object, block_size: int, entropy: bytes
286
+ ):
287
+ computed_key = HMAC.new(key_hash, salt, hash_algo)
288
+ if entropy is not None:
289
+ computed_key.update(entropy)
290
+
291
+ return computed_key
292
+
293
+
294
+ def find_masterkey_for_blob(blob_bytes: bytes, masterkeys: Any) -> "Any | None":
295
+ blob = DPAPI_BLOB(blob_bytes)
296
+ masterkey = bin_to_string(blob["GuidMasterKey"])
297
+ return find_masterkey(masterkey=masterkey, masterkeys=masterkeys)
298
+
299
+
300
+ def find_masterkey(masterkey: str, masterkeys: Any) -> "Any | None":
301
+ masterkey = masterkey.lower()
302
+ return next((key for key in masterkeys if key.guid.lower() == masterkey), None)
@@ -0,0 +1,75 @@
1
+ import logging
2
+ from binascii import hexlify
3
+ from Cryptodome.Hash import SHA1
4
+
5
+ from impacket.dpapi import (
6
+ MasterKeyFile,
7
+ MasterKey,
8
+ )
9
+
10
+ from dploot.lib.dpapi import decrypt_masterkey
11
+
12
+
13
+ class Masterkey:
14
+ def __init__(self, guid, blob = None, sid:str = None, key = None, sha1 = None, user: str = "None") -> None:
15
+ self.guid = guid
16
+ self.blob = blob
17
+ self.sid = str(sid).upper()
18
+ self.user = user
19
+
20
+ self.key = key
21
+ self._sha1 = sha1
22
+
23
+ if self.blob is not None:
24
+ self.generate_hash()
25
+
26
+ def __str__(self) -> str:
27
+ return f"{{{self.guid}}}:{self.sha1}" if self.key is not None else ""
28
+
29
+ def decrypt(self, domain_backupkey = None, password = None, nthash = None, dpapi_systemkey = None) -> bool:
30
+ key = decrypt_masterkey(
31
+ masterkey=self.blob,
32
+ domain_backupkey=domain_backupkey,
33
+ sid=self.sid,
34
+ password=password,
35
+ nthash=nthash,
36
+ dpapi_systemkey=dpapi_systemkey
37
+ )
38
+ if key is not None:
39
+ self.key = key
40
+ return True
41
+ return False
42
+
43
+ def generate_hash(self):
44
+ hashes = []
45
+ mkf = MasterKeyFile(self.blob)
46
+ data = self.blob[len(mkf) :]
47
+ if mkf["MasterKeyLen"] > 0:
48
+ mk = MasterKey(data[:mkf["MasterKeyLen"]])
49
+ try:
50
+ iteration_count = mk["MasterKeyIterationCount"]
51
+ iv = hexlify(mk["Salt"]).decode("ascii")
52
+ encryted_data = hexlify(mk["data"]).decode("ascii")
53
+ version = 1 if len(encryted_data) == 208 else 2
54
+ hash_algo = "sha1" if len(encryted_data) == 208 else "sha512"
55
+ crypt_algo = "des3" if len(encryted_data) == 208 else "aes256"
56
+ hashes = [f"{self.user}:$DPAPImk${version}*{context}*{self.sid}*{crypt_algo}*{hash_algo}*{iteration_count}*{iv}*{len(encryted_data)}*{encryted_data}"for context in [1,2,3]]
57
+ except Exception as e:
58
+ import traceback
59
+ traceback.print_exc()
60
+ print(e)
61
+ return hashes
62
+
63
+ def dump(self) -> None:
64
+ print(self)
65
+
66
+ @property
67
+ def sha1(self):
68
+ if self._sha1 is not None:
69
+ return self._sha1
70
+ if self.key is not None:
71
+ try:
72
+ self._sha1 = hexlify(SHA1.new(self.key).digest()).decode("latin-1")
73
+ except Exception as e:
74
+ logging.debug(f"Could not generate sha1 for masterkey {self.guid}: {e}")
75
+ return self._sha1