def BYTE(i, x): bit = (i + 1) * 8 return If( UGT(i, x.size() / 8 - 1), BitVecVal(0, x.size()), (LShR(x, (x.size() - bit))) & 0xff )
def _analyze_state(state): instruction = state.get_current_instruction() node = state.node if instruction["opcode"] != "CALL": return [] call_value = state.mstate.stack[-3] target = state.mstate.stack[-2] eth_sent_total = BitVecVal(0, 256) constraints = copy(node.constraints) for tx in state.world_state.transaction_sequence: if tx.caller == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF: # There's sometimes no overflow check on balances added. # But we don't care about attacks that require more 2^^256 ETH to be sent. constraints += [ BVAddNoOverflow(eth_sent_total, tx.call_value, False) ] eth_sent_total = Sum(eth_sent_total, tx.call_value) constraints += [ UGT(call_value, eth_sent_total), target == state.environment.sender ] try: transaction_sequence = solver.get_transaction_sequence( state, constraints) debug = str(transaction_sequence) issue = Issue( contract=node.contract_name, function_name=node.function_name, address=instruction["address"], swc_id=UNPROTECTED_ETHER_WITHDRAWAL, title="Ether thief", _type="Warning", bytecode=state.environment.code.bytecode, description= "Arbitrary senders other than the contract creator can withdraw ETH from the contract" + " account without previously having sent an equivalent amount of ETH to it. This is likely to be" + " a vulnerability.", debug=debug, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), ) except UnsatError: logging.debug("[ETHER_THIEF] no model found") return [] return [issue]
def constraints(self): constraints = [] if self.concrete: for calldata_byte in self.starting_calldata: if type(calldata_byte) == int: self._calldata.append(BitVecVal(calldata_byte, 8)) else: self._calldata.append(calldata_byte) constraints.append( self.calldatasize == len(self.starting_calldata)) else: x = BitVec("x", 256) constraints.append( ForAll(x, Implies(self[x] != 0, UGT(self.calldatasize, x)))) return constraints
def __getitem__(self, item: Union[int, slice]) -> Any: if isinstance(item, slice): start, step, stop = item.start, item.step, item.stop try: if start is None: start = 0 if step is None: step = 1 if stop is None: stop = self.calldatasize current_index = (start if isinstance(start, BitVecRef) else BitVecVal(start, 256)) dataparts = [] while simplify(current_index != stop): dataparts.append(self[current_index]) current_index = simplify(current_index + step) except Z3Exception: raise IndexError("Invalid Calldata Slice") values, constraints = zip(*dataparts) result_constraints = [] for c in constraints: result_constraints.extend(c) return simplify(Concat(values)), result_constraints if self.concrete: try: return self._calldata[get_concrete_int(item)], () except IndexError: return BitVecVal(0, 8), () else: constraints = [ Implies(self._calldata[item] != 0, UGT(self.calldatasize, item)) ] return self._calldata[item], constraints
def visit_MLIL_CMP_UGT(self, expr): left, right = self.visit_both_sides(expr) return UGT(left, right)
def visit_MLIL_CMP_UGT(self, expr): left = self.visit(expr.left) right = self.visit(expr.right) if None not in (left, right): return UGT(left, right)
def visit_MLIL_CMP_UGT(self, expr): left, right = self.visit_both_sides(expr) if right.size() != left.size(): right = ZeroExt(left.size() - right.size(), right) return UGT(left, right)
def gt_(self, global_state): state = global_state.mstate exp = UGT(util.pop_bitvec(state), util.pop_bitvec(state)) state.stack.append(exp) return [global_state]
def operate_GT(self, *values): s0 = hlp.convert_to_bitvec(values[0]) s1 = hlp.convert_to_bitvec(values[1]) return hlp.get_concrete_int(UGT(s0, s1))
def GT(x, y): return If(UGT(x, y), BitVecVal(1, x.size()), BitVecVal(0, x.size()))
n_bits = 64 # Input vars X = BitVec('X', n_bits) A = BitVec('A', n_bits) B = BitVec('B', n_bits) # Constants BitWidth = BitVecVal(n_bits, n_bits) # Requirements rule.require(ULT(A, BitWidth)) rule.require(ULT(B, BitWidth)) # Non optimized result nonopt = SHR(B, SHL(A, X)) # Optimized result Mask = SHR(B, SHL(A, Int2BV(IntVal(-1), n_bits))) opt = If( UGT(A, B), AND(SHL(A - B, X), Mask), If( UGT(B, A), AND(SHR(B - A, X), Mask), AND(X, Mask) ) ) rule.check(nonopt, opt)
def _analyze_state(state, node): issues = [] instruction = state.get_current_instruction() if instruction["opcode"] != "CALL": return [] call_value = state.mstate.stack[-3] target = state.mstate.stack[-2] not_creator_constraints, constrained = get_non_creator_constraints( state) if constrained: return [] eth_sent_total = BitVecVal(0, 256) for tx in state.world_state.transaction_sequence[1:]: eth_sent_total += tx.call_value try: model = solver.get_model( node.constraints + not_creator_constraints + [ UGT(call_value, eth_sent_total), state.environment.sender == ARBITRARY_SENDER_ADDRESS, target == state.environment.sender, ]) transaction_sequence = solver.get_transaction_sequence( state, node.constraints + not_creator_constraints + [ call_value > eth_sent_total, state.environment.sender == ARBITRARY_SENDER_ADDRESS, target == state.environment.sender, ], ) debug = "Transaction Sequence: " + str(transaction_sequence) issue = Issue( contract=node.contract_name, function_name=node.function_name, address=instruction["address"], swc_id=UNPROTECTED_ETHER_WITHDRAWAL, title="Ether thief", _type="Warning", bytecode=state.environment.code.bytecode, description= "Arbitrary senders other than the contract creator can withdraw ETH from the contract" + " account without previously having sent an equivalent amount of ETH to it. This is likely to be" + " a vulnerability.", debug=debug, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), ) issues.append(issue) except UnsatError: logging.debug("[ETHER_THIEF] no model found") return issues