1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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'
|