Merge delegatecall tests

pull/88/head
Bernhard Mueller 7 years ago
parent 226aecff14
commit 3d0447dbb8
  1. 78
      mythril/analysis/modules/delegatecall.py
  2. 57
      mythril/analysis/modules/delegatecall_forward.py
  3. 71
      mythril/analysis/modules/delegatecall_to_dynamic.py
  4. 4
      mythril/analysis/modules/external_calls.py

@ -0,0 +1,78 @@
from z3 import *
import re
from mythril.analysis.ops import *
from mythril.analysis.report import Issue
import logging
'''
MODULE DESCRIPTION:
Check for invocations of delegatecall(msg.data) in the fallback function.
'''
def execute(statespace):
logging.debug("Executing module: DELEGATECALL")
issues = []
visited = []
for call in statespace.calls:
state = call.state
address = state.get_current_instruction()['address']
if (call.type == "DELEGATECALL"):
if (call.node.function_name == "fallback"):
stack = call.state.mstate.stack
meminstart = get_variable(stack[-3])
if meminstart.type == VarType.CONCRETE:
if (re.search(r'calldata.*_0', str(state.mstate.memory[meminstart.val]))):
issue = Issue(call.node.contract_name, call.node.function_name, address, "CALLDATA forwarded with delegatecall()", "Informational")
issue.description = \
"This contract forwards its calldata 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"
if (call.to.type == VarType.CONCRETE):
issue.description += ("DELEGATECALL target: " + hex(call.to.val))
else:
issue.description += "DELEGATECALL target: " + str(call.to)
issues.append(issue)
if (call.to.type == VarType.SYMBOLIC):
issue = Issue(call.node.contract_name, call.node.function_name, address, call.type + " to a user-supplied address")
if ("calldata" in str(call.to)):
issue.description = \
"This contract delegates execution to a contract address obtained from calldata. "
else:
m = re.search(r'storage_([a-z0-9_&^]+)', str(call.to))
if (m):
index = m.group(1)
func = statespace.find_storage_write(idx)
if (func):
issue.description = "This contract delegates execution to a contract address in storage slot " + str(index) + ". This storage slot can be written to by calling the function '" + func + "'. "
else:
logging.debug("[DELEGATECALL] No storage writes to index " + str(index))
issue.description += "Be aware that the called contract gets unrestricted access to this contract's state."
issues.append(issue)
return issues

@ -1,57 +0,0 @@
from z3 import *
import re
from mythril.analysis.ops import *
from mythril.analysis.report import Issue
import logging
'''
MODULE DESCRIPTION:
Check for invocations of delegatecall(msg.data) in the fallback function.
'''
def execute(statespace):
logging.debug("Executing module: DELEGATECALL_FORWARD")
issues = []
visited = []
for call in statespace.calls:
state = call.state
address = state.get_current_instruction()['address']
# Only needs to be checked once per call instructions (essentially just static analysis)
if address in visited:
continue
else:
visited.append(address)
if (call.type == "DELEGATECALL") and (call.node.function_name == "fallback"):
stack = call.state.mstate.stack
meminstart = get_variable(stack[-3])
if meminstart.type == VarType.CONCRETE:
if (re.search(r'calldata.*_0', str(state.mstate.memory[meminstart.val]))):
issue = Issue(call.node.contract_name, call.node.function_name, address, "CALLDATA forwarded with delegatecall()", "Informational")
issue.description = \
"This contract forwards its calldata 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"
if (call.to.type == VarType.CONCRETE):
issue.description += ("DELEGATECALL target: " + hex(call.to.val))
else:
issue.description += "DELEGATECALL target: " + str(call.to)
issues.append(issue)
return issues

@ -1,71 +0,0 @@
from z3 import *
from mythril.analysis.ops import *
from mythril.analysis.report import Issue
import re
import logging
'''
MODULE DESCRIPTION:
Check for invocations of delegatecall/callcode to a user-supplied address
'''
def execute(statespace):
logging.debug("Executing module: DELEGATECALL_TO_DYNAMIC")
issues = []
for call in statespace.calls:
state = call.state
address = state.get_current_instruction()['address']
if (call.type == "DELEGATECALL" or call.type == "CALLCODE"):
if (call.to.type == VarType.SYMBOLIC):
if ("calldata" in str(call.to)):
issue = Issue(call.node.contract_name, call.node.function_name, address, call.type + " to dynamic address")
issue.description = \
"The function " + call.node.function_name + " delegates execution to a contract address obtained from calldata.\n" \
"Recipient address: " + str(call.to)
issues.append(issue)
else:
m = re.search(r'storage_([a-z0-9_&^]+)', str(call.to))
if (m):
index = m.group(1)
logging.debug("DELEGATECALL to contract address in storage")
try:
for s in statespace.sstors[index]:
if s.tainted:
issue = Issue(call.type + " to dynamic address in storage", "Warning")
issue.description = \
"The function " + call.node.function_name + " in contract '" + call.node.contract_name + " delegates execution to a contract address stored in a state variable. " \
"There is a check on storage index " + str(index) + ". This storage index 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."
issues.append(issue)
break
except KeyError:
logging.debug("[ETHER_SEND] No storage writes to index " + str(index))
else:
issue = Issue(call.node.contract_name, call.node.function_name, address, "DELEGATECALL to dynamic address", "Informational")
issue.description = \
"The function " + call.node.function_name + " in contract '" + call.node.contract_name + " delegates execution to a contract with a dynamic address." \
"To address:" + str(call.to)
issues.append(issue)
return issues

@ -14,7 +14,7 @@ Check for call.value()() to an untrusted address
def execute(statespace):
logging.debug("Executing module: CALL_TO_DYNAMIC_WITH_GAS")
logging.debug("Executing module: EXTERNAL_CALLS")
issues = []
@ -25,7 +25,7 @@ def execute(statespace):
if (call.type == "CALL"):
logging.debug("[CALL_TO_DYNAMIC_WITH_GAS] Call to: " + str(call.to) + ", value " + str(call.value) + ", gas = " + str(call.gas))
logging.debug("[EXTERNAL_CALLS] 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))):
Loading…
Cancel
Save