mirror of https://github.com/ConsenSys/mythril
parent
226aecff14
commit
3d0447dbb8
@ -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 |
|
Loading…
Reference in new issue