def parse_command(cmd): global slow_stop_flag if cmd == 's': Cracker.print_status() elif cmd == 'q': Comunicator.printer("Stopping...", reprint=False) fast_stop() elif cmd == 'f': slow_stop_flag = True Comunicator.finished = True Comunicator.printer( "Will finnish current job and stop. Press 'd' to cancel.") elif cmd == 'd': if Comunicator.finished: slow_stop_flag = False Comunicator.finished = False Comunicator.printer( "Finish command cancelled. Will continue working.") elif Comunicator.interactive: if cmd == 'p': # TODO if finished pause might not work... Comunicator.paused = True Comunicator.printer("Pause command sent to hashcat") # TODO send pause command elif cmd == 'r': # TODO if process stops resume might not work Comunicator.paused = False Comunicator.printer("Resume command sent to hashcat") # TODO send resume command elif cmd == 'c': # TODO implement checkpoint command pass # checkpoint
def process_result(): # Disable communicator until we start another job Comunicator.disable() # Check if process exited cleanly if Cracker.crt_process is not None: Cracker.crt_process.check_clean_exit() show_stdout = list( filter( None, SingleProcess(Cracker.attack_command + " --show").split_stdout())) password = "" # Check if we cracked something! if len(show_stdout) != 0: for line in show_stdout: cracked_obj = Configuration.hashcat_show_regex.match(line) die( cracked_obj is None, "REGEX error! could not match the --show line:%s" % show_stdout) password = cracked_obj.group(1) msg = "[FAIL] Password for '%s' is not contained in rule '%s'" %\ (Cracker.mac_ssid_job, Cracker.crt_rule["name"]) if len(password) > 7: msg = "[SUCCESS] The password for '%s' is '%s'" % ( Cracker.mac_ssid_job, password) Comunicator.printer(msg) Cracker.safe_send_result(password) Cracker.clean_variables()
def print_status(): def pad(msg): width = 13 return msg.ljust(width, '.') + ": " def human_format(num): magnitude = 0 while abs(num) >= 1000: magnitude += 1 num /= 1000.0 # add more suffixes if you need them return '%.1f%sH/s' % (num, ['', 'K', 'M', 'G', 'T', 'P' ][magnitude]) def space_format(num): return f'{num:,}' hashcat_status = Cracker.crt_process.get_dict() output = pad("Current rule") + "%s\n" % Cracker.crt_rule["name"] eta = hashcat_status["eta"] if hashcat_status["eta"] == "": eta = "Calculating" output += pad("Eta") + "%s\n" % eta if hashcat_status["speed"] > 0: if len(hashcat_status["devices"]) > 2: total_speed = -1 for idx, speed in enumerate( sorted(hashcat_status["devices"].keys())): if total_speed == -1: total_speed = speed continue output += pad( "Speed #%d" % idx) + "%s\n" % human_format(speed) if total_speed != -1: output += pad( "Total Speed") + "%s\n" % human_format(total_speed) else: output += pad("Total Speed") + "%s\n" % human_format( hashcat_status["speed"]) if hashcat_status["progress"] > 0: progress_len = len(space_format(Cracker.crt_rule["wordsize"])) output += pad("Progress") + "%s/%s\n" % (space_format( hashcat_status["progress"]).rjust( progress_len, ' '), space_format(Cracker.crt_rule["wordsize"])) if output.endswith("\n"): output = output[:-1] Comunicator.printer(output)
def resume_work(): if os.path.exists(Configuration.save_result_filename): with open(Configuration.save_result_filename) as fp: password = fp.read() Cracker.safe_send_result(password) return while True: try: Cracker.req.stopwork(suppress_stdout=True) break except Cracker.req.ServerDown: Comunicator.printer(Requester.DownMessage) sleep(10) return
def do_work(): # Something just finished! if Cracker.crt_process is not None and Cracker.crt_process.isdead(): Cracker.process_result() # Process is still running - update eta if Cracker.crt_process is not None: Cracker.update_eta() return if slow_stop_flag: Comunicator.info_logger( "Slow shutdown signal received - shutting down!") sys.exit(0) # Before getting more work make sure we are up to date Cracker.complete_missing() # Test capabilities once if not Cracker.capabilities_tested: Configuration.test_capabilities() Cracker.capabilities_tested = True # Nothing is running - getting more work try: work = Cracker.req.getwork() except Cracker.req.ServerDown: Comunicator.printer(Comunicator.printer(Requester.DownMessage)) return die(work is True, "A server side error occured while getting work!") # No work to be done right now if work is None: Comunicator.printer( "No work to be done, checking in 10 seconds again.") return # Redundant check if work is False: Comunicator.warning_logger("Capabilities out of date!") return # Make status seem a bit more responsive Cracker.old_eta = "Cracking process starting" Cracker.start_cracking(work)
def run(): Configuration.initialize() Cracker.crt_workload = 4 # TODO get value from parameters, adjust from keyboard signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) Cracker.req = Requester(Configuration.apikey, Comunicator.error_printer) Cracker.resume_work() Comunicator.initialize() Comunicator.printer("Cracker initialized", reprint=False) # Disable terminal echo os.system("stty -echo") try: last_time = None while True: now_time = datetime.now() if last_time is None or (now_time - last_time).total_seconds() > 10: last_time = now_time Cracker.crack_existing_handshakes() cmd = Comunicator.get_command() if cmd is not None: Cracker.parse_command(cmd) sleep(0.1) except Exception as e: Configuration.dual_print( Configuration.logger.critical, "Caught unexpected exception: '%s'" % (traceback.format_exc())) Cracker.clean_variables() die(True, e) finally: # Reenable terminal echo os.system("stty echo") pass
def run(): Comunicator.initialize() signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) Configuration.initialize() Cracker.crt_workload = Configuration.hashcat_workload # TODO maybe adjust from keyboard Cracker.req = Requester(Configuration.apikey, Comunicator.error_logger) Cracker.resume_work() Comunicator.printer("Cracker initialized", reprint=False) try: # Disable terminal echo os.system("stty -echo") last_time = None while True: now_time = datetime.now() if last_time is None or (now_time - last_time).total_seconds() > 10: last_time = now_time Cracker.do_work() cmd = Comunicator.get_command() if cmd is not None: Cracker.parse_command(cmd) sleep(0.1) except Exception as e: Cracker.clean_variables() Comunicator.fatal_debug_printer( "Caught unexpected exception: '%s' '%s'" % (e, traceback.format_exc())) finally: # Reenable terminal echo os.system("stty echo") pass
def load_config(): """ Loads api key from file defined in variable Configuration.apikey_path. Ignores lines prefixed by '#', any leading ' ' and trailing '\n' The key is stored in Configuration.apikey :return: None """ error_string = "" try: with open(Configuration.config_file) as file: config = json.load(file) def load_key(lkey): try: return config[lkey], "" except KeyError: return None, "Missing vital information '%s' from config file\n" % lkey Configuration.apikey, err = load_key("apikey") error_string += err Configuration.john_path, err = load_key("john_path") error_string += err Configuration.remote_server, err = load_key("server_location") error_string += err Configuration.hashcat_workload, err = load_key( "hashcat_workload") error_string += err except json.decoder.JSONDecodeError as e: Comunicator.fatal_regular_message( "Configuration file '%s' is not a valid json with error '%s'. Fix" "file or completely remove to restore to default state." % (Configuration.config_file, e)) except FileNotFoundError: with open(Configuration.config_file, "w") as fd: json.dump(Configuration.empty_config, fd) Comunicator.fatal_regular_message( "Configuration file '%s' did not exist. Empty file was generated, please" "fill in data for the cracker to properly work." % Configuration.config_file) if len(error_string) > 0: if error_string.endswith("\n"): error_string = error_string[:-1] Comunicator.fatal_regular_message(error_string) # Check remote server location if Configuration.remote_server is None or len( Configuration.remote_server) < 1: Comunicator.fatal_regular_message( "Invalid or missing remote server location. Please write server location" "in configuration file Ex. " "'\"server_location\": \"http://127.0.0.1:9645/\"'") if not (Configuration.remote_server.startswith("https://") or Configuration.remote_server.startswith("http://")): Comunicator.fatal_regular_message( "Server location should start with either 'https://' or 'http://'" ) if not Configuration.remote_server.endswith("/"): Configuration.remote_server += "/" Configuration.remote_server += "api/v1/" Comunicator.printer("Using remote server '%s'" % Configuration.remote_server) # Check hashcat workload if type(Configuration.hashcat_workload) is not int: Comunicator.fatal_regular_message( "Key 'hashcat_workload' from configuration file '%s' has invalid type %s" " instead of int" % (Configuration.config_file, type(Configuration.hashcat_workload))) if Configuration.hashcat_workload < 1 or Configuration.hashcat_workload > 4: Comunicator.dual_printer( Comunicator.logger.warn, "Hashcat workload should be 1-4. Value found is %d." "Using default 4") Configuration.hashcat_workload = 4 # Check API key if Configuration.apikey is None or len(Configuration.apikey) < 10: Comunicator.fatal_regular_message( "Invalid or missing api key in config file '%s'. Please generate key " "and write it on the configuration file." % Configuration.config_file) # Check john path if len(Configuration.john_path) == 0: Configuration.john_path = None elif not os.path.exists(Configuration.john_path): Comunicator.fatal_regular_message( "Supplied path for john the ripper '%s' is not valid" % Configuration.john_path)
def gather_capabilities(): """ Returns a dictionary of the client capabilities. The list has two types of items: 1) Installed programs in the form of 'program': True 2) Files in the form of 'filename': sha1hash(file) The hashes are used server side to check if the files changed in any way This function also calls Configuration.check_file() to check if files changed in any way :return: Dictionary as described above """ sha1_file_changed = False # Check if local john configuration exists if os.path.isfile("john-local.conf") and Configuration.check_file( "john-local.conf", "john-local.conf"): sha1_file_changed = True for directory in Configuration.capab_dirs: if not os.path.isdir(directory): continue all_files = os.listdir(directory) for file in all_files: path = os.path.join(directory, file) if os.path.isfile(path) and not file.startswith("."): if Configuration.check_file(path, file): sha1_file_changed = True if sha1_file_changed: try: with open(Configuration.sha1s_filename, "w+") as fd: json.dump(Configuration.old_sha1s, fd, indent=4) except Exception as e: Comunicator.fatal_debug_printer( "Error trying to dump data in %s: %s" % (Configuration.sha1s_filename, e)) one_program = False for program in Configuration.programs: # John path needs to be hardcoded it seems # Only the key is relevant for hashcat/john - we mark them with True if program == "john": if Configuration.john_path is not None: if not os.path.exists(Configuration.john_path): Comunicator.fatal_debug_printer( "Supplied john path '%s' is invalid. Check config file!" % Configuration.john_path) Configuration.capabilities[program] = True one_program = True else: Comunicator.printer( "John the ripper not installed, some rules will not run until it is installed and " "path supplied in config file '%s'" % Configuration.config_file) continue if which(program) is not None: Configuration.capabilities[program] = True one_program = True else: Comunicator.printer( "'%s' not installed, some rules will not run until it is installed" % program) if not one_program: Comunicator.fatal_regular_message( "None of the cracking programs are installed, cracking not possible!" )