def __init__(self, r2_obj): self.r2_obj = r2_obj self.stopaddr = None self.bt = self.r2_obj.debug_dbt() self.arch_obj = Arch(self.r2_obj) self.term_colors = TermColors() self.pc_name = self.arch_obj.get_reg("program_counter") self.pdj = self.r2_obj.debug_pdj_single(self.pc_name)[0] self.pc = self.pdj["offset"] self.type = self.pdj["type"] if self.type != "invalid": self.instr = Instruction(self.r2_obj, self.pc_name)
class CrashGraph: def __init__(self, object, file_name): """" Initialized the object's internal data. Args: object: Crash object file_name: png output file name """ self.r2_obj = object.r2_obj self.r2_dbg_obj = object.r2_dbg_obj self.r2_stand_obj = object.r2_stand_obj self.graph_obj = Graph() self.count = 1 self.file_name = file_name def crash_graph(self): """ Graph generation """ self.r2_stand_obj.analyze() self.arch_obj = Arch(self.r2_obj) self.r2_dbg_obj.debug_setenv("dbg.btdepth", "256") #afl func_list = self.r2_stand_obj.aflqj() if not func_list: print("No functions") sys.exit(-1) #set bps for function in func_list: self.r2_dbg_obj.debug_break(function) self.node_list = [] self.range_list = [] first = True while True: # continue self.r2_dbg_obj.debug_continue() get_info = self.r2_dbg_obj.debug_infoj() signal = get_info["signal"] self.r2_dbg_obj.debug_dmmSy() self.pc = self.arch_obj.get_reg("program_counter") self.pdj = self.r2_dbg_obj.debug_pdj_single(self.pc) bt = self.r2_dbg_obj.debug_dbt() # check access violation signal if Checks.check_signal(signal): regs = self.r2_dbg_obj.debug_registers() node_name = "crash_node" message = "" is_exploitable = False if self.pdj[0]["type"] == "invalid": print("ERROR: Function crash_graph, the type is invalid!") return False opcode = self.pdj[0]["opcode"] crash_address = self.pdj[0]["offset"] is_exploitable = CrashAnal.exploitable(signal, self.r2_dbg_obj) if is_exploitable: exploitable_message = "PROBABLY EXPLOITABLE" message = "crash details" else: exploitable_message = "PROBABLY NOT EXPLOITABLE" message = "details" label = "Exploitable: " + exploitable_message + " \n " + "crash instruction: \n" + str( hex(crash_address) ) + " - " + str( opcode ) + " \nbacktrace: \n " + bt + " \n " + "registers: \n" + regs + " \n " self.node = self.graph_obj.create_node(node_name, "green", label) self.node_list.append(self.node) # create edge try: caller_address = bt.splitlines()[1].split()[1] except Exception as ex: print("WARNING: caller address - backtrace error: %s" % ex) caller_address = "0x0" self.create_edge(int(caller_address, 16), message) break if signal != "SIGTRAP": break if first: self.create_node("gold") first = False continue self.create_node("gold") try: caller_address = self.pdj[0]["xrefs"][0]["addr"] except Exception as ex: print("WARNING: caller address - xref not there: %s" % ex) try: caller_address = bt.splitlines()[1].split()[1] except Exception as ex: print("WARNING: caller address - backtrace erro: %s" % ex) continue # create edge self.create_edge(caller_address, self.count) # create png self.graph_obj.create_png(self.file_name) def create_node(self, color): function_address = self.pdj[0]["fcn_addr"] func_address_end = self.pdj[0]["fcn_last"] r = range(function_address, func_address_end) self.range_list.append(r) func_name = self.pdj[0]["flags"][0] node_name = func_name label = func_name + " [ " + str(hex(function_address)) + " - " + str( hex(func_address_end)) + " ] " self.node = self.graph_obj.create_node(node_name, color, label) self.node_list.append(self.node) def create_edge(self, caller_address, label): for range_addr in self.range_list: if caller_address in range_addr: for single_node in self.node_list: a = range_addr[0] b = single_node.obj_dict["attributes"]["label"].split()[2] if int(range_addr[0]) == int(b, 16): self.graph_obj.create_edge(single_node, self.node, label) self.count += 1 return
class Engine: def __init__(self, r2_obj): self.r2_obj = r2_obj self.stopaddr = None self.bt = self.r2_obj.debug_dbt() self.arch_obj = Arch(self.r2_obj) self.term_colors = TermColors() self.pc_name = self.arch_obj.get_reg("program_counter") self.pdj = self.r2_obj.debug_pdj_single(self.pc_name)[0] self.pc = self.pdj["offset"] self.type = self.pdj["type"] if self.type != "invalid": self.instr = Instruction(self.r2_obj, self.pc_name) def print_crash_info(self): print(self.term_colors.bold() + "backtrace" + self.term_colors.reset()) print(self.bt) print(self.term_colors.bold() + "registers" + self.term_colors.reset()) print(self.r2_obj.debug_registers()) def stack_overf_libc(self): libs = ["fortify_fail", "stack_chk_fail"] try: if libs[0] in self.bt or libs[1] in self.bt: stack_report = "Stack error - Generally it could be exploitable, due to suspicious functions in the backtrace" print(self.term_colors.red() + stack_report + self.term_colors.default()) self.print_crash_info() return stack_report return False except Exception as ex: print("stack_overf_libc - error type: %s" % ex) return False def crash_on_pc(self): try: dinfo = self.r2_obj.debug_infoj() self.stopaddr = dinfo["stopaddr"] if self.type == "div": self.not_exploitable() return False if self.stopaddr == self.pc or self.type == "invalid": crash_on_pc_report = "Crash on PC - Generally it is exploitable, the PC could be tainted" print(self.term_colors.red() + crash_on_pc_report + self.term_colors.default()) self.print_crash_info() return crash_on_pc_report return False except Exception as ex: print("crash_on_pc - error type: %s" % ex) return False def crash_on_branch(self): if self.type != "invalid": try: is_branch_instr = self.instr.is_branch_instr() except Exception as ex: print("crash_on_branch - error type: %s" % ex) return False if is_branch_instr: crash_on_branch_report = "Crash on branch instruction - Generally it is exploitable, the branch address could be tainted" print(self.term_colors.red() + crash_on_branch_report + self.term_colors.default()) self.print_crash_info() return crash_on_branch_report return False return False def invalid_write(self): try: if self.type != "invalid": is_mem_write = self.instr.is_memory_write() if is_mem_write: dest_address = self.instr.get_dest_address() if dest_address and int(dest_address, 16) == self.stopaddr: size = self.instr.write_size() invalid_write_report = "Invalid write crash - Generally it is exploitable, the write value/address could be tainted" if size: print(self.term_colors.red() + invalid_write_report + " - Invalid write of size %d" % size + self.term_colors.default()) else: print(self.term_colors.red() + invalid_write_report + "Invalid write" + self.term_colors.default()) self.print_crash_info() return invalid_write_report return False except Exception as ex: print("invalid_write - error type: %s" % ex) return False def heap_error(self): suspicius_heap_functions = """free malloc calloc realloc""".split() try: for i in suspicius_heap_functions: if i in self.bt: heap_report = "Heap error - Generally it could be exploitable, due to suspicious functions in the backtrace" print(self.term_colors.red() + heap_report + self.term_colors.default()) self.print_crash_info() return heap_report return False except Exception as ex: print("heap_error - error type: %s" % ex) return False def read_access_violation(self): """ Check if it is a read access violation, and if it could be exploitable :return: True if it could be exploitable """ try: if self.type != "invalid": is_mem_read = self.instr.is_memory_read() if is_mem_read: if self.instr.call_check(self.arch_obj.get_regs()): uaf_report = "It could be just a read access violation, but after few instruction there is a suspicious call, and if the address could be controlled by an attacker, it could lead to code execution" print(self.term_colors.red() + uaf_report + self.term_colors.default()) self.print_crash_info() return uaf_report return False return False except Exception as ex: print("read_access_violation - error type: %s" % ex) return False def not_exploitable(self): not_exploitable_report = "PROBABLY NOT EXPLOITABLE" print(self.term_colors.green() + not_exploitable_report + self.term_colors.default())
def crash_graph(self): """ Graph generation """ self.r2_stand_obj.analyze() self.arch_obj = Arch(self.r2_obj) self.r2_dbg_obj.debug_setenv("dbg.btdepth", "256") #afl func_list = self.r2_stand_obj.aflqj() if not func_list: print("No functions") sys.exit(-1) #set bps for function in func_list: self.r2_dbg_obj.debug_break(function) self.node_list = [] self.range_list = [] first = True while True: # continue self.r2_dbg_obj.debug_continue() get_info = self.r2_dbg_obj.debug_infoj() signal = get_info["signal"] self.r2_dbg_obj.debug_dmmSy() self.pc = self.arch_obj.get_reg("program_counter") self.pdj = self.r2_dbg_obj.debug_pdj_single(self.pc) bt = self.r2_dbg_obj.debug_dbt() # check access violation signal if Checks.check_signal(signal): regs = self.r2_dbg_obj.debug_registers() node_name = "crash_node" message = "" is_exploitable = False if self.pdj[0]["type"] == "invalid": print("ERROR: Function crash_graph, the type is invalid!") return False opcode = self.pdj[0]["opcode"] crash_address = self.pdj[0]["offset"] is_exploitable = CrashAnal.exploitable(signal, self.r2_dbg_obj) if is_exploitable: exploitable_message = "PROBABLY EXPLOITABLE" message = "crash details" else: exploitable_message = "PROBABLY NOT EXPLOITABLE" message = "details" label = "Exploitable: " + exploitable_message + " \n " + "crash instruction: \n" + str( hex(crash_address) ) + " - " + str( opcode ) + " \nbacktrace: \n " + bt + " \n " + "registers: \n" + regs + " \n " self.node = self.graph_obj.create_node(node_name, "green", label) self.node_list.append(self.node) # create edge try: caller_address = bt.splitlines()[1].split()[1] except Exception as ex: print("WARNING: caller address - backtrace error: %s" % ex) caller_address = "0x0" self.create_edge(int(caller_address, 16), message) break if signal != "SIGTRAP": break if first: self.create_node("gold") first = False continue self.create_node("gold") try: caller_address = self.pdj[0]["xrefs"][0]["addr"] except Exception as ex: print("WARNING: caller address - xref not there: %s" % ex) try: caller_address = bt.splitlines()[1].split()[1] except Exception as ex: print("WARNING: caller address - backtrace erro: %s" % ex) continue # create edge self.create_edge(caller_address, self.count) # create png self.graph_obj.create_png(self.file_name)