Skip to content

Commit c5829db

Browse files
author
SkelSec
committed
Merge branch 'main' of https://github.com/skelsec/pypykatz
2 parents c321787 + 97e41b2 commit c5829db

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

@@ -130,13 +139,40 @@ def process_session(self, kerberos_logon_session):
130139

131140
self.current_cred.username = kerberos_logon_session.credentials.UserName.read_string(self.reader)
132141
self.current_cred.domainname = kerberos_logon_session.credentials.Domaine.read_string(self.reader)
133-
#if self.current_cred.username.endswith('$') is True:
134-
# self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader), bytes_expected=True)
135-
# if self.current_cred.password is not None:
136-
# self.current_cred.password = self.current_cred.password.hex()
137-
#else:
138-
# self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader))
139-
self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader))
142+
143+
# Extract keys from pKeyList
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+
key_list.read(self.reader, self.decryptor_template.hash_password_struct)
147+
148+
for key in key_list.KeyEntries:
149+
if key.generic.Size > 0:
150+
if key.generic.Size <= 24: # AES128
151+
keydata = key.generic.Checksump.read_raw(self.reader, key.generic.Size)
152+
if keydata:
153+
dec_key, _ = self.decrypt_password(keydata, bytes_expected=True)
154+
if dec_key:
155+
self.current_cred.aes_key128 = dec_key
156+
elif key.generic.Size <= 32: # AES256
157+
keydata = key.generic.Checksump.read_raw(self.reader, key.generic.Size)
158+
if keydata:
159+
dec_key, _ = self.decrypt_password(keydata, bytes_expected=True)
160+
if dec_key:
161+
self.current_cred.aes_key256 = dec_key
162+
163+
# Process normal password if present
164+
if kerberos_logon_session.credentials.Password.Length != 0:
165+
if self.current_cred.username.endswith('$') is True:
166+
self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(
167+
kerberos_logon_session.credentials.Password.read_maxdata(self.reader),
168+
bytes_expected=True
169+
)
170+
if self.current_cred.password is not None:
171+
self.current_cred.password = self.current_cred.password.hex()
172+
else:
173+
self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(
174+
kerberos_logon_session.credentials.Password.read_maxdata(self.reader)
175+
)
140176

141177
if kerberos_logon_session.SmartcardInfos.value != 0:
142178
csp_info = kerberos_logon_session.SmartcardInfos.read(self.reader, override_finaltype = self.decryptor_template.csp_info_struct)
@@ -145,71 +181,6 @@ def process_session(self, kerberos_logon_session):
145181
if csp_info.CspDataLength != 0:
146182
self.current_cred.cardinfo = csp_info.CspData.get_infos()
147183

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

11841184
def read(self, reader):
11851185
self.Data = self.Value.read_raw(reader, self.Length)
1186-
return self.Data
1186+
return self.Data
1187+
1188+
class KerberosHashPassword:
1189+
def __init__(self):
1190+
self.salt = None # Unicode string
1191+
self.iterations = None # Usually 4096 for AES
1192+
self.aes128 = None
1193+
self.aes256 = None
1194+
1195+
class KerberosCredential:
1196+
def __init__(self):
1197+
self.luid = None
1198+
self.username = None
1199+
self.domainname = None
1200+
self.password = None
1201+
self.password_raw = None
1202+
self.aes_key128 = None
1203+
self.aes_key256 = None
1204+
1205+
def __str__(self):
1206+
t = '\t\t== Kerberos ==\n'
1207+
t += '\t\tUsername: %s\n' % self.username
1208+
t += '\t\tDomain: %s\n' % self.domainname
1209+
if self.password:
1210+
t += '\t\tPassword: %s\n' % self.password
1211+
if self.aes_key128:
1212+
t += '\t\tAES128 Key: %s\n' % self.aes_key128.hex()
1213+
if self.aes_key256:
1214+
t += '\t\tAES256 Key: %s\n' % self.aes_key256.hex()
1215+
return t
1216+
1217+
def to_dict(self):
1218+
t = {}
1219+
t['credtype'] = 'kerberos'
1220+
t['username'] = self.username
1221+
t['domainname'] = self.domainname
1222+
t['password'] = self.password
1223+
t['password_raw'] = self.password_raw
1224+
if self.aes_key128:
1225+
t['aes128'] = self.aes_key128.hex()
1226+
if self.aes_key256:
1227+
t['aes256'] = self.aes_key256.hex()
1228+
return t
1229+
1230+
def to_grep_line(self):
1231+
lines = []
1232+
if self.aes_key128:
1233+
lines.append('kerberos\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s' % (
1234+
self.username,
1235+
self.domainname,
1236+
'',
1237+
'',
1238+
'',
1239+
self.aes_key128.hex(),
1240+
'',
1241+
''
1242+
))
1243+
if self.aes_key256:
1244+
lines.append('kerberos\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s' % (
1245+
self.username,
1246+
self.domainname,
1247+
'',
1248+
'',
1249+
'',
1250+
'',
1251+
self.aes_key256.hex(),
1252+
''
1253+
))
1254+
return lines

0 commit comments

Comments
 (0)