From 8fd6fbd4b457d128236f820e66bfcd308dcf94a6 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Fri, 17 Nov 2017 16:28:21 +0700 Subject: [PATCH] Fix detection of 2300 gas in call --- = | 0 .../modules/call_to_dynamic_with_gas.py | 71 +++++++++++++++++++ mythril/analysis/security.py | 3 +- 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 = create mode 100644 mythril/analysis/modules/call_to_dynamic_with_gas.py diff --git a/= b/= new file mode 100644 index 00000000..e69de29b diff --git a/mythril/analysis/modules/call_to_dynamic_with_gas.py b/mythril/analysis/modules/call_to_dynamic_with_gas.py new file mode 100644 index 00000000..c89e27e4 --- /dev/null +++ b/mythril/analysis/modules/call_to_dynamic_with_gas.py @@ -0,0 +1,71 @@ +from z3 import * +from mythril.analysis.ops import * +from mythril.analysis.report import Issue +import re +import logging + + +''' +MODULE DESCRIPTION: + +Check for call.value()() to an untrusted address +''' + +def execute(statespace): + + issues = [] + + for call in statespace.calls: + + if (call.type == "CALL"): + + logging.debug("[CALL_TO_DYNAMIC_WITH_GAS] Call to: " + str(call.to) + ", value " + str(call.value) + ", gas = " + str(call.gas)) + + if (call.to.type == VarType.SYMBOLIC and (call.gas.type == VarType.CONCRETE and call.gas.val > 2300) or (call.gas.type == VarType.SYMBOLIC and "2300" not in str(call.gas))): + + issue = Issue("CALL with gas to dynamic address", "Warning") + + target = str(call.to) + is_valid = False + + if ("calldata" in target or "caller" in target): + + issue.description = \ + "The function " + call.node.function_name + " contains a call.value() to " + + if ("calldata" in target): + issue.description += "an address provided as a function argument. " + else: + issue.description += "the address of the transaction sender. " + + is_valid = True + else: + m = re.search(r'storage_([a-z0-9_&^]+)', str(call.to)) + + if (m): + index = m.group(1) + + try: + + for s in statespace.sstors[index]: + + if s.tainted: + + issue.description += \ + "The target address of the call is found at storage position " + str(index) + \ + "This storage position can be written to by calling the function '" + s.node.function_name + "'.\n" \ + "Make sure that the contract address cannot be set by untrusted users. " + + is_valid = True + break + + except KeyError: + logging.debug("[CALL_TO_DYNAMIC_WITH_GAS] No storage writes to index " + str(index)) + continue + + if is_valid: + issue.description += "The available gas is forwarded to the called contract.\nMake sure that the logic of the calling contract is not adversely affected if the called contract misbehaves (e.g. reentrancy)." + + issues.append(issue) + + return issues diff --git a/mythril/analysis/security.py b/mythril/analysis/security.py index a4cfa9f6..9c257762 100644 --- a/mythril/analysis/security.py +++ b/mythril/analysis/security.py @@ -1,5 +1,5 @@ from mythril.analysis.report import Report -from .modules import delegatecall_forward, unchecked_suicide, ether_send, unchecked_retval, delegatecall_to_dynamic, integer_underflow +from .modules import delegatecall_forward, unchecked_suicide, ether_send, unchecked_retval, delegatecall_to_dynamic, integer_underflow, call_to_dynamic_with_gas def fire_lasers(statespace): @@ -8,6 +8,7 @@ def fire_lasers(statespace): issues += delegatecall_forward.execute(statespace) issues += delegatecall_to_dynamic.execute(statespace) + issues += call_to_dynamic_with_gas.execute(statespace) issues += unchecked_suicide.execute(statespace) issues += unchecked_retval.execute(statespace) issues += ether_send.execute(statespace)