DonPAPI/lib/dpapi.py
Pierre-Alexandre Vandewoestyne 9fb50bfef7 Patching PVK decryption fuckup
2022-01-29 15:44:10 +01:00

755 lines
29 KiB
Python

#!/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 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, (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))