# Impacket - Collection of Python classes for working with network protocols. # # Copyright Fortra, LLC and its affiliated companies # # All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # DPAPI and Windows Vault parsing structures and manipulation # # Author: # Alberto Solino (@agsolino) # # 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, timezone from binascii import unhexlify, hexlify from struct import pack from hashlib import pbkdf2_hmac from Cryptodome.Hash import HMAC, SHA512, SHA1, MD4 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.fromtimestamp(getUnixTime(self['LastWritten']), tz=timezone.utc))) 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','