|
|
@ -11,24 +11,16 @@ from mythril.analysis.potential_issues import ( |
|
|
|
from mythril.laser.ethereum.transaction.symbolic import ACTORS |
|
|
|
from mythril.laser.ethereum.transaction.symbolic import ACTORS |
|
|
|
from mythril.analysis.swc_data import UNPROTECTED_ETHER_WITHDRAWAL |
|
|
|
from mythril.analysis.swc_data import UNPROTECTED_ETHER_WITHDRAWAL |
|
|
|
from mythril.laser.ethereum.state.global_state import GlobalState |
|
|
|
from mythril.laser.ethereum.state.global_state import GlobalState |
|
|
|
from mythril.laser.ethereum.transaction import ContractCreationTransaction |
|
|
|
from mythril.analysis import solver |
|
|
|
|
|
|
|
from mythril.exceptions import UnsatError |
|
|
|
from mythril.laser.smt import UGT, UGE |
|
|
|
from mythril.laser.smt import UGT |
|
|
|
from mythril.laser.smt.bool import And |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__) |
|
|
|
log = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
DESCRIPTION = """ |
|
|
|
DESCRIPTION = """ |
|
|
|
|
|
|
|
|
|
|
|
Search for cases where Ether can be withdrawn to a user-specified address. |
|
|
|
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 |
|
|
|
An issue is reported if: |
|
|
|
increased their Ether balance. |
|
|
|
|
|
|
|
|
|
|
|
- The transaction sender does not match contract creator; |
|
|
|
|
|
|
|
- The sender address can be chosen arbitrarily; |
|
|
|
|
|
|
|
- The receiver address is identical to the sender address; |
|
|
|
|
|
|
|
- The sender can withdraw *more* than the total amount they sent over all transactions. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -40,7 +32,7 @@ class EtherThief(DetectionModule): |
|
|
|
swc_id = UNPROTECTED_ETHER_WITHDRAWAL |
|
|
|
swc_id = UNPROTECTED_ETHER_WITHDRAWAL |
|
|
|
description = DESCRIPTION |
|
|
|
description = DESCRIPTION |
|
|
|
entry_point = EntryPoint.CALLBACK |
|
|
|
entry_point = EntryPoint.CALLBACK |
|
|
|
pre_hooks = ["STOP"] |
|
|
|
post_hooks = ["CALL", "STATICCALL"] |
|
|
|
|
|
|
|
|
|
|
|
def reset_module(self): |
|
|
|
def reset_module(self): |
|
|
|
""" |
|
|
|
""" |
|
|
@ -51,7 +43,6 @@ class EtherThief(DetectionModule): |
|
|
|
|
|
|
|
|
|
|
|
def _execute(self, state: GlobalState) -> None: |
|
|
|
def _execute(self, state: GlobalState) -> None: |
|
|
|
""" |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
:param state: |
|
|
|
:param state: |
|
|
|
:return: |
|
|
|
:return: |
|
|
|
""" |
|
|
|
""" |
|
|
@ -64,7 +55,6 @@ class EtherThief(DetectionModule): |
|
|
|
|
|
|
|
|
|
|
|
def _analyze_state(self, state): |
|
|
|
def _analyze_state(self, state): |
|
|
|
""" |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
:param state: |
|
|
|
:param state: |
|
|
|
:return: |
|
|
|
:return: |
|
|
|
""" |
|
|
|
""" |
|
|
@ -73,15 +63,18 @@ class EtherThief(DetectionModule): |
|
|
|
|
|
|
|
|
|
|
|
constraints = copy(state.world_state.constraints) |
|
|
|
constraints = copy(state.world_state.constraints) |
|
|
|
|
|
|
|
|
|
|
|
attacker_address_bitvec = ACTORS.attacker |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constraints += [ |
|
|
|
constraints += [ |
|
|
|
UGT( |
|
|
|
UGT( |
|
|
|
state.world_state.balances[attacker_address_bitvec], |
|
|
|
state.world_state.balances[ACTORS.attacker], |
|
|
|
state.world_state.starting_balances[attacker_address_bitvec], |
|
|
|
state.world_state.starting_balances[ACTORS.attacker], |
|
|
|
) |
|
|
|
) |
|
|
|
] |
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
|
|
# Pre-solve so we only add potential issues if the attacker's balance is increased. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
solver.get_model(constraints) |
|
|
|
|
|
|
|
|
|
|
|
potential_issue = PotentialIssue( |
|
|
|
potential_issue = PotentialIssue( |
|
|
|
contract=state.environment.active_account.contract_name, |
|
|
|
contract=state.environment.active_account.contract_name, |
|
|
|
function_name=state.environment.active_function_name, |
|
|
|
function_name=state.environment.active_function_name, |
|
|
@ -92,13 +85,14 @@ class EtherThief(DetectionModule): |
|
|
|
bytecode=state.environment.code.bytecode, |
|
|
|
bytecode=state.environment.code.bytecode, |
|
|
|
description_head="Anyone can withdraw ETH from the contract account.", |
|
|
|
description_head="Anyone can withdraw ETH from the contract account.", |
|
|
|
description_tail="Arbitrary senders other than the contract creator can withdraw ETH from the contract" |
|
|
|
description_tail="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" |
|
|
|
+ " account. This is likely to be a vulnerability.", |
|
|
|
+ " a vulnerability.", |
|
|
|
|
|
|
|
detector=self, |
|
|
|
detector=self, |
|
|
|
constraints=constraints, |
|
|
|
constraints=constraints, |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
return [potential_issue] |
|
|
|
return [potential_issue] |
|
|
|
|
|
|
|
except UnsatError: |
|
|
|
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
detector = EtherThief() |
|
|
|
detector = EtherThief() |
|
|
|