Exemple #1
0
def test_concrete_call_symbolic_to():
    # arrange
    address = "0x10"

    active_account = Account(address)
    active_account.code = Disassembly("00")
    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable("calldata_3", VarType.SYMBOLIC)
    meminstart = Variable(1, VarType.CONCRETE)
    call = Call(node, state, None, None, to, None)

    # act
    issues = _concrete_call(call, state, address, meminstart)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == "Call data forwarded with delegatecall()"
    assert issue.type == "Informational"
    assert (
        issue.description
        == "This contract forwards its call data via DELEGATECALL in its fallback function."
        " This means that any function in the called contract can be executed."
        " Note that the callee contract will have access to the storage of the "
        "calling contract.\n DELEGATECALL target: calldata_3"
    )
Exemple #2
0
def test_delegate_call(sym_mock, concrete_mock, curr_instruction):
    # arrange
    # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call")
    # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call")
    sym_mock.return_value = []
    concrete_mock.return_value = []
    curr_instruction.return_value = {"address": "0x10"}

    active_account = Account("0x10")
    active_account.code = Disassembly("00")

    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, Node)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]
    state.mstate.stack = [1, 2, 3]
    assert state.get_current_instruction() == {"address": "0x10"}

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "fallback"

    to = Variable("storage_1", VarType.SYMBOLIC)
    call = Call(node, state, None, "DELEGATECALL", to, None)

    statespace = MagicMock()
    statespace.calls = [call]

    # act
    execute(statespace)

    # assert
    assert concrete_mock.call_count == 1
    assert sym_mock.call_count == 1
Exemple #3
0
def test_symbolic_call_calldata_to(mocker):
    # arrange
    address = "0x10"

    state = GlobalState(None, None, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable("calldata", VarType.SYMBOLIC)
    call = Call(node, state, None, "Type: ", to, None)

    mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None)
    statespace = SymExecWrapper(1)

    mocker.patch.object(statespace, "find_storage_write")
    statespace.find_storage_write.return_value = "Function name"

    # act
    issues = _symbolic_call(call, state, address, statespace)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == "Type:  to a user-supplied address"
    assert issue.type == "Informational"
    assert (
        issue.description ==
        "This contract delegates execution to a contract address obtained from calldata. "
        "Be aware that the called contract gets unrestricted access to this contract's state."
    )
Exemple #4
0
    def _new_node_state(self, state, edge_type=JumpType.UNCONDITIONAL, condition=None):
        new_node = Node(state.environment.active_account.contract_name)
        old_node = state.node
        state.node = new_node
        new_node.constraints = state.mstate.constraints
        self.nodes[new_node.uid] = new_node
        self.edges.append(Edge(old_node.uid, new_node.uid, edge_type=edge_type, condition=condition))

        if edge_type == JumpType.RETURN:
            new_node.flags |= NodeFlags.CALL_RETURN
        elif edge_type == JumpType.CALL:
            try:
                if 'retval' in str(state.mstate.stack[-1]):
                    new_node.flags |= NodeFlags.CALL_RETURN
                else:
                    new_node.flags |= NodeFlags.FUNC_ENTRY
            except StackUnderflowException:
                new_node.flags |= NodeFlags.FUNC_ENTRY
        address = state.environment.code.instruction_list[state.mstate.pc]['address']

        environment = state.environment
        disassembly = environment.code
        if address in state.environment.code.addr_to_func:
            # Enter a new function

            environment.active_function_name = disassembly.addr_to_func[address]
            new_node.flags |= NodeFlags.FUNC_ENTRY

            logging.info(
                "- Entering function " + environment.active_account.contract_name + ":" + new_node.function_name)
        elif address == 0:
            environment.active_function_name = "fallback"

        new_node.function_name = environment.active_function_name
Exemple #5
0
def test_concrete_call():
    # arrange
    address = "0x10"

    state = GlobalState(None, None, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable(1, VarType.CONCRETE)
    meminstart = Variable(1, VarType.CONCRETE)
    call = Call(node, state, None, None, to, None)

    # act
    issues = _concrete_call(call, state, address, meminstart)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == "Call data forwarded with delegatecall()"
    assert issue.type == 'Informational'
    assert issue.description == "This contract forwards its call data via DELEGATECALL in its fallback function." \
                                " This means that any function in the called contract can be executed." \
                                " Note that the callee contract will have access to the storage of the " \
                                "calling contract.\n DELEGATECALL target: 0x1"
Exemple #6
0
    def _new_node_state(
        self, state: GlobalState, edge_type=JumpType.UNCONDITIONAL, condition=None
    ) -> None:
        """

        :param state:
        :param edge_type:
        :param condition:
        """
        new_node = Node(state.environment.active_account.contract_name)
        old_node = state.node
        state.node = new_node
        new_node.constraints = state.mstate.constraints
        if self.requires_statespace:
            self.nodes[new_node.uid] = new_node
            self.edges.append(
                Edge(
                    old_node.uid, new_node.uid, edge_type=edge_type, condition=condition
                )
            )

        if edge_type == JumpType.RETURN:
            new_node.flags |= NodeFlags.CALL_RETURN
        elif edge_type == JumpType.CALL:
            try:
                if "retval" in str(state.mstate.stack[-1]):
                    new_node.flags |= NodeFlags.CALL_RETURN
                else:
                    new_node.flags |= NodeFlags.FUNC_ENTRY
            except StackUnderflowException:
                new_node.flags |= NodeFlags.FUNC_ENTRY

        address = state.environment.code.instruction_list[state.mstate.pc]["address"]

        environment = state.environment
        disassembly = environment.code
        if isinstance(
            state.world_state.transaction_sequence[-1], ContractCreationTransaction
        ):
            environment.active_function_name = "constructor"
        elif address in disassembly.address_to_function_name:
            # Enter a new function
            environment.active_function_name = disassembly.address_to_function_name[
                address
            ]
            new_node.flags |= NodeFlags.FUNC_ENTRY

            log.debug(
                "- Entering function "
                + environment.active_account.contract_name
                + ":"
                + new_node.function_name
            )
        elif address == 0:
            environment.active_function_name = "fallback"

        new_node.function_name = environment.active_function_name
def test_symbolic_call_storage_to(mocker):
    # arrange
    address = "0x10"

    active_account = Account(address)
    active_account.code = Disassembly("00")
    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable("storage_1", VarType.SYMBOLIC)
    call = Call(node, state, None, "Type: ", to, None)

    mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None)
    statespace = SymExecWrapper(1)

    mocker.patch.object(statespace, "find_storage_write")
    statespace.find_storage_write.return_value = "Function name"

    # act
    issues = detector._symbolic_call(call, state, address, statespace)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == "Type:  to a user-supplied address"
    assert issue.type == "Informational"
    assert issue.description == (
        "This contract delegates execution to a contract address in storage slot 1."
        " This storage slot can be written to by calling the function `Function name`."
        " Be aware that the called contract gets unrestricted access to this contract's state."
    )
Exemple #8
0
def test_delegate_call_not_fallback(sym_mock, concrete_mock):
    # arrange
    # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call")
    # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call")
    sym_mock.return_value = []
    concrete_mock.return_value = []

    node = Node("example")
    node.function_name = "not_fallback"

    to = Variable("storage_1", VarType.SYMBOLIC)
    call = Call(node, None, None, "DELEGATECALL", to, None)

    statespace = MagicMock()
    statespace.calls = [call]

    # act
    issues = execute(statespace)

    # assert
    assert issues == []
    assert concrete_mock.call_count == 0
    assert sym_mock.call_count == 0