def run(self): os.putenv("LANG", "C") os.putenv("ASAN_SYMBOLIZER_PATH", self.asan_symbolizer_path) cmd = self.program print "Running %s" % cmd cmd_obj = TimeoutCommand(cmd) cmd_obj.run(self.timeout, get_output=True) buf = cmd_obj.stderr self.asan.parse_buffer(buf) if self.asan.reason is not None: crash_data = CCrashData(self.asan.pc, self.asan.reason) i = 0 for line in self.asan.stack_trace: crash_data.add_data("stack trace", "%d" % i, (line[0], line[1])) i += 1 crash_data.add_data("registers", "pc", self.asan.pc) crash_data.add_data("registers", "bp", self.asan.bp) crash_data.add_data("registers", "sp", self.asan.sp) crash_data.add_data("disassembly", int(self.asan.pc), "") j = 0 for line in self.asan.additional: crash_data.add_data("information", j, line) j += 1 crash_data.disasm = [self.asan.pc, ""] if not self.asan.reason.startswith("SIG"): crash_data.exploitable = "EXPLOITABLE" else: crash_data.exploitable = "UNKNOWN" crash_data.add_data("exploitability", "reason", self.asan.reason) crash_data_buf = crash_data.dump_json() crash_data_dict = crash_data.dump_dict() line = "Program received %s at PC 0x%x SP 0x%x BP 0x%x" print line % (self.asan.reason, self.asan.pc, self.asan.sp, self.asan.bp) print for i, line in enumerate(self.asan.stack_trace): if i > 10: break print "0x%08x %s" % (line[0], line[1]) print print "Yep, we got a crash! \o/" print return crash_data_dict return
def run(self): global buf os.putenv("LANG", "C") logfile = mkstemp()[1] try: cmd = '/bin/bash -c "/usr/bin/gdb -q --batch --command=%s --args %s" 2>/dev/null > %s' cmd %= (self.gdb_commands, self.program, logfile) print cmd print "Running %s" % cmd cmd_obj = TimeoutCommand(cmd) #cmd_obj.shell = True cmd_obj.run(self.timeout) buf = open(logfile, "rb").readlines() self.parse_dump(buf) if self.signal: crash_data = CCrashData(self.pc, self.signal) i = 0 for stack in self.stack: crash_data.add_data("stack trace", "%d" % i, stack) i += 1 for reg in self.registers: crash_data.add_data("registers", reg, self.registers[reg]) crash_data.add_data("disassembly", int(self.pc), self.disasm) for dis in self.disasm_around: if type(dis[0]) is int or dis[0].isdigit(): crash_data.add_data("disassembly", dis[0], dis[1]) crash_data.disasm = [self.pc, self.disasm] if self.exploitability is not None: crash_data.exploitable = self.exploitability if self.exploitability_reason is not None: crash_data.add_data("exploitability", "reason", self.exploitability_reason) crash_data_buf = crash_data.dump_json() crash_data_dict = crash_data.dump_dict() print print "Yep, we got a crash! \o/" print return crash_data_dict return finally: os.remove(logfile)
def main(args): crash_data_dict = None tr = vtrace.getTrace() global timeout if os.getenv("NIGHTMARE_TIMEOUT"): timeout = float(os.getenv("NIGHTMARE_TIMEOUT")) if args[0] in ["--attach", "-A"]: if len(args) == 1: usage() sys.exit(1) else: pid = int(args[1]) if timeout != 0: # Schedule a timer to detach from the process after some seconds timer = threading.Timer(timeout, kill_process, (tr, False, )) timer.start() tr.attach(pid) else: if timeout != 0: # Schedule a timer to kill the process after 5 seconds timer = threading.Timer(timeout, kill_process, (tr, True, )) timer.start() tr.execute(" ".join(args)) tr.run() signal = tr.getCurrentSignal() signal_name = signal_to_name(signal) ignore_list = ["Unknown", "SIGUSR1", "SIGUSR2", "SIGTTIN", "SIGPIPE", "SIGINT"] while signal is None or signal_name in ignore_list: signal = tr.getCurrentSignal() signal_name = signal_to_name(signal) try: tr.run() except: break if timeout != 0: timer.cancel() # Don't do anything else, the process is gone if os.name != "nt" and not tr.attached: return None if signal is not None: print tr, hex(signal) print " ".join(sys.argv) crash_name = os.getenv("NFP_INFO_CRASH") # Create the object to store all the crash data crash_data = CCrashData(tr.getProgramCounter(), sig2name(signal)) if crash_name is None: crash_name = "info.crash" #f = open(crash_name, "wb") exploitability_reason = None if os.name != "nt" and signal == 4: # Due to illegal instruction exploitable = EXPLOITABLE exploitability_reason = "Illegal instruction" elif os.name == "nt" and signal in [0xc0000096, 0xc000001d]: # Due to illegal or privileged instruction exploitable = EXPLOITABLE if signal == 0xc000001d: exploitability_reason = "Illegal instruction" else: exploitability_reason = "Privileged instruction" else: exploitable = NOT_EXPLOITABLE crash_data.add_data("process", "pid", tr.getPid()) if os.name == "nt": print "Process %d crashed with exception 0x%x (%s)" % (tr.getPid(), signal, win32_exc_to_name(signal)) else: print "Process %d crashed with signal %d (%s)" % (tr.getPid(), signal, signal_to_name(signal)) i = 0 for t in tr.getThreads(): i += 1 crash_data.add_data("threads", "%d" % i, t) stack_trace = tr.getStackTrace() total = len(stack_trace) i = 0 for x in stack_trace: i += 1 sym = tr.getSymByAddr(x[0], exact=False) if sym is None: sym = "" crash_data.add_data("stack trace", "%d" % i, [x[0], str(sym)]) total -= 1 regs = tr.getRegisterContext().getRegisters() for reg in regs: crash_data.add_data("registers", reg, regs[reg]) if reg.startswith("r"): line = reg.ljust(5) + "%016x" % regs[reg] try: mem = tr.readMemory(regs[reg], 32) mem = base64.b64encode(mem) crash_data.add_data("registers memory", reg, mem) line += "\t" + repr(mem) except: pass for reg in COMMON_REGS: if reg in regs: if reg in crash_data.data["registers memory"]: print reg, hex(regs[reg]), repr(base64.b64decode(crash_data.data["registers memory"][reg])) else: print reg, hex(regs[reg]) print total_around = 40 if 'rip' in regs or 'rsp' in regs or 'rbp' in regs: if len("%08x" % regs['rip']) > 8 or len("%08x" % regs['rsp']) > 8 or len("%08x" % regs['rbp']) > 8: mode = CS_MODE_64 else: mode = CS_MODE_32 else: mode = CS_MODE_32 md = Cs(CS_ARCH_X86, mode) md.skipdata = True pc = tr.getProgramCounter() crash_mnem = None crash_ops = None try: pc_mem = tr.readMemory(pc-total_around/2, total_around) offset = regs["rip"]-total_around/2 ret = [] found = False for x in md.disasm(pc_mem, 0): line = "%016x %s %s" % ((offset + x.address), x.mnemonic, x.op_str) crash_data.add_data("disassembly", offset + x.address, "%s %s" %(x.mnemonic, x.op_str)) if offset + x.address == pc: crash_data.disasm = [x.address + offset, "%s %s" %(x.mnemonic, x.op_str)] line += "\t\t<--------- CRASH" print line found = True ret.append(line) if not found: offset = pc = tr.getProgramCounter() pc_mem = tr.readMemory(pc, total_around) for x in md.disasm(pc_mem, 0): line = "%016x %s %s" % ((offset + x.address), x.mnemonic, x.op_str) if offset + x.address == pc: line += "\t\t<--------- CRASH" crash_data.disasm = [x.address + offset, "%s %s" % (x.mnemonic, x.op_str)] print line except: # Due to invalid memory at $PC if signal != 6: exploitable = True exploitability_reason = "Invalid memory at program counter" print "Exception:", sys.exc_info()[1] if crash_mnem: if crash_mnem in ["call", "jmp"] or \ crash_mnem.startswith("jmp") or \ crash_mnem.startswith("call"): if crash_ops.find("[") > -1: # Due to jump/call with a register that maybe controllable exploitable = EXPLOITABLE exploitability_reason = "Jump or call with a probably controllable register" elif crash_mnem.startswith(".byte"): # Due to illegal instruction exploitable = MAYBE_EXPLOITABLE exploitability_reason = "Illegal instruction" elif crash_mnem.startswith("in") or \ crash_mnem.startswith("out") or \ crash_mnem in ["hlt", "iret", "clts", "lgdt", "lidt", "lldt", "lmsw", "ltr", "cli", "sti"]: if crash_mnem != "int": # Due to privileged instruction (which makes no sense in user-land) exploitable = MAYBE_EXPLOITABLE exploitability_reason = "Privileged instruction" #print >>f #print >>f, "Maps:" i = 0 for m in tr.getMemoryMaps(): i += 1 line = "%016x %s %s %s" % (m[0], str(m[1]).rjust(8), get_permision_str(m[2]), m[3]) crash_data.add_data("memory maps", "%d" % i, m) #print >>f, line #print >>f if exploitable > 0: crash_data.exploitable = is_exploitable(exploitable) crash_data.add_data("exploitability", "reason", exploitability_reason) #print >>f, "Exploitable: %s. %s." % (is_exploitable(exploitable), exploitability_reason) else: #print >>f, "Exploitable: Unknown." pass crash_data_buf = crash_data.dump_json() crash_data_dict = crash_data.dump_dict() print "Yep, we got a crash! \o/" print #print "Dumping JSON...." #print crash_data_buf #print if tr.attached: try: tr.kill() except: pass try: tr.release() except: pass return crash_data_dict
def main(args): crash_data_dict = None tr = vtrace.getTrace() global timeout if os.getenv("NIGHTMARE_TIMEOUT"): timeout = float(os.getenv("NIGHTMARE_TIMEOUT")) if args[0] in ["--attach", "-A"]: if len(args) == 1: usage() sys.exit(1) else: pid = int(args[1]) # Schedule a timer to detach from the process after some seconds timer = threading.Timer(timeout, kill_process, ( tr, False, )) timer.start() tr.attach(pid) else: # Schedule a timer to kill the process after 5 seconds timer = threading.Timer(timeout, kill_process, ( tr, True, )) timer.start() tr.execute(" ".join(args)) tr.run() signal = tr.getCurrentSignal() signal_name = signal_to_name(signal) ignore_list = [ "Unknown", "SIGUSR1", "SIGUSR2", "SIGTTIN", "SIGPIPE", "SIGINT" ] while signal is None or signal_name in ignore_list: signal = tr.getCurrentSignal() signal_name = signal_to_name(signal) try: tr.run() except: break timer.cancel() # Don't do anything else, the process is gone if os.name != "nt" and not tr.attached: return None if signal is not None: print tr, hex(signal) print " ".join(sys.argv) crash_name = os.getenv("NFP_INFO_CRASH") # Create the object to store all the crash data crash_data = CCrashData(tr.getProgramCounter(), sig2name(signal)) if crash_name is None: crash_name = "info.crash" #f = open(crash_name, "wb") exploitability_reason = None if os.name != "nt" and signal == 4: # Due to illegal instruction exploitable = EXPLOITABLE exploitability_reason = "Illegal instruction" elif os.name == "nt" and signal in [0xc0000096, 0xc000001d]: # Due to illegal or privileged instruction exploitable = EXPLOITABLE if signal == 0xc000001d: exploitability_reason = "Illegal instruction" else: exploitability_reason = "Privileged instruction" else: exploitable = NOT_EXPLOITABLE crash_data.add_data("process", "pid", tr.getPid()) if os.name == "nt": print "Process %d crashed with exception 0x%x (%s)" % ( tr.getPid(), signal, win32_exc_to_name(signal)) else: print "Process %d crashed with signal %d (%s)" % ( tr.getPid(), signal, signal_to_name(signal)) i = 0 for t in tr.getThreads(): i += 1 crash_data.add_data("threads", "%d" % i, t) stack_trace = tr.getStackTrace() total = len(stack_trace) i = 0 for x in stack_trace: i += 1 sym = tr.getSymByAddr(x[0], exact=False) if sym is None: sym = "" crash_data.add_data("stack trace", "%d" % i, [x[0], str(sym)]) total -= 1 regs = tr.getRegisterContext().getRegisters() for reg in regs: crash_data.add_data("registers", reg, regs[reg]) if reg.startswith("r"): line = reg.ljust(5) + "%016x" % regs[reg] try: mem = tr.readMemory(regs[reg], 32) mem = base64.b64encode(mem) crash_data.add_data("registers memory", reg, mem) line += "\t" + repr(mem) except: pass for reg in COMMON_REGS: if reg in regs: if reg in crash_data.data["registers memory"]: print reg, hex(regs[reg]), repr( base64.b64decode( crash_data.data["registers memory"][reg])) else: print reg, hex(regs[reg]) print total_around = 40 if 'rip' in regs: if len("%08x" % regs['rip']) > 8: decoder = Decode64Bits else: decoder = Decode32Bits else: decoder = Decode32Bits pc = tr.getProgramCounter() pc_line = None try: pc_mem = tr.readMemory(pc - total_around / 2, total_around) offset = regs["rip"] - total_around / 2 ret = [] found = False for x in Decode(0, pc_mem, decoder): line = "%016x %s" % ((offset + x.offset), str(x)) crash_data.add_data("disassembly", offset + x.offset, str(x)) if offset + x.offset == pc: crash_data.disasm = [x.offset + offset, str(x)] line += "\t\t<--------- CRASH" print line pc_line = x found = True ret.append(line) if found: #print "\n".join(ret) pass else: offset = pc = tr.getProgramCounter() pc_mem = tr.readMemory(pc, total_around) for x in Decode(0, pc_mem, decoder): line = "%016x %s" % ((offset + x.offset), str(x)) if offset + x.offset == pc: line += "\t\t<--------- CRASH" pc_line = x print line except: # Due to invalid memory at $PC if signal != 6: exploitable = True exploitability_reason = "Invalid memory at program counter" print "Exception:", sys.exc_info()[1] if pc_line: if str(pc_line.mnemonic) in ["CALL", "JMP"] or \ str(pc_line.mnemonic).startswith("JMP") or \ str(pc_line.mnemonic).startswith("CALL"): if str(pc_line.operands).find("[") > -1: # Due to jump/call with a register that maybe controllable exploitable = EXPLOITABLE exploitability_reason = "Jump or call with a probably controllable register" elif str(pc_line.mnemonic).startswith("DB "): # Due to illegal instruction exploitable = MAYBE_EXPLOITABLE exploitability_reason = "Illegal instruction" elif str(pc_line.mnemonic).startswith("IN") or \ str(pc_line.mnemonic).startswith("OUT") or \ str(pc_line.mnemonic) in ["HLT", "IRET", "CLTS", "LGDT", "LIDT", "LLDT", "LMSW", "LTR", "CLI", "STI"]: if str(pc_line.mnemonic) != "INT": # Due to privileged instruction (which makes no sense in user-land) exploitable = MAYBE_EXPLOITABLE exploitability_reason = "Privileged instruction" #print >>f #print >>f, "Maps:" i = 0 for m in tr.getMemoryMaps(): i += 1 line = "%016x %s %s %s" % (m[0], str( m[1]).rjust(8), get_permision_str(m[2]), m[3]) crash_data.add_data("memory maps", "%d" % i, m) #print >>f, line #print >>f if exploitable > 0: crash_data.exploitable = is_exploitable(exploitable) crash_data.add_data("exploitability", "reason", exploitability_reason) #print >>f, "Exploitable: %s. %s." % (is_exploitable(exploitable), exploitability_reason) else: #print >>f, "Exploitable: Unknown." pass crash_data_buf = crash_data.dump_json() crash_data_dict = crash_data.dump_dict() print "Yep, we got a crash! \o/" print #print "Dumping JSON...." #print crash_data_buf #print if tr.attached: try: tr.kill() except: pass try: tr.release() except: pass return crash_data_dict