Skip to content

Commit 1d9d69d

Browse files
author
SkelSec
committed
windows 24H2 support, many fixes
1 parent c5829db commit 1d9d69d

File tree

9 files changed

+411
-139
lines changed

9 files changed

+411
-139
lines changed

pypykatz/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
__version__ = "0.6.10"
2+
__version__ = "0.6.11"
33
__banner__ = \
44
"""
55
# pypyKatz %s

pypykatz/alsadecryptor/packages/kerberos/decryptor.py

+60-88
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def __init__(self):
2222
self.pin:str = None
2323
self.pin_raw:bytes = None
2424
self.cardinfo = None
25+
self.aes_key128 = None
26+
self.aes_key256 = None
2527

2628
def __str__(self):
2729
t = '\t== Kerberos ==\n'
@@ -39,7 +41,10 @@ def __str__(self):
3941
t += '\t\t\tReaderName: %s\n' % self.cardinfo['ReaderName']
4042
t += '\t\t\tContainerName: %s\n' % self.cardinfo['ContainerName']
4143
t += '\t\t\tCSPName: %s\n' % self.cardinfo['CSPName']
42-
44+
if self.aes_key128:
45+
t += '\t\tAES128 Key: %s\n' % self.aes_key128.hex()
46+
if self.aes_key256:
47+
t += '\t\tAES256 Key: %s\n' % self.aes_key256.hex()
4348
# TODO: check if users actually need this.
4449
# I think it's not useful to print out the kerberos ticket data as string, as noone uses it directly.
4550
# It is better to use the -k flag an export the tickets
@@ -62,14 +67,18 @@ def to_dict(self):
6267
t['tickets'] = []
6368
for ticket in self.tickets:
6469
t['tickets'] = ticket.to_dict()
65-
70+
if self.aes_key128:
71+
t['aes128'] = self.aes_key128.hex()
72+
if self.aes_key256:
73+
t['aes256'] = self.aes_key256.hex()
6674
return t
6775

6876

6977
class KerberosDecryptor(PackageDecryptor):
70-
def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
78+
def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo, with_tickets = True):
7179
super().__init__('Kerberos', lsa_decryptor, sysinfo, reader)
7280
self.decryptor_template = decryptor_template
81+
self.with_tickets = with_tickets
7382
self.credentials = []
7483

7584
self.current_ticket_type = None
@@ -83,10 +92,8 @@ async def find_first_entry(self):
8392

8493
async def handle_ticket(self, kerberos_ticket):
8594
try:
86-
#input(kerberos_ticket)
8795
kt = await KerberosTicket.aparse(kerberos_ticket, self.reader, self.decryptor_template.sysinfo, self.current_ticket_type)
8896
self.current_cred.tickets.append(kt)
89-
#print(str(kt))
9097
except Exception as e:
9198
raise e
9299

@@ -131,100 +138,65 @@ async def process_session(self, kerberos_logon_session):
131138

132139
self.current_cred.username = await kerberos_logon_session.credentials.UserName.read_string(self.reader)
133140
self.current_cred.domainname = await kerberos_logon_session.credentials.Domaine.read_string(self.reader)
134-
pwdata = await kerberos_logon_session.credentials.Password.read_maxdata(self.reader)
135-
self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(pwdata)
136-
137-
if kerberos_logon_session.SmartcardInfos.value != 0:
138-
csp_info = await kerberos_logon_session.SmartcardInfos.read(self.reader, override_finaltype = self.decryptor_template.csp_info_struct)
139-
pin_enc = await csp_info.PinCode.read_maxdata(self.reader)
140-
self.current_cred.pin, raw_dec = self.decrypt_password(pin_enc)
141-
if csp_info.CspDataLength != 0:
142-
self.current_cred.cardinfo = csp_info.CspData.get_infos()
143141

144142
#### key list (still in session) this is not a linked list (thank god!)
145143
if kerberos_logon_session.pKeyList.value != 0:
146144
key_list = await kerberos_logon_session.pKeyList.read(self.reader, override_finaltype = self.decryptor_template.keys_list_struct)
147-
#print(key_list.cbItem)
148145
await key_list.read(self.reader, self.decryptor_template.hash_password_struct)
146+
149147
for key in key_list.KeyEntries:
150-
pass
151-
### GOOD
152-
#keydata_enc = key.generic.Checksump.read_raw(self.reader, key.generic.Size)
153-
#print(keydata_enc)
154-
#keydata, raw_dec = self.decrypt_password(keydata_enc, bytes_expected=True)
155-
#print(keydata_enc.hex())
156-
#input('KEY?')
157-
148+
if key.generic.Size > 0:
149+
if key.generic.Size <= 24: # AES128
150+
keydata = await key.generic.Checksump.read_raw(self.reader, key.generic.Size)
151+
if keydata:
152+
dec_key, _ = self.decrypt_password(keydata, bytes_expected=True)
153+
if dec_key:
154+
self.current_cred.aes_key128 = dec_key
155+
elif key.generic.Size <= 32: # AES256
156+
keydata = await key.generic.Checksump.read_raw(self.reader, key.generic.Size)
157+
if keydata:
158+
dec_key, _ = self.decrypt_password(keydata, bytes_expected=True)
159+
if dec_key:
160+
self.current_cred.aes_key256 = dec_key
158161

159-
#print(key.generic.Checksump.value)
160-
161-
#self.log_ptr(key.generic.Checksump.value, 'Checksump', datasize = key.generic.Size)
162-
#if self.reader.reader.sysinfo.BuildNumber < WindowsBuild.WIN_10_1507.value and key.generic.Size > LSAISO_DATA_BLOB.size:
163-
# if key.generic.Size <= LSAISO_DATA_BLOB.size + (len("KerberosKey") - 1) + 32: #AES_256_KEY_LENGTH
164-
# input('1')
165-
# data_blob = key.generic.Checksump.read(self.reader, override_finaltype = LSAISO_DATA_BLOB)
166-
# data_blob.read(self.reader, key.generic.Size - LSAISO_DATA_BLOB.size)
167-
#
168-
# input('data blob end')
169-
# """
170-
# kprintf(L"\n\t * LSA Isolated Data: %.*S", blob->typeSize, blob->data);
171-
# kprintf(L"\n\t Unk-Key : "); kull_m_string_wprintf_hex(blob->unkKeyData, sizeof(blob->unkKeyData), 0);
172-
# kprintf(L"\n\t Encrypted: "); kull_m_string_wprintf_hex(blob->data + blob->typeSize, blob->origSize, 0);
173-
# kprintf(L"\n\t\t SS:%u, TS:%u, DS:%u", blob->structSize, blob->typeSize, blob->origSize);
174-
# 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);
175-
# kull_m_string_wprintf_hex(blob->unkData2, sizeof(blob->unkData2), 0); kprintf(L", 5:0x%x", blob->unk5);
176-
# """
177-
# else:
178-
# input('2')
179-
# key.generic.Checksump.read(self.reader, override_finaltype = LSAISO_DATA_BLOB)
180-
# print('unkData1 : %s' % data_struct.unkData1.hex())
181-
# print('unkData2 : %s' % data_struct.unkData2.hex())
182-
# print('Encrypted : %s' % data_struct.data.hex()) #another extra struct should wrap this data! ENC_LSAISO_DATA_BLOB
183-
#
184-
#else:
185-
#
186-
# if self.reader.reader.sysinfo.BuildNumber < WindowsBuild.WIN_VISTA.value:
187-
# input('3')
188-
# key.generic.Checksump.read(self.reader, override_finaltype = LSAISO_DATA_BLOB)
189-
# print('unkData1 : %s' % data_struct.unkData1.hex())
190-
# print('unkData2 : %s' % data_struct.unkData2.hex())
191-
# print('Encrypted : %s' % data_struct.data.hex()) #another extra struct should wrap this data! ENC_LSAISO_DATA_BLOB
192-
#
193-
# else:
194-
# input('4')
195-
# #we need to decrypt as well!
196-
# self.reader.move(key.generic.Checksump.value)
197-
# enc_data = self.reader.read(key.generic.Size)
198-
# print(hexdump(enc_data))
199-
# dec_data = self.lsa_decryptor.decrypt(enc_data)
200-
# print(hexdump(dec_data))
201-
# t_reader = GenericReader(dec_data)
202-
# data_struct = LSAISO_DATA_BLOB(t_reader)
203-
# print('unkData1 : %s' % data_struct.unkData1.hex())
204-
# print('unkData2 : %s' % data_struct.unkData2.hex())
205-
# print('Encrypted : %s' % data_struct.data.hex()) #another extra struct should wrap this data! ENC_LSAISO_DATA_BLOB
206-
#
207-
#input()
208162

163+
if kerberos_logon_session.credentials.Password.Length != 0:
164+
pwdata = await kerberos_logon_session.credentials.Password.read_maxdata(self.reader)
165+
if self.current_cred.username.endswith('$') is True:
166+
self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(pwdata, bytes_expected=True)
167+
if self.current_cred.password is not None:
168+
self.current_cred.password = self.current_cred.password.hex()
169+
else:
170+
self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(pwdata)
209171

210-
if kerberos_logon_session.Tickets_1.Flink.value != 0 and \
211-
kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location and \
212-
kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location - 4 :
213-
self.current_ticket_type = KerberosTicketType.TGS
214-
await self.walk_list(kerberos_logon_session.Tickets_1.Flink, self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
172+
if kerberos_logon_session.SmartcardInfos.value != 0:
173+
csp_info = await kerberos_logon_session.SmartcardInfos.read(self.reader, override_finaltype = self.decryptor_template.csp_info_struct)
174+
pin_enc = await csp_info.PinCode.read_maxdata(self.reader)
175+
self.current_cred.pin, raw_dec = self.decrypt_password(pin_enc)
176+
if csp_info.CspDataLength != 0:
177+
self.current_cred.cardinfo = csp_info.CspData.get_infos()
178+
215179

216-
if kerberos_logon_session.Tickets_2.Flink.value != 0 and \
217-
kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location and \
218-
kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location - 4 :
219-
self.current_ticket_type = KerberosTicketType.CLIENT
220-
await self.walk_list(kerberos_logon_session.Tickets_2.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
180+
if self.with_tickets is True:
181+
if kerberos_logon_session.Tickets_1.Flink.value != 0 and \
182+
kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location and \
183+
kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location - 4 :
184+
self.current_ticket_type = KerberosTicketType.TGS
185+
await self.walk_list(kerberos_logon_session.Tickets_1.Flink, self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
186+
187+
if kerberos_logon_session.Tickets_2.Flink.value != 0 and \
188+
kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location and \
189+
kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location - 4 :
190+
self.current_ticket_type = KerberosTicketType.CLIENT
191+
await self.walk_list(kerberos_logon_session.Tickets_2.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
192+
193+
if kerberos_logon_session.Tickets_3.Flink.value != 0 and \
194+
kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location and \
195+
kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location - 4 :
196+
self.current_ticket_type = KerberosTicketType.TGT
197+
await self.walk_list(kerberos_logon_session.Tickets_3.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
198+
self.current_ticket_type = None
221199

222-
if kerberos_logon_session.Tickets_3.Flink.value != 0 and \
223-
kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location and \
224-
kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location - 4 :
225-
self.current_ticket_type = KerberosTicketType.TGT
226-
await self.walk_list(kerberos_logon_session.Tickets_3.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
227-
self.current_ticket_type = None
228200
self.credentials.append(self.current_cred)
229201

230202

pypykatz/alsadecryptor/packages/kerberos/templates.py

+93-2
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def get_template(sysinfo):
100100
template.hash_password_struct = KERB_HASHPASSWORD_6_1607
101101
template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
102102

103-
elif sysinfo.buildnumber >= WindowsBuild.WIN_11_2022.value:
103+
elif WindowsBuild.WIN_11_2022.value <= sysinfo.buildnumber < WindowsBuild.WIN_11_24H2.value:
104104
template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
105105
template.first_entry_offset = 6
106106
template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_10_1607
@@ -109,6 +109,15 @@ def get_template(sysinfo):
109109
template.hash_password_struct = KERB_HASHPASSWORD_6_1607
110110
template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
111111

112+
elif sysinfo.buildnumber >= WindowsBuild.WIN_11_24H2.value:
113+
template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
114+
template.first_entry_offset = 6
115+
template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_24H2
116+
template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_11
117+
template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
118+
template.hash_password_struct = KERB_HASHPASSWORD_6_1607
119+
template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
120+
112121
else:
113122
raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber))
114123

@@ -1078,6 +1087,88 @@ async def load(reader):
10781087
res.SmartcardInfos = await PVOID.load(reader)
10791088
return res
10801089

1090+
1091+
class KIWI_KERBEROS_LOGON_SESSION_24H2:
1092+
def __init__(self):
1093+
self.UsageCount = None
1094+
self.unk0 = None
1095+
self.unk1 = None
1096+
self.unk2 = None
1097+
self.unk4 = None
1098+
self.unk5 = None
1099+
self.unk6 = None
1100+
self.LocallyUniqueIdentifier = None
1101+
self.unk7 = None
1102+
self.unk8 = None
1103+
self.unk8b = None
1104+
self.unk9 = None
1105+
self.unk11 = None
1106+
self.unk12 = None
1107+
self.credentials = None
1108+
self.unk14 = None
1109+
self.unk15 = None
1110+
self.unk16 = None
1111+
self.unk17 = None
1112+
self.unk18 = None
1113+
self.unk19 = None
1114+
self.unk20 = None
1115+
self.unk21 = None
1116+
self.unk22 = None
1117+
self.unk23 = None
1118+
self.pKeyList = None
1119+
self.unk26 = None
1120+
self.Tickets_1 = None
1121+
self.unk27 = None
1122+
self.Tickets_2 = None
1123+
self.unk28 = None
1124+
self.Tickets_3 = None
1125+
self.unk29 = None
1126+
self.SmartcardInfos = None
1127+
1128+
@staticmethod
1129+
async def load(reader):
1130+
res = KIWI_KERBEROS_LOGON_SESSION_24H2()
1131+
res.UsageCount = await ULONG.loadvalue(reader)
1132+
await reader.align()
1133+
res.unk0 = await LIST_ENTRY.load(reader)
1134+
res.unk1 = await PVOID.loadvalue(reader)
1135+
await reader.align()
1136+
res.unk2 = await FILETIME.loadvalue(reader)
1137+
res.unk4 = await PVOID.loadvalue(reader)
1138+
res.unk5 = await PVOID.loadvalue(reader)
1139+
res.unk6 = await PVOID.loadvalue(reader)
1140+
res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
1141+
res.unk7 = await FILETIME.loadvalue(reader)
1142+
res.unk8 = await PVOID.loadvalue(reader)
1143+
res.unk8b = await ULONG.loadvalue(reader)
1144+
await reader.align()
1145+
res.unk9 = await FILETIME.load(reader)
1146+
res.unk11 = await PVOID.loadvalue(reader)
1147+
res.unk12 = await PVOID.loadvalue(reader)
1148+
await reader.align(8)
1149+
res.credentials = await KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607.load(reader)
1150+
res.unk14 = await ULONG.loadvalue(reader)
1151+
res.unk15 = await ULONG.loadvalue(reader)
1152+
res.unk16 = await ULONG.loadvalue(reader)
1153+
res.unk17 = await ULONG.loadvalue(reader)
1154+
res.unk18 = await PVOID.loadvalue(reader)
1155+
res.unk19 = await PVOID.loadvalue(reader)
1156+
res.unk20 = await PVOID.loadvalue(reader)
1157+
res.unk21 = await PVOID.loadvalue(reader)
1158+
res.unk22 = await PVOID.loadvalue(reader)
1159+
res.unk23 = await PVOID.loadvalue(reader)
1160+
await reader.align()
1161+
res.pKeyList = await PVOID.load(reader)
1162+
res.unk26 = await PVOID.loadvalue(reader)
1163+
res.Tickets_1 = await LIST_ENTRY.load(reader)
1164+
res.unk27 = await FILETIME.loadvalue(reader)
1165+
res.Tickets_2 = await LIST_ENTRY.load(reader)
1166+
res.unk28 = await FILETIME.loadvalue(reader)
1167+
res.Tickets_3 = await LIST_ENTRY.load(reader)
1168+
res.unk29 = await FILETIME.loadvalue(reader)
1169+
res.SmartcardInfos = await PVOID.load(reader)
1170+
return res
1171+
10811172
class KIWI_KERBEROS_LOGON_SESSION_10_1607_X86:
10821173
def __init__(self):
10831174
self.UsageCount = None
@@ -1827,7 +1918,7 @@ async def load(reader):
18271918
res.Type = await DWORD.loadvalue(reader)
18281919
await reader.align()
18291920
res.Size = await SIZE_T.loadvalue(reader)
1830-
res.Checksump = await PVOID.loadvalue(reader)
1921+
res.Checksump = await PVOID.load(reader) #loadvalue before?
18311922
return res
18321923

18331924

pypykatz/alsadecryptor/packages/msv/decryptor.py

+25-12
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,23 @@ async def parse(entry, reader):
108108
"""
109109
Converts KIWI_MSV1_0_LIST type objects into a unified class
110110
"""
111-
lsc = LogonSession()
112-
lsc.authentication_id = entry.LocallyUniqueIdentifier
113-
lsc.session_id = entry.Session
114-
lsc.username = await entry.UserName.read_string(reader)
115-
lsc.domainname = await entry.Domaine.read_string(reader)
116-
lsc.logon_server = await entry.LogonServer.read_string(reader)
117-
if entry.LogonTime != 0:
118-
lsc.logon_time = filetime_to_dt(entry.LogonTime).isoformat()
119-
ts = await entry.pSid.read(reader)
120-
lsc.sid = str(ts)
121-
lsc.luid = entry.LocallyUniqueIdentifier
122-
return lsc
111+
try:
112+
lsc = LogonSession()
113+
lsc.authentication_id = entry.LocallyUniqueIdentifier
114+
lsc.session_id = entry.Session
115+
lsc.username = await entry.UserName.read_string(reader)
116+
lsc.domainname = await entry.Domaine.read_string(reader)
117+
lsc.logon_server = await entry.LogonServer.read_string(reader)
118+
if entry.LogonTime != 0:
119+
lsc.logon_time = filetime_to_dt(entry.LogonTime).isoformat()
120+
ts = await entry.pSid.read(reader)
121+
lsc.sid = str(ts)
122+
lsc.luid = entry.LocallyUniqueIdentifier
123+
return lsc
124+
except Exception as e:
125+
import traceback
126+
traceback.print_exc()
127+
input()
123128

124129
def to_dict(self):
125130
t = {}
@@ -308,8 +313,16 @@ async def find_first_entry(self):
308313
t = await self.reader.read(1)
309314
self.logon_session_count = int.from_bytes(t, byteorder = 'big', signed = False)
310315

316+
additional_offset = 0
317+
if self.decryptor_template.first_entry_offset_correction != 0:
318+
offsetpos = position + self.decryptor_template.first_entry_offset_correction
319+
await self.reader.move(offsetpos)
320+
t = await self.reader.read(4)
321+
additional_offset = int.from_bytes(t, byteorder = 'little', signed = False)
322+
311323
#getting logon session ptr
312324
ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)
325+
ptr_entry_loc += additional_offset
313326
ptr_entry = await self.reader.get_ptr(ptr_entry_loc)
314327
return ptr_entry, ptr_entry_loc
315328

0 commit comments

Comments
 (0)