Skip to content

Commit faab020

Browse files
author
Skelsec
committed
kerberos fix 2, adding tests, win2022 IV offsetfix
1 parent 4b5b4b4 commit faab020

22 files changed

+620
-78
lines changed

pypykatz/_version.py

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

2-
__version__ = "0.6.5"
2+
__version__ = "0.6.6"
33
__banner__ = \
44
"""
55
# pypyKatz %s

pypykatz/alsadecryptor/lsa_decryptor_nt5.py

+35-36
Original file line numberDiff line numberDiff line change
@@ -16,76 +16,75 @@ def __init__(self, reader, decryptor_template, sysinfo):
1616
self.feedback_offset = None
1717
self.des_key = None
1818
self.random_key = None
19-
self.acquire_crypto_material()
2019

21-
def acquire_crypto_material(self):
20+
async def acquire_crypto_material(self):
2221
self.log('Acquireing crypto stuff...')
23-
sigpos = self.find_signature()
24-
self.reader.move(sigpos)
22+
sigpos = await self.find_signature()
23+
await self.reader.move(sigpos)
2524
#data = self.reader.peek(0x50)
2625
#self.log('Memory looks like this around the signature\n%s' % hexdump(data, start = sigpos))
2726

2827
for x in [self.decryptor_template.feedback_ptr_offset , self.decryptor_template.old_feedback_offset]:
2928
self.feedback_offset = x
3029

3130
try:
32-
self.feedback = self.get_feedback(sigpos)
31+
self.feedback = await self.get_feedback(sigpos)
3332
#self.log('Feedback bytes:\n%s' % hexdump(self.feedback, start = 0))
34-
self.des_key = self.get_key(sigpos)
35-
self.random_key = self.get_random(sigpos)
33+
self.des_key = await self.get_key(sigpos)
34+
self.random_key = await self.get_random(sigpos)
3635
#self.log('randomkey bytes:\n%s' % hexdump(self.random_key, start = 0))
3736
except:
3837
import traceback
3938
traceback.print_exc()
40-
input()
39+
#input()
4140
else:
4241
break
4342

4443

45-
def get_feedback(self, sigpos):
44+
async def get_feedback(self, sigpos):
4645
if self.decryptor_template.arch == 'x86':
47-
new_ptr = self.reader.get_ptr_with_offset(sigpos + self.feedback_offset)
48-
self.reader.move(new_ptr)
49-
return self.reader.read(8)
46+
new_ptr = await self.reader.get_ptr_with_offset(sigpos + self.feedback_offset)
47+
await self.reader.move(new_ptr)
48+
return await self.reader.read(8)
5049
else:
51-
self.reader.move(sigpos + self.feedback_offset)
52-
offset = LONG(self.reader).value
50+
await self.reader.move(sigpos + self.feedback_offset)
51+
offset = await LONG.loadvalue(self.reader)
5352
newpos = sigpos + self.feedback_offset + 4 + offset
54-
self.reader.move(newpos)
55-
return self.reader.read(8)
53+
await self.reader.move(newpos)
54+
return await self.reader.read(8)
5655

57-
def get_key(self, sigpos):
56+
async def get_key(self, sigpos):
5857
if self.decryptor_template.arch == 'x86':
59-
new_ptr = self.reader.get_ptr_with_offset(sigpos + self.decryptor_template.desx_key_ptr_offset)
60-
self.reader.move(new_ptr)
61-
des_key_ptr = self.decryptor_template.key_struct_ptr(self.reader)
62-
des_key = des_key_ptr.read(self.reader)
58+
new_ptr = await self.reader.get_ptr_with_offset(sigpos + self.decryptor_template.desx_key_ptr_offset)
59+
await self.reader.move(new_ptr)
60+
des_key_ptr = await self.decryptor_template.key_struct_ptr.load(self.reader)
61+
des_key = await des_key_ptr.read(self.reader)
6362
else:
64-
self.reader.move(sigpos + self.decryptor_template.desx_key_ptr_offset)
65-
offset = LONG(self.reader).value
63+
await self.reader.move(sigpos + self.decryptor_template.desx_key_ptr_offset)
64+
offset = await LONG.loadvalue(self.reader)
6665
newpos = sigpos + self.decryptor_template.desx_key_ptr_offset + 4 + offset
67-
self.reader.move(newpos)
68-
des_key_ptr = self.decryptor_template.key_struct_ptr(self.reader)
69-
des_key = des_key_ptr.read(self.reader)
66+
await self.reader.move(newpos)
67+
des_key_ptr = await self.decryptor_template.key_struct_ptr.load(self.reader)
68+
des_key = await des_key_ptr.read(self.reader)
7069

7170
return des_key
7271

73-
def get_random(self, sigpos):
72+
async def get_random(self, sigpos):
7473
if self.decryptor_template.arch == 'x86':
75-
random_key_ptr = self.reader.get_ptr_with_offset(sigpos + self.decryptor_template.randomkey_ptr_offset)
76-
random_key_ptr = self.reader.get_ptr_with_offset(random_key_ptr)
77-
self.reader.move(random_key_ptr)
74+
random_key_ptr = await self.reader.get_ptr_with_offset(sigpos + self.decryptor_template.randomkey_ptr_offset)
75+
random_key_ptr = await self.reader.get_ptr_with_offset(random_key_ptr)
76+
await self.reader.move(random_key_ptr)
7877
else:
79-
self.reader.move(sigpos + self.decryptor_template.randomkey_ptr_offset)
80-
offset = LONG(self.reader).value
78+
await self.reader.move(sigpos + self.decryptor_template.randomkey_ptr_offset)
79+
offset = await LONG.loadvalue(self.reader)
8180
newpos = sigpos + self.decryptor_template.desx_key_ptr_offset + 4 + offset
82-
self.reader.move(newpos)
81+
await self.reader.move(newpos)
8382

84-
return self.reader.read(256)
83+
return await self.reader.read(256)
8584

86-
def find_signature(self):
85+
async def find_signature(self):
8786
self.log('Looking for main struct signature in memory...')
88-
fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.signature)
87+
fl = await self.reader.find_in_module('lsasrv.dll', self.decryptor_template.signature)
8988
if len(fl) == 0:
9089
self.logger.log('signature not found! %s' % self.decryptor_template.signature.hex())
9190
raise Exception('LSA signature not found!')

pypykatz/alsadecryptor/lsa_template_nt5.py

+34-11
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Tamas Jos (@skelsec)
55
#
66

7-
from pypykatz.alsadecryptor.win_datatypes import POINTER
7+
from pypykatz.alsadecryptor.win_datatypes import ULONG, PVOID, POINTER
88
from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild
99
from pypykatz.alsadecryptor.package_commons import PackageTemplate
1010

@@ -37,12 +37,19 @@ def get_template(sysinfo):
3737
raise Exception('NT 6 is in another castle!')
3838

3939
class SYMCRYPT_NT5_DES_EXPANDED_KEY:
40-
def __init__(self, reader):
40+
def __init__(self):
4141
self.roundKey = []
42+
43+
@staticmethod
44+
async def load(reader):
45+
s = SYMCRYPT_NT5_DES_EXPANDED_KEY()
4246
for _ in range(16):
43-
r = int.from_bytes(reader.read(4), 'little', signed = False)
44-
l = int.from_bytes(reader.read(4), 'little', signed = False)
45-
self.roundKey.append([r, l])
47+
x = await reader.read(4)
48+
r = int.from_bytes(x, 'little', signed = False)
49+
x = await reader.read(4)
50+
l = int.from_bytes(x, 'little', signed = False)
51+
s.roundKey.append([r, l])
52+
return s
4653

4754
def __str__(self):
4855
t = 'SYMCRYPT_NT5_DES_EXPANDED_KEY\r\n'
@@ -51,10 +58,18 @@ def __str__(self):
5158
return t
5259

5360
class SYMCRYPT_NT5_DESX_EXPANDED_KEY:
54-
def __init__(self, reader):
55-
self.inputWhitening = reader.read(8)
56-
self.outputWhitening = reader.read(8)
57-
self.desKey = SYMCRYPT_NT5_DES_EXPANDED_KEY(reader)
61+
def __init__(self):
62+
self.inputWhitening = None
63+
self.outputWhitening = None
64+
self.desKey = None
65+
66+
@staticmethod
67+
async def load(reader):
68+
s = SYMCRYPT_NT5_DESX_EXPANDED_KEY()
69+
s.inputWhitening = await reader.read(8)
70+
s.outputWhitening = await reader.read(8)
71+
s.desKey = await SYMCRYPT_NT5_DES_EXPANDED_KEY.load(reader)
72+
return s
5873

5974
def __str__(self):
6075
t = 'SYMCRYPT_NT5_DESX_EXPANDED_KEY\r\n'
@@ -64,8 +79,16 @@ def __str__(self):
6479
return t
6580

6681
class PSYMCRYPT_NT5_DESX_EXPANDED_KEY(POINTER):
67-
def __init__(self, reader):
68-
super().__init__(reader, SYMCRYPT_NT5_DESX_EXPANDED_KEY)
82+
def __init__(self):
83+
super().__init__()
84+
85+
@staticmethod
86+
async def load(reader):
87+
p = PVOID()
88+
p.location = reader.tell()
89+
p.value = await reader.read_uint()
90+
p.finaltype = SYMCRYPT_NT5_DESX_EXPANDED_KEY
91+
return p
6992

7093
class LSA_x64_nt5_1(LsaTemplate_NT5):
7194
def __init__(self):

pypykatz/alsadecryptor/lsa_template_nt6.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,8 @@ def __init__(self):
408408
self.key_pattern = LSADecyptorKeyPattern()
409409
self.key_pattern.signature = b'\x83\x64\x24\x30\x00\x48\x8d\x45\xe0\x44\x8b\x4d\xd8\x48\x8d\x15'
410410
self.key_pattern.IV_length = 16
411-
self.key_pattern.offset_to_IV_ptr = 71
411+
#self.key_pattern.offset_to_IV_ptr = 71
412+
self.key_pattern.offset_to_IV_ptr = 58
412413
self.key_pattern.offset_to_DES_key_ptr = -89
413414
self.key_pattern.offset_to_AES_key_ptr = 16
414415

pypykatz/alsadecryptor/packages/kerberos/decryptor.py

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pypykatz.alsadecryptor.package_commons import PackageDecryptor
99
from pypykatz.alsadecryptor.win_datatypes import PLIST_ENTRY, PRTL_AVL_TABLE
1010
from pypykatz.commons.common import WindowsMinBuild
11+
from pypykatz.commons.common import hexdump
1112

1213
class KerberosCredential:
1314
def __init__(self):
@@ -97,6 +98,8 @@ async def start(self):
9798
return
9899

99100
if self.sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
101+
#TODO: fix this
102+
return
100103
await self.reader.move(entry_ptr_loc)
101104
entry_ptr = await PLIST_ENTRY.load(self.reader)
102105
await self.walk_list(entry_ptr, self.process_session_elist)
@@ -114,6 +117,8 @@ async def start(self):
114117
await self.process_session(kerberos_logon_session)
115118

116119
async def process_session_elist(self, elist):
120+
#TODO: fix this
121+
return
117122
await self.reader.move(elist.location)
118123
await self.reader.read_uint() #Flink do not remove this line!
119124
await self.reader.read_uint() #Blink do not remove this line!

pypykatz/alsadecryptor/packages/msv/decryptor.py

+9-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import io
77
import json
88
import base64
9-
from pypykatz.commons.common import WindowsMinBuild, KatzSystemArchitecture, AGenericReader, UniversalEncoder, hexdump
9+
from pypykatz.commons.common import WindowsBuild, WindowsMinBuild, KatzSystemArchitecture, AGenericReader, UniversalEncoder, hexdump
1010
from pypykatz.commons.filetime import filetime_to_dt
1111
from pypykatz.alsadecryptor.packages.msv.templates import MSV1_0_PRIMARY_CREDENTIAL_STRANGE_DEC
1212
from pypykatz.alsadecryptor.packages.credman.templates import KIWI_CREDMAN_LIST_STARTER, KIWI_CREDMAN_SET_LIST_ENTRY
@@ -32,7 +32,7 @@ def to_dict(self):
3232
t['LMHash'] = self.LMHash
3333
t['SHAHash'] = self.SHAHash
3434
t['DPAPI'] = self.DPAPI
35-
t['isoProt'] = self.isoProt
35+
#t['isoProt'] = self.isoProt # removed this because it's not implemented fully and messes up the testcases
3636
return t
3737

3838
def to_json(self):
@@ -288,15 +288,14 @@ def __init__(self, reader, decryptor_template, lsa_decryptor, credman_template,
288288
async def find_first_entry(self):
289289
#finding signature
290290
position = await self.find_signature('lsasrv.dll',self.decryptor_template.signature)
291-
292291
#getting logon session count
293-
if self.sysinfo.architecture == KatzSystemArchitecture.X64 and self.sysinfo.buildnumber > WindowsMinBuild.WIN_BLUE.value:
294-
ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.offset2)
295-
await self.reader.move(ptr_entry_loc)
296-
t = await self.reader.read(1)
297-
self.logon_session_count = int.from_bytes(t, byteorder = 'big', signed = False)
298-
else:
299-
self.logon_session_count = 1
292+
self.logon_session_count = 1
293+
if self.sysinfo.architecture == KatzSystemArchitecture.X64:
294+
if self.sysinfo.buildnumber >= WindowsBuild.WIN_8.value or (WindowsMinBuild.WIN_8.value <= self.sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value and self.sysinfo.msv_dll_timestamp > 0x60000000):
295+
ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.offset2)
296+
await self.reader.move(ptr_entry_loc)
297+
t = await self.reader.read(1)
298+
self.logon_session_count = int.from_bytes(t, byteorder = 'big', signed = False)
300299

301300
#getting logon session ptr
302301
ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)

pypykatz/alsadecryptor/packages/msv/templates.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Author:
44
# Tamas Jos (@skelsec)
55
#
6-
import io
6+
from pypykatz import logger
77
from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild
88
from pypykatz.alsadecryptor.win_datatypes import BOOLEAN, HANDLE, USHORT, ULONG, LSA_UNICODE_STRING, LSAISO_DATA_BLOB, \
99
BYTE, PVOID, WORD, DWORD, POINTER, LUID, PSID, ANSI_STRING
@@ -24,6 +24,8 @@ def __init__(self):
2424

2525
@staticmethod
2626
def get_template(sysinfo):
27+
logger.debug('buildnumber: %s' % sysinfo.buildnumber)
28+
logger.debug('msv_dll_timestamp: %s' % sysinfo.msv_dll_timestamp)
2729
template = MsvTemplate()
2830
template.encrypted_credentials_list_struct = KIWI_MSV1_0_CREDENTIAL_LIST
2931
template.log_template('encrypted_credentials_list_struct', template.encrypted_credentials_list_struct)
@@ -66,8 +68,7 @@ def get_template(sysinfo):
6668
else:
6769
template.decrypted_credential_struct = MSV1_0_PRIMARY_CREDENTIAL_10_1607_DEC
6870

69-
template.log_template('decrypted_credential_struct', template.decrypted_credential_struct)
70-
71+
template.log_template('decrypted_credential_struct', template.decrypted_credential_struct)
7172
if sysinfo.architecture == KatzSystemArchitecture.X64:
7273
if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value:
7374
template.signature = b'\x4c\x8b\xdf\x49\xc1\xe3\x04\x48\x8b\xcb\x4c\x03\xd8'
@@ -90,9 +91,16 @@ def get_template(sysinfo):
9091
template.offset2 = -4
9192

9293
elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
93-
template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74'
94-
template.first_entry_offset = 16
95-
template.offset2 = -4
94+
if sysinfo.msv_dll_timestamp > 0x60000000:
95+
# new win2012 update weirdness
96+
template.list_entry = PKIWI_MSV1_0_LIST_63
97+
template.signature = b'\x8b\xde\x48\x8d\x0c\x5b\x48\xc1\xe1\x05\x48\x8d\x05'
98+
template.first_entry_offset = 34
99+
template.offset2 = -6
100+
else:
101+
template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74'
102+
template.first_entry_offset = 16
103+
template.offset2 = -4
96104

97105
elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
98106
template.signature = b'\x8b\xde\x48\x8d\x0c\x5b\x48\xc1\xe1\x05\x48\x8d\x05'
@@ -121,7 +129,7 @@ def get_template(sysinfo):
121129
template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74'
122130
template.first_entry_offset = 23
123131
template.offset2 = -4
124-
132+
125133
elif WindowsBuild.WIN_11_2022.value <= sysinfo.buildnumber < WindowsBuild.WIN_11_2023.value: #20348
126134
template.signature = b'\x45\x89\x34\x24\x4c\x8b\xff\x8b\xf3\x45\x85\xc0\x74'
127135
template.first_entry_offset = 24

pypykatz/apypykatz.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from pypykatz.commons.common import UniversalEncoder
2323
from minidump.aminidumpfile import AMinidumpFile
2424
from minikerberos.common.ccache import CCACHE
25+
from minikerberos.common.kirbi import Kirbi
2526
from pypykatz._version import __version__
2627

2728
class apypykatz:
@@ -245,7 +246,7 @@ async def get_kerberos(self, with_tickets = True):
245246
for cred in dec.credentials:
246247
for ticket in cred.tickets:
247248
for fn in ticket.kirbi_data:
248-
self.kerberos_ccache.add_kirbi(ticket.kirbi_data[fn].native)
249+
self.kerberos_ccache.add_kirbi(Kirbi(ticket.kirbi_data[fn]))
249250

250251
if cred.luid in self.logon_sessions:
251252
self.logon_sessions[cred.luid].kerberos_creds.append(cred)

pypykatz/commons/common.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ def __init__(self):
457457
self.major_version = 6
458458

459459
def __str__(self):
460-
return '%s %s' % (self.architecture.name, self.buildnumber)
460+
return 'ARCH:%s BUILD:%s MSV_TS:%s OS(guess): %s' % (self.architecture.name, self.buildnumber, self.msv_dll_timestamp, self.operating_system)
461461

462462
@staticmethod
463463
def from_live_reader(lr):

pypykatz/lsadecryptor/cmdhelper.py

+20-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
from pypykatz.pypykatz import pypykatz
1616
from pypykatz.commons.common import UniversalEncoder
1717
from pypykatz.lsadecryptor.packages.msv.decryptor import LogonSession
18-
18+
from minidump.minidumpfile import MinidumpFile
19+
from pypykatz.commons.common import KatzSystemInfo
1920

2021

2122
class LSACMDHelper:
@@ -35,7 +36,7 @@ def add_args(self, parser, live_parser):
3536

3637

3738
group = parser.add_parser('lsa', help='Get secrets from memory dump')
38-
group.add_argument('cmd', choices=['minidump','rekall'])
39+
group.add_argument('cmd', choices=['minidump','rekall','info'])
3940
group.add_argument('memoryfile', help='path to the dump file')
4041
group.add_argument('-t','--timestamp_override', type=int, help='enforces msv timestamp override (0=normal, 1=anti_mimikatz)')
4142
group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format')
@@ -205,6 +206,23 @@ def run(self, args):
205206
args.packages.append('ktickets')
206207
mimi = pypykatz.parse_memory_dump_rekall(args.memoryfile, args.timestamp_override, packages=args.packages)
207208
results['rekall'] = mimi
209+
210+
elif args.cmd == 'info':
211+
if args.directory:
212+
dir_fullpath = os.path.abspath(args.memoryfile)
213+
file_pattern = '*.dmp'
214+
if args.recursive == True:
215+
globdata = os.path.join(dir_fullpath, '**', file_pattern)
216+
else:
217+
globdata = os.path.join(dir_fullpath, file_pattern)
218+
else:
219+
globdata = args.memoryfile
220+
221+
for filename in glob.glob(globdata, recursive=args.recursive):
222+
minidump = MinidumpFile.parse(filename)
223+
sysinfo = KatzSystemInfo.from_minidump(minidump)
224+
print('[%s] %s' % (filename, sysinfo))
225+
208226

209227
###### Minidump
210228
elif args.cmd == 'minidump':

0 commit comments

Comments
 (0)