Skip to content

Commit 97e41b2

Browse files
authored
Merge pull request #173 from blacklanternsecurity/aesKeys
aesKey extraction
2 parents 0b2d070 + 7f8810f commit 97e41b2

File tree

2 files changed

+113
-74
lines changed

2 files changed

+113
-74
lines changed

pypykatz/lsadecryptor/packages/kerberos/decryptor.py

+44-73
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ def __init__(self):
2121
self.pin:str = None
2222
self.pin_raw:bytes = None
2323
self.cardinfo = None
24+
self.aes_key128 = None
25+
self.aes_key256 = None
2426

2527
def __str__(self):
2628
t = '\t== Kerberos ==\n'
@@ -38,6 +40,10 @@ def __str__(self):
3840
t += '\t\t\tReaderName: %s\n' % self.cardinfo['ReaderName']
3941
t += '\t\t\tContainerName: %s\n' % self.cardinfo['ContainerName']
4042
t += '\t\t\tCSPName: %s\n' % self.cardinfo['CSPName']
43+
if self.aes_key128:
44+
t += '\t\tAES128 Key: %s\n' % self.aes_key128.hex()
45+
if self.aes_key256:
46+
t += '\t\tAES256 Key: %s\n' % self.aes_key256.hex()
4147

4248
# TODO: check if users actually need this.
4349
# I think it's not useful to print out the kerberos ticket data as string, as noone uses it directly.
@@ -61,7 +67,10 @@ def to_dict(self):
6167
t['tickets'] = []
6268
for ticket in self.tickets:
6369
t['tickets'] = ticket.to_dict()
64-
70+
if self.aes_key128:
71+
t['aes128'] = self.aes_key128.hex()
72+
if self.aes_key256:
73+
t['aes256'] = self.aes_key256.hex()
6574
return t
6675

6776

@@ -125,13 +134,40 @@ def process_session(self, kerberos_logon_session):
125134

126135
self.current_cred.username = kerberos_logon_session.credentials.UserName.read_string(self.reader)
127136
self.current_cred.domainname = kerberos_logon_session.credentials.Domaine.read_string(self.reader)
128-
#if self.current_cred.username.endswith('$') is True:
129-
# self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader), bytes_expected=True)
130-
# if self.current_cred.password is not None:
131-
# self.current_cred.password = self.current_cred.password.hex()
132-
#else:
133-
# self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader))
134-
self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader))
137+
138+
# Extract keys from pKeyList
139+
if kerberos_logon_session.pKeyList.value != 0:
140+
key_list = kerberos_logon_session.pKeyList.read(self.reader, override_finaltype = self.decryptor_template.keys_list_struct)
141+
key_list.read(self.reader, self.decryptor_template.hash_password_struct)
142+
143+
for key in key_list.KeyEntries:
144+
if key.generic.Size > 0:
145+
if key.generic.Size <= 24: # AES128
146+
keydata = key.generic.Checksump.read_raw(self.reader, key.generic.Size)
147+
if keydata:
148+
dec_key, _ = self.decrypt_password(keydata, bytes_expected=True)
149+
if dec_key:
150+
self.current_cred.aes_key128 = dec_key
151+
elif key.generic.Size <= 32: # AES256
152+
keydata = key.generic.Checksump.read_raw(self.reader, key.generic.Size)
153+
if keydata:
154+
dec_key, _ = self.decrypt_password(keydata, bytes_expected=True)
155+
if dec_key:
156+
self.current_cred.aes_key256 = dec_key
157+
158+
# Process normal password if present
159+
if kerberos_logon_session.credentials.Password.Length != 0:
160+
if self.current_cred.username.endswith('$') is True:
161+
self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(
162+
kerberos_logon_session.credentials.Password.read_maxdata(self.reader),
163+
bytes_expected=True
164+
)
165+
if self.current_cred.password is not None:
166+
self.current_cred.password = self.current_cred.password.hex()
167+
else:
168+
self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(
169+
kerberos_logon_session.credentials.Password.read_maxdata(self.reader)
170+
)
135171

136172
if kerberos_logon_session.SmartcardInfos.value != 0:
137173
csp_info = kerberos_logon_session.SmartcardInfos.read(self.reader, override_finaltype = self.decryptor_template.csp_info_struct)
@@ -140,71 +176,6 @@ def process_session(self, kerberos_logon_session):
140176
if csp_info.CspDataLength != 0:
141177
self.current_cred.cardinfo = csp_info.CspData.get_infos()
142178

143-
#### key list (still in session) this is not a linked list (thank god!)
144-
if kerberos_logon_session.pKeyList.value != 0:
145-
key_list = kerberos_logon_session.pKeyList.read(self.reader, override_finaltype = self.decryptor_template.keys_list_struct)
146-
#print(key_list.cbItem)
147-
key_list.read(self.reader, self.decryptor_template.hash_password_struct)
148-
for key in key_list.KeyEntries:
149-
pass
150-
### GOOD
151-
#keydata_enc = key.generic.Checksump.read_raw(self.reader, key.generic.Size)
152-
#print(keydata_enc)
153-
#keydata, raw_dec = self.decrypt_password(keydata_enc, bytes_expected=True)
154-
#print(keydata_enc.hex())
155-
#input('KEY?')
156-
157-
158-
#print(key.generic.Checksump.value)
159-
160-
#self.log_ptr(key.generic.Checksump.value, 'Checksump', datasize = key.generic.Size)
161-
#if self.reader.reader.sysinfo.BuildNumber < WindowsBuild.WIN_10_1507.value and key.generic.Size > LSAISO_DATA_BLOB.size:
162-
# if key.generic.Size <= LSAISO_DATA_BLOB.size + (len("KerberosKey") - 1) + 32: #AES_256_KEY_LENGTH
163-
# input('1')
164-
# data_blob = key.generic.Checksump.read(self.reader, override_finaltype = LSAISO_DATA_BLOB)
165-
# data_blob.read(self.reader, key.generic.Size - LSAISO_DATA_BLOB.size)
166-
#
167-
# input('data blob end')
168-
# """
169-
# kprintf(L"\n\t * LSA Isolated Data: %.*S", blob->typeSize, blob->data);
170-
# kprintf(L"\n\t Unk-Key : "); kull_m_string_wprintf_hex(blob->unkKeyData, sizeof(blob->unkKeyData), 0);
171-
# kprintf(L"\n\t Encrypted: "); kull_m_string_wprintf_hex(blob->data + blob->typeSize, blob->origSize, 0);
172-
# kprintf(L"\n\t\t SS:%u, TS:%u, DS:%u", blob->structSize, blob->typeSize, blob->origSize);
173-
# kprintf(L"\n\t\t 0:0x%x, 1:0x%x, 2:0x%x, 3:0x%x, 4:0x%x, E:", blob->unk0, blob->unk1, blob->unk2, blob->unk3, blob->unk4);
174-
# kull_m_string_wprintf_hex(blob->unkData2, sizeof(blob->unkData2), 0); kprintf(L", 5:0x%x", blob->unk5);
175-
# """
176-
# else:
177-
# input('2')
178-
# key.generic.Checksump.read(self.reader, override_finaltype = LSAISO_DATA_BLOB)
179-
# print('unkData1 : %s' % data_struct.unkData1.hex())
180-
# print('unkData2 : %s' % data_struct.unkData2.hex())
181-
# print('Encrypted : %s' % data_struct.data.hex()) #another extra struct should wrap this data! ENC_LSAISO_DATA_BLOB
182-
#
183-
#else:
184-
#
185-
# if self.reader.reader.sysinfo.BuildNumber < WindowsBuild.WIN_VISTA.value:
186-
# input('3')
187-
# key.generic.Checksump.read(self.reader, override_finaltype = LSAISO_DATA_BLOB)
188-
# print('unkData1 : %s' % data_struct.unkData1.hex())
189-
# print('unkData2 : %s' % data_struct.unkData2.hex())
190-
# print('Encrypted : %s' % data_struct.data.hex()) #another extra struct should wrap this data! ENC_LSAISO_DATA_BLOB
191-
#
192-
# else:
193-
# input('4')
194-
# #we need to decrypt as well!
195-
# self.reader.move(key.generic.Checksump.value)
196-
# enc_data = self.reader.read(key.generic.Size)
197-
# print(hexdump(enc_data))
198-
# dec_data = self.lsa_decryptor.decrypt(enc_data)
199-
# print(hexdump(dec_data))
200-
# t_reader = GenericReader(dec_data)
201-
# data_struct = LSAISO_DATA_BLOB(t_reader)
202-
# print('unkData1 : %s' % data_struct.unkData1.hex())
203-
# print('unkData2 : %s' % data_struct.unkData2.hex())
204-
# print('Encrypted : %s' % data_struct.data.hex()) #another extra struct should wrap this data! ENC_LSAISO_DATA_BLOB
205-
#
206-
#input()
207-
208179
if self.with_tickets is True:
209180
if kerberos_logon_session.Tickets_1.Flink.value != 0 and \
210181
kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location and \

pypykatz/lsadecryptor/packages/kerberos/templates.py

+69-1
Original file line numberDiff line numberDiff line change
@@ -1118,4 +1118,72 @@ def __init__(self, reader):
11181118

11191119
def read(self, reader):
11201120
self.Data = self.Value.read_raw(reader, self.Length)
1121-
return self.Data
1121+
return self.Data
1122+
1123+
class KerberosHashPassword:
1124+
def __init__(self):
1125+
self.salt = None # Unicode string
1126+
self.iterations = None # Usually 4096 for AES
1127+
self.aes128 = None
1128+
self.aes256 = None
1129+
1130+
class KerberosCredential:
1131+
def __init__(self):
1132+
self.luid = None
1133+
self.username = None
1134+
self.domainname = None
1135+
self.password = None
1136+
self.password_raw = None
1137+
self.aes_key128 = None
1138+
self.aes_key256 = None
1139+
1140+
def __str__(self):
1141+
t = '\t\t== Kerberos ==\n'
1142+
t += '\t\tUsername: %s\n' % self.username
1143+
t += '\t\tDomain: %s\n' % self.domainname
1144+
if self.password:
1145+
t += '\t\tPassword: %s\n' % self.password
1146+
if self.aes_key128:
1147+
t += '\t\tAES128 Key: %s\n' % self.aes_key128.hex()
1148+
if self.aes_key256:
1149+
t += '\t\tAES256 Key: %s\n' % self.aes_key256.hex()
1150+
return t
1151+
1152+
def to_dict(self):
1153+
t = {}
1154+
t['credtype'] = 'kerberos'
1155+
t['username'] = self.username
1156+
t['domainname'] = self.domainname
1157+
t['password'] = self.password
1158+
t['password_raw'] = self.password_raw
1159+
if self.aes_key128:
1160+
t['aes128'] = self.aes_key128.hex()
1161+
if self.aes_key256:
1162+
t['aes256'] = self.aes_key256.hex()
1163+
return t
1164+
1165+
def to_grep_line(self):
1166+
lines = []
1167+
if self.aes_key128:
1168+
lines.append('kerberos\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s' % (
1169+
self.username,
1170+
self.domainname,
1171+
'',
1172+
'',
1173+
'',
1174+
self.aes_key128.hex(),
1175+
'',
1176+
''
1177+
))
1178+
if self.aes_key256:
1179+
lines.append('kerberos\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s' % (
1180+
self.username,
1181+
self.domainname,
1182+
'',
1183+
'',
1184+
'',
1185+
'',
1186+
self.aes_key256.hex(),
1187+
''
1188+
))
1189+
return lines

0 commit comments

Comments
 (0)