# 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: # Alberto Solino (@agsolino) # # Description: # DPAPI and Windows Vault parsing structures and manipulation # # 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 sys from struct import unpack from datetime import datetime from binascii import unhexlify, hexlify from struct import pack from Cryptodome.Hash import HMAC, SHA512, SHA1 from Cryptodome.Cipher import AES, DES3 from Cryptodome.Util.Padding import unpad from Cryptodome.PublicKey import RSA from Cryptodome.Util.number import bytes_to_long from six import PY3 from impacket.ese import getUnixTime from impacket.structure import Structure, hexdump from impacket.uuid import bin_to_string from impacket.dcerpc.v5.enum import Enum from impacket.dcerpc.v5.dtypes import RPC_SID # Algorithm classes ALG_CLASS_ANY = (0) ALG_CLASS_SIGNATURE = (1 << 13) ALG_CLASS_MSG_ENCRYPT = (2 << 13) ALG_CLASS_DATA_ENCRYPT = (3 << 13) ALG_CLASS_HASH = (4 << 13) ALG_CLASS_KEY_EXCHANGE = (5 << 13) ALG_CLASS_ALL = (7 << 13) # Algorithm types ALG_TYPE_ANY = (0) ALG_TYPE_DSS = (1 << 9) ALG_TYPE_RSA = (2 << 9) ALG_TYPE_BLOCK = (3 << 9) ALG_TYPE_STREAM = (4 << 9) ALG_TYPE_DH = (5 << 9) ALG_TYPE_SECURECHANNEL = (6 << 9) ALG_SID_ANY = (0) ALG_SID_RSA_ANY = 0 ALG_SID_RSA_PKCS = 1 ALG_SID_RSA_MSATWORK = 2 ALG_SID_RSA_ENTRUST = 3 ALG_SID_RSA_PGP = 4 ALG_SID_DSS_ANY = 0 ALG_SID_DSS_PKCS = 1 ALG_SID_DSS_DMS = 2 ALG_SID_ECDSA = 3 # Block cipher sub ids ALG_SID_DES = 1 ALG_SID_3DES = 3 ALG_SID_DESX = 4 ALG_SID_IDEA = 5 ALG_SID_CAST = 6 ALG_SID_SAFERSK64 = 7 ALG_SID_SAFERSK128 = 8 ALG_SID_3DES_112 = 9 ALG_SID_CYLINK_MEK = 12 ALG_SID_RC5 = 13 ALG_SID_AES_128 = 14 ALG_SID_AES_192 = 15 ALG_SID_AES_256 = 16 ALG_SID_AES = 17 ALG_SID_SKIPJACK = 10 ALG_SID_TEK = 11 CRYPT_MODE_CBCI = 6 # ANSI CBC Interleaved CRYPT_MODE_CFBP = 7 # ANSI CFB Pipelined CRYPT_MODE_OFBP = 8 # ANSI OFB Pipelined CRYPT_MODE_CBCOFM = 9 # ANSI CBC + OF Masking CRYPT_MODE_CBCOFMI = 10 # ANSI CBC + OFM Interleaved ALG_SID_RC2 = 2 ALG_SID_RC4 = 1 ALG_SID_SEAL = 2 # Diffie - Hellman sub - ids ALG_SID_DH_SANDF = 1 ALG_SID_DH_EPHEM = 2 ALG_SID_AGREED_KEY_ANY = 3 ALG_SID_KEA = 4 ALG_SID_ECDH = 5 # Hash sub ids ALG_SID_MD2 = 1 ALG_SID_MD4 = 2 ALG_SID_MD5 = 3 ALG_SID_SHA = 4 ALG_SID_SHA1 = 4 ALG_SID_MAC = 5 ALG_SID_RIPEMD = 6 ALG_SID_RIPEMD160 = 7 ALG_SID_SSL3SHAMD5 = 8 ALG_SID_HMAC = 9 ALG_SID_TLS1PRF = 10 ALG_SID_HASH_REPLACE_OWF = 11 ALG_SID_SHA_256 = 12 ALG_SID_SHA_384 = 13 ALG_SID_SHA_512 = 14 # secure channel sub ids ALG_SID_SSL3_MASTER = 1 ALG_SID_SCHANNEL_MASTER_HASH = 2 ALG_SID_SCHANNEL_MAC_KEY = 3 ALG_SID_PCT1_MASTER = 4 ALG_SID_SSL2_MASTER = 5 ALG_SID_TLS1_MASTER = 6 ALG_SID_SCHANNEL_ENC_KEY = 7 ALG_SID_ECMQV = 1 def getFlags(myenum, flags): return '|'.join([name for name, member in myenum.__members__.items() if member.value & flags]) class FLAGS(Enum): CRYPTPROTECT_UI_FORBIDDEN = 0x1 CRYPTPROTECT_LOCAL_MACHINE = 0x4 CRYPTPROTECT_CRED_SYNC = 0x8 CRYPTPROTECT_AUDIT = 0x10 CRYPTPROTECT_VERIFY_PROTECTION = 0x40 CRYPTPROTECT_CRED_REGENERATE = 0x80 CRYPTPROTECT_SYSTEM = 0x20000000 # algorithm identifier definitions class ALGORITHMS(Enum): CALG_MD2 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD2) CALG_MD4 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD4) CALG_MD5 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD5) CALG_SHA = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA) CALG_SHA1 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA1) CALG_RSA_SIGN = (ALG_CLASS_SIGNATURE | ALG_TYPE_RSA | ALG_SID_RSA_ANY) CALG_DSS_SIGN = (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_DSS_ANY) CALG_NO_SIGN = (ALG_CLASS_SIGNATURE | ALG_TYPE_ANY | ALG_SID_ANY) CALG_RSA_KEYX = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_RSA|ALG_SID_RSA_ANY) CALG_DES = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_DES) CALG_3DES_112 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_3DES_112) CALG_3DES = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_3DES) CALG_DESX = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_DESX) CALG_RC2 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_RC2) CALG_RC4 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_STREAM|ALG_SID_RC4) CALG_SEAL = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_STREAM|ALG_SID_SEAL) CALG_DH_SF = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_DH_SANDF) CALG_DH_EPHEM = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_DH_EPHEM) CALG_AGREEDKEY_ANY = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_AGREED_KEY_ANY) CALG_KEA_KEYX = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_KEA) CALG_HUGHES_MD5 = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_ANY|ALG_SID_MD5) CALG_SKIPJACK = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_SKIPJACK) CALG_TEK = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_TEK) CALG_SSL3_SHAMD5 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) CALG_SSL3_MASTER = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SSL3_MASTER) CALG_SCHANNEL_MASTER_HASH = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SCHANNEL_MASTER_HASH) CALG_SCHANNEL_MAC_KEY = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SCHANNEL_MAC_KEY) CALG_SCHANNEL_ENC_KEY = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SCHANNEL_ENC_KEY) CALG_PCT1_MASTER = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_PCT1_MASTER) CALG_SSL2_MASTER = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SSL2_MASTER) CALG_TLS1_MASTER = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_TLS1_MASTER) CALG_RC5 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_RC5) CALG_HMAC = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) CALG_TLS1PRF = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_TLS1PRF) CALG_HASH_REPLACE_OWF = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HASH_REPLACE_OWF) CALG_AES_128 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES_128) CALG_AES_192 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES_192) CALG_AES_256 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES_256) CALG_AES = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES) CALG_SHA_256 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) CALG_SHA_384 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384) CALG_SHA_512 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) CALG_ECDH = (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_ECDH) CALG_ECMQV = (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_ANY | ALG_SID_ECMQV) CALG_ECDSA = (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_ECDSA) class CREDENTIAL_FLAGS(Enum): CRED_FLAGS_PASSWORD_FOR_CERT = 0x1 CRED_FLAGS_PROMPT_NOW = 0x2 CRED_FLAGS_USERNAME_TARGET = 0x4 CRED_FLAGS_OWF_CRED_BLOB = 0x8 CRED_FLAGS_REQUIRE_CONFIRMATION = 0x10 CRED_FLAGS_WILDCARD_MATCH = 0x20 CRED_FLAGS_VSM_PROTECTED = 0x40 CRED_FLAGS_NGC_CERT = 0x80 class CREDENTIAL_TYPE(Enum): CRED_TYPE_GENERIC = 0x1 CRED_TYPE_DOMAIN_PASSWORD = 0x2 CRED_TYPE_DOMAIN_CERTIFICATE = 0x3 CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 0x4 CRED_TYPE_GENERIC_CERTIFICATE = 0x5 CRED_TYPE_DOMAIN_EXTENDED = 0x6 CRED_TYPE_MAXIMUM = 0x7 CRED_TYPE_MAXIMUM_EX = 0x8 class CREDENTIAL_PERSIST(Enum): CRED_PERSIST_NONE = 0x0 CRED_PERSIST_SESSION = 0x1 CRED_PERSIST_LOCAL_MACHINE = 0x2 CRED_PERSIST_ENTERPRISE = 0x3 ALGORITHMS_DATA = { # Algorithm: key/SaltLen, CryptHashModule, Mode, IVLen, BlockSize ALGORITHMS.CALG_SHA.value: (160//8, SHA1, None, None, 512//8), ALGORITHMS.CALG_HMAC.value: (160//8, SHA512, None, None, 512//8), ALGORITHMS.CALG_3DES.value: (192//8, DES3, DES3.MODE_CBC, 64//8), ALGORITHMS.CALG_SHA_512.value: (128//8, SHA512, None, None, 1024//8), ALGORITHMS.CALG_AES_256.value: (256//8, AES, AES.MODE_CBC, 128//8), } class MasterKeyFile(Structure): structure = ( ('Version', ' ALGORITHMS_DATA[self['HashAlgo']][4]: derivedKey = HMAC.new(sessionKey, digestmod = ALGORITHMS_DATA[self['HashAlgo']][1]).digest() else: derivedKey = sessionKey if len(derivedKey) < ALGORITHMS_DATA[self['CryptAlgo']][0]: # Extend the key derivedKey += b'\x00'*ALGORITHMS_DATA[self['HashAlgo']][4] ipad = bytearray([ i ^ 0x36 for i in bytearray(derivedKey)][:ALGORITHMS_DATA[self['HashAlgo']][4]]) opad = bytearray([ i ^ 0x5c for i in bytearray(derivedKey)][:ALGORITHMS_DATA[self['HashAlgo']][4]]) derivedKey = ALGORITHMS_DATA[self['HashAlgo']][1].new(ipad).digest() + \ ALGORITHMS_DATA[self['HashAlgo']][1].new(opad).digest() derivedKey = fixparity(derivedKey) return derivedKey def decrypt(self, key, entropy = None): keyHash = SHA1.new(key).digest() sessionKey = HMAC.new(keyHash, self['Salt'], ALGORITHMS_DATA[self['HashAlgo']][1]) if entropy is not None: sessionKey.update(entropy) sessionKey = sessionKey.digest() # Derive the key derivedKey = self.deriveKey(sessionKey) cipher = ALGORITHMS_DATA[self['CryptAlgo']][1].new(derivedKey[:ALGORITHMS_DATA[self['CryptAlgo']][0]], mode=ALGORITHMS_DATA[self['CryptAlgo']][2], iv=b'\x00'*ALGORITHMS_DATA[self['CryptAlgo']][3]) cleartext = unpad(cipher.decrypt(self['Data']), ALGORITHMS_DATA[self['CryptAlgo']][1].block_size) # Now check the signature # ToDo Fix this, it's just ugly, more testing so we can remove one toSign = (self.rawData[20:][:len(self.rawData)-20-len(self['Sign'])-4]) # Calculate the different HMACKeys keyHash2 = keyHash + b"\x00"*ALGORITHMS_DATA[self['HashAlgo']][1].block_size ipad = bytearray([i ^ 0x36 for i in bytearray(keyHash2)][:ALGORITHMS_DATA[self['HashAlgo']][1].block_size]) opad = bytearray([i ^ 0x5c for i in bytearray(keyHash2)][:ALGORITHMS_DATA[self['HashAlgo']][1].block_size]) a = ALGORITHMS_DATA[self['HashAlgo']][1].new(ipad) a.update(self['HMac']) hmacCalculated1 = ALGORITHMS_DATA[self['HashAlgo']][1].new(opad) hmacCalculated1.update(a.digest()) if entropy is not None: hmacCalculated1.update(entropy) hmacCalculated1.update(toSign) hmacCalculated3 = HMAC.new(keyHash, self['HMac'], ALGORITHMS_DATA[self['HashAlgo']][1]) if entropy is not None: hmacCalculated3.update(entropy) hmacCalculated3.update(toSign) if hmacCalculated1.digest() == self['Sign'] or hmacCalculated3.digest() == self['Sign']: return cleartext else: return None class VAULT_ATTRIBUTE(Structure): structure = ( ('Id', ' 20: if data[16:][:6] == b'\x00'*6: self.structure += self.padding if unpack('= 100: self.structure += self.id100 if len(data[16:]) >= 9: self.structure += self.extended Structure.__init__(self, data, alignment) def dump(self): print("[ATTRIBUTE %d]" % self['Id']) if len(self.rawData) > 28: print("Size : 0x%x" % self['Size']) if self['IVPresent'] > 0: print("IVSize : 0x%x" % self['IVSize']) print("IV : %s" % hexlify(self['IV'])) print("Data : %s" % hexlify(self['Data'])) class VAULT_ATTRIBUTE_MAP_ENTRY(Structure): structure = ( ('Id', ' 0: self.attributesLen.append(self.mapEntries[i]['Offset']-self.mapEntries[i-1]['Offset']) self.attributesLen.append(len(self.rawData) - self.mapEntries[i]['Offset'] ) self.attributes = list() for i, entry in enumerate(self.mapEntries): attribute = VAULT_ATTRIBUTE(self.rawData[entry['Offset']:][:self.attributesLen[i]]) self.attributes.append(attribute) # Do we have remaining data? self['Data'] = self.rawData[self.mapEntries[-1]['Offset']+len(self.attributes[-1].getData()):] def dump(self): print("[VCRD]") print("SchemaGuid : %s" % bin_to_string(self['SchemaGuid'])) print("LastWritten : %s" % (datetime.utcfromtimestamp(getUnixTime(self['LastWritten'])))) print("FriendlyName: %s" % (self['FriendlyName'].decode('utf-16le'))) print() for i,entry in enumerate(self.mapEntries): entry.dump() self.attributes[i].dump() print() print("Remaining : %s" % (hexlify(self['Data']))) print() class VAULT_VPOL(Structure): structure = ( ('Version', '=16: if data[0:1] == b'\x24' or data[0:1] == b'\x34': self.structure = self.structureKDBM else: self.structure = self.structureKSSM Structure.__init__(self, data, alignment) class VAULT_VPOL_KEYS(Structure): structure = ( ('Key1',':', BCRYPT_KEY_WRAP), ('Key2',':', BCRYPT_KEY_WRAP), ) def dump(self): print("[VAULT_VPOL_KEYS]") if self['Key1']['Size'] > 0x24: print('Key1:') hexdump(self['Key1']['bKeyBlob']) print('Key2:') hexdump(self['Key2']['bKeyBlob']) else: print('Key1: 0x%s' % hexlify(self['Key1']['bKeyBlob']['bKey']).decode('latin-1')) print('Key2: 0x%s' % hexlify(self['Key2']['bKeyBlob']['bKey']).decode('latin-1')) print() class VAULT_INTERNET_EXPLORER(Structure): structure = ( ('Version','