summaryrefslogtreecommitdiff
path: root/foreign/client_handling/lazagne/config/DPAPI/blob.py
diff options
context:
space:
mode:
Diffstat (limited to 'foreign/client_handling/lazagne/config/DPAPI/blob.py')
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/blob.py139
1 files changed, 139 insertions, 0 deletions
diff --git a/foreign/client_handling/lazagne/config/DPAPI/blob.py b/foreign/client_handling/lazagne/config/DPAPI/blob.py
new file mode 100644
index 0000000..6b76bc4
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/blob.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Code based from these two awesome projects:
+- DPAPICK : https://bitbucket.org/jmichel/dpapick
+- DPAPILAB : https://github.com/dfirfpi/dpapilab
+"""
+import codecs
+import traceback
+
+from .eater import DataStruct
+from . import crypto
+
+from foreign.client_handling.lazagne.config.write_output import print_debug
+from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC
+from foreign.client_handling.lazagne.config.crypto.pyDes import CBC
+from foreign.client_handling.lazagne.config.winstructure import char_to_int
+
+AES_BLOCK_SIZE = 16
+
+
+class DPAPIBlob(DataStruct):
+ """Represents a DPAPI blob"""
+
+ def __init__(self, raw=None):
+ """
+ Constructs a DPAPIBlob. If raw is set, automatically calls parse().
+ """
+ self.version = None
+ self.provider = None
+ self.mkguid = None
+ self.mkversion = None
+ self.flags = None
+ self.description = None
+ self.cipherAlgo = None
+ self.keyLen = 0
+ self.hmac = None
+ self.strong = None
+ self.hashAlgo = None
+ self.hashLen = 0
+ self.cipherText = None
+ self.salt = None
+ self.blob = None
+ self.sign = None
+ self.cleartext = None
+ self.decrypted = False
+ self.signComputed = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ """Parses the given data. May raise exceptions if incorrect data are
+ given. You should not call this function yourself; DataStruct does
+
+ data is a DataStruct object.
+ Returns nothing.
+
+ """
+ self.version = data.eat("L")
+ self.provider = b"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % data.eat("L2H8B")
+
+ # For HMAC computation
+ blobStart = data.ofs
+
+ self.mkversion = data.eat("L")
+ self.mkguid = b"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % data.eat("L2H8B")
+ self.flags = data.eat("L")
+ self.description = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8")
+ self.cipherAlgo = crypto.CryptoAlgo(data.eat("L"))
+ self.keyLen = data.eat("L")
+ self.salt = data.eat_length_and_string("L")
+ self.strong = data.eat_length_and_string("L")
+ self.hashAlgo = crypto.CryptoAlgo(data.eat("L"))
+ self.hashLen = data.eat("L")
+ self.hmac = data.eat_length_and_string("L")
+ self.cipherText = data.eat_length_and_string("L")
+
+ # For HMAC computation
+ self.blob = data.raw[blobStart:data.ofs]
+ self.sign = data.eat_length_and_string("L")
+
+ def decrypt(self, masterkey, entropy=None, strongPassword=None):
+ """Try to decrypt the blob. Returns True/False
+ :rtype : bool
+ :param masterkey: decrypted masterkey value
+ :param entropy: optional entropy for decrypting the blob
+ :param strongPassword: optional password for decrypting the blob
+ """
+ for algo in [crypto.CryptSessionKeyXP, crypto.CryptSessionKeyWin7]:
+ try:
+ sessionkey = algo(masterkey, self.salt, self.hashAlgo, entropy=entropy, strongPassword=strongPassword)
+ key = crypto.CryptDeriveKey(sessionkey, self.cipherAlgo, self.hashAlgo)
+
+ if "AES" in self.cipherAlgo.name:
+ cipher = AESModeOfOperationCBC(key[:int(self.cipherAlgo.keyLength)],
+ iv="\x00" * int(self.cipherAlgo.ivLength))
+ self.cleartext = b"".join([cipher.decrypt(self.cipherText[i:i + AES_BLOCK_SIZE]) for i in
+ range(0, len(self.cipherText), AES_BLOCK_SIZE)])
+ else:
+ cipher = self.cipherAlgo.module(key, CBC, "\x00" * self.cipherAlgo.ivLength)
+ self.cleartext = cipher.decrypt(self.cipherText)
+
+ padding = char_to_int(self.cleartext[-1])
+ if padding <= self.cipherAlgo.blockSize:
+ self.cleartext = self.cleartext[:-padding]
+
+ # check against provided HMAC
+ self.signComputed = algo(masterkey, self.hmac, self.hashAlgo, entropy=entropy, verifBlob=self.blob)
+ self.decrypted = self.signComputed == self.sign
+
+ if self.decrypted:
+ return True
+ except Exception:
+ print_debug('DEBUG', traceback.format_exc())
+
+ self.decrypted = False
+ return self.decrypted
+
+ def decrypt_encrypted_blob(self, mkp, entropy_hex=False):
+ """
+ This function should be called to decrypt a dpapi blob.
+ It will find the associcated masterkey used to decrypt the blob.
+ :param mkp: masterkey pool object (MasterKeyPool)
+ """
+ mks = mkp.get_master_keys(self.mkguid)
+ if not mks:
+ return False, 'Unable to find MK for blob {mk_guid}'.format(mk_guid=self.mkguid)
+
+ entropy = None
+ if entropy_hex:
+ entropy = codecs.decode(entropy_hex, 'hex')
+
+ for mk in mks:
+ if mk.decrypted:
+ self.decrypt(mk.get_key(), entropy=entropy)
+ if self.decrypted:
+ return True, self.cleartext
+
+ return False, 'Unable to decrypt master key'