def run_coretaint(self, p, white_calls, faddr, check_func):
        """
        Runs the coretaint module on the provided parameters
        split up the coretaint part from the forward and backward taint trackers to prevent duplicate code
        :return:
        """
        self._ct = CoreTaint(p, interfunction_level=0, smart_call=True, only_tracker=True,
                             follow_unsat=True, shuffle_sat=True, white_calls=white_calls,
                             exploration_strategy=self._exploration_strategy,
                             try_thumb=True, taint_returns_unfollowed_calls=True,
                             taint_arguments_unfollowed_calls=True,
                             exit_on_decode_error=True, force_paths=True, allow_untaint=False)

        s = get_initial_state(self._p, self._ct, faddr)
        summarized_f = prepare_function_summaries(self._p)
        self._ct.set_alarm(TIMEOUT_TAINT, n_tries=TIMEOUT_TRIES)

        try:
            # to trigger it refer to httpd 0x16410. Switch case is mostly UNSAT!
            self._ct.run(s, (), (), summarized_f=summarized_f, force_thumb=False, use_smart_concretization=False,
                         check_func=check_func, init_bss=False)
        except TimeOutException:
            log.warning("Timeout Triggered")
        except Exception as e:
            log.warning(f"Exception in coretaint: {str(e)}")

        self._ct.unset_alarm()
Beispiel #2
0
    def _get_role(self, no, key_addr, reg_name, key_name=None):
        """
        Retrieve the role of a binary by inferring whether it is a setter or a getter

        :param no: node containing the call to a set or getter function
        :param key_addr: address of the keyword used to infer the role
        :param reg_name: register containing the key_addr
        :return: The role and the function used to infer it whether the role could be inferred, None and None otherwise
        """

        p = self._current_p

        if not BinaryDependencyGraph.is_call(p.factory.block(no.addr)):
            return None

        # detect the role
        self._cpf_used = None
        f_addr = no.addr
        self._candidate_role_function = no.successors[0].addr

        # prepare the under-contrainted-based initial state
        # we do not allow untaint as we just want to see where the key data key is leading to
        self._core_taint = CoreTaint(p, interfunction_level=2, smart_call=False,
                                     follow_unsat=True,
                                     try_thumb=True,
                                     exit_on_decode_error=True, force_paths=True, allow_untaint=False,
                                     logger_obj=log)

        # the used register is not a parameter register
        if are_parameters_in_registers(p) and reg_name not in arg_reg_names(p):
            return Role.UNKNOWN

        self._current_par_name = reg_name
        self._current_key_addr = key_addr
        self._current_f_addr = f_addr

        s = get_initial_state(p, self._core_taint, f_addr)
        s = self._apply_taint(s, key_addr, key_name)
        # enter into the call
        sim = p.factory.simgr(s)
        sim.step()
        s = sim.active[0]
        if reg_name:
            setattr(s.regs, reg_name, BVV(key_addr, p.arch.bits))

        summarized_f = prepare_function_summaries(p)
        self._f_arg_vals = []
        self._set_f_vals = True

        self._core_taint.set_alarm(TIMEOUT_TAINT, n_tries=TIMEOUT_TRIES)
        try:
            self._core_taint.run(s, (), (), summarized_f=summarized_f, force_thumb=False,
                                 check_func=self._check_key_usage, init_bss=False)
        except TimeOutException:
            log.warning("Timeout Triggered")
        except Exception as e:
            log.warning(f"Exception in Coretaint: {str(e)}")

        self._core_taint.unset_alarm()
        return self._current_role
Beispiel #3
0
    def _find_recv(self, current_path):
        """
        Attempts to find the port and ip using to send the data to other binaries.
        the heuristic is the following:
         * first we look for the htons instruction and we retrieve the port, the we look in the nearby memory to
         retrieve the IP address. This heuristic is based on the fact that both port and ip are set in the same
         sock_addr struct

        :param current_path: angr current path
        :return: None
        """

        p = self._p
        self._read_from_socket = False
        caller_blocks = self._get_caller_blocks(current_path, M_GET_KEYWORD)

        for caller_block in caller_blocks:
            self._ct = CoreTaint(p, smart_call=False, not_follow_any_calls=True,
                                 follow_unsat=True,
                                 try_thumb=True,
                                 exit_on_decode_error=True, force_paths=True,
                                 taint_returns_unfollowed_calls=True,
                                 taint_arguments_unfollowed_calls=True,
                                 allow_untaint=False)

            self._ct.set_alarm(TIMEOUT_TAINT, n_tries=TIMEOUT_TRIES)
            try:
                s = get_initial_state(p, self._ct, caller_block)
                self._ct.run(s, (), (), force_thumb=False,
                             check_func=self._check_recv)
            except TimeOutException:
                self._log.warning("Hard timeout triggered")
            except Exception as e:
                self._log.error(f"Find recv: Something went terribly wrong: {str(e)}")
            self._ct.restore_signal_handler()
    def _get_interesting_returns(self, faddr):
        """
        Finds those returns within a function that can possibly lead tainted data outside the function.

        :param faddr: function address
        :return: None
        """

        interesting_returns = []
        p = self._p
        cfg = self._cfg

        try:
            fun = cfg.functions[faddr]
            returns = list(fun.endpoints_with_type['return'])

            for r in returns:

                addrs = [r.addr]
                steps = 1

                # if possible go a further step back to
                # get also return stubs
                no = cfg.model.get_any_node(r.addr)
                if no:
                    preds = no.predecessors
                    addrs = [pred.addr for pred in preds]
                    steps = 2

                for a in addrs:
                    s = get_initial_state(self._p, self._ct, a)

                    simgr = p.factory.simgr(s, save_unconstrained=True, save_unsat=True)
                    simgr.step()
                    if steps == 2:
                        simgr.step()

                    stashes = [x for y in simgr.stashes.values() for x in y]
                    for stash in stashes:
                        val = getattr(stash.regs, ret_reg_name(p))
                        if val.concrete:
                            continue
                        to_add = [r.addr] if steps == 1 else [r.addr, a]
                        interesting_returns.append(to_add)
        except:
            pass
        return interesting_returns
Beispiel #5
0
    def _find_binding(self, current_path):
        """
        Attempts to find the port and ip using to send the data to other binaries.
        The heuristic is the following:
        * first we look for the htons instruction and we retrieve the port, the we look in the nearby memory to
          retrieve the IP address. This heuristic is based on the fact that both port and ip are set in the same
          sock_addr struct
        :param current_path: angr current path
        :return: None
        """

        p = self._p
        htons_callers = self._get_caller_blocks(current_path, 'htons')
        for caller_block in htons_callers:
            self._sink_addrs = []
            self._last_port = None
            self._ct = CoreTaint(p, smart_call=False,
                                 interfunction_level=0,
                                 follow_unsat=True,
                                 try_thumb=True,
                                 exit_on_decode_error=True, force_paths=True,
                                 taint_returns_unfollowed_calls=True,
                                 taint_arguments_unfollowed_calls=True,
                                 allow_untaint=False)

            summarized_f = {}

            # summary the htons and inet_pton
            addrs = get_dyn_sym_addrs(p, ['htons'])
            for a in addrs:
                summarized_f[a] = self._htons

            addrs = get_dyn_sym_addrs(p, ['inet_pton'])
            for a in addrs:
                summarized_f[a] = self._inet_pton

            self._ct.set_alarm(TIMEOUT_TAINT, n_tries=TIMEOUT_TRIES)
            try:
                faddr = self._cfg.model.get_any_node(caller_block).function_address
                s = get_initial_state(self._p, self._ct, faddr)
                self._ct.run(s, (), (), summarized_f=summarized_f, force_thumb=False,
                             check_func=self._check_ip_address)
            except TimeOutException:
                self._log.warning("Hard timeout triggered")
            except Exception as e:
                self._log.error(f"Find binding: Something went terribly wrong: {str(e)}")
            self._ct.restore_signal_handler()
Beispiel #6
0
    def _find_tainted_callers(self, key_addr, f_addr):
        """
        Retrieve the role of a binary by inferring whether it is a setter or a getter

        :param key_addr: address of the keyword used to infer the role
        :param f_addr: address of the function where we start our analysis
        :return: A list of callsites
        """

        p = self._current_p

        self._tainted_callsites = []
        # prepare the under-contrainted-based initial state
        # we do not allow untaint as we just want to see where the data key is leading to
        self._core_taint = CoreTaint(p, interfunction_level=0, smart_call=False,
                                     follow_unsat=True,
                                     try_thumb=True,
                                     exit_on_decode_error=True, force_paths=True, allow_untaint=False,
                                     logger_obj=log)

        self._current_key_addr = key_addr
        s = get_initial_state(p, self._core_taint, f_addr)
        s = self._apply_taint(s, key_addr)
        summarized_f = prepare_function_summaries(p)

        self._core_taint.set_alarm(TIMEOUT_TAINT, n_tries=TIMEOUT_TRIES)

        try:
            self._core_taint.run(s, (), (), summarized_f=summarized_f, force_thumb=False,
                                 check_func=self._find_taint_callers, init_bss=False)
        except TimeOutException:
            log.warning("Timeout Triggered")
        except Exception as e:
            log.warning(f"Exception in Coretaint: {str(e)}")

        self._core_taint.unset_alarm()
        callsites = []
        for cs in self._tainted_callsites:
            try:
                if self._current_cfg.model.get_any_node(cs[0]).function_address == f_addr and cs not in callsites:
                    callsites.append(cs)
            except:
                pass

        return callsites
Beispiel #7
0
    def _find_file_name(self, current_path, check_func):
        """
        Find the filename.

        :param current_path: angr current path.
        :param check_func:  checker function
        :return: None
        """

        p = self._p

        self._check_func = check_func
        caller_blocks = self._get_caller_blocks(current_path, 'fopen')
        for caller_block in caller_blocks:
            self._ct = CoreTaint(p,
                                 not_follow_any_calls=True,
                                 smart_call=False,
                                 follow_unsat=True,
                                 try_thumb=True,
                                 exit_on_decode_error=True,
                                 force_paths=True,
                                 taint_returns_unfollowed_calls=True,
                                 taint_arguments_unfollowed_calls=True,
                                 allow_untaint=False)

            self._ct.set_alarm(TIMEOUT_TAINT, n_tries=TIMEOUT_TRIES)
            try:
                s = get_initial_state(self._p, self._ct, caller_block)
                self._ct.run(s, (), (),
                             force_thumb=False,
                             check_func=self._save_file_name)
            except TimeOutException:
                self._log.warning("Hard timeout triggered")
            except Exception as e:
                self._log.error(
                    f"file.py: Something went terribly wrong: {str(e)}")
            self._ct.restore_signal_handler()
Beispiel #8
0
    def _vuln_analysis(self, bdg_node, seed_addr, info):
        """
        Run the analysis for children (i.e, slave binaries)

        :param bdg_node:  BDG node
        :param seed_addr: address of the seed of taint
        :param info: binary's info
        :return:
        """

        self._current_p = bdg_node.p
        self._current_cfg = bdg_node.cfg
        self._current_cpf_name = bdg_node.find_cpf_data_key(
            info[RoleInfo.DATAKEY]).name
        self._current_seed_addr = seed_addr
        self._current_role_info = info
        self._taint_names_applied = []
        self._visited_bb = 0

        ana_start_time = time.time()
        if bdg_node.bin not in self._stats:
            self._stats[bdg_node.bin] = {
                'n_paths': 0,
                'ana_time': 0,
                'visited_bb': 0,
                'n_runs': 0,
                'to': 0,
            }

        # prepare the under-constrained-based initial state
        self._ct = CoreTaint(self._current_p,
                             interfunction_level=1,
                             smart_call=True,
                             follow_unsat=True,
                             black_calls=(info[RoleInfo.ROLE_FUN], ),
                             try_thumb=True,
                             shuffle_sat=True,
                             exit_on_decode_error=True,
                             force_paths=True,
                             taint_returns_unfollowed_calls=True,
                             allow_untaint=True,
                             logger_obj=log)

        try:
            summarized_f = prepare_function_summaries(self._current_p)
            s = get_initial_state(self._current_p, self._ct,
                                  info[RoleInfo.X_REF_FUN])
            self._find_sink_addresses()

            self._ct.set_alarm(TIMEOUT_TAINT, n_tries=TIMEOUT_TRIES)

            self._ct.run(s, (), (),
                         summarized_f=summarized_f,
                         force_thumb=False,
                         check_func=self._check_sink,
                         init_bss=False)
        except TimeOutException:
            log.warning("Hard timeout triggered")
        except Exception as e:
            log.error(f"Coretaint: Something went terribly wrong: {str(e)}")

        self._ct.unset_alarm()

        # stats
        self._stats[bdg_node.bin]['to'] += 1 if self._ct.triggered_to() else 0
        self._stats[bdg_node.bin]['visited_bb'] += self._visited_bb
        self._stats[bdg_node.bin]['n_paths'] += self._ct.n_paths
        self._stats[bdg_node.bin]['ana_time'] += (time.time() - ana_start_time)
        self._stats[bdg_node.bin]['n_runs'] += 1