Source code for mythril.analysis.module.modules.ether_thief

"""This module contains the detection code for unauthorized ether
withdrawal."""
import logging
from copy import copy

from mythril.analysis.module.base import DetectionModule, EntryPoint
from mythril.analysis.potential_issues import (
    get_potential_issues_annotation,
    PotentialIssue,
)
from mythril.laser.ethereum.transaction.symbolic import ACTORS
from mythril.analysis.swc_data import UNPROTECTED_ETHER_WITHDRAWAL
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.analysis import solver
from mythril.exceptions import UnsatError
from mythril.laser.smt import UGT

log = logging.getLogger(__name__)

DESCRIPTION = """
Search for cases where Ether can be withdrawn to a user-specified address.
An issue is reported if there is a valid end state where the attacker has successfully
increased their Ether balance.
"""


[docs]class EtherThief(DetectionModule): """This module search for cases where Ether can be withdrawn to a user- specified address.""" name = "Any sender can withdraw ETH from the contract account" swc_id = UNPROTECTED_ETHER_WITHDRAWAL description = DESCRIPTION entry_point = EntryPoint.CALLBACK post_hooks = ["CALL", "STATICCALL"]
[docs] def reset_module(self): """ Resets the module by clearing everything :return: """ super().reset_module()
def _execute(self, state: GlobalState) -> None: """ :param state: :return: """ potential_issues = self._analyze_state(state) annotation = get_potential_issues_annotation(state) annotation.potential_issues.extend(potential_issues) def _analyze_state(self, state): """ :param state: :return: """ state = copy(state) instruction = state.get_current_instruction() constraints = copy(state.world_state.constraints) constraints += [ UGT( state.world_state.balances[ACTORS.attacker], state.world_state.starting_balances[ACTORS.attacker], ), state.environment.sender == ACTORS.attacker, state.current_transaction.caller == state.current_transaction.origin, ] try: # Pre-solve so we only add potential issues if the attacker's balance is increased. solver.get_model(constraints) potential_issue = PotentialIssue( contract=state.environment.active_account.contract_name, function_name=state.environment.active_function_name, address=instruction["address"] - 1, # In post hook we use offset of previous instruction swc_id=UNPROTECTED_ETHER_WITHDRAWAL, title="Unprotected Ether Withdrawal", severity="High", bytecode=state.environment.code.bytecode, description_head="Any sender can withdraw Ether from the contract account.", description_tail="Arbitrary senders other than the contract creator can profitably extract Ether " "from the contract account. Verify the business logic carefully and make sure that appropriate " "security controls are in place to prevent unexpected loss of funds.", detector=self, constraints=constraints, ) return [potential_issue] except UnsatError: return []
detector = EtherThief()