def getmissing(self): """ Get missing capabilities :return: True - An error occurred [{capability}] - List of capabilities :raises Requester.ServerDown: The server could not be reached """ url = Configuration.remote_server + "getmissing" Comunicator.info_logger("Getting missing capabilites at '%s'" % url) response = None try: response = requests.post(url, json={ "apikey": self.apikey, "capabilities": Configuration.capabilities }, timeout=10) except requests.exceptions.ConnectionError: raise Requester.ServerDown except requests.exceptions.Timeout: Comunicator.fatal_regular_message("Backend is unresponsive") if response.status_code == 502: raise Requester.ServerDown data, err = Requester._decode_json(response) if err != "": self.err_printer( "Error while retrieving missing capabilites '%s'" % err) return True return data
def interrupt(process, cmd, wait_time=2.0): try: pid = process.pid if type(cmd) is list: cmd = ' '.join(cmd) Comunicator.info_logger('sending interrupt to PID %d (%s)' % (pid, cmd)) if wait_time == 0.0: os.kill(pid, signal.SIGTERM) process.terminate() return os.kill(pid, signal.SIGINT) start_time = time.time() # Time since Interrupt was sent while process.poll() is None: # Process is still running time.sleep(0.1) if time.time() - start_time > wait_time: # We waited too long for process to die, terminate it. Comunicator.info_logger( 'Waited > %0.2f seconds for process to die, killing it' % wait_time) os.kill(pid, signal.SIGTERM) process.terminate() break except OSError as e: if 'No such process' in str(e): return raise e # process cannot be killed
def stopwork(self, suppress_stdout=False): """ Stop current job :return: True - An error occurred None - Current job stopped :raises Requester.ServerDown: The server could not be reached """ url = Configuration.remote_server + "stopwork" Comunicator.info_logger("Stopping work from '%s'" % url) response = None try: response = requests.post(url, data={"apikey": self.apikey}, timeout=10) except requests.exceptions.ConnectionError: raise Requester.ServerDown except requests.exceptions.Timeout: Comunicator.fatal_regular_message("Backend is unresponsive") if response.status_code == 502: raise Requester.ServerDown _, err = Requester._decode_json(response) if err != "": msg = "Error stopping work '%s'" % err if suppress_stdout: Comunicator.error_logger(msg) else: self.err_printer(msg) return True return None
def getfile(self, filename, path): """ Download capability file :param filename: Filename of the capability to download :param path: Local relative path where to save the downloaded file :return: None - File downloaded :raises Requester.ServerDown: The server could not be reached """ url = Configuration.remote_server + "getfile" Comunicator.info_logger("Getting file '%s' from '%s'" % (filename, url)) try: with requests.post(url, data={ "apikey": self.apikey, "file": filename }, stream=True, timeout=10) as req: req.raise_for_status() with open(path, "wb+") as fd: for chunk in req.iter_content(chunk_size=8192): if chunk: fd.write(chunk) except requests.exceptions.ConnectionError: raise Requester.ServerDown except requests.exceptions.Timeout: Comunicator.fatal_regular_message("Backend is unresponsive") return None
def checkfile(self, filename): """ Check if a capability can be downloaded :return: True - An error occurred None - The file can be downloaded :raises Requester.ServerDown: The server could not be reached """ url = Configuration.remote_server + "checkfile" Comunicator.info_logger("Checking if file '%s' exists at '%s'" % (filename, url)) response = None try: response = requests.post(url, data={ "apikey": self.apikey, "file": filename }, timeout=10) except requests.exceptions.ConnectionError: raise Requester.ServerDown except requests.exceptions.Timeout: Comunicator.fatal_regular_message("Backend is unresponsive") if response.status_code == 502: raise Requester.ServerDown _, err = Requester._decode_json(response) if err != "": self.err_printer("Error downloading '%s': '%s'" % (filename, err)) return True return None
def sendeta(self, eta): """ Send eta for current :return: True - An error occurred None - Eta successfully sent :raises Requester.ServerDown: The server could not be reached """ url = Configuration.remote_server + "sendeta" Comunicator.info_logger("Sending eta to '%s': '%s'" % (url, eta)) response = None try: response = requests.post(url, data={ "apikey": self.apikey, "eta": eta }, timeout=10) except requests.exceptions.ConnectionError: raise Requester.ServerDown except requests.exceptions.Timeout: Comunicator.fatal_regular_message("Backend is unresponsive") if response.status_code == 502: raise Requester.ServerDown _, err = Requester._decode_json(response) if err != "": self.err_printer("Error sending eta '%s'" % err) return True return None
def complete_missing(): gather_flag = False try: missings = Cracker.req.getmissing() except Cracker.req.ServerDown: return die(missings is True, "Server side error occurred.") if missings is None: return for missing in missings: if missing["type"] == "program": Comunicator.info_logger("Please install program '%s'" % missing["name"]) elif missing["type"] in [ "dict", "maskfile", "generator", "john-local.conf" ]: Comunicator.dual_printer( Comunicator.logger.info, "Downloading '%s'..." % missing["path"]) gather_flag = True if "/" in missing["path"]: directory, filename = missing["path"].rsplit('/', 1) # Create directory if they do not exist os.makedirs(directory, exist_ok=True) else: filename = missing["path"] try: if Cracker.req.checkfile(filename) is None and \ Cracker.req.getfile(filename, missing["path"]) is None: Comunicator.dual_printer( Comunicator.logger.info, "Downloaded '%s'" % missing["path"]) if missing["type"] == "generator": os.chmod( missing["path"], stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) except Cracker.req.ServerDown: return else: Comunicator.warning_logger("Unknown missing type '%s'" % missing) if gather_flag: Configuration.gather_capabilities()
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 fast_stop(): if Cracker.crt_process is not None: Comunicator.info_logger("Killing running process %s" % Cracker.crt_process.get_command()) # Kill currently running process Cracker.crt_process.terminate() # Clean current varibles so all tempfiles are deleted Cracker.clean_variables() try: if Cracker.req is not None: Cracker.req.stopwork() except Cracker.req.ServerDown: pass Comunicator.stop() sys.exit(0)
def start_cracking(work): Cracker.mac_ssid_job = "%s-%s" % (work["handshake"]["mac"], work["handshake"]["ssid"]) msg = "Running '%s' with rule '%s'" % (Cracker.mac_ssid_job, work["rule"]["name"]) Comunicator.enable(interactive=False) Comunicator.dual_printer(Comunicator.logger.info, msg) _, Cracker.path_temp_file = mkstemp(prefix="psknow_crack") if work["handshake"]["file_type"] == "16800": with open(Cracker.path_temp_file, "w") as fd: fd.write(work["handshake"]["data"]) else: with open(Cracker.path_temp_file, "wb") as fd: fd.write(b64decode(work["handshake"]["data"].encode("utf8"))) # Memorize attack type - we need it to decode the output attack_type = work["handshake"]["handshake_type"] Cracker.crt_rule = work["rule"] attacked_file = Cracker.path_temp_file # Get commands needed to run hashcat generator_command, Cracker.attack_command, Cracker.scrambler =\ Cracker.get_attack_command(Cracker.crt_rule, attack_type, attacked_file, work["handshake"]["ssid"]) Comunicator.info_logger( "Trying rule %s on '%s-%s'" % (Cracker.crt_rule["name"], work["handshake"]["mac"], work["handshake"]["ssid"])) if Cracker.is_already_cracked(Cracker.attack_command): Comunicator.warning_logger( "'%s' has already been cracked. Attempting to send result." % Cracker.mac_ssid_job) Cracker.process_result() return if generator_command == "": Cracker.crt_process = SingleProcess(Cracker.attack_command) else: Cracker.crt_process = DoubleProcess(generator_command, Cracker.attack_command)
def getwork(self): """ Send a request for work to the server :return: None - Nothing can be down with capabilities False - A sha1 has does not match, capabilities need updating True - An error occurred {data} - Work data requested :raises Requester.ServerDown: The server could not be reached """ url = Configuration.remote_server + "getwork" Comunicator.info_logger("Requesting work from '%s'" % url) response = None try: response = requests.post(url, json={ "apikey": self.apikey, "capabilities": Configuration.capabilities }, timeout=10) except requests.exceptions.ConnectionError: raise Requester.ServerDown except requests.exceptions.Timeout: Comunicator.fatal_regular_message("Backend is unresponsive") if response.status_code == 502: raise Requester.ServerDown data, err = Requester._decode_json(response) if err != "": if err == Configuration.cap_updated: return False if err == Configuration.no_work_message: return None self.err_printer("Error retrieving data from server '%s'" % err) return True return data
def pausework(self): """ Pause current job :return: True - An error occurred None - Current job paused :raises Requester.ServerDown: The server could not be reached """ url = Configuration.remote_server + "pausework" Comunicator.info_logger("Pausing work from '%s'" % url) try: response = requests.post(url, data={"apikey": self.apikey}) except requests.exceptions.ConnectionError: raise Requester.ServerDown if response.status_code == 502: raise Requester.ServerDown _, err = Requester._decode_json(response) if err != "": self.err_printer("Error pausing work '%s'" % err) return True return None
def sendresult(self, password): """ Send results for current job :param password: password for the current job, can be "" :return: False - The job expired True - An error occurred None - Current job stopped :raises Requester.ServerDown: The server could not be reached """ url = Configuration.remote_server + "sendresult" Comunicator.info_logger("Sending result at '%s'" % url) response = None try: response = requests.post(url, data={ "apikey": self.apikey, "password": password }, timeout=10) except requests.exceptions.ConnectionError: raise Requester.ServerDown except requests.exceptions.Timeout: Comunicator.fatal_regular_message("Backend is unresponsive") if response.status_code == 502: raise Requester.ServerDown _, err = Requester._decode_json(response) if err != "": if err == Configuration.no_job_message: return False self.err_printer("Error while sending result '%s'" % err) return True return None
def signal_handler(signum, _): if signum == signal.SIGINT or signum == signal.SIGTERM: Comunicator.info_logger("Received signal %d. Exitting!" % signum) fast_stop() else: Comunicator.info_logger("Received %s signal" % signum)
def slow_stop(signum, _): global slow_stop_flag Comunicator.info_logger("Received %s signal. Slow stopping!" % signum) slow_stop_flag = True
def __init__(self, cmd, crit=True, nolog=False): super(SingleProcess, self).__init__() if len(cmd) == 0: Comunicator.fatal_debug_printer( "Empty command '%s' send to SingleProcess" % cmd) self.critical = crit # Logging data self.cmd = cmd if not nolog: if type(cmd) is str: Comunicator.info_logger("Executing command: '%s'" % self.cmd) else: Comunicator.info_logger("Executing command: '%s'" % " ".join(self.cmd)) # Output variables need to be mutable in order to modify them # from generic thread self.out = [] self.err = [] self.out_r, self.out_w = SingleProcess.get_pipe_wrapper() self.err_r, self.err_w = SingleProcess.get_pipe_wrapper() self.in_r, self.in_w = None, None self.in_writer_thread = None self.reaped = False self.stop_in_thread = False self.ended = False # Why an entire thread just to read from pipes? # Because if a pipe is full the program writing to the pipe will # get stuck until data is read from the pipe. If we simply call # wait for the process without reading data it will get stuck. # If we call readlines before we wait we might get stuck because # the writing end of the pipe is never closed... despite the program # not running anymore. self.err_reader_thread = Thread(target=self._all_reader_thread, args=(self.err_r, self.err)) if SingleProcess.command_is_hashcat(self.cmd): self.in_r, self.in_w = SingleProcess.get_pipe_wrapper() self.in_w.write("s") self.in_writer_thread = Thread( target=self.___hashcat_writer_thread, args=(self.in_w, )) self.out_reader_thread = Thread(target=self._hashcat_out_thread, args=(self.out_r, self.out, self.hashcat_progress, self)) else: self.out_reader_thread = Thread(target=self._all_reader_thread, args=(self.out_r, self.out)) if type(cmd) is str: cmd = cmd.split(' ') try: self.proc = Popen(cmd, stdin=self.in_r, stdout=self.out_w, stderr=self.err_w) except Exception as e: Comunicator.fatal_debug_printer( "Error while trying to run command '%s':\n%s" % (cmd, e)) if self.in_writer_thread is not None: self.in_writer_thread.start() self.err_reader_thread.start() self.out_reader_thread.start()