def normalizeIntervals(intervals): """ Given a list of [min, max) intervals, - sort them in increasing order, and - collapse contiguous intervals into a single larger interval returns: new list of intervals """ assert isinstance(intervals, list) intervals.sort( key=lambda pair: -1 if (isAst(pair[0]) or isAst(pair[1])) else pair[0] ) # sort all symbolic intervals to beginning, otherwise sort by low coordinate newIntervals = [] while intervals: interval = intervals.pop() # gets the interval with largest max if intervals: prevInterval = intervals[-1] # the interval before that while isDefinitelyEqual( prevInterval[1], interval[0] ): # this interval is contiguous with the previous interval intervals.pop() # remove prevInterval interval[0] = prevInterval[ 0] # `interval` now covers the entire range if not intervals: break # no intervals left prevInterval = intervals[ -1] # now compare with the interval before _that_ # not contiguous with the previous interval newIntervals.append(interval) return newIntervals
def intervalkey(pair): if isAst(pair[0]) and isAst(pair[1]): lower = pair[0] if lower.op == '__add__': try: bvs = next(arg.args[0] for arg in lower.args if arg.op == 'BVS') bvv = next(arg.args[0] for arg in lower.args if arg.op == 'BVV') return (bvs, bvv) except StopIteration: pass elif lower.op == 'BVS': return (lower.args[0], 0) elif not isAst(pair[0]): return ('\xff', pair[0]) return ('', -1)
def _tainted_write(state): addr = state.inspect.mem_write_address #expr = state.inspect.mem_write_expr #l.debug("wrote {} (with leaf_asts {}) to {} (with leaf_asts {})".format( #describeAst(expr), #list(describeAst(leaf) for leaf in expr.leaf_asts()), #describeAst(addr), #list(describeAst(leaf) for leaf in addr.leaf_asts()))) return isAst(addr) and is_tainted(addr)
def _tainted_read(state): addr = state.inspect.mem_read_address #expr = state.inspect.mem_read_expr #l.debug("read {} (with leaf_asts {}) from {} (with leaf_asts {})".format( #describeAst(expr), #list(describeAst(leaf) for leaf in expr.leaf_asts()), #describeAst(addr), #list(describeAst(leaf) for leaf in addr.leaf_asts()))) return isAst(addr) and is_tainted(addr)
def _tainted_branch(state): guard = state.inspect.exit_guard return isAst(guard) and is_tainted(guard) and \ state.solver.satisfiable(extra_constraints=[guard == True]) and \ state.solver.satisfiable(extra_constraints=[guard == False])
def arm(self, state): """ Setup hooks and breakpoints to perform Spectre gadget vulnerability detection. Also set up concretization to ensure addresses always point to secret data when possible. """ if self._armed: l.warn("called arm() on already-armed SpectreExplicitState") return state.inspect.b('mem_read', when=angr.BP_AFTER, condition=_tainted_read, action=detected_spectre_read) state.inspect.b('mem_write', when=angr.BP_AFTER, condition=_tainted_write, action=detected_spectre_write) state.inspect.b('exit', when=angr.BP_BEFORE, condition=_tainted_branch, action=detected_spectre_branch) state.options.add(angr.options.SYMBOLIC_WRITE_ADDRESSES) state.options.add(angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY) state.options.add(angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS) state.options.add(angr.options.SYMBOLIC_INITIAL_VALUES) secretStart = 0x1100000 # a should-be-unused part of the virtual memory space, after where CLE puts its 'externs' object secretMustEnd = 0x2000000 # secrets must be stored somewhere in [secretStart, secretMustEnd) notSecretAddresses = [] # see notes in MemoryLayout.__init__ for (var, val) in self.vars: assert isAst(var) assert isinstance(val, AbstractValue) if val.value is not None: state.add_constraints(var == val.value) if val.secret: raise ValueError( "not implemented yet: secret arguments passed by value") elif isinstance(val, AbstractPointer): if val.cannotPointSecret: notSecretAddresses.append(var) (mlayout, newStart) = memLayoutForPointee(var, val.pointee, secretStart, secretMustEnd) secretStart = newStart # update to account for what was used in the call to memLayoutForPointee self.secretIntervals.extend(mlayout.secretIntervals) notSecretAddresses.extend(mlayout.notSecretAddresses) for (a, (v, bits)) in mlayout.concreteAssignments.items(): #print("Assigning address {} to value {}, {} bits".format(describeAst(a), describeAst(v), bits)) if bits == 8: state.mem[a].uint8_t = v elif bits == 16: state.mem[a].uint16_t = v elif bits == 32: state.mem[a].uint32_t = v elif bits == 64: state.mem[a].uint64_t = v else: raise ValueError( "unexpected bitlength: {}".format(bits)) elif isinstance(val, AbstractPointerToUnconstrainedPublic): if val.cannotPointSecret: notSecretAddresses.append(var) #print("Secret intervals:") #for (mn, mx) in self.secretIntervals: #print("[{}, {})".format(describeAst(mn), describeAst(mx))) #print("Not-secret addresses:") #for addr in notSecretAddresses: #print(describeAst(addr)) self.secretIntervals = normalizeIntervals(self.secretIntervals) for (mn, mx) in self.secretIntervals: if isAst(mn): if state.solver.solution(mn, secretStart): mn_as_int = secretStart state.solver.add(mn == mn_as_int) length = state.solver.eval_one( mx - mn_as_int ) # should be only one possible value of that expression, under these constraints if length is None: raise ValueError( "Expected one solution for {} but got these: {}". format(mx - mn_as_int, state.solver.eval(mx - mn_as_int))) mx_as_int = mn_as_int + length state.solver.add(mx == mx_as_int) secretStart += length else: raise ValueError( "Can't resolve secret address {} to desired value {}". format(mn, secretStart)) elif isAst(mx): raise ValueError( "not implemented yet: interval min {} is concrete but max {} is symbolic" .format(mn, mx)) else: mn_as_int = mn mx_as_int = mx for i in range(mn_as_int, mx_as_int): state.mem[i].uint8_t = oob_memory_fill("secret", 8, state) for addr in notSecretAddresses: state.solver.add( claripy.And(*[ claripy.Or(addr < mn, addr >= mx) for (mn, mx) in self.secretIntervals ])) state.memory.read_strategies.insert( 0, TargetedStrategy(self.secretIntervals)) state.memory.write_strategies.insert( 0, TargetedStrategy(self.secretIntervals)) #state.inspect.b('address_concretization', when=angr.BP_AFTER, condition=concretization_succeeded, action=log_concretization) self._armed = True