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 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 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 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 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!" )