Ejemplo n.º 1
0
def CSTtoMEM_write(arg1, cst, constraint, assertion, n=1, clmax=LMAX):
    """
    reg <- cst 
    mem(arg2) <- reg
    """
    if (clmax <= 0):
        return []

    res = []
    addr_reg = arg1[0]
    addr_cst = arg1[1]
    # 1. First strategy (direct)
    # reg <- cst
    # mem(arg1) <- reg
    for reg in range(0, Arch.ssaRegCount):
        if (reg == Arch.ipNum() or reg == Arch.spNum() or reg == addr_reg):
            continue
        # Find reg <- cst
        # maxdepth 3 or it's too slow
        cst_to_reg_chains = search(QueryType.CSTtoREG,
                                   reg,
                                   cst,
                                   constraint.add(RegsNotModified([addr_reg])),
                                   assertion,
                                   n,
                                   clmax - 1,
                                   maxdepth=3)
        if (not cst_to_reg_chains):
            continue
        # Search for mem(arg1) <- reg
        # We get all reg2,cst2 s.t mem(arg1) <- reg2+cst2
        possible_mem_writes = DBPossibleMemWrites(addr_reg,
                                                  addr_cst,
                                                  constraint,
                                                  assertion,
                                                  n=1)
        # 1.A. Ideally we look for reg2=reg and cst2=0 (direct_writes)
        possible_mem_writes_reg = possible_mem_writes.get(reg)
        if (possible_mem_writes_reg):
            direct_writes = possible_mem_writes[reg].get(0, [])
        else:
            direct_writes = []
        padding = constraint.getValidPadding(Arch.octets())
        for write_gadget in direct_writes:
            for cst_to_reg_chain in cst_to_reg_chains:
                # Pad the gadgets
                write_chain = ROPChain([write_gadget])
                for i in range(0, write_gadget.spInc - Arch.octets(),
                               Arch.octets()):
                    write_chain.addPadding(padding)
                full_chain = cst_to_reg_chain.addChain(write_chain, new=True)
                if (len(full_chain) <= clmax):
                    res.append(full_chain)
                if (len(res) >= n):
                    return res
        # 1.B.
    return res
Ejemplo n.º 2
0
def build_call_linux86(funcName, funcArgs, constraint, assertion, clmax=None, optimizeLen=False):
    # Find the address of the fonction 
    (funcName2, funcAddr) = getFunctionAddress(funcName)
    if( funcName2 is None ):
        return "Couldn't find function '{}' in the binary".format(funcName)
    
    # Check if bad bytes in function address 
    if( not constraint.badBytes.verifyAddress(funcAddr) ):
        return "'{}' address ({}) contains bad bytes".format(funcName2, string_special('0x'+format(funcAddr, '0'+str(Arch.octets()*2)+'x')))
    
    # Check if lmax too small
    if( (1 + len(funcArgs) + (lambda x: 1 if len(x)>0 else 0)(funcArgs)) > clmax ):
        return "Not enough bytes to call function '{}'".format(funcName)
    
    # Find a gadget for the fake return address
    if( funcArgs ):
        offset = (len(funcArgs)-1)*Arch.octets() # Because we do +octets() at the beginning of the loop
        skip_args_chains = []
        i = 4 # Try 4 more maximum 
        while( i > 0 and (not skip_args_chains)):
            offset += Arch.octets() 
            skip_args_chains = search(QueryType.MEMtoREG, Arch.ipNum(), \
                        (Arch.spNum(),offset), constraint, assertion, n=1, optimizeLen=optimizeLen)
            i -= 1
            
        if( not skip_args_chains ):
            return "Couldn't build ROP-Chain"
        skip_args_chain = skip_args_chains[0]
    else:
        # No arguments 
        skip_args_chain = None
    
    # Build the ropchain with the arguments 
    args_chain = ROPChain()
    arg_n = len(funcArgs)
    for arg in reversed(funcArgs):
        if( isinstance(arg, int) ):
            args_chain.addPadding(arg, comment="Arg{}: {}".format(arg_n, string_ropg(hex(arg))))
            arg_n -= 1
        else:
            return "Type of argument '{}' not supported yet :'(".format(arg)
    
    # Build call chain (function address + fake return address)
    call_chain = ROPChain()
    call_chain.addPadding(funcAddr, comment=string_ropg(funcName2))
    if( funcArgs ):
        skip_args_addr = int( validAddrStr(skip_args_chain.chain[0], constraint.getBadBytes(), Arch.bits())  ,16)
        call_chain.addPadding(skip_args_addr, comment="Address of: "+string_bold(str(skip_args_chain.chain[0])))
    
    return call_chain.addChain(args_chain)
Ejemplo n.º 3
0
 def verify(self, gadget):
     if( self.ret == self.jmp == self.call == False ):
         return (True, [])
     elif( self.ret and gadget.retType == RetType.RET ):
         return (True, [])
     elif( self.jmp and gadget.retType == RetType.JMP ):
         return (True, [])
     elif( self.call and gadget.retType == RetType.CALL ):
         return (True, [])
         
     # If unknown ret, check if ret possible
     # Is a ret sometimes possible ? 
     if( self.ret ):
         for p in gadget.getSemantics(Arch.ipNum()):
             if( isinstance(p.expr, MEMExpr)):
                 addr = p.expr.addr
                 (isInc, inc) = addr.isRegIncrement(Arch.spNum())    
                 # Normal ret if the final value of the IP is value that was in memory before the last modification of SP ( i.e final_IP = MEM[final_sp - size_of_a_register )        
                 if( isInc and inc == (gadget.spInc - (Arch.octets())) ):
                     return (True, [p.cond])
     # Or a jump ? 
     if( self.jmp ):
         for p in gadget.getSemantics(Arch.ipNum()):
             if( isinstance(p.expr, SSAExpr )):
                 return (True, [p.cond])
     return (False, [])
Ejemplo n.º 4
0
 def verify(self, gadget):
     for addr in gadget.addrList:
         addrBytes = re.findall('..',format(addr, '0'+str(Arch.octets()*2)+'x'))
         ok = True
         for byte in self.bytes:
             if( byte in addrBytes):
                 ok = False
                 break
         # No bad bytes found, so valid address
         if( ok ):
             return (True, []) 
     return (False, [])
Ejemplo n.º 5
0
def find_best_valid_writes(addr, string, constraint, limit=None):
    """
    When using the write strategy, can have bad bytes in addresses too... 
    Try adjust it
    """
    def string_into_reg(string):
        bytes_list = [b for b in string]
        # Get base value
        if (Arch.octets() != len(bytes_list)):
            value = constraint.getValidPadding(Arch.octets() - len(bytes_list))
            if (value is None):
                return None
        else:
            value = 0

        if (Arch.isLittleEndian()):
            tmp = 0
            for byte in reversed(bytes_list):
                value = (value << 8) + ord(byte)
            return value
        elif (Arch.isBigEndian()):
            tmp = 0
            for byte in bytes_list:
                tmp = (tmp << 8) + byte
            return (tmp << (8 * len(bytes_list))) + value
        else:
            return None

    res = []
    tmp_addr = addr
    if (not limit):
        limit = addr + len(string) + 10
    while (tmp_addr + len(string) <= limit):
        res = []
        fail = False
        offset = 0
        while (not fail and offset < len(string)):
            # Get the next write address
            ok = False
            for i in reversed(range(1, Arch.octets() + 1)):
                if (constraint.badBytes.verifyAddress(tmp_addr + offset + i)):
                    ok = True
                    break
            if (not ok):
                fail = True
                break
            else:
                value = string_into_reg(string[offset:i + offset])
                res.append((tmp_addr + offset, value))
                offset += i
        if (not fail):
            return res
    return None
Ejemplo n.º 6
0
    def string_into_reg(string):
        bytes_list = [b for b in string]
        # Get base value
        if (Arch.octets() != len(bytes_list)):
            value = constraint.getValidPadding(Arch.octets() - len(bytes_list))
            if (value is None):
                return None
        else:
            value = 0

        if (Arch.isLittleEndian()):
            tmp = 0
            for byte in reversed(bytes_list):
                value = (value << 8) + ord(byte)
            return value
        elif (Arch.isBigEndian()):
            tmp = 0
            for byte in bytes_list:
                tmp = (tmp << 8) + byte
            return (tmp << (8 * len(bytes_list))) + value
        else:
            return None
Ejemplo n.º 7
0
def build_call(funcName, funcArgs, constraint, assertion):
    # Find the address of the fonction
    (funcName2, funcAddr) = getFunctionAddress(funcName)
    if (funcName2 is None):
        return "Couldn't find function '{}' in the binary".format(funcName)

    # Check if bad bytes in function address
    if (not constraint.badBytes.verifyAddress(funcAddr)):
        return "'{}' address ({}) contains bad bytes".format(
            funcName2,
            string_special('0x' + format(funcAddr, '0' +
                                         str(Arch.octets() * 2) + 'x')))

    # Find a gadget for the fake return address
    offset = len(
        funcArgs) * 8 - 8  # Because we do +8 at the beginning of the loop
    skip_args_chains = []
    i = 4
    while (i > 0 and (not skip_args_chains)):
        offset += 8
        skip_args_chains = search(QueryType.MEMtoREG, Arch.ipNum(), \
                    (Arch.spNum(),offset), constraint, assertion, n=1)
        i -= 1

    if (not skip_args_chains):
        return "Couldn't build ROP-Chain"
    skip_args_chain = skip_args_chains[0]

    # Build the ropchain with the arguments
    args_chain = ROPChain()
    arg_n = len(funcArgs)
    for arg in reversed(funcArgs):
        if (isinstance(arg, int)):
            args_chain.addPadding(arg,
                                  comment="Arg{}: {}".format(
                                      arg_n, string_ropg(hex(arg))))
            arg_n -= 1
        else:
            return "Type of argument '{}' not supported yet :'(".format(arg)

    # Build call chain (function address + fake return address)
    call_chain = ROPChain()
    call_chain.addPadding(funcAddr, comment=string_ropg(funcName2))
    skip_args_addr = int(
        validAddrStr(skip_args_chain.chain[0], constraint.getBadBytes(),
                     Arch.bits()), 16)
    call_chain.addPadding(skip_args_addr,
                          comment="Address of: " +
                          string_bold(str(skip_args_chain.chain[0])))

    return call_chain.addChain(args_chain)
Ejemplo n.º 8
0
def _CSTtoREG_pop(reg,
                  cst,
                  constraint,
                  assertion,
                  n=1,
                  clmax=LMAX,
                  comment=None):
    """
    Returns a payload that puts cst into register reg by poping it from the stack
    """
    # Test clmax
    if (clmax <= 0):
        return []
    # Test n
    if (n < 1):
        return []
    # Check if the cst is incompatible with the constraint
    if (not constraint.badBytes.verifyAddress(cst)):
        return []

    if (not comment):
        comment = "Constant: " + string_bold("0x{:x}".format(cst))

    # Direct pop from the stack
    res = []
    if (reg == Arch.ipNum()):
        constraint2 = constraint.remove([CstrTypeID.CHAINABLE])
    else:
        constraint2 = constraint.add(Chainable(ret=True))
    possible = DBPossiblePopOffsets(reg, constraint2, assertion)
    for offset in sorted(filter(lambda x: x >= 0, possible.keys())):
        # If offsets are too big to fit in the lmax just break
        if (offset > clmax * Arch.octets()):
            break
        # Get possible gadgets
        possible_gadgets = [g for g in possible[offset]\
            if g.spInc >= Arch.octets() \
            and g.spInc - Arch.octets() > offset \
            and (g.spInc/Arch.octets()-1) <= clmax] # Test if padding is too much for clmax
        # Pad the gadgets
        padding = constraint.getValidPadding(Arch.octets())
        for gadget in possible_gadgets:
            chain = ROPChain([gadget])
            for i in range(0, gadget.spInc - Arch.octets(), Arch.octets()):
                if (i == offset):
                    chain.addPadding(cst, comment)
                else:
                    chain.addPadding(padding)
            if (len(chain) <= clmax):
                res.append(chain)
            if (len(res) >= n):
                return res
    return res
Ejemplo n.º 9
0
def build_call_linux64(funcName,
                       funcArgs,
                       constraint,
                       assertion,
                       clmax=None,
                       optimizeLen=False):
    # Arguments registers
    # (Args should go in these registers for x64)
    argsRegsNames = ['rdi', 'rsi', 'rdx', 'rcx', 'r8', 'r9']
    argsRegs = [Arch.n2r(name) for name in argsRegsNames]
    # Find the address of the fonction
    (funcName2, funcAddr) = getFunctionAddress(funcName)
    if (funcName2 is None):
        return "Couldn't find function '{}' in the binary".format(funcName)

    # Check if bad bytes in function address
    if (not constraint.badBytes.verifyAddress(funcAddr)):
        return "'{}' address ({}) contains bad bytes".format(
            funcName2,
            string_special('0x' + format(funcAddr, '0' +
                                         str(Arch.octets() * 2) + 'x')))

    # Check how many arguments
    if (len(funcArgs) > 6):
        return "Doesn't support function call with more than 6 arguments with Linux X64 calling convention :("

    # Find a gadget for the fake return address
    if (funcArgs):
        # Build the ropchain with the arguments
        args_chain = popMultiple(map(lambda x, y: (x, ) + y,
                                     argsRegs[:len(funcArgs)], funcArgs),
                                 constraint,
                                 assertion,
                                 clmax=clmax,
                                 optimizeLen=optimizeLen)
        if (not args_chain):
            return "Couldn't load arguments in registers"
    else:
        # No arguments
        args_chain = ROPChain()

    # Build call chain (function address + fake return address)
    return args_chain.addPadding(funcAddr, comment=string_ropg(funcName2))
Ejemplo n.º 10
0
def build_dshell(shellcode, constraint, assertion, address, limit, lmax):
    """
    Returns a PwnChain() instance or None
    """
    # Build exploit
    #################

    res = PwnChain()

    #Find address for the payload
    if (not address):
        # Get the .bss address
        # TODO
        notify("Getting delivery address for shellcode")
        address = getSectionAddress('.bss')
        addr_str = ".bss"
        if (not address):
            verbose("Couldn't find .bss address")
            return []
    else:
        addr_str = hex(address)

    if (not limit):
        limit = address + Arch.minPageSize()

    # Deliver shellcode
    notify("Building chain to copy shellcode in memory")
    verbose("{}/{} bytes available".format(lmax * Arch.octets(),
                                           lmax * Arch.octets()))
    (shellcode_address, STRtoMEM_chain) = STRtoMEM(shellcode,
                                                   address,
                                                   constraint,
                                                   assertion,
                                                   limit=limit,
                                                   lmax=lmax,
                                                   addr_str=addr_str,
                                                   hex_info=True,
                                                   optimizeLen=True)
    address = shellcode_address
    addr_str = hex(address)
    if (not STRtoMEM_chain):
        verbose("Could not copy shellcode into memory")
        return None

    # Building mprotect
    notify("Building mprotect() chain")
    # Getting page to make executable
    # Arg of mprotect MUST be a valid multiple of page size
    over_page_size = address % Arch.minPageSize()
    page_address = address - over_page_size
    length = len(shellcode) + 1 + over_page_size
    flag = 7
    lmax2 = lmax - len(STRtoMEM_chain)
    verbose("{}/{} bytes available".format(lmax2 * Arch.octets(),
                                           lmax * Arch.octets()))
    if (lmax2 <= 0):
        return None
    if (Arch.currentArch == Arch.ArchX86):
        mprotect_chain = build_mprotect32(page_address,
                                          length,
                                          flag,
                                          constraint.add(Chainable(ret=True)),
                                          assertion,
                                          clmax=lmax2 - 2,
                                          optimizeLen=True)
    elif (Arch.currentArch == Arch.ArchX64):
        mprotect_chain = build_mprotect64(page_address,
                                          length,
                                          flag,
                                          constraint.add(Chainable(ret=True)),
                                          assertion,
                                          clmax=lmax2 - 2,
                                          optimizeLen=True)
    else:
        mprotect_chain = None
        verbose("mprotect call not supported for architecture {}".format(
            Arch.currentArch.name))
        return None
    if (not mprotect_chain):
        return None
    verbose("Done")

    # Jump to shellcode
    notify("Searching chain to jump to shellcode")
    verbose("{}/{} bytes available".format(
        (lmax2 - len(mprotect_chain)) * Arch.octets(), lmax * Arch.octets()))
    jmp_shellcode_chains = search(QueryType.CSTtoREG,
                                  Arch.ipNum(),
                                  address,
                                  constraint,
                                  assertion,
                                  clmax=lmax - len(STRtoMEM_chain) -
                                  len(mprotect_chain),
                                  optimizeLen=True)
    if (not jmp_shellcode_chains):
        verbose("Couldn't find a jump to the shellcode")
        return None
    verbose("Done")
    notify("Done")

    # Build PwnChain res and return
    res.add(mprotect_chain,
            "Call mprotect({},{},{})".format(hex(page_address), length, flag))
    res.add(STRtoMEM_chain, "Copy shellcode to {}".format(addr_str))
    res.add(jmp_shellcode_chains[0],
            "Jump to shellcode (address {})".format(addr_str))
    return res
Ejemplo n.º 11
0
def _adjust_ret(qtype, arg1, arg2, env, n):
    """
    Search with basic but adjust the bad returns they have 
    """
    global LMAX
    ID = StrategyType.ADJUST_RET

    ## Test for special cases
    # Test lmax
    if (env.getLmax() <= 0):
        return FailRecord(lmax=True)
    # Limit number of calls to ...
    elif (env.nbCalls(ID) >= 2):
        return FailRecord()
    # Test for ip
    # Reason: can not adjust ip if ip is the
    # target of the query :/
    elif (arg1 == Arch.ipNum()):
        return FailRecord()

    # Set env
    env.addCall(ID)
    saved_adjust_ret = env.getImpossible_adjust_ret().copy()

    ########################################
    res = []
    res_fail = FailRecord()
    padding = env.getConstraint().getValidPadding(Arch.octets())
    # Get possible gadgets
    constraint = env.getConstraint()
    env.setConstraint(constraint.add(Chainable(jmp=True, call=True)))
    possible = _basic(qtype, arg1, arg2, env, 10 * n)
    env.setConstraint(constraint)

    if (not possible):
        res_fail.merge(possible)
        possible = []

    # Try to adjust them
    for chain in possible:
        g = chain.chain[0]
        ret_reg = g.retValue.reg.num
        # Check if we already know that ret_reg can't be adjusted
        if (env.checkImpossible_adjust_ret(ret_reg)):
            continue
        #Check if ret_reg not modified within the gadget
        elif (ret_reg in g.modifiedRegs()):
            continue
        # Check if stack is preserved
        elif (g.spInc is None):
            continue

        # Find adjustment
        if (g.spInc < 0):
            offset = -1 * g.spInc
            padding_length = 0
        else:
            padding_length = g.spInc / Arch.octets()
            if (g.retType == RetType.JMP):
                offset = 0
            else:
                offset = Arch.octets()
        if (isinstance(arg1, int)):
            arg1_reg = arg1
        else:
            arg1_reg = arg1[0]

        # Get adjustment gadgets
        env.setConstraint(constraint.add(RegsNotModified([arg1_reg])))
        saved_lmax = env.getLmax()
        env.setLmax(LMAX)
        adjust_gadgets = _search(QueryType.MEMtoREG, Arch.ipNum(), \
                (Arch.spNum(),offset), env, n=1)
        env.setConstraint(constraint)
        env.setLmax(saved_lmax)

        if (not adjust_gadgets):
            res_fail.merge(adjust_gadgets)
            continue
        else:
            adjust_addr = int(validAddrStr(adjust_gadgets[0].chain[0],\
                    constraint.getBadBytes(), Arch.bits()),  16)

        # Find gadgets to put the gadget address in the jmp/call register
        if (isinstance(arg2, int)):
            arg2_reg = arg2
        else:
            arg2_reg = arg2[0]

        env.setConstraint(constraint.add(RegsNotModified([arg2_reg])))
        env.subLmax(1 + padding_length)
        env.pushComment(
            StrategyType.CSTtoREG_POP,
            "Address of " + string_bold(str(adjust_gadgets[0].chain[0])))
        adjust = _search(QueryType.CSTtoREG, ret_reg, adjust_addr, env, n=1)
        env.popComment(StrategyType.CSTtoREG_POP)
        env.addLmax(1 + padding_length)
        env.setConstraint(constraint)
        if (adjust):
            res.append(adjust[0].addGadget(g).addPadding(padding,
                                                         n=padding_length))
            if (len(res) >= n):
                break
        else:
            # Update the search record to say that reg_ret cannot be adjusted
            env.addImpossible_adjust_ret(ret_reg)
            res_fail.merge(adjust)
    ########################################
    # Restore env
    env.impossible_adjust_ret = saved_adjust_ret
    env.removeCall(ID)
    return res if res else res_fail
Ejemplo n.º 12
0
def _CSTtoMEM_write(arg1, cst, env, n=1):
    """
    reg <- cst 
    mem(arg2) <- reg
    """
    ID = StrategyType.CSTtoMEM_WRITE

    ## Test for special cases
    # Test lmax
    if (env.getLmax() <= 0):
        return FailRecord(lmax=True)
    # Limit number of calls to ...
    elif (env.nbCalls(ID) >= 99):
        return FailRecord()

    # Set env
    env.addCall(ID)
    ######################################
    res = []
    res_fail
    addr_reg = arg1[0]
    addr_cst = arg1[1]
    # 1. First strategy (direct)
    # reg <- cst
    # mem(arg1) <- reg
    for reg in Arch.registers():
        if (reg == Arch.ipNum() or reg == Arch.spNum() or reg == addr_reg):
            continue
        # Find reg <- cst
        constraint = env.getConstraint()
        env.setConstraint(constraint.add(RegsNotModified([addr_reg])))
        env.subLmax(1)
        cst_to_reg_chains = _search(QueryType.CSTtoREG, reg, cst, env, n)
        env.addLmax(1)
        env.setConstraint(constraint)
        if (not cst_to_reg_chains):
            res_fail.merge(cst_to_reg_chains)
            continue
        # Search for mem(arg1) <- reg
        # We get all reg2,cst2 s.t mem(arg1) <- reg2+cst2
        possible_mem_writes = DBPossibleMemWrites(addr_reg,
                                                  addr_cst,
                                                  env.getConstraint(),
                                                  env.getAssertion(),
                                                  n=1)
        # 1.A. Ideally we look for reg2=reg and cst2=0 (direct_writes)
        possible_mem_writes_reg = possible_mem_writes.get(reg)
        if (possible_mem_writes_reg):
            direct_writes = possible_mem_writes[reg].get(0, [])
        else:
            direct_writes = []
        padding = constraint.getValidPadding(Arch.octets())
        for write_gadget in direct_writes:
            for cst_to_reg_chain in cst_to_reg_chains:
                # Pad the gadgets
                write_chain = ROPChain([write_gadget])
                for i in range(0, write_gadget.spInc - Arch.octets(),
                               Arch.octets()):
                    write_chain.addPadding(padding)
                full_chain = cst_to_reg_chain.addChain(write_chain, new=True)
                if (len(full_chain) <= env.getLmax()):
                    res.append(full_chain)
                if (len(res) >= n):
                    break
            if (len(res) >= n):
                break
        if (len(res) >= n):
            break
        # 1.B. Otherwise we try to adjust the cst2
        # To be implemented

    # 2d Strategy: indirect
    # reg <- arg2 - cst
    # mem(arg1) <- reg + cst

    # TO IMPLEMENT IN ANOTHER FUNCTION !

    ###################
    # Restore env
    env.removeCall(ID)
    return res if res else res_fail
Ejemplo n.º 13
0
def _CSTtoREG_pop(reg, cst, env, n=1):
    """
    Returns a payload that puts cst into register reg by poping it from the stack
    """
    ID = StrategyType.CSTtoREG_POP

    ## Test for special cases
    # Test lmax
    if (env.getLmax() <= 0):
        return FailRecord(lmax=True)
    # Limit number of calls to ...
    elif (env.nbCalls(ID) >= 99):
        return FailRecord()
    # Check if the cst is in badBytes
    elif (not env.getConstraint().badBytes.verifyAddress(cst)):
        return FailRecord()

    # Set env
    env.addCall(ID)
    # Get comment
    if (env.hasComment(ID)):
        envHadComment = True
        comment = env.popComment(ID)
    else:
        envHadComment = False
        comment = "Constant: " + string_bold("0x{:x}".format(cst))

    ########################
    # Direct pop from the stack
    res = []
    res_fail = FailRecord()
    # Adapt constraint if ip <- cst
    if (reg != Arch.ipNum()):
        constraint2 = env.getConstraint().add(Chainable(ret=True))
    else:
        constraint2 = env.getConstraint()

    possible = DBPossiblePopOffsets(reg, constraint2, env.getAssertion())
    for offset in sorted(filter(lambda x: x >= 0, possible.keys())):
        # If offsets are too big to fit in the lmax just break
        if (offset > env.getLmax() * Arch.octets()):
            break
        # Get possible gadgets
        possible_gadgets = [g for g in possible[offset]\
            if g.spInc >= Arch.octets() \
            and g.spInc - Arch.octets() > offset \
            and (g.spInc/Arch.octets()-1) <= env.getLmax()] # Test if padding is too much for clmax
        # Pad the gadgets
        padding = env.getConstraint().getValidPadding(Arch.octets())
        for gadget in possible_gadgets:
            chain = ROPChain([gadget])
            for i in range(0, gadget.spInc - Arch.octets(), Arch.octets()):
                if (i == offset):
                    chain.addPadding(cst, comment)
                else:
                    chain.addPadding(padding)
            if (len(chain) <= env.getLmax()):
                res.append(chain)
            if (len(res) >= n):
                break
        if (len(res) >= n):
            break
    #########################

    # Restore env
    env.removeCall(ID)
    if (envHadComment):
        env.pushComment(ID, comment)

    return res if res else res_fail
Ejemplo n.º 14
0
def _basic(qtype, arg1, arg2, env, n=1, enablePreConds=False):
    """
    Search for gadgets basic method ( without chaining ) 
    Direct Database check  
    """
    if (env.getLmax() <= 0):
        return FailRecord(lmax=True)

    if (env.getNoPadding()):
        maxSpInc = None
    else:
        maxSpInc = env.getLmax() * Arch.octets()

    # Check for special gadgets
    if (qtype == QueryType.INT80 or qtype == QueryType.SYSCALL):
        gadgets = DBSearch(qtype,
                           arg1,
                           arg2,
                           env.getConstraint(),
                           env.getAssertion(),
                           n=n,
                           maxSpInc=maxSpInc)
        res = [ROPChain().addGadget(g) for g in gadgets]
        return res

    # Check if the type is IP <- ...
    # In this case we remove the CHAINABLE constraint which makes no sense
    if (arg1 == Arch.ipNum()):
        constraint2 = env.getConstraint().remove([CstrTypeID.CHAINABLE])
    else:
        constraint2 = env.getConstraint()

    # Check to add assertions when looking for Memory gadgets
    if (qtype == QueryType.CSTtoMEM or qtype == QueryType.REGtoMEM):
        assertion2 = env.getAssertion().add(
            RegsNoOverlap([(arg1[0], Arch.spNum())]))
    else:
        assertion2 = env.getAssertion()

    # Regular gadgets
    # maxSpInc -> +1 because we don't count the ret but -1 because the gadget takes one place
    gadgets = DBSearch(qtype,
                       arg1,
                       arg2,
                       constraint2,
                       assertion2,
                       n,
                       enablePreConds=enablePreConds,
                       maxSpInc=maxSpInc)
    if (enablePreConds):
        return [(ROPChain().addGadget(g[0]), g[1]) for g in gadgets]
    elif (env.getNoPadding()):
        return [ROPChain().addGadget(g) for g in gadgets]
    else:
        res = []
        padding = constraint2.getValidPadding(Arch.octets())
        for g in gadgets:
            chain = ROPChain().addGadget(g)
            # Padding the chain if possible
            if (g.spInc > 0):
                for i in range(0, g.spInc / Arch.octets() - 1):
                    chain.addPadding(padding)
            # Adding to the result
            res.append(chain)
    if (len(res) == 0):
        return FailRecord()
    else:
        return res
Ejemplo n.º 15
0
def store_constant_address(qtype,
                           cst_addr,
                           value,
                           constraint=None,
                           assertion=None,
                           clmax=None,
                           optimizeLen=False):
    """
    Does a XXXtoMEM kind of query BUT the memory address is 
    a simple constant ! 
    
    Expected qtypes are only XXXtoMEM
    cst_addr is the store address
    value is the value to store, a single cst or a couple (reg,cst)
    """
    if (clmax is None):
        clmax = STORE_CONSTANT_ADDRESS_LMAX
    elif (clmax <= 0):
        return None

    if (constraint is None):
        constr = Constraint()
    else:
        constr = constraint
    if (assertion is None):
        a = Assertion()
    else:
        a = assertion

    # Tranform the query type
    if (qtype == QueryType.CSTtoMEM):
        qtype2 = QueryType.CSTtoREG
    elif (qtype == QueryType.REGtoMEM):
        qtype2 = QueryType.REGtoREG
    elif (qtype == QueryType.MEMtoREG):
        qtype2 = QueryType.MEMtoREG
    else:
        raise Exception(
            "Query type {} should not appear in this function!".format(qtype))

    tried_values = []
    tried_cst_addr = []
    best = None  # If optimizeLen
    shortest = clmax  # Shortest ROPChain found if optimizeLen ;)

    for ((addr_reg, addr_cst), (reg,cst), gadget) in \
    sorted(DBAllPossibleWrites(constr.add(Chainable(ret=True)), a), \
    key=lambda x: 0 if (x[1] == value) else 1) :
        # DOn't use rip or rsp...
        if( reg == Arch.ipNum() or reg == Arch.spNum()\
            or addr_reg == Arch.ipNum() or addr_reg == Arch.spNum()):
            continue
        res = None
        # Check if directly the registers we want to write ;)
        value_is_reg = False
        value_to_reg = []
        addr_to_reg = []
        if ((reg, cst) == value):
            value_to_reg = [ROPChain()]
            value_is_reg = True

        # adapt value
        if (not isinstance(value, tuple)):
            adjusted_value = value - cst
        else:
            adjusted_value = (value[0], value[1] - cst)
        adjusted_cst_addr = cst_addr - addr_cst
        # Get spInc
        gadget_paddingLen = (gadget.spInc / Arch.octets()) - 1
        # Check if tried before
        if ((reg, cst) in tried_values):
            continue
        elif ((addr_reg, addr_cst) in tried_cst_addr):
            continue
        ### Try to do reg first then addr_reg
        # Try to put the value into reg
        clmax2 = shortest - gadget_paddingLen - 1
        if (not value_is_reg):
            value_to_reg = search(qtype2,
                                  reg,
                                  adjusted_value,
                                  constr,
                                  a,
                                  clmax=clmax2,
                                  n=1,
                                  optimizeLen=optimizeLen)
            if (not value_to_reg):
                tried_values.append((reg, cst))
                continue
            else:
                clmax2 = clmax2 - len(value_to_reg[0])
        # Try to put the cst_addr in addr_reg
        addr_to_reg = search(QueryType.CSTtoREG,
                             addr_reg,
                             adjusted_cst_addr,
                             constr.add(RegsNotModified([reg])),
                             a,
                             clmax=clmax2,
                             n=1,
                             optimizeLen=optimizeLen)
        if (addr_to_reg):
            # If we found a solution
            # Combine them and return
            # Padd the gadget
            res = value_to_reg[0].addChain(addr_to_reg[0]).addGadget(gadget)
            if (gadget.spInc > 0):
                padding_value = constr.getValidPadding(Arch.octets())
                res = res.addPadding(padding_value,
                                     n=(gadget.spInc / Arch.octets()) - 1)
            if (optimizeLen):
                if (best):
                    best = min(best, res)
                else:
                    best = res
                shortest = len(best)
            else:
                return res

        ### Try to do addr_reg first and then reg
        clmax2 = shortest - gadget_paddingLen - 1
        # Try to put the cst_addr in addr_reg
        addr_to_reg = search(QueryType.CSTtoREG,
                             addr_reg,
                             adjusted_cst_addr,
                             constr,
                             a,
                             clmax=clmax2,
                             n=1,
                             optimizeLen=optimizeLen)
        if (not addr_to_reg):
            tried_cst_addr.append((addr_reg, addr_cst))
            continue
        else:
            clmax2 = clmax2 - len(addr_to_reg[0])
        # Try to put the value into reg
        if (not value_is_reg):
            value_to_reg = search(qtype2,
                                  reg,
                                  adjusted_value,
                                  constr.add(RegsNotModified([addr_reg])),
                                  a,
                                  clmax=clmax2,
                                  n=1,
                                  optimizeLen=optimizeLen)
        if (value_to_reg):
            # If we found a solution
            # Combine them and return
            # Padd the gadget
            res = addr_to_reg[0].addChain(value_to_reg[0]).addGadget(gadget)
            if (gadget.spInc > 0):
                padding_value = constr.getValidPadding(Arch.octets())
                res = res.addPadding(padding_value,
                                     n=(gadget.spInc / Arch.octets()) - 1)
            if (optimizeLen):
                if (best):
                    best = min(best, res)
                else:
                    best = res
                shortest = len(best)
            else:
                return res

        # 5 = two pops for addr_reg and reg + 1 for the write gadget
        # So since 5 is the shortest possible with two pops we can return
        # We can have < 5 if reg is already equal to 'value' argument
        # But we try this case first (see sorted()) when getting possibleWrites ;)
        if (((not optimizeLen) or (not value_is_reg)) and (not best is None)
                and len(best) <= 5):
            return best
        elif (optimizeLen and (not best is None) and len(best) <= 3):
            return best
    return best
Ejemplo n.º 16
0
def call(args):
    global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON
    # Parsing arguments
    if( not args):
        print_help()
        return
    OUTPUT = OUTPUT_CONSOLE
    funcName = None
    i = 0
    seenOutput = False
    seenFunction = False
    seenBadBytes = False
    seenKeepRegs = False
    seenShortest = False
    seenLmax = False
    seenOffset = False
    clmax = None
    offset = 0 
    
    constraint = Constraint()
    assertion = getBaseAssertion()
    while i < len(args):
        if( args[i] in [OPTION_LIST, OPTION_LIST_SHORT]):
            func_list = getAllFunctions()
            print_functions(func_list)
            return 
        if( args[i] in [OPTION_BAD_BYTES, OPTION_BAD_BYTES_SHORT]):
            if( seenBadBytes ):
                error("Error. '" + args[i] + "' option should be used only once")
                return 
            if( i+1 >= len(args)):
                error("Error. Missing bad bytes after option '"+args[i]+"'")
                return 
            seenBadBytes = True
            (success, res) = parse_bad_bytes(args[i+1])
            if( not success ):
                error(res)
                return
            i = i+2
            constraint = constraint.add(BadBytes(res))
        elif( args[i] in [OPTION_KEEP_REGS, OPTION_KEEP_REGS_SHORT]):
            if( seenKeepRegs ):
                error("Error. '" + args[i] + "' option should be used only once")
                return 
            if( i+1 >= len(args)):
                error("Error. Missing register after option '"+args[i]+"'")
                return 
            seenKeepRegs = True
            (success, res) = parse_keep_regs(args[i+1])
            if( not success ):
                error(res)
                return
            i = i+2
            constraint = constraint.add(RegsNotModified(res))
        elif( args[i] in [OPTION_CALL, OPTION_CALL_SHORT] ):
            if( not loadedBinary() ):
                error("Error. You should load a binary before building ROPChains")
                return 
            elif( seenFunction ):
                error("Option '{}' should be used only once".format(args[i]))
                return  
            userInput = ''
            i +=1
            while( i < len(args) and args[i][0] != "-"):
                userInput += args[i]
                i += 1
            (funcName, funcArgs ) = parseFunction(userInput)
            if( not funcName):
                return 
            seenFunction = True
        elif( args[i] in [OPTION_OUTPUT, OPTION_OUTPUT_SHORT]):
            if( seenOutput ):
                error("Option '{}' should be used only once".format(args[i]))
                return 
            if( i+1 >= len(args)):
                error("Error. Missing output format after option '"+args[i]+"'")
                return 
            if( args[i+1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]):
                OUTPUT = args[i+1]
                seenOutput = True
                i += 2
            else:
                error("Error. Unknown output format: {}".format(args[i+1]))
                return 
        elif( args[i] in [OPTION_SHORTEST, OPTION_SHORTEST_SHORT]):
            if( seenShortest ):
                error("Option '{}' should be used only once".format(args[i]))
                return 
            seenShortest = True
            i += 1
        elif( args[i] == OPTION_LMAX or args[i] == OPTION_LMAX_SHORT ):
            if( seenLmax ):
                error("Option '{}' should be used only once".format(args[i]))
                return 
            if( i+1 >= len(args)):
                error("Error. Missing length after option '"+args[i]+"'")
                return 
            try:
                clmax = int(args[i+1])
                if( clmax < Arch.octets() ):
                    raise Exception()
                # Convert number of bytes into number of ropchain elements
                clmax /= Arch.octets()
            except:
                error("Error. '" + args[i+1] +"' bytes is not valid")
                return 
            i += 2 
            seenLmax = True
        elif( args[i] == OPTION_OFFSET or args[i] == OPTION_OFFSET_SHORT ):
            if( seenOffset ):
                error("Option '{}' should be used only once".format(args[i]))
                return 
            if( i+1 >= len(args)):
                error("Error. Missing offset after option '"+args[i]+"'")
                return 
            try:
                offset = int(args[i+1])
                if( offset < 0 ):
                    raise Exception()
            except:
                try:
                    offset = int(args[i+1], 16)
                    if( offset < 0 ):
                        raise Exception()
                except: 
                    error("Error. '" + args[i+1] +"' is not a valid offset")
                    return 
            i += 2 
            seenOffset = True
        elif( args[i] in [OPTION_HELP, OPTION_HELP_SHORT]):
            print_help()
            return 
        else:
            error("Error. Unknown option '{}'".format(args[i]))
            return 
    
    if( not funcName ):
        error("Missing function to call")
    else:
        # Set offset 
        if( not set_offset(offset) ):
            error("Error. Your offset is too big :'(")
            return 
        try:
            res = build_call(funcName, funcArgs, constraint, assertion, clmax=clmax, optimizeLen=seenShortest)
            if( isinstance(res, str) ):
                error(res)
            else:
                print_chains([res], "Built matching ROPChain", constraint.getBadBytes())
        except Exception, e:
            reset_offset()
            raise sys.exc_info()[1], None, sys.exc_info()[2]
        # Reset offset 
        reset_offset()
Ejemplo n.º 17
0
def pwn(args):
    global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON
    global EXPLOIT_LMAX

    if (not args):
        print_help()
        return

    # Check if gadgets have been loaded in the DB
    if (not Database.gadgets):
        error("Oops. You have to load gadgets before pwning ;) ")
        return

    # Set default output format
    OUTPUT = OUTPUT_CONSOLE
    clmax = EXPLOIT_LMAX

    seenOutput = False
    seenLmax = False
    seenBadBytes = False
    seenVerbose = False

    constraint = Constraint()
    assertion = Assertion()
    subcommand = None

    i = 0
    while i < len(args):
        # Parse options
        if (args[i][0] == '-'):
            if (args[i] in [OPTION_BAD_BYTES, OPTION_BAD_BYTES_SHORT]):
                if (seenBadBytes):
                    error("Error. '" + args[i] +
                          "' option should be used only once")
                    return
                if (i + 1 >= len(args)):
                    error("Error. Missing bad bytes after option '" + args[i] +
                          "'")
                    return
                seenBadBytes = True
                (success, res) = parse_bad_bytes(args[i + 1])
                if (not success):
                    error(res)
                    return
                i = i + 2
                constraint = constraint.add(BadBytes(res))
            elif (args[i] == OPTION_LMAX or args[i] == OPTION_LMAX_SHORT):
                if (seenLmax):
                    error("Error. '" + arg +
                          "' option should be used only once.")
                    return
                if (i + 1 >= len(args)):
                    error("Error. Missing output format after option '" + arg +
                          "'")
                    return
                try:
                    clmax = int(args[i + 1])
                    if (clmax < Arch.octets()):
                        raise Exception()
                    # Convert number of bytes into number of ropchain elements
                    clmax /= Arch.octets()
                except:
                    error("Error. '" + args[i + 1] + "' bytes is not valid")
                    return
                i = i + 2
                seenLmax = True

            elif (args[i] in [OPTION_OUTPUT, OPTION_OUTPUT_SHORT]):
                if (seenOutput):
                    error("Option '{}' should be used only once".format(
                        args[i]))
                    return
                if (i + 1 >= len(args)):
                    error("Error. Missing output format after option '" +
                          args[i] + "'")
                    return
                if (args[i + 1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]):
                    OUTPUT = args[i + 1]
                    seenOutput = True
                    i += 2
                else:
                    error("Error. Unknown output format: {}".format(args[i +
                                                                         1]))
                    return
            elif (args[i] in [OPTION_HELP, OPTION_HELP_SHORT]):
                print_help()
                return
            elif (args[i] in [OPTION_VERBOSE, OPTION_VERBOSE_SHORT]):
                seenVerbose = True
                i += 1
            else:
                error("Error. Unknown option '{}'".format(args[i]))
                return
        # Parse a subcommand
        else:
            verbose_mode(seenVerbose)
            payload = None
            if (args[i] == CMD_DELIVER_SHELLCODE):
                payload = dshell(args[i + 1:],
                                 constraint,
                                 assertion,
                                 lmax=clmax)
            else:
                error("Error. Unknown subcommand : '{}'".format(args[i]))
                verbose_mode(False)
                return

            verbose_mode(False)
            # Print result
            if (payload):
                print(
                    string_bold("\n\tBuilt exploit - {} bytes\n".format(
                        payload.len_bytes)))
                if (OUTPUT == OUTPUT_CONSOLE):
                    print(
                        payload.strConsole(Arch.bits(),
                                           constraint.getBadBytes()))
                elif (OUTPUT == OUTPUT_PYTHON):
                    print(
                        payload.strPython(Arch.bits(),
                                          constraint.getBadBytes()))
            else:
                error("Couldn't generate exploit")
            return

    # Test parsing result
    if (subcommand is None):
        error("Missing subcommand")
        return
Ejemplo n.º 18
0
def _adjust_ret(qtype,
                arg1,
                arg2,
                constraint,
                assertion,
                n,
                clmax=LMAX,
                record=None,
                comment=""):
    """
    Search with basic but adjust the bad returns they have 
    """
    # Test clmax
    if (clmax <= 0):
        return []

    # Test for ip
    if (arg1 == Arch.ipNum()):
        return []
    # Test for search record
    if (record is None):
        record = SearchRecord()

    res = []
    possible = _basic(qtype, arg1, arg2, \
            constraint.add(Chainable(jmp=True, call=True)), assertion, n)
    padding = constraint.getValidPadding(Arch.currentArch.octets)
    for chain in possible:
        g = chain.chain[0]
        ret_reg = g.retValue.reg.num
        # Check if we already know that ret_reg can't be adjusted
        if (record.impossible_AdjustRet.check(ret_reg)):
            continue
        #Check if ret_reg not modified within the gadget
        if (ret_reg in g.modifiedRegs()):
            continue
        # Check if stack is preserved
        if (g.spInc is None):
            continue

        # Find adjustment
        if (g.spInc < 0):
            offset = -1 * g.spInc
            padding_length = 0
        else:
            padding_length = g.spInc
            if (g.retType == RetType.JMP):
                offset = 0
            else:
                offset = Arch.octets()
        adjust_gadgets = search(QueryType.MEMtoREG, Arch.ipNum(), \
                (Arch.spNum(),offset), constraint.add(RegsNotModified([arg1])), assertion, n=1, record=record)
        if (not adjust_gadgets):
            continue
        else:
            adjust_addr = int(validAddrStr(adjust_gadgets[0].chain[0],\
                    constraint.getBadBytes(), Arch.bits()),  16)
        # Put the gadget address in the register
        adjust = search(QueryType.CSTtoREG, ret_reg, adjust_addr, \
            constraint.add(RegsNotModified([arg2[0]])), assertion, n=1, clmax=clmax-len(chain),record=record,\
            comment="Address of "+string_bold(str(adjust_gadgets[0].chain[0])))
        if (adjust):
            res.append(adjust[0].addGadget(g).addPadding(padding,
                                                         n=padding_length))
            if (len(res) >= n):
                return res
        else:
            # Update the search record to say that reg_ret cannot be adjusted
            record.impossible_AdjustRet.add(ret_reg)
    return res
Ejemplo n.º 19
0
def parse_args(args):
    """
    Parse the user supplied arguments to the 'find' function
    Returns either a tuple (True, GadgetType, x, y, constraint, nb_res, clmax )
    Or if not supported or invalid arguments, returns a tuple (False, msg)
    
    ---> See parse_user_request() specification for the list of possible tuples
         and values/types of x and y     
    """
    global OUTPUT

    seenExpr = False
    seenBadBytes = False
    seenKeepRegs = False
    seenOutput = False
    seenNbResults = False
    seenLmax = False

    i = 0  # Argument counter
    constraint = Constraint()
    nbResults = 1  # Default 1 result
    clmax = LMAX  # Max length
    OUTPUT = OUTPUT_CONSOLE
    while (i < len(args)):
        arg = args[i]
        # Look for options
        if (arg[0] == '-' and arg[1] != '>'):
            if (seenExpr):
                return (False,
                        "Error. Options must come before the search request")
            # bad bytes option
            if (arg == OPTION_BAD_BYTES or arg == OPTION_BAD_BYTES_SHORT):
                if (seenBadBytes):
                    return (False, "Error. '" + arg +
                            "' option should be used only once")
                if (i + 1 >= len(args)):
                    return (False, "Error. Missing bad bytes after option '" +
                            arg + "'")
                seenBadBytes = True
                (success, res) = parse_bad_bytes(args[i + 1])
                if (not success):
                    return (False, res)
                i = i + 1
                constraint = constraint.add(BadBytes(res))
            # Keep regs option
            elif (arg == OPTION_KEEP_REGS or arg == OPTION_KEEP_REGS_SHORT):
                if (seenKeepRegs):
                    return (False, "Error. '" + arg +
                            "' option should be used only once.")
                if (i + 1 >= len(args)):
                    return (False, "Error. Missing registers after option '" +
                            arg + "'")
                seenKeepRegs = True
                (success, res) = parse_keep_regs(args[i + 1])
                if (not success):
                    return (False, res)
                i = i + 1
                constraint = constraint.add(RegsNotModified(res))
            # Output option
            elif (arg == OPTION_OUTPUT or arg == OPTION_OUTPUT_SHORT):
                if (seenOutput):
                    return (False, "Error. '" + arg +
                            "' option should be used only once.")
                if (i + 1 >= len(args)):
                    return (False,
                            "Error. Missing output format after option '" +
                            arg + "'")
                if (args[i + 1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]):
                    OUTPUT = args[i + 1]
                    seenOutput = True
                    i = i + 1
                else:
                    return (False, "Error. '" + args[i + 1] +
                            "' output format is not supported")
            # Nb of results option
            elif (arg == OPTION_NB_RESULTS or arg == OPTION_NB_RESULTS_SHORT):
                if (seenNbResults):
                    return (False, "Error. '" + arg +
                            "' option should be used only once.")
                if (i + 1 >= len(args)):
                    return (False,
                            "Error. Missing output format after option '" +
                            arg + "'")
                try:
                    nbResults = int(args[i + 1])
                    if (nbResults <= 0):
                        raise Exception()
                except:
                    return (False, "Error. '" + args[i + 1] +
                            "' is not a valid number")
                i = i + 1
                seenNbResults = True
            elif (arg == OPTION_LMAX or arg == OPTION_LMAX_SHORT):
                if (seenLmax):
                    return (False, "Error. '" + arg +
                            "' option should be used only once.")
                if (i + 1 >= len(args)):
                    return (False,
                            "Error. Missing output format after option '" +
                            arg + "'")
                try:
                    clmax = int(args[i + 1])
                    if (clmax < Arch.octets()):
                        raise Exception()
                    # Convert number of bytes into number of ropchain elements
                    clmax /= Arch.octets()
                except:
                    return (False,
                            "Error. '" + args[i + 1] + "' bytes is not valid")
                i = i + 1
                seenLmax = True

            # Otherwise Ignore
            else:
                return (False, "Error. Unknown option: '{}' ".format(arg))
        # If not option it should be a request expr=expr
        else:
            if (seenExpr):
                return (False, "Error. Unexpected extra expression: '" +
                        ' '.join(args[i:]) + "'")
            else:
                seenExpr = True
                parsed_query = parse_query(''.join(args[i:]))
                if (not parsed_query[0]):
                    return (False, parsed_query[1])
                else:
                    i = len(args)
        i = i + 1
    if (not seenExpr):
        return (False, "Error. Missing specification of gadget to find")
    else:
        return parsed_query + (constraint, nbResults, clmax)
Ejemplo n.º 20
0
 def add(self, chain, descr):
     self.ROPChains.append(chain)
     self.info.append(descr)
     self.len_bytes += len(chain) * Arch.octets()
Ejemplo n.º 21
0
def STRtoMEM_memcpy(string,
                    addr,
                    constraint,
                    assertion,
                    lmax=STR_TO_MEM_LMAX,
                    addr_str=None,
                    hex_info=False):
    """
    MEMCPY STRATEGY
    Copy the string using memcpy function 
    """
    if (not addr_str):
        addr_str = "0x" + format(addr, '0' + str(Arch.octets() * 2) + 'x')

    # Getting strcpy function
    (func_name, func_addr) = getFunctionAddress('memcpy')
    if (not func_addr):
        verbose('Could not find memcpy function')
        return None
    elif (not constraint.badBytes.verifyAddress(func_addr)):
        verbose("memcpy address ({}) contains bad bytes".format(
            hex(func_addr)))
        return None

    # We decompose the string in substrings to be copied
    substrings_addr = findBytes(string, badBytes=constraint.getBadBytes())
    if (not substrings_addr):
        return None
    elif (len(substrings_addr) * 5 > lmax):
        verbose(
            "Memcpy: ROP-Chain too long (length: {}, available bytes: {}) ".
            format(
                len(substrings_addr) * 5 * Arch.octets(),
                lmax * Arch.octets()))
        return None

    # Get a pop-pop-pop-ret gadget
    pppr_chains = _basic(QueryType.MEMtoREG,
                         Arch.ipNum(), [Arch.spNum(), 3 * Arch.octets()],
                         constraint.add(
                             StackPointerIncrement(4 * Arch.octets())),
                         assertion,
                         clmax=1,
                         noPadding=True)
    if (not pppr_chains):
        verbose("Memcpy: Could not find suitable pop-pop-pop-ret gadget")
        return None
    pppr_gadget = pppr_chains[0].chain[0]  # Get the first gadget

    # Build chain
    res = ROPChain()
    offset = 0
    custom_stack = addr
    for (substring_addr, substring_str) in substrings_addr:
        if (hex_info):
            substring_info = "'" + '\\x' + '\\x'.join(
                ["%02x" % ord(c) for c in substring_str]) + "'"
        else:
            substring_info = "'" + substring_str + "'"

        res.addPadding(func_addr, comment=string_ropg(func_name))
        res.addGadget(pppr_gadget)
        res.addPadding(len(substring_str),
                       comment="Arg3: " + string_ropg(str(len(substring_str))))
        res.addPadding(substring_addr,
                       comment="Arg2: " + string_ropg(substring_info))
        res.addPadding(custom_stack,
                       comment="Arg1: " +
                       string_ropg("{} + {}".format(addr_str, offset)))

        # Adjust
        custom_stack = custom_stack + len(substring_str)
        offset = offset + len(substring_str)

    return res
Ejemplo n.º 22
0
def pwn(args):
    global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON
    global EXPLOIT_LMAX
    
    
    if( not args ):
        print_help()
        return 
        
    # Check if gadgets have been loaded in the DB 
    if( not Database.gadgets ):
        error("Oops. You have to load gadgets before pwning ;) ")
        return 
        
    # Set default output format 
    OUTPUT = OUTPUT_CONSOLE
    clmax = EXPLOIT_LMAX
    
    seenOutput = False
    seenLmax = False
    seenBadBytes = False
    seenVerbose = False
    seenOutfile = False
    seenPaddingByte = False
    seenPaddingLen = False

    constraint = Constraint()
    assertion = Assertion()
    subcommand = None
    outfile = None
    paddingByteStr = None 
    paddingLen = 0
    
    i = 0 
    while i < len(args):
        # Parse options     
        if( args[i][0] == '-' ):
            if( args[i] in [OPTION_BAD_BYTES, OPTION_BAD_BYTES_SHORT]):
                if( seenBadBytes ):
                    error("Error. '" + args[i] + "' option should be used only once")
                    return 
                if( i+1 >= len(args)):
                    error("Error. Missing bad bytes after option '"+args[i]+"'")
                    return 
                seenBadBytes = True
                (success, res) = parse_bad_bytes(args[i+1])
                if( not success ):
                    error(res)
                    return
                i = i+2
                constraint = constraint.add(BadBytes(res))
            elif( args[i] == OPTION_LMAX or args[i] == OPTION_LMAX_SHORT ):
                if( seenLmax ):
                    error("Error. '" + arg + "' option should be used only once.")
                    return 
                if( i+1 >= len(args)):
                    error("Error. Missing output format after option '"+arg+"'")
                    return 
                try:
                    clmax = int(args[i+1])
                    if( clmax < Arch.octets() ):
                        raise Exception()
                    # Convert number of bytes into number of ropchain elements
                    clmax /= Arch.octets()
                except:
                    error("Error. '" + args[i+1] +"' bytes is not valid")
                    return 
                i = i +2
                seenLmax = True
                       
            elif( args[i] in [OPTION_OUTPUT, OPTION_OUTPUT_SHORT]):
                if( seenOutput ):
                    error("Option '{}' should be used only once".format(args[i]))
                    return 
                if( i+1 >= len(args)):
                    error("Error. Missing output format after option '"+args[i]+"'")
                    return 
                if( args[i+1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]):
                    OUTPUT = args[i+1]
                    seenOutput = True
                    i += 2
                else:
                    error("Error. Unknown output format: {}".format(args[i+1]))
                    return 
            elif( args[i] in [OPTION_OUTFILE_SHORT, OPTION_OUTFILE] ):
                if( seenOutfile ):
                    error("Option '{}' should be used only once".format(args[i]))
                    return 
                if( i+1 >= len(args)):
                    error("Error. Missing file name after option '"+args[i]+"'")
                    return 
                seenOutfile = True
                outfile = args[i+1]
                i += 2
            elif( args[i] in [OPTION_PADDING_BYTE, OPTION_PADDING_BYTE_SHORT] ):
                if( seenPaddingByte ):
                    error("Option '{}' should be used only once".format(args[i]))
                    return 
                if( i+1 >= len(args)):
                    error("Error. Missing byte after option '"+args[i]+"'")
                    return 
                seenPaddingByte = True
                try:
                    paddingByte = int(args[i+1], 16)
                except:
                    error("Error. '{}' is not a valid byte".format(args[i+1]))
                    return 
                if( paddingByte > 0xff ):
                    error("Error. '{}' can't be stored in one byte".format(args[i+1]))
                    return
                paddingByteStr = "\\x"+format(paddingByte, "02x")
                i += 2
            elif( args[i] in [OPTION_PADDING_LEN, OPTION_PADDING_LEN_SHORT] ):
                if( seenPaddingLen ):
                    error("Option '{}' should be used only once".format(args[i]))
                    return 
                if( i+1 >= len(args)):
                    error("Error. Missing length after option '"+args[i]+"'")
                    return 
                seenPaddingLen = True
                try:
                    paddingLen = int(args[i+1], 10)
                except:
                    try:
                        paddingLen = int(args[i+1], 16)
                    except:
                        error("Error. '{}' is not a valid byte".format(args[i+1]))
                        return 
                if( paddingLen < 0 ):
                    error("Error. Padding length must be > 0")
                    return 
                i += 2
            elif( args[i] in [OPTION_HELP, OPTION_HELP_SHORT]):
                print_help()
                return 
            elif( args[i] in [OPTION_VERBOSE, OPTION_VERBOSE_SHORT]):
                seenVerbose = True
                i += 1
            else:
                error("Error. Unknown option '{}'".format(args[i]))
                return 
                
        # Parse a subcommand
        else:
            # Before parsing, check arguments 
            # Check arguments 
            if( seenPaddingByte and not seenPaddingLen ):
                error("Error. You have to specify padding length if you provide a padding byte. See the '-pl,--padding-length' option")
                return 
            # Parse subcommand 
            verbose_mode(seenVerbose)
            payload = None
            if( args[i] == CMD_DELIVER_SHELLCODE ):
                payload = dshell(args[i+1:], constraint, assertion, lmax=clmax)
            else:
                error("Error. Unknown subcommand : '{}'".format(args[i]))
                verbose_mode(False)
                return 
                
            verbose_mode(False)
            # Print result 
            if( payload == "help"):
                return 
            elif( not payload is None ):
                print(string_bold("\n\tBuilt exploit - {} bytes\n".format(payload.len_bytes)))
                # Normal output 
                if( OUTPUT == OUTPUT_CONSOLE ):
                    payload_string = payload.strConsole(Arch.bits(), constraint.getBadBytes())
                elif( OUTPUT == OUTPUT_PYTHON ):
                    payload_string = payload.strPython(Arch.bits(), constraint.getBadBytes(), \
                        noTab=seenOutfile, paddingByteStr=paddingByteStr, paddingLen=paddingLen)
                # Output to file
                if( outfile ):
                    try: 
                        f = open(outfile, "w")
                        f.write(remove_colors(payload_string))
                        f.close()
                        print(string_bold("\n\tWrote payload in file '{}'\n".format(outfile)))
                    except:
                        error("Error. Couldn't not write payload in file '{}'".format(outfile))
                else:
                    print(payload_string)
            else:
                error("Couldn't generate exploit")
            return 
                
    # Test parsing result 
    if( subcommand is None ):
        error("Missing subcommand")
        return
Ejemplo n.º 23
0
def _basic(qtype,
           arg1,
           arg2,
           constraint,
           assertion,
           n=1,
           clmax=LMAX,
           noPadding=False):
    """
    Search for gadgets basic method ( without chaining ) 
    Direct Database check  
    """
    # Test clmax
    if (clmax <= 0):
        return []

    if (not noPadding):
        maxSpInc = clmax * Arch.octets()
    else:
        maxSpInc = None

    # Check for special gadgets
    if (qtype == QueryType.INT80 or qtype == QueryType.SYSCALL):
        gadgets = DBSearch(qtype,
                           arg1,
                           arg2,
                           constraint,
                           assertion,
                           n=1,
                           maxSpInc=maxSpInc)
        res = [ROPChain().addGadget(g) for g in gadgets]
        return res

    # Check if the type is IP <- ...
    # In this case we remove the CHAINABLE constraint which makes no sense
    if (arg1 == Arch.ipNum()):
        constraint2 = constraint.remove([CstrTypeID.CHAINABLE])
    else:
        constraint2 = constraint

    # Check to add assertions when looking for Memory gadgets
    if (qtype == QueryType.CSTtoMEM or qtype == QueryType.REGtoMEM):
        assertion2 = assertion.add(RegsNoOverlap([(arg1[0], Arch.spNum())]))
    else:
        assertion2 = assertion

    # Regular gadgets
    # maxSpInc -> +1 because we don't count the ret but -1 because the gadget takes one place
    gadgets = DBSearch(qtype,
                       arg1,
                       arg2,
                       constraint2,
                       assertion2,
                       n,
                       maxSpInc=maxSpInc)
    if (noPadding):
        return [ROPChain().addGadget(g) for g in gadgets]
    else:
        res = []
        padding = constraint2.getValidPadding(Arch.currentArch.octets)
        for g in gadgets:
            chain = ROPChain().addGadget(g)
            # Padding the chain if possible
            if (g.spInc > 0):
                for i in range(0, g.spInc / Arch.octets() - 1):
                    chain.addPadding(padding)
            # Adding to the result
            res.append(chain)
    return res