Add a minimal TOD module

TOD_MODULE
Nikhil Parasaram 5 years ago
parent df886eb81c
commit 2c6fcc4db7
  1. 157
      mythril/analysis/modules/transaction_order_dependence.py
  2. 3
      mythril/laser/smt/expression.py

@ -7,17 +7,11 @@ from mythril.analysis.potential_issues import (
get_potential_issues_annotation, get_potential_issues_annotation,
) )
from mythril.analysis.swc_data import TX_ORDER_DEPENDENCE from mythril.analysis.swc_data import TX_ORDER_DEPENDENCE
from mythril.laser.ethereum.state.constraints import Constraints
from mythril.laser.ethereum.transaction.symbolic import ACTORS from mythril.laser.ethereum.transaction.symbolic import ACTORS
from mythril.laser.ethereum.transaction.transaction_models import (
ContractCreationTransaction,
)
from mythril.analysis.modules.base import DetectionModule from mythril.analysis.modules.base import DetectionModule
from mythril.laser.smt import UGT, symbol_factory, Or, BitVec from mythril.laser.smt import Or
from mythril.laser.ethereum.natives import PRECOMPILE_COUNT
from mythril.laser.ethereum.state.global_state import GlobalState from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.exceptions import UnsatError from mythril.exceptions import UnsatError
from copy import copy
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -31,21 +25,14 @@ an informational issue.
""" """
def _is_precompile_call(global_state: GlobalState): class BalanceAnnotation:
to = global_state.mstate.stack[-2] # type: BitVec def __init__(self, caller):
constraints = copy(global_state.world_state.constraints) self.caller = caller
constraints += [
Or(
to < symbol_factory.BitVecVal(1, 256),
to > symbol_factory.BitVecVal(PRECOMPILE_COUNT, 256),
)
]
try: class StorageAnnotation:
solver.get_model(constraints) def __init__(self, caller):
return False self.caller = caller
except UnsatError:
return True
class TransactionOrderDependence(DetectionModule): class TransactionOrderDependence(DetectionModule):
@ -59,6 +46,7 @@ class TransactionOrderDependence(DetectionModule):
description=DESCRIPTION, description=DESCRIPTION,
entrypoint="callback", entrypoint="callback",
pre_hooks=["CALL"], pre_hooks=["CALL"],
post_hooks=["BALANCE", "SLOAD"],
) )
def _execute(self, state: GlobalState) -> None: def _execute(self, state: GlobalState) -> None:
@ -72,88 +60,85 @@ class TransactionOrderDependence(DetectionModule):
annotation = get_potential_issues_annotation(state) annotation = get_potential_issues_annotation(state)
annotation.potential_issues.extend(potential_issues) annotation.potential_issues.extend(potential_issues)
@staticmethod
def annotate_balance_storage_vals(state, opcode):
val = state.mstate.stack[-1]
if opcode == "BALANCE":
annotation = BalanceAnnotation
else:
annotation = StorageAnnotation
if len(val.get_annotations(annotation)) == 0:
state.mstate.stack[-1].annotate(annotation(state.environment.sender))
return []
def _analyze_state(self, state: GlobalState): def _analyze_state(self, state: GlobalState):
""" """
:param state: :param state:
:return: :return:
""" """
gas = state.mstate.stack[-1] opcode = state.get_current_instruction()["opcode"]
to = state.mstate.stack[-2] if opcode != "CALL":
opcode = state.environment.code.instruction_list[state.mstate.pc - 1][
"opcode"
]
if opcode in ("BALANCE", "SLOAD"):
self.annotate_balance_storage_vals(state, opcode)
return []
value = state.mstate.stack[-3]
if (
len(value.get_annotations(StorageAnnotation))
+ len(value.get_annotations(BalanceAnnotation))
== 0
):
return []
callers = []
storage_annotations = value.get_annotations(StorageAnnotation)
if len(storage_annotations) == 1:
callers.append(storage_annotations[0].caller)
balance_annotations = value.get_annotations(BalanceAnnotation)
if len(balance_annotations) == 1:
callers.append(balance_annotations[0].caller)
address = state.get_current_instruction()["address"] address = state.get_current_instruction()["address"]
call_constraint = False
for caller in callers:
call_constraint = Or(call_constraint, ACTORS.attacker == caller)
try: try:
constraints = Constraints([UGT(gas, symbol_factory.BitVecVal(2300, 256))]) constraints = state.world_state.constraints + [call_constraint]
solver.get_transaction_sequence( solver.get_transaction_sequence(state, constraints)
state, constraints + state.world_state.constraints
description_head = "Transaction Order dependence."
description_tail = (
"The callee address of an external message call can be set by "
"the caller. Note that the callee can contain arbitrary code and may re-enter any function "
"in this contract. Review the business logic carefully to prevent averse effects on the "
"contract state."
) )
# Check whether we can also set the callee address issue = PotentialIssue(
contract=state.environment.active_account.contract_name,
try: function_name=state.environment.active_function_name,
new_constraints = constraints + [to == ACTORS.attacker] address=address,
swc_id=TX_ORDER_DEPENDENCE,
solver.get_transaction_sequence( title="Transaction Order Dependence",
state, new_constraints + state.world_state.constraints bytecode=state.environment.code.bytecode,
) severity="Medium",
description_head=description_head,
description_head = "A call to a user-supplied address is executed." description_tail=description_tail,
description_tail = ( constraints=constraints,
"The callee address of an external message call can be set by " detector=self,
"the caller. Note that the callee can contain arbitrary code and may re-enter any function " )
"in this contract. Review the business logic carefully to prevent averse effects on the "
"contract state."
)
issue = PotentialIssue(
contract=state.environment.active_account.contract_name,
function_name=state.environment.active_function_name,
address=address,
swc_id=TransactionOrderDependence,
title="External Call To User-Supplied Address",
bytecode=state.environment.code.bytecode,
severity="Medium",
description_head=description_head,
description_tail=description_tail,
constraints=new_constraints,
detector=self,
)
except UnsatError:
if _is_precompile_call(state):
return []
log.debug(
"[EXTERNAL_CALLS] Callee address cannot be modified. Reporting informational issue."
)
description_head = "The contract executes an external message call."
description_tail = (
"An external function call to a fixed contract address is executed. Make sure "
"that the callee contract has been reviewed carefully."
)
issue = PotentialIssue(
contract=state.environment.active_account.contract_name,
function_name=state.environment.active_function_name,
address=address,
swc_id=TX_ORDER_DEPENDENCE,
title="Transaction Order Dependence",
bytecode=state.environment.code.bytecode,
severity="Low",
description_head=description_head,
description_tail=description_tail,
constraints=constraints,
detector=self,
)
except UnsatError: except UnsatError:
log.debug("[EXTERNAL_CALLS] No model found.") log.debug("[Transaction Order Dependence] No model found.")
return [] return []
return [issue] return [issue]
detector = ExternalCalls() detector = TransactionOrderDependence()

@ -54,6 +54,9 @@ class Expression(Generic[T]):
def __hash__(self) -> int: def __hash__(self) -> int:
return self.raw.__hash__() return self.raw.__hash__()
def get_annotations(self, annotation: Any):
return list(filter(lambda x: isinstance(x, annotation), self.annotations))
G = TypeVar("G", bound=Expression) G = TypeVar("G", bound=Expression)

Loading…
Cancel
Save