remove dead code and unused deps

This commit is contained in:
zblurx 2023-07-22 15:34:28 +02:00
parent 5be83bd047
commit c07dd6c01d
6 changed files with 0 additions and 806 deletions

View File

@ -1,306 +0,0 @@
#!/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 <jmichel.p@gmail.com> ##
## ##
## This program is distributed under GPLv3 licence (see LICENCE.txt) ##
## ##
#############################################################################
import binascii
import struct
import hmac
import hashlib
from donpapi.lib.dpapi_pick import crypto
from donpapi.lib.dpapi_pick import eater
'''
crypto_CryptoAlgo={
0x6601: {"name":"DES", "keyLength":64/8, "blockLength":64/8, "IVLength":64/8, "module":"des", "keyFixup":"des_set_odd_parity"},
0x6603: {"name":"DES3", "keyLength":192/8, "blockLength":64/8, "IVLength":64/8, "module":"triple_des", "keyFixup":"des_set_odd_parity"},
0x6611: {"name":"AES", "keyLength":128/8, "blockLength":128/8, "IVLength":128/8},
0x660e: {"name":"AES-128", "keyLength":128/8, "blockLength":128/8, "IVLength":128/8},
0x660f: {"name":"AES-192", "keyLength":192/8, "blockLength":128/8, "IVLength":128/8},
0x6610: {"name":"AES-256", "keyLength":256/8, "blockLength":128/8, "IVLength":128/8},
0x8009: {"name":"HMAC", "digestLength":160/8, "blockLength":512/8},
0x8003: {"name":"md5", "digestLength":128/8, "blockLength":512/8},
0x8004: {"name":"sha1", "digestLength":160/8, "blockLength":512/8},
0x800c: {"name":"sha256", "digestLength":256/8, "blockLength":512/8},
0x800d: {"name":"sha384", "digestLength":384/8, "blockLength":1024/8},
0x800e: {"name":"sha512", "digestLength":512/8, "blockLength":1024/8}
}'''
class RPC_SID(eater.DataStruct):
"""Represents a RPC_SID structure. See MSDN for documentation"""
def __init__(self, raw=None):
self.version = None
self.idAuth = None
self.subAuth = None
eater.DataStruct.__init__(self, raw)
def parse(self, data):
#print('parse rpc')
self.version = data.eat("B")
n = data.eat("B")
self.idAuth = struct.unpack(">Q", b"\0\0" + data.eat("6s"))[0]
self.subAuth = data.eat("%dL" % n)
def __str__(self):
s = ["S-%d-%d" % (self.version, self.idAuth)]
s += ["%d" % x for x in self.subAuth]
return "-".join(s)
def __repr__(self):
return """RPC_SID(%s):
revision = %d
identifier-authority = %r
subAuthorities = %r""" % (self, self.version, self.idAuth, self.subAuth)
class CredSystem(eater.DataStruct):
"""This represents the DPAPI_SYSTEM token which is stored as an LSA
secret.
Sets 2 properties:
self.machine
self.user
"""
def __init__(self, raw=None):
self.machine = None
self.user = None
self.revision = None
eater.DataStruct.__init__(self, raw)
def parse(self, data):
self.revision = data.eat("L")
self.machine = data.eat("20s")
self.user = data.eat("20s")
def __repr__(self):
s = ["DPAPI_SYSTEM:"]
if self.user is not None:
s.append("\tUser Credential : %s" % self.user.encode('hex'))
if self.machine is not None:
s.append("\tMachine Credential: %s" % self.machine.encode('hex'))
return "\n".join(s)
class CredhistEntry(eater.DataStruct):
"""Represents an entry in the Credhist file"""
def __init__(self, raw=None):
self.pwdhash = None
self.hmac = None
self.revision = None
self.hashAlgo = None
self.rounds = None
self.cipherAlgo = None
self.shaHashLen = None
self.ntHashLen = None
self.iv = None
self.userSID = None
self.encrypted = None
self.revision2 = None
self.guid = None
self.ntlm = None
eater.DataStruct.__init__(self, raw)
def __getstate__(self):
d = dict(self.__dict__)
for k in ["cipherAlgo", "hashAlgo"]:
if k in d:
d[k] = d[k].algnum
return d
def __setstate__(self, d):
for k in ["cipherAlgo", "hashAlgo"]:
if k in d:
d[k] = crypto.CryptoAlgo(d[k])#'''crypto_CryptoAlgo[d[k]]'''
self.__dict__.update(d)
def parse(self, data):
#print('parse2')
self.revision = data.eat("L")
#print('parse3')
self.hashAlgo = crypto.CryptoAlgo(data.eat("L"))#'''crypto_CryptoAlgo[data.eat("L")]'''
self.rounds = data.eat("L")
data.eat("L")
#print('parse4')
self.cipherAlgo = crypto.CryptoAlgo(data.eat("L"))#'''crypto_CryptoAlgo'''
self.shaHashLen = data.eat("L")
self.ntHashLen = data.eat("L")
self.iv = data.eat("16s")
#print('parse5')
self.userSID = RPC_SID()
self.userSID.parse(data)
n = self.shaHashLen + self.ntHashLen
n += -n % self.cipherAlgo.blockSize#self.cipherAlgo['blockLength'] #blockSize
self.encrypted = data.eat_string(n)
#print('parse6')
self.revision2 = data.eat("L")
self.guid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B")
#print(self.__repr__)
def decryptWithKey(self, enckey):
"""Decrypts this credhist entry using the given encryption key."""
#print(f'using key {enckey}')
cleartxt = crypto.dataDecrypt(self.cipherAlgo, self.hashAlgo, self.encrypted,
enckey, self.iv, self.rounds)
#print(f'cleartext {cleartxt}')
self.pwdhash = cleartxt[:self.shaHashLen]
self.ntlm = cleartxt[self.shaHashLen:self.shaHashLen + self.ntHashLen].rstrip(b"\x00")
if len(self.ntlm) != 16:
self.ntlm = None
def decryptWithHash(self, pwdhash):
"""Decrypts this credhist entry with the given user's password hash.
Simply computes the encryption key with the given hash then calls
self.decryptWithKey() to finish the decryption.
"""
self.decryptWithKey(crypto.derivePwdHash(pwdhash, str(self.userSID)))#self.crypto ?
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 decryptWithPassword(self, password):
"""Decrypts this credhist entry with the given user's password.
Simply computes the password hash then calls self.decryptWithHash()
"""
return self.decryptWithHash(hashlib.sha1(password.encode("UTF-16LE")).digest())
'''
def jtr_shadow(self):
"""Returns a string that can be passed to John the Ripper to crack this
CREDHIST entry. Requires to use a recent jumbo version of JtR plus
the configuration snipplet in the "3rdparty" directory of DPAPIck.
Unless you know what you are doing, you shall not call this function
yourself. Instead, use the method provided by CredHistPool object.
"""
rv = []
if self.pwdhash is not None:
rv.append("%s:$dynamic_1400$%s" % (self.userSID, self.pwdhash.encode('hex')))
if self.ntlm is not None:
rv.append("%s:$NT$%s" % (self.userSID, self.ntlm.encode('hex')))
return "\n".join(rv)
'''
def __repr__(self):
s = ["CredHist entry",
"\trevision = %x\n" % self.revision,
"\thash = %r" % self.hashAlgo,
"\trounds = %i" % self.rounds,
"\tcipher = %r" % self.cipherAlgo,
"\tshaHashLen = %i" % self.shaHashLen,
"\tntHashLen = %i" % self.ntHashLen,
"\tuserSID = %s" % self.userSID,
"\tguid = %s" % self.guid,
"\tiv = %s" % binascii.hexlify(self.iv)]#.encode("hex")
if self.pwdhash is not None:
s.append("\tpwdhash = %s" % binascii.hexlify(self.pwdhash))#.encode("hex")
if self.ntlm is not None:
s.append("\tNTLM = %s" % binascii.hexlify(self.ntlm))#.encode("hex")
return "\n".join(s)
class CredHistFile(eater.DataStruct):
"""Represents a CREDHIST file.
Be aware that currently, it is not possible to check whether the decryption
succeeded or not. To circumvent that and optimize a little bit crypto
operations, once a credhist entry successfully decrypts a masterkey, the
whole CredHistFile is flagged as valid. Then, no further decryption occurs.
"""
def __init__(self, raw=None):
self.entries_list = []
self.entries = {}
self.valid = False
self.footmagic = None
self.curr_guid = None
eater.DataStruct.__init__(self, raw)
def parse(self, data):
while True:
l = data.pop("L")
if l == 0:
break
self.addEntry(data.pop_string(l - 4))
self.footmagic = data.eat("L")
self.curr_guid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B")
#print(f'GUID:{self.curr_guid}')
def addEntry(self, blob):
"""Creates a CredhistEntry object with blob then adds it to the store"""
x = CredhistEntry(blob)
self.entries[x.guid] = x
self.entries_list.append(x)
def validate(self):
"""Simply flags a file as successfully decrypted. See the class
documentation for information.
"""
self.valid = True
def decryptWithHash(self, h):
"""Try to decrypt each entry with the given hash"""
if self.valid:
return
curhash = h
for entry in self.entries_list:
try:
entry.decryptWithHash(curhash)
curhash = entry.pwdhash
except Exception as ex:
#print('erro Decrypting')
#print(ex)
continue
def decryptWithPassword(self, pwd):
"""Try to decrypt each entry with the given password.
This function simply computes the SHA-1 hash with the password, then
calls self.decryptWithHash()
"""
#print(f'password : {pwd}')
return self.decryptWithHash(hashlib.sha1(pwd.encode("UTF-16LE")).digest())
def jtr_shadow(self, validonly=False):
"""Returns a string that can be passed to John the Ripper to crack the
CREDHIST entries. Requires to use a recent jumbo version of JtR plus
the configuration snipplet in the "3rdparty" directory of DPAPIck.
If validonly is set to True, will only extract CREDHIST entries
that are known to have sucessfully decrypted a masterkey.
"""
if validonly and not self.valid:
return ""
s = []
for e in self.entries.itervalues():
s.append(e.jtr_shadow())
return "\n".join(s)
def __repr__(self):
s = ["CredHistPool: %s" % self.curr_guid]
for e in self.entries:
s.append("---")
s.append(repr(self.entries[e]))
s.append("====")
return "\n".join(s)
# vim:ts=4:expandtab:sw=4

View File

@ -1,339 +0,0 @@
#!/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 <jmichel.p@gmail.com> ##
## ##
## This program is distributed under GPLv3 licence (see LICENCE.txt) ##
## ##
#############################################################################
import hashlib
import struct
import array
import M2Crypto
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)
@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)
m2name = property(lambda self: self.algo.m2)
keyLength = property(lambda self: int(self.algo.keyLength / 8))
ivLength = property(lambda self: int(self.algo.IVLength / 8))
blockSize = property(lambda self: int(self.algo.blockLength / 8))
digestLength = property(lambda self: int(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(0x6603, name="DES3", keyLength=192, IVLength=64, blockLength=64, m2="des_ede3_cbc",
keyFixup=des_set_odd_parity)
CryptoAlgo.add_algo(0x6609, name="DES2", keyLength=128, IVLength=64, blockLength=64, m2="des_ede_cbc",
keyFixup=des_set_odd_parity)
CryptoAlgo.add_algo(0x6611, name="AES", keyLength=128, IVLength=128, blockLength=128, m2="aes_128_cbc")
CryptoAlgo.add_algo(0x660e, name="AES-128", keyLength=128, IVLength=128, blockLength=128, m2="aes_128_cbc")
CryptoAlgo.add_algo(0x660f, name="AES-192", keyLength=192, IVLength=128, blockLength=128, m2="aes_192_cbc")
CryptoAlgo.add_algo(0x6610, name="AES-256", keyLength=256, IVLength=128, blockLength=128, m2="aes_256_cbc")
CryptoAlgo.add_algo(0x6601, name="DES", keyLength=64, IVLength=64, blockLength=64, m2="des_cbc",
keyFixup=des_set_odd_parity)
CryptoAlgo.add_algo(0x8009, name="HMAC", digestLength=160, blockLength=512)
CryptoAlgo.add_algo(0x8001, name="md2", digestLength=128, blockLength=128)
CryptoAlgo.add_algo(0x8002, name="md4", digestLength=128, 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):
"""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 (integrity check)
:returns: decryption key
:rtype : str
"""
if len(masterkey) > 20:
masterkey = hashlib.sha1(masterkey).digest()
masterkey += "\x00" * hashAlgo.blockSize
ipad = "".join(chr(ord(masterkey[i]) ^ 0x36) for i in range(hashAlgo.blockSize))
opad = "".join(chr(ord(masterkey[i]) ^ 0x5c) for i in range(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:
digest.update(strongPassword)
return digest.digest()
def CryptSessionKeyWin7(masterkey, nonce, hashAlgo, entropy=None, strongPassword=None):
"""Computes the decryption key for XP 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 (integrity check)
:returns: decryption key
:rtype : str
"""
if len(masterkey) > 20:
masterkey = hashlib.sha1(masterkey).digest()
digest = M2Crypto.EVP.HMAC(masterkey, hashAlgo.name)
digest.update(nonce)
if entropy is not None:
digest.update(entropy)
if strongPassword is not None:
digest.update(strongPassword)
return digest.final()
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 += "\x00" * hashAlgo.blockSize
ipad = "".join(chr(ord(h[i]) ^ 0x36) for i in range(hashAlgo.blockSize))
opad = "".join(chr(ord(h[i]) ^ 0x5c) for i in range(hashAlgo.blockSize))
k = hashlib.new(hashAlgo.name, ipad).digest() + hashlib.new(hashAlgo.name, opad).digest()
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 = M2Crypto.RC4.RC4(dg.digest())
deskey = arcfour.update(lsakey[12:60]) + arcfour.final()
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 xrange(1000):
dg.update(lsakey[28:60])
c = M2Crypto.EVP.Cipher(alg="aes_256_ecb", key=dg.digest(), iv="", op=M2Crypto.decrypt)
c.set_padding(0)
keys = c.update(lsakey[60:]) + c.final()
size = struct.unpack_from("<L", keys)[0]
keys = keys[16:16 + size]
currentkey = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % struct.unpack("<L2H8B", keys[4:20])
nb = struct.unpack("<L", keys[24:28])[0]
off = 28
kd = {}
for i in xrange(nb):
g = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % struct.unpack("<L2H8B", keys[off:off + 16])
t, l = struct.unpack_from("<2L", keys[off + 16:])
k = keys[off + 24:off + 24 + l]
kd[g] = {"type": t, "key": k}
off += 24 + l
return (currentkey, kd)
def SystemFunction005(secret, key):
"""This function is used to decrypt LSA secrets.
Reproduces the corresponding Windows internal function.
Taken from creddump project https://code.google.com/p/creddump/
"""
decrypted_data = ''
j = 0
algo = CryptoAlgo(0x6603)
for i in range(0, len(secret), 8):
enc_block = secret[i:i + 8]
block_key = key[j:j + 7]
des_key = []
des_key.append(ord(block_key[0]) >> 1)
des_key.append(((ord(block_key[0]) & 0x01) << 6) | (ord(block_key[1]) >> 2))
des_key.append(((ord(block_key[1]) & 0x03) << 5) | (ord(block_key[2]) >> 3))
des_key.append(((ord(block_key[2]) & 0x07) << 4) | (ord(block_key[3]) >> 4))
des_key.append(((ord(block_key[3]) & 0x0F) << 3) | (ord(block_key[4]) >> 5))
des_key.append(((ord(block_key[4]) & 0x1F) << 2) | (ord(block_key[5]) >> 6))
des_key.append(((ord(block_key[5]) & 0x3F) << 1) | (ord(block_key[6]) >> 7))
des_key.append(ord(block_key[6]) & 0x7F)
des_key = algo.do_fixup_key("".join([chr(x << 1) for x in des_key]))
cipher = M2Crypto.EVP.Cipher(alg="des_ecb", key=des_key, iv="", op=M2Crypto.decrypt)
cipher.set_padding(0)
decrypted_data += cipher.update(enc_block) + cipher.final()
j += 7
if len(key[j:j + 7]) < 7:
j = len(key[j:j + 7])
dec_data_len = struct.unpack("<L", decrypted_data[:4])[0]
return decrypted_data[8:8 + dec_data_len]
def decrypt_lsa_secret(secret, lsa_keys):
"""This function replaces SystemFunction005 for newer Windows"""
keyid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % struct.unpack("<L2H8B", secret[4:20])
if keyid not in lsa_keys:
return None
algo = struct.unpack("<L", secret[20:24])[0]
dg = hashlib.sha256()
dg.update(lsa_keys[keyid]["key"])
for i in xrange(1000):
dg.update(secret[28:60])
c = M2Crypto.EVP.Cipher(alg="aes_256_ecb", key=dg.digest(), iv="", op=M2Crypto.decrypt)
c.set_padding(0)
clear = c.update(secret[60:]) + c.final()
size = struct.unpack_from("<L", clear)[0]
return clear[16:16 + size]
def pbkdf2(passphrase, salt, keylen, iterations, digest='sha1'):
"""Implementation of PBKDF2 that allows specifying digest algorithm.
Returns the corresponding expanded key which is keylen long.
"""
print(f'PKBF of {passphrase} -- {salt} -- {keylen} -- {iterations} -- {digest}')
buff = b""
i = 1
while len(buff) < keylen:
U = salt + struct.pack("!L", i)
i += 1
derived = M2Crypto.EVP.hmac(passphrase, U, digest)
for r in xrange(iterations - 1):
actual = M2Crypto.EVP.hmac(passphrase, derived, digest)
#print(f'R:{r} :{len(derived)} {derived} : {len(actual)} {actual}')
#derived = ''.join([chr(ord(x) ^ ord(y)) for (x, y) in zip(derived, actual)])
derived = ''.join([chr(x ^ y) for (x, y) in zip(derived, actual)]).encode('utf8')
#print(f'r{r} - {len(derived)} : {derived}')
buff += derived
#print('ret')
return buff[:int(keylen)]
def derivePwdHash(pwdhash, userSID, digest='sha1'):
"""Internal use. Computes the encryption key from a user's password hash"""
return M2Crypto.EVP.hmac(pwdhash, (userSID + "\0").encode("UTF-16LE"), 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)
print(hname)
derived = pbkdf2(encKey, iv, cipherAlgo.keyLength + cipherAlgo.ivLength, rounds, hname)
print(f'derived {derived}')
key, iv = derived[:cipherAlgo.keyLength], derived[cipherAlgo.keyLength:]
key = key[:cipherAlgo.keyLength]
iv = iv[:cipherAlgo.ivLength]
print(key)
print(iv)
cipher = M2Crypto.EVP.Cipher(cipherAlgo.m2name, key, iv, M2Crypto.decrypt, 0)
cipher.set_padding(0)
print(cipher)
cleartxt = cipher.update(raw) + cipher.final()
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 = M2Crypto.EVP.HMAC(pwdhash, hname)
encKey.update(hmacSalt)
encKey = encKey.final()
rv = M2Crypto.EVP.HMAC(encKey, hname)
rv.update(value)
return rv.final()
# vim:ts=4:expandtab:sw=4

View File

@ -1,128 +0,0 @@
#!/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 <jmichel.p@gmail.com> ##
## ##
## This program is distributed under GPLv3 licence (see LICENCE.txt) ##
## ##
#############################################################################
import struct
class Eater(object):
"""This class is a helper for parsing binary structures."""
def __init__(self, raw, offset=0, end=None, endianness="<"):
self.raw = raw
self.ofs = offset
if end is None:
end = len(raw)
self.end = end
self.endianness = endianness
def prepare_fmt(self, fmt):
"""Internal use. Prepend endianness to the given format if it is not
already specified.
fmt is a format string for struct.unpack()
Returns a tuple of the format string and the corresponding data size.
"""
if fmt[0] not in ["<", ">", "!", "@"]:
fmt = self.endianness+fmt
return fmt, struct.calcsize(fmt)
def read(self, fmt):
"""Parses data with the given format string without taking away bytes.
Returns an array of elements or just one element depending on fmt.
"""
fmt, sz = self.prepare_fmt(fmt)
v = struct.unpack_from(fmt, self.raw, self.ofs)
if len(v) == 1:
v = v[0]
return v
def eat(self, fmt):
"""Parses data with the given format string.
Returns an array of elements or just one element depending on fmt.
"""
fmt, sz = self.prepare_fmt(fmt)
v = struct.unpack_from(fmt, self.raw, self.ofs)
if len(v) == 1:
v = v[0]
self.ofs += sz
return v
def eat_string(self, length):
"""Eats and returns a string of length characters"""
return self.eat("%us" % length)
def eat_length_and_string(self, fmt):
"""Eats and returns a string which length is obtained after eating
an integer represented by fmt
"""
l = self.eat(fmt)
return self.eat_string(l)
def pop(self, fmt):
"""Eats a structure represented by fmt from the end of raw data"""
fmt, sz = self.prepare_fmt(fmt)
self.end -= sz
v = struct.unpack_from(fmt, self.raw, self.end)
if len(v) == 1:
v = v[0]
return v
def pop_string(self, length):
"""Pops and returns a string of length characters"""
return self.pop("%us" % length)
def pop_length_and_string(self, fmt):
"""Pops and returns a string which length is obtained after poping an
integer represented by fmt.
"""
l = self.pop(fmt)
return self.pop_string(l)
def remain(self):
"""Returns all the bytes that have not been eated nor poped yet."""
return self.raw[self.ofs:self.end]
def eat_sub(self, length):
"""Eats a sub-structure that is contained in the next length bytes"""
sub = self.__class__(self.raw[self.ofs:self.ofs+length], endianness=self.endianness)
self.ofs += length
return sub
def __nonzero__(self):
return self.ofs < self.end
class DataStruct(object):
"""Don't use this class unless you know what you are doing!"""
def __init__(self, raw=None):
if raw is not None:
self.parse(Eater(raw, endianness="<"))
def parse(self, eater_obj):
raise NotImplementedError("This function must be implemented in subclasses")

View File

@ -1673,8 +1673,6 @@ class MySeatBelt:
f"[{self.options.target_ip}] Found Directory %s -> doing nothing" % longname)
else:
self.logging.debug(f"[{self.options.target_ip}] Found file %s" % longname)
if "CREDHIST" in longname:
self.download_credhist(user, tmp_pwd, longname, type='USER')
except Exception as ex:
self.logging.debug(
f"[{self.options.target_ip}] {bcolors.WARNING}Exception in get_masterkeys for {longname}{bcolors.ENDC}")
@ -1736,8 +1734,6 @@ class MySeatBelt:
self.logging.debug("Found (not SID) Directory %s" % longname)
else:
self.logging.debug("Found file %s" % longname)
if "CREDHIST" in longname:
self.download_credhist(user, tmp_pwd, longname, type='MACHINE')
except Exception as ex:
self.logging.error(
@ -1746,32 +1742,6 @@ class MySeatBelt:
self.logging.debug(
f"[{self.options.target_ip}] {bcolors.OKBLUE}[-] Gathered Masterkeys for {len(self.users)} users{bcolors.ENDC}")
def download_credhist(self, user, tmp_pwd, longname, type='MACHINE'):
# Downloading file
try:
self.logging.debug(
f"[{self.options.target_ip}] [...] Downloading CREDHIST {user.username} {tmp_pwd} {longname}")
# from DonPAPI.lib.dpapi_pick.credhist import CredHistFile
# localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname))
'''f=open(localfile,'rb')
credhistdata = f.read()
f.close()
myCredhistfile = CredHistFile(raw=credhistdata)
print(repr(myCredhistfile))
#myCredhistfile = CredHistFile(raw=credhistdata)
for username in self.options.credz:
if username in user.username: # pour fonctionner aussi avec le .domain ou les sessions multiple citrix en user.domain.001 ?
self.logging.debug(f"[{self.options.target_ip}] [...] Testing {len(self.options.credz[username])} credz for user {user.username} CREDHIST")
for password in self.options.credz[username]:
ret=myCredhistfile.decryptWithPassword(password)
print(ret)
'''
except Exception as ex:
self.logging.error(f"[{self.options.target_ip}] {bcolors.FAIL}Error in Decrypting Credhist{bcolors.ENDC}")
self.logging.debug(ex)
def download_masterkey(self, user, path, guid, type):
guid = guid.lower()
if is_guid(guid):

View File

@ -29,10 +29,7 @@ python = "^3.9"
impacket = "^0.9.23"
pyasn = "^1.6.1"
LnkParse3 = "^1.2.0"
wheel = "^0.37.0"
M2Crypto = "^0.38.0"
pycryptodome = "^3.11.0"
swig = "^4.1.1"
[tool.poetry.dev-dependencies]