def __init__(self, path): super(ELF, self).__init__() self.path = path self.r2 = r2pipe.open(path) self.info = DotMap({i:self.r2.cmdj("ij")["bin"][i] for i in ELF.info}) self._base = 0 l = [] for m in ELF.info: l.append(m) l.append(self.info[m]) log.info("Opening binary {}".format(self.path), ("{:32}{}\n"*len(ELF.info)).format(*l).strip()) self.sym = DotMap() for i in self.r2.cmdj("isj"): self.sym[i["name"]] = i["vaddr"] self.sections = {i["name"]:i["vaddr"] for i in self.r2.cmdj("iSj")} self.sym.got = DotMap({i["name"]:i["vaddr"] for i in self.r2.cmdj("irj")}) self.sym.plt = DotMap({i["name"]:i["plt"] for i in self.r2.cmdj("iij")}) log.debug("GOT {} entries".format(len(self.sym.got)), "\n".join(["{:32}0x{:016x}".format(r[0], r[1]) for r in self.sym.got.items()])) log.debug("PLT {} entries".format(len(self.sym.plt)), "\n".join(["{:32}0x{:016x}".format(r[0], r[1]) for r in self.sym.plt.items()]))
def recvuntil(self, delim): data = self.recv(len(delim), nolog=True) while data[-len(delim):] != delim: data += self.recv(1, nolog=True) log.debug("Received {} bytes\n{}".format(green(len(data)), hexdump(data))) return data
def recvall(self): data = b"" try: while True: data += self.recv(1, nolog=True) except: log.debug("Received {} bytes\n{}".format(green(len(data)), hexdump(data))) return data
def payload(self, startlen=0, startitem=1): # assumptions: only writes, no overlapping writes, width are already ok # todo: reduce assumptions by handling more general cases e.g. big startlen # handle not multiple of 4/8 # addresses with null byte at the end of payload (max 1) or polymorphic payload # generalize different bits support writes = list(sorted(self.writes, key=lambda x: x.what)) dsts = list(sorted(set(map(lambda x: x.where, writes)), reverse=1)) n = startlen i = startitem pay = b"" positions = {} tmp = [] for w in writes: pad = w.what - n bits = 64 while pad > bits / 8 and len( dsts) > 0: # maybe add here check on null byte d = dsts.pop() positions[d] = i i += 1 pay += p64(d).replace(b"{", b"\{").replace(b"}", b"\}") pad -= int(bits / 8) if pad < 4: pay += b"A" * pad else: pay += bytes([ord(x) for x in "%{}c".format(pad)]) pay += b"%{}$" + bytes([ord(x) for x in "{}".format(w.width)]) + b"n" tmp.append(w.where) n = w.what # pad to multiple of address size, taking into account eventual starting slide, update i for d in dsts: pay += p64(d).replace(b"{", b"\{").replace(b"}", b"\}") positions[d] = i i += 1 tmp = list(map(lambda x: positions[x], tmp)) tmp2 = "" for b in pay: tmp2 += chr(b) pay = bytes([ord(x) for x in tmp2.format(*tmp) ]) # fix addresses containing '{', '}' log.debug("Format string payload", pay) return pay
def ret2csu_gadgets(self): ins = list(reversed(self.r2.cmdj("af@sym.__libc_csu_init; pdfj@sym.__libc_csu_init;")["ops"])) assert(ins[0]["type"] == "ret") tmp = [ins[0]] for i in range(1, len(ins)): if ins[i]["type"] != "pop": break tmp.insert(0, ins[i]) log.debug("ret2csu pops gadget", "\n".join(map(lambda x: "0x{:08x}: {}".format(x["offset"], x["disasm"]), tmp))) while ins[i]["type"] != "ucall": i += 1 tmp2 = [ins[i]] while i < len(ins): i += 1 if ins[i]["type"] != "mov": break tmp2.insert(0, ins[i]) log.debug("ret2csu mov&call gadget", "\n".join(map(lambda x: "0x{:08x}: {}".format(x["offset"], x["disasm"]), tmp2))) return tmp[0]["offset"], tmp2[0]["offset"]
def send(self, data): self.s.sendall(data) log.debug("Sent {} bytes\n{}".format(green(len(data)), hexdump(data)))
def recv(self, size, nolog=False): data = self.s.recv(size) if not nolog: log.debug("Received {} bytes\n{}".format(green(len(data)), hexdump(data))) return data
def recvline(self): data = self.p.stdout.readline() log.debug("Received {} bytes\n{}".format(green(len(data)), hexdump(data))) return data
def send(self, data): self.p.stdin.write(data) log.debug("Sent {} bytes\n{}".format(green(len(data)), hexdump(data))) self.p.stdin.flush()