Beispiel #1
0
    def test_instruction_semantics_call(self):
        # call #0xdead
        raw = b'\xb0\x12\xad\xde'
        ip = 0xc0de

        ins, _ = decode_instruction(ip, raw)

        state = blank_state()
        state.cpu.registers['R0'] = BitVecVal(ip + len(raw),
                                              16)  # ip preincrement
        state.cpu.registers['R1'] = BitVecVal(0x1234, 16)

        new_states = state.cpu.step_call(state, ins)

        self.assertEqual(len(new_states), 1)

        new_state = new_states[0]

        lo = new_state.memory[0x1232]
        hi = new_state.memory[0x1233]
        pushed_val = Concat(hi, lo)

        self.assertEqual(intval(pushed_val), ip + len(raw))
        self.assertEqual(intval(new_state.cpu.registers['R1']), 0x1232)
        self.assertEqual(intval(new_state.cpu.registers['R0']), 0xdead)
Beispiel #2
0
    def may_write_to(self, z3_index, z3_value, storage, constraint_list, consider_length):
        z3_index = simplify(get_bv(z3_index))
        z3_value = simplify(get_bv(z3_value))

        same_slot = False

        add_constraints = []

        # z3-index directly pointing to this slot
        concret_index = BitVecVal(self.slot_counter, 256)

        # Compare expression equivalence
        if eq(concret_index, z3_index):
            same_slot = True

        # If not structurally equivalent, check if there is an assignment that allows them to be equivalent
        if not same_slot and not are_z3_satisfiable(constraint_list + [z3_index == concret_index]):
            return False, None
        add_constraints.append(simplify(z3_index == self.slot_counter))

        index_str = str(z3_index)
        # Rule out keccak symbolic variable as the function prevents someone from arbitrarily controlling the index

        if len(z3_index.children()) < 2 and index_str.startswith("keccak") \
                or "+" in index_str and index_str[:index_str.index("+")].strip() in keccak_map :
            return False, None # Todo Here I might do something more elaborate if I see that it does actually not solve critical writings

        # Problem because writing to an array has a keccak offset, but if the index can be arbitrarely choosen z3 finds
        # a solution for the controllable symbolic variable to match the index to any slot.
        #sym_ind_name = extract_sym_names(z3_index)
        #if any([name for name in sym_ind_name if name.startswith("keccak")]) and any([name for name in sym_ind_name if not name.startswith("keccak")]):
        #    return False, None

        # If the slot is or may be the same and the slot we currently analyze is the same, we found a possible write
        if self.bitlength == 256:
            return True, add_constraints

        # If not, the slot is still written in its entirety but the observed chunk is loaded and overwritten by itself
        to_bit, from_bit = self.bit_counter + self.bitlength - 1, self.bit_counter
        # to_bit, from_bit = BitVecVal(self.bit_counter + self.bitlength - 1, 256), BitVecVal(self.bit_counter, 256)
        chunk_writing = Extract(to_bit, from_bit, z3_value)

        chunk_content = Extract(to_bit, from_bit,
                                get_bv(get_storage_slot(BitVecVal(self.slot_counter, 256), storage)))

        # if the current content of the observed chunk and the respective chunk of the written value can be different
        # like by a different variable assignment, then we found it
        if are_z3_satisfiable(constraint_list + [Not(chunk_content == chunk_writing)]):
            # It is actually not important to use the constraint that the values are different, overwriting with the same
            # value is still writing. On the other hand it avoids references to storage that later have to be solved with
            # intertransactional analysis although the violation can be triggered in one transaction
            # add_constraints.append(simplify(Not(chunk_content == chunk_writing)))
            return True, add_constraints

        # For the 256-bit chunks the last step should not be necessary, but a compiler could generate some code that
        # overwrites a slot content with itself. This function would have a false positive in that case.

        return False, None
Beispiel #3
0
    def and_(self, global_state):
        stack = global_state.mstate.stack
        op1, op2 = stack.pop(), stack.pop()
        if type(op1) == BoolRef:
            op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256))
        if type(op2) == BoolRef:
            op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256))

        stack.append(op1 & op2)

        return [global_state]
Beispiel #4
0
def amino_bitvec_unary_restriction(
        amino_list: List[AminoRef] = z3_enum_aminos
) -> List[ConstraintRef]:
    amino_sort_size = amino_list[0].sort().size() - 1

    return [
        Implies(
            Extract(i + 1, i + 1, amino) == BitVecVal(1, 1),
            Extract(i, i, amino) == BitVecVal(1, 1),
        ) for i in range(amino_sort_size)
        for amino in amino_list
    ]
Beispiel #5
0
    def and_(self, global_state):
        try:
            stack = global_state.mstate.stack
            op1, op2 = stack.pop(), stack.pop()
            if type(op1) == BoolRef:
                op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256))
            if type(op2) == BoolRef:
                op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256))

            stack.append(op1 & op2)
        except IndexError:
            raise StackUnderflowException()
        return [global_state]
Beispiel #6
0
    def byte_(self, global_state):
        mstate = global_state.mstate
        op0, op1 = mstate.stack.pop(), mstate.stack.pop()
        if not isinstance(op1, ExprRef):
            op1 = BitVecVal(op1, 256)
        try:
            index = util.get_concrete_int(op0)
            offset = (31 - index) * 8
            result = Concat(BitVecVal(0, 248), Extract(offset + 7, offset, op1))
        except AttributeError:
            logging.debug("BYTE: Unsupported symbolic byte offset")
            result = BitVec(str(simplify(op1)) + "[" + str(simplify(op0)) + "]", 256)

        mstate.stack.append(simplify(result))
        return [global_state]
Beispiel #7
0
    def set_function_id(self, fid: BitVecRef = None):
        if isinstance(fid, str) and len(fid) == 8:
            fid = BitVecVal(int(fid, 16), 32)
        elif isinstance(fid, int):
            fid = BitVecVal(fid, 32)
        elif isinstance(fid, BitVecRef) and fid.size() == 32:
            pass
        elif fid is None:
            fid = BitVec('function_id', 32)
        else:
            raise SettingError('illegal function id given')

        for i in range(4):
            fragment = Extract(i * 8 + 7, i * 8, fid)
            self.mstore8(3 - i, fragment)
 def div_(self, global_state):
     op0, op1 = util.pop_bitvec(global_state.mstate), util.pop_bitvec(global_state.mstate)
     if op1 == 0:
         global_state.mstate.stack.append(BitVecVal(0, 256))
     else:
         global_state.mstate.stack.append(UDiv(op0, op1))
     return [global_state]
Beispiel #9
0
def execute_contract_creation(laser_evm,
                              contract_initialization_code,
                              contract_name=None) -> Account:
    """ Executes a contract creation transaction from all open states"""
    # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here
    open_states = laser_evm.open_states[:]
    del laser_evm.open_states[:]

    new_account = laser_evm.world_state.create_account(0,
                                                       concrete_storage=True,
                                                       dynamic_loader=None)
    if contract_name:
        new_account.contract_name = contract_name

    for open_world_state in open_states:
        next_transaction_id = get_next_transaction_id()
        transaction = ContractCreationTransaction(
            world_state=open_world_state,
            identifier=next_transaction_id,
            gas_price=BitVec("gas_price{}".format(next_transaction_id), 256),
            gas_limit=8000000,  # block gas limit
            origin=BitVec("origin{}".format(next_transaction_id), 256),
            code=Disassembly(contract_initialization_code),
            caller=BitVecVal(CREATOR_ADDRESS, 256),
            callee_account=new_account,
            call_data=[],
            call_data_type=CalldataType.SYMBOLIC,
            call_value=BitVec("call_value{}".format(next_transaction_id), 256),
        )
        _setup_global_state_for_execution(laser_evm, transaction)
    laser_evm.exec(True)

    return new_account
Beispiel #10
0
def execute_message_call(laser_evm, callee_address: str) -> None:
    """ Executes a message call transaction from all open states """
    # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here
    open_states = laser_evm.open_states[:]
    del laser_evm.open_states[:]

    for open_world_state in open_states:
        if open_world_state[callee_address].deleted:
            debug("Can not execute dead contract, skipping.")
            continue

        next_transaction_id = get_next_transaction_id()
        transaction = MessageCallTransaction(
            world_state=open_world_state,
            identifier=next_transaction_id,
            gas_price=BitVec("gas_price{}".format(next_transaction_id), 256),
            gas_limit=8000000,  # block gas limit
            origin=BitVec("origin{}".format(next_transaction_id), 256),
            caller=BitVecVal(ATTACKER_ADDRESS, 256),
            callee_account=open_world_state[callee_address],
            call_data=SymbolicCalldata(next_transaction_id),
            call_data_type=CalldataType.SYMBOLIC,
            call_value=BitVec("call_value{}".format(next_transaction_id), 256),
        )
        _setup_global_state_for_execution(laser_evm, transaction)

    laser_evm.exec()
Beispiel #11
0
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
	)
Beispiel #12
0
    def sha3_(self, global_state):
        global keccak_function_manager

        state = global_state.mstate
        environment = global_state.environment
        op0, op1 = state.stack.pop(), state.stack.pop()

        try:
            index, length = util.get_concrete_int(op0), util.get_concrete_int(op1)
        # FIXME: broad exception catch
        except:
            # Can't access symbolic memory offsets
            if is_expr(op0):
                op0 = simplify(op0)
            state.stack.append(BitVec("KECCAC_mem[" + str(op0) + "]", 256))
            return [global_state]

        try:
            state.mem_extend(index, length)
            data = b''.join([util.get_concrete_int(i).to_bytes(1, byteorder='big')
                             for i in state.memory[index: index + length]])

        except AttributeError:
            argument = str(state.memory[index]).replace(" ", "_")

            result = BitVec("KECCAC[{}]".format(argument), 256)
            keccak_function_manager.add_keccak(result, state.memory[index])
            state.stack.append(result)
            return [global_state]

        keccak = utils.sha3(utils.bytearray_to_bytestr(data))
        logging.debug("Computed SHA3 Hash: " + str(binascii.hexlify(keccak)))

        state.stack.append(BitVecVal(util.concrete_int_from_bytes(keccak, 0), 256))
        return [global_state]
Beispiel #13
0
    def __init__(
        self,
        active_account,
        sender,
        calldata,
        gasprice,
        callvalue,
        origin,
        code=None,
        calldata_type=CalldataType.SYMBOLIC,
    ):
        # Metadata

        self.active_account = active_account
        self.active_function_name = ""

        self.address = BitVecVal(int(active_account.address, 16), 256)

        # Ib
        self.code = active_account.code if code is None else code

        self.sender = sender
        self.calldata = calldata
        self.calldata_type = calldata_type
        self.gasprice = gasprice
        self.origin = origin
        self.callvalue = callvalue
Beispiel #14
0
    def eq_(self, global_state):
        state = global_state.mstate

        op1 = state.stack.pop()
        op2 = state.stack.pop()

        if type(op1) == BoolRef:
            op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256))

        if type(op2) == BoolRef:
            op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256))

        exp = op1 == op2

        state.stack.append(exp)
        return [global_state]
def zero_extension(formula, bit_places):
    """Set the rest of bits on the left to 0.
    """
    complement = BitVecVal(0, formula.size() - bit_places)
    formula = Concat(complement, (Extract(bit_places - 1, 0, formula)))

    return formula
Beispiel #16
0
    def _load(self, item: Union[int, ExprRef], clean=False) -> Any:
        x = BitVecVal(item, 256) if isinstance(item, int) else item

        symbolic_base_value = If(
            x > self._size,
            BitVecVal(0, 8),
            BitVec("{}_calldata_{}".format(self.tx_id, str(item)), 8),
        )

        return_value = symbolic_base_value
        for r_index, r_value in self._reads:
            return_value = If(r_index == item, r_value, return_value)

        if not clean:
            self._reads.append((item, symbolic_base_value))
        return simplify(return_value)
Beispiel #17
0
 def stop_(self, global_state):
     state = global_state.mstate
     state.stack.append(BitVecVal(0, 256))
     if len(global_state.call_stack) is 0:
         return []
     global_state.mstate.pc = global_state.call_stack.pop()
     return [global_state]
Beispiel #18
0
    def test_instruction_semantics_jl(self):
        # jl #0x124e
        raw = b'\x0c\x38'
        ip = 0x1234

        ins, ins_len = decode_instruction(ip, raw)

        state = blank_state()
        state.cpu.registers['R0'] = BitVecVal(
            ip + ins_len, 16)  # ip is always preincremented

        expected_taken = 0x124e
        expected_not_taken = 0x1236

        new_states = state.cpu.step_jl(state, ins)

        self.assertEqual(len(new_states), 2)

        taken_states = [
            st for st in new_states
            if intval(st.cpu.registers['R0']) == expected_taken
        ]
        not_taken_states = [
            st for st in new_states
            if intval(st.cpu.registers['R0']) == expected_not_taken
        ]

        self.assertEqual(len(taken_states), 1)
        self.assertEqual(len(not_taken_states), 1)
 def sdiv_(self, global_state):
     s0, s1 = util.pop_bitvec(global_state.mstate), util.pop_bitvec(global_state.mstate)
     if s1 == 0:
         global_state.mstate.stack.append(BitVecVal(0, 256))
     else:
         global_state.mstate.stack.append(s0 / s1)
     return [global_state]
Beispiel #20
0
def test_checkBitVecRef256():
    from z3 import BitVec, BitVecVal
    print(checkBitVecRef256(BitVecVal(111,256)))
    print(checkBitVecRef256(BitVec('x',256)))
    #print(isBitVecRef256(34))a
    print(checkBitVecRef256(BitVec('y',128)))
    print('hoge')
Beispiel #21
0
 def array_to_bv64(array):
     return Concat(Select(array, BitVecVal(7, 32)),
                   Select(array, BitVecVal(6, 32)),
                   Select(array, BitVecVal(5, 32)),
                   Select(array, BitVecVal(4, 32)),
                   Select(array, BitVecVal(3, 32)),
                   Select(array, BitVecVal(2, 32)),
                   Select(array, BitVecVal(1, 32)),
                   Select(array, BitVecVal(0, 32)))
Beispiel #22
0
 def __init__(self, value, size):
     Expr.__init__(self)
     if (isinstance(value, int) or isinstance(value, long)):
         self.value = value
     else:
         self.value = int(value, 16)
     self.size = size
     self.z3 = BitVecVal(value, size)
 def calldatasize_(self, global_state):
     state = global_state.mstate
     environment = global_state.environment
     if environment.calldata_type == CalldataType.SYMBOLIC:
         state.stack.append(BitVec("calldatasize_" + environment.active_account.contract_name, 256))
     else:
         state.stack.append(BitVecVal(len(environment.calldata), 256))
     return [global_state]
Beispiel #24
0
 def calldatasize(self) -> ExprRef:
     """
     :return: Calldata size for this calldata object
     """
     result = self.size
     if isinstance(result, int):
         return BitVecVal(result, 256)
     return result
Beispiel #25
0
    def call_(self, global_state):

        instr = global_state.get_current_instruction()
        environment = global_state.environment

        try:
            callee_address, callee_account, call_data, value, call_data_type, gas, memory_out_offset, memory_out_size = get_call_parameters(
                global_state, self.dynamic_loader, True)
        except ValueError as e:
            logging.info(
                "Could not determine required parameters for call, putting fresh symbol on the stack. \n{}".format(e)
            )
            # TODO: decide what to do in this case
            global_state.mstate.stack.append(global_state.new_bitvec("retval_" + str(instr['address']), 256))
            return [global_state]
        global_state.mstate.stack.append(global_state.new_bitvec("retval_" + str(instr['address']), 256))

        if 0 < int(callee_address, 16) < 5:
            logging.info("Native contract called: " + callee_address)
            if call_data == [] and call_data_type == CalldataType.SYMBOLIC:
                logging.debug("CALL with symbolic data not supported")
                return [global_state]

            try:
                mem_out_start = helper.get_concrete_int(memory_out_offset)
                mem_out_sz = memory_out_size.as_long()
            except AttributeError:
                logging.debug("CALL with symbolic start or offset not supported")
                return [global_state]

            global_state.mstate.mem_extend(mem_out_start, mem_out_sz)
            call_address_int = int(callee_address, 16)
            try:
                data = natives.native_contracts(call_address_int, call_data)
            except natives.NativeContractException:
                contract_list = ['ecerecover', 'sha256', 'ripemd160', 'identity']
                for i in range(mem_out_sz):
                    global_state.mstate.memory[mem_out_start + i] = global_state.new_bitvec(contract_list[call_address_int - 1] +
                                                                           "(" + str(call_data) + ")", 256)

                return [global_state]

            for i in range(min(len(data), mem_out_sz)):  # If more data is used then it's chopped off
                global_state.mstate.memory[mem_out_start + i] = data[i]

            # TODO: maybe use BitVec here constrained to 1
            return [global_state]

        transaction = MessageCallTransaction(global_state.world_state,
                                             callee_account,
                                             BitVecVal(int(environment.active_account.address, 16), 256),
                                             call_data=call_data,
                                             gas_price=environment.gasprice,
                                             call_value=value,
                                             origin=environment.origin,
                                             call_data_type=call_data_type)
        raise TransactionStartSignal(transaction, self.op_code)
Beispiel #26
0
    def extract_function_id(self, condition, block_state):
        if self.extracting_fid:
            m = get_model_and_time(condition)
            if m:
                fid = m[BitVec('function_id', 32)]
                # the case contract has only one function
                if fid is None:
                    self.function_ids.add(BitVecVal(0, 32))
                    if 'callable' in block_state:
                        self.callable_function_ids.add(BitVecVal(0, 32))

                else:
                    self.function_ids.add(fid)
                    if 'callable' in block_state:
                        self.callable_function_ids.add(fid)
                    return True

        return False
Beispiel #27
0
def sanitize_expr(exp):
    """
    Converts ints to bit vector expressions.
    :param exp:
    :return:
    """
    if type(exp) in [int]:
        return BitVecVal(exp, 256)
    return exp
def right_one_extension(formula, bit_places):
    """Set the rest of bits on the right to 1.
    """
    complement = BitVecVal(0, formula.size() - bit_places) - 1
    formula = Concat(
        Extract(formula.size() - 1,
                formula.size() - bit_places, formula), complement)

    return formula
Beispiel #29
0
 def set_region_bit(bv, p):
   i = region_names.index(REGIONS[p.y][p.x])
   chunks = []
   if i < bits - 1:
     chunks.append(Extract(bits - 1, i + 1, bv))
   chunks.append(BitVecVal(1, 1))
   if i > 0:
     chunks.append(Extract(i - 1, 0, bv))
   return Concat(*chunks)
Beispiel #30
0
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]