2021-09-27 09:20:43 +00:00
#!/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 :
2022-01-29 14:44:10 +00:00
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 ' )
2021-09-27 09:20:43 +00:00
else :
logging . debug ( " Error in decryptedKey with PVK " )
2022-01-29 14:44:10 +00:00
# Lets try to decrypt it with another method
# return -1
2021-09-27 09:20:43 +00:00
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 \n key1: { key1 } \n key2: { key2 } \n key3: { 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 ) )