Exemple #1
0
    def _reap(self, now=False):
        if self.reaped:
            return True

        if now and self.proc.poll() is None:
            # Wait for the first process to stop executing
            self.proc.wait()

        if self.proc.poll() is not None:
            # Stop ___hashcat_writer_thread as soon as possible (takes a bit because of the sleep(1))
            self.stop_in_thread = True  # This stops the ___hashcat_writer_thread

            # The process stopped executing, close it's write pipes
            self.err_w = SingleProcess._close_helper(
                self.err_w)[0]  # Stops err_reader_thread
            self.out_w = SingleProcess._close_helper(
                self.out_w)[0]  # Stops out_reader_thread

            # After we closed the writing end of the pipe _all_reader_thread should stop
            self.err_reader_thread = SingleProcess._join_helper(
                self.err_reader_thread)[0]
            self.out_reader_thread = SingleProcess._join_helper(
                self.out_reader_thread)[0]

            # This process might take a bit to shutdown because it has a sleep(1)
            # Mark stop_in_thread as true ASAP in order to give the thread time to stop
            self.in_writer_thread = SingleProcess._join_helper(
                self.in_writer_thread)[0]

            # Convert error from list to string
            self.err = "".join(self.err)

            # Mark the second process as completely stopped
            self.reaped = True

            if self.critical and self.proc.poll() != 0:
                # Second process could be hashcat which sometimes returns 1 but no error
                if SingleProcess.command_is_hashcat(
                        self.cmd) and self.proc.poll() != 1:
                    Comunicator.debug_logger(
                        "Process %s exited with status %d. Stderr:\n%s" %
                        (self.cmd, self.proc.poll(), self.err))
                    self._force_cleanup()
                    Comunicator.fatal_debug_printer(
                        "Fatal error encountered in critical single process. See logs."
                    )

            return True
        return False
Exemple #2
0
    def _reap_snd(self, now=False):
        if self.snd_reaped:
            return True

        if now and self.snd_proc.poll() is None:
            self.snd_proc.wait()
            # self.snd_out, _ = self.snd_proc.get_output()

        if self.snd_proc.poll() is not None:
            # Process stopped so close the writing end of the pipes
            self.snd_err_w.close()
            self.snd_err_w = None

            self.snd_out_w.close()
            self.snd_out_w = None

            # Cleanup the reading pipe
            self.comm_r.close()
            self.comm_r = None

            # After we closed the writing end of the pipe _all_reader_thread should stop
            self.snd_err_reader_thread.join()
            self.snd_err_reader_thread = None

            self.snd_out_reader_thread.join()
            self.snd_out_reader_thread = None

            # Convert error from list to string
            self.snd_err = "".join(self.snd_err)

            # Mark the second process as completely stopped
            self.snd_reaped = True

            if self.critical and self.snd_proc.poll() != 0:
                # Second process could be hashcat which sometimes returns 1 but no error
                if DoubleProcess.command_is_hashcat(
                        self.snd_cmd) and self.snd_proc.poll() != 1:
                    Comunicator.debug_logger(
                        "Second process %s exited with status %d. Stderr:\n%s"
                        % (self.snd_cmd, self.snd_proc.poll(), self.snd_err))
                    self._force_cleanup()
                    Comunicator.fatal_debug_printer(
                        "Fatal error encountered in critical second process. See logs."
                    )

            return True

        return False
Exemple #3
0
    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
Exemple #4
0
    def _reap_fst(self, now=False):
        if self.fst_reaped:
            return True

        if now and self.fst_proc.poll() is None:
            # Wait for the first process to stop executing
            self.fst_proc.wait()

        if self.fst_proc.poll() is not None:
            # The first process stopped executing, close it's write pipes
            self.fst_err_w.close()
            self.fst_err_w = None

            self.comm_w.close()
            self.comm_w = None

            # After we closed the writing end of the err pipe _all_reader_thread should stop
            self.fst_err_reader_thread.join()
            self.fst_err_reader_thread = None

            # Convert error from list to string
            self.fst_err = "".join(self.fst_err)

            # Mark the first process as completely stopped
            self.fst_reaped = True

            # TODO this can be generic. If this becomes static the poll needs to be checked against None
            if self.critical and self.fst_proc.poll() != 0:
                Comunicator.error_logger(
                    "First process %s exited with status %d. Stderr:\n%s" %
                    (self.fst_cmd, self.fst_proc.poll(), self.fst_err))
                self._force_cleanup()
                Comunicator.fatal_debug_printer(
                    "Fatal error encountered in critical first process. See logs."
                )

            return True
        return False
Exemple #5
0
    def load_sha1s():
        """
            To reduce startup time sha1's are stored in the file Configuration.sha1s_filename
            and only recalculated if the file has changed after the hash was calculated.
            This function loads the sha1 hashes along with the time when the hash was calculated into
            the variable Configuration.old_sha1s
        :return:
            None
        """
        Configuration.old_sha1s = {}
        if not os.path.exists(Configuration.sha1s_filename):
            with open(Configuration.sha1s_filename, "w+") as _:
                return

        try:
            with open(Configuration.sha1s_filename) as fd:
                Configuration.old_sha1s = json.load(fd)
        except json.decoder.JSONDecodeError:
            return
        except Exception as e:
            Comunicator.fatal_debug_printer(
                "Error trying to load %s data: %s" %
                (Configuration.sha1s_filename, e))
Exemple #6
0
    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()
Exemple #7
0
    def __init__(self, fst_cmd, snd_cmd, crit=True):
        super(DoubleProcess, self).__init__()
        if len(fst_cmd) == 0 or len(snd_cmd) == 0:
            Comunicator.fatal_debug_printer(
                "One empty command in chained processes '%s' | '%s'" %
                (fst_cmd, snd_cmd))

        self.critical = crit
        self.ended = False

        # Logging data
        self.command = fst_cmd + ' | ' + snd_cmd
        self.fst_cmd = fst_cmd
        self.snd_cmd = snd_cmd

        disp1 = fst_cmd if type(fst_cmd) is str else " ".join(fst_cmd)
        disp2 = snd_cmd if type(snd_cmd) is str else " ".join(snd_cmd)

        Comunicator.debug_logger("Executing chained commands: '%s | %s'" %
                                 (disp1, disp2))

        # Output variables need to be mutable in order to modify them
        # from generic thread
        self.snd_out = []
        self.snd_err = []
        self.fst_err = []

        self.comm_r, self.comm_w = DoubleProcess.get_pipe_wrapper()
        self.snd_out_r, self.snd_out_w = DoubleProcess.get_pipe_wrapper()
        self.fst_err_r, self.fst_err_w = DoubleProcess.get_pipe_wrapper()
        self.snd_err_r, self.snd_err_w = DoubleProcess.get_pipe_wrapper()

        self.fst_reaped = False
        self.snd_reaped = 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.fst_err_reader_thread = Thread(target=self._all_reader_thread,
                                            args=(self.fst_err_r,
                                                  self.fst_err))
        self.snd_err_reader_thread = Thread(target=self._all_reader_thread,
                                            args=(self.snd_err_r,
                                                  self.snd_err))

        if DoubleProcess.command_is_hashcat(self.snd_cmd):
            self.snd_out_reader_thread = Thread(
                target=self._hashcat_out_thread,
                args=(self.snd_out_r, self.snd_out, self.hashcat_progress,
                      self))
        else:
            self.snd_out_reader_thread = Thread(target=self._all_reader_thread,
                                                args=(self.snd_out_r,
                                                      self.snd_out))

        if type(snd_cmd) is str:
            snd_cmd = snd_cmd.split(' ')
        try:
            self.snd_proc = Popen(snd_cmd,
                                  stdin=self.comm_r,
                                  stdout=self.snd_out_w,
                                  stderr=self.snd_err_w)
        except Exception as e:
            Comunicator.fatal_debug_printer(
                "Error while trying to run command '%s':\n%s" % (snd_cmd, e))

        if type(fst_cmd) is str:
            fst_cmd = fst_cmd.split(' ')
        try:
            self.fst_proc = Popen(fst_cmd,
                                  stdin=DoubleProcess.get_devnull_r(),
                                  stdout=self.comm_w,
                                  stderr=self.fst_err_w)
        except Exception as e:
            Comunicator.fatal_debug_printer(
                "Error while trying to run command '%s':\n%s" % (fst_cmd, e))

        self.fst_err_reader_thread.start()
        self.snd_err_reader_thread.start()
        self.snd_out_reader_thread.start()
Exemple #8
0
    def _hashcat_out_thread(read_pipe, output_list, hashcat_progress,
                            process_object):
        multiple_devices = False
        # TODO implement time estimation for maskfiles
        guess_queue_flag = False

        for line in read_pipe:
            output_list.append(line)

            if "[s]tatus" in line:
                process_object.cracking_started = True
                continue

            # TODO implement time estimation for maskfiles
            match = hashcat_guess_re.match(line)
            if match is not None:
                guess_queue_num = int(match.group(1))
                if guess_queue_num > 1:
                    guess_queue_flag = True

            with open("file", "a") as fd:
                fd.write(line)

            match = hashcat_progress_re.match(line)
            if match is not None:
                hashcat_progress["progress"] = int(match.group(1))

            match = hashcat_eta_re.match(line)
            if match is not None:
                hashcat_progress["eta"] = match.group(1)

            if guess_queue_flag:
                hashcat_progress["eta"] = "Unable to estimate for current rule"
                hashcat_progress["progress"] = -1

            match = hashcat_speed_re.match(line)
            if match is not None:
                str_speed, str_modifier = match.group(2).split()

                if str_modifier[0].lower() == 'h':
                    modifier = 1
                elif str_modifier[0].lower() == 'k':
                    modifier = 1000
                elif str_modifier[0].lower() == 'm':
                    modifier = 1000000
                elif str_modifier[0].lower() == 'g':
                    modifier = 1000000000
                else:
                    modifier = None
                    Comunicator.fatal_debug_printer(
                        "Error parsing hashcat speed on line '%s'" % line)
                speed = int(float(str_speed) * modifier)

                device = match.group(1).rstrip('.').lstrip('.')[1:]

                if device[0] == '*':
                    multiple_devices = True
                    hashcat_progress["devices"][0] = speed
                else:
                    devnum = int(device)
                    if devnum > 1:
                        multiple_devices = True
                    hashcat_progress["devices"][devnum] = speed

                if not multiple_devices:
                    hashcat_progress["speed"] = hashcat_progress["devices"][
                        0] = hashcat_progress["devices"][1]

                if 0 in hashcat_progress["devices"]:
                    hashcat_progress["speed"] = hashcat_progress["devices"][0]
        read_pipe.close()
Exemple #9
0
    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!"
            )