예제 #1
0
def search(qtype, arg1, arg2, constraint, assertion, n=1, clmax=LMAX, enablePreConds=False, \
            record=None, noPadding=False, comment=None, maxdepth=4):
    """
    Searches for gadgets 
    enablePreConds : return couples (GAdget, preCond) 
    record : stores info about the current search 
    maxdepth : only for CSTtoREG_transitivity
    """
    # Test clmax
    if (clmax <= 0):
        return []

    # Set the search record and increase its depth of 1
    if (record is None):
        record = SearchRecord()
    record.incDepth()
    # Search basic
    res = _basic(qtype, arg1, arg2, constraint.add(Chainable(ret=True)),
                 assertion, n, clmax)
    # Search chaining
    if (len(res) < n and (qtype not in [QueryType.SYSCALL, QueryType.INT80])):
        res += _chain(qtype, arg1, arg2, constraint, assertion, record,
                      n - len(res), clmax, comment)
    # Reset the depth of the search record
    record.decDepth()
    return sorted(res)
예제 #2
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
예제 #3
0
def _search_first_hit(qtype, arg1, arg2, env, n=1):
    """
    Searches for gadgets 
    """

    # Search basic
    # Add Chainable constraint in env.constraint
    constraint = env.getConstraint()
    env.setConstraint(constraint.add(Chainable(ret=True)))
    res = _basic(qtype, arg1, arg2, env, n)
    # Restore normal constraint
    env.setConstraint(constraint)
    # Search chaining
    if (len(res) < n and (qtype not in [QueryType.SYSCALL, QueryType.INT80])):
        res += _chain(qtype, arg1, arg2, env, n - len(res))
    return sorted(res)
예제 #4
0
def _search_first_hit(qtype, arg1, arg2, env, n=1):
    """
    Searches for gadgets 
    """

    # Search basic
    res = []
    res_fail = FailRecord()
    # Add Chainable constraint in env.constraint
    constraint = env.getConstraint()
    env.setConstraint(constraint.add(Chainable(ret=True)))
    basic = _basic(qtype, arg1, arg2, env, n)
    analyze_res(res, res_fail, basic)
    # Restore normal constraint
    env.setConstraint(constraint)
    # Search chaining
    if (len(res) < n and (qtype not in [QueryType.SYSCALL, QueryType.INT80])):
        chain = _chain(qtype, arg1, arg2, env, n - len(res))
        analyze_res(res, res_fail, chain)
    return sorted(res) if res else res_fail
예제 #5
0
def MEMtoREG_transitivity(reg, arg2, constraint, assertion, n=1, clmax=LMAX):
    if (clmax <= 0):
        return []

    res = []
    for inter in range(0, Arch.ssaRegCount):
        if (inter == reg or inter in constraint.getRegsNotModified()
                or inter == Arch.ipNum() or inter == Arch.spNum()):
            continue

        # Find arg1 <- inter
        REGtoREG_record = SearchRecord(maxdepth=4)
        REGtoREG_record.unusable_REGtoREG.append(reg)
        inter_to_reg = search(QueryType.REGtoREG,
                              reg, (inter, 0),
                              constraint,
                              assertion,
                              n,
                              clmax - 1,
                              record=REGtoREG_record)
        if (inter_to_reg):
            len_min = min([len(chain) for chain in inter_to_reg])
            # Try to find inter <- arg2
            # First strategy basic
            arg2_to_inter = _basic(QueryType.MEMtoREG, inter, arg2,
                                   constraint.add(Chainable(ret=True)),
                                   assertion, n, clmax - len_min)
            res += [chain1.addChain(chain2, new=True) for chain1 in arg2_to_inter \
                for chain2 in inter_to_reg if len(chain1)+len(chain2) <= clmax  ]
            # Second strategy read reg (TODO)
            if (len(res) < n):
                pass
        # Did we get enough chains ?
        if (len(res) >= n):
            return res
    # Return the best we got
    return res
예제 #6
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
예제 #7
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
예제 #8
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
예제 #9
0
파일: Utils.py 프로젝트: ufwt/ropgenerator
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
예제 #10
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