diff --git a/myth b/myth index d22439da..7fd22588 100755 --- a/myth +++ b/myth @@ -368,7 +368,7 @@ elif (args.graph) or (args.fire_lasers): else: states = StateSpace(contracts, max_depth=args.max_depth) # except: - # exitWithError(args.outform, "Symbolic exection error: " + str(e)) + # exitWithError(args.outform, "Symbolic execution error: " + str(e)) if args.enable_physics is not None: physics = True @@ -388,13 +388,13 @@ elif (args.graph) or (args.fire_lasers): for contract in contracts: - try: - if (args.dynld): - states = StateSpace([contract], dynloader=DynLoader(eth), max_depth=args.max_depth) - else: - states = StateSpace([contract], max_depth=args.max_depth) - except Exception as e: - exitWithError(args.outform, "Symbolic exection error: " + str(e)) + #try: + if (args.dynld): + states = StateSpace([contract], dynloader=DynLoader(eth), max_depth=args.max_depth) + else: + states = StateSpace([contract], max_depth=args.max_depth) + #except Exception as e: + # exitWithError(args.outform, "Symbolic exection error: " + str(e)) issues = fire_lasers(states) diff --git a/mythril/analysis/modules/delegatecall_forward.py b/mythril/analysis/modules/delegatecall_forward.py index a3190bd2..b8fab1d5 100644 --- a/mythril/analysis/modules/delegatecall_forward.py +++ b/mythril/analysis/modules/delegatecall_forward.py @@ -11,6 +11,7 @@ MODULE DESCRIPTION: Check for invocations of delegatecall(msg.data) in the fallback function. ''' + def execute(statespace): logging.debug("Executing module: DELEGATECALL_FORWARD") @@ -29,20 +30,20 @@ def execute(statespace): if (call.type == "DELEGATECALL") and (call.node.function_name == "fallback"): - stack = call.state.stack + stack = call.state.mstate.stack meminstart = get_variable(stack[-3]) if meminstart.type == VarType.CONCRETE: - if (re.search(r'calldata.*_0', str(call.state.memory[meminstart.val]))): + if (re.search(r'calldata.*_0', str(call.state.mstate.memory[meminstart.val]))): - issue = Issue(call.node.module_name, call.node.function_name, call.addr, "CALLDATA forwarded with delegatecall()", "Informational") + issue = Issue(call.node.contract_name, call.node.function_name, call.addr, "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: diff --git a/mythril/analysis/modules/delegatecall_to_dynamic.py b/mythril/analysis/modules/delegatecall_to_dynamic.py index 1b7ef07d..5cc8fae0 100644 --- a/mythril/analysis/modules/delegatecall_to_dynamic.py +++ b/mythril/analysis/modules/delegatecall_to_dynamic.py @@ -45,7 +45,7 @@ def execute(statespace): 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.module_name + " delegates execution to a contract address stored in a state variable. " \ + "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) @@ -56,10 +56,10 @@ def execute(statespace): else: - issue = Issue(call.node.module_name, call.node.function_name, call.addr, "DELEGATECALL to dynamic address", "Informational") + issue = Issue(call.node.contract_name, call.node.function_name, call.addr, "DELEGATECALL to dynamic address", "Informational") issue.description = \ - "The function " + call.node.function_name + " in contract '" + call.node.module_name + " delegates execution to a contract with a dynamic address." \ + "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) diff --git a/mythril/analysis/modules/depreciated_ops.py b/mythril/analysis/modules/depreciated_ops.py index dfae4098..9e8ced74 100644 --- a/mythril/analysis/modules/depreciated_ops.py +++ b/mythril/analysis/modules/depreciated_ops.py @@ -9,6 +9,7 @@ MODULE DESCRIPTION: Check for constraints on tx.origin (i.e., access to some functionality is restricted to a specific origin). ''' + def execute(statespace): logging.debug("Executing module: DEPRECIATED OPCODES") @@ -24,10 +25,10 @@ def execute(statespace): if(instruction['opcode'] == "ORIGIN"): - issue = Issue(node.contract_name, node.function_name, instruction['address'], "Use of tx.origin", "Warning", \ + issue = Issue(node.contract_name, node.function_name, instruction['address'], "Use of tx.origin", "Warning", "Function " + node.function_name + " retrieves the transaction origin (tx.origin) using the ORIGIN opcode. Use tx.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin" ) - + issues.append(issue) - + return issues diff --git a/mythril/analysis/modules/suicide.py b/mythril/analysis/modules/suicide.py index 29f2d185..a7bf7ddd 100644 --- a/mythril/analysis/modules/suicide.py +++ b/mythril/analysis/modules/suicide.py @@ -7,7 +7,6 @@ import re import logging - ''' MODULE DESCRIPTION: @@ -15,6 +14,7 @@ Check for SUICIDE instructions that either can be reached by anyone, or where ms (i.e. there's a write to that index is unconstrained by msg.sender). ''' + def execute(statespace): logging.debug("Executing module: UNCHECKED_SUICIDE") @@ -34,8 +34,8 @@ def execute(statespace): description = "The function " + node.function_name + " executes the SUICIDE instruction." - state = node.states[instruction['address']] - to = state.stack.pop() + stack = copy.deepcopy(state.mstate.stack) + to = stack.pop() if ("caller" in str(to)): description += "\nThe remaining Ether is sent to the caller's address.\n" diff --git a/mythril/analysis/ops.py b/mythril/analysis/ops.py index 0622d206..b93c2e2b 100644 --- a/mythril/analysis/ops.py +++ b/mythril/analysis/ops.py @@ -20,33 +20,29 @@ class Variable: class Op: - def __init__(self, node, addr): + def __init__(self, node, state, addr): self.node = node + self.state = state self.addr = addr + class Call(Op): - def __init__(self, node, addr, _type, to, gas, value = Variable(0, VarType.CONCRETE), data = None): + def __init__(self, node, state, addr, _type, to, gas, value=Variable(0, VarType.CONCRETE), data=None): - super().__init__(node, addr) + super().__init__(node, state, addr) self.to = to self.gas = gas self.type = _type self.value = value self.data = data -class Suicide(Op): - - def __init__(self, node, addr, call_type, to, value): - super().__init__(node, addr) - self.to = to class SStore(Op): - def __init__(self, node, addr, value): - super().__init__(node, addr) + def __init__(self, node, state, addr, value): + super().__init__(node, state, addr) self.value = value - self.tainted = False def get_variable(i): @@ -54,6 +50,3 @@ def get_variable(i): return Variable(helper.get_concrete_int(i), VarType.CONCRETE) except AttributeError: return Variable(simplify(i), VarType.SYMBOLIC) - - - diff --git a/mythril/analysis/symbolic.py b/mythril/analysis/symbolic.py index 4dccb942..ea80d280 100644 --- a/mythril/analysis/symbolic.py +++ b/mythril/analysis/symbolic.py @@ -54,14 +54,14 @@ class StateSpace: continue if (meminstart.type == VarType.CONCRETE and meminsz.type == VarType.CONCRETE): - self.calls.append(Call(self.nodes[key], instruction['address'], op, to, gas, value, state.mstate.memory[meminstart.val:meminsz.val*4])) + self.calls.append(Call(self.nodes[key], state, instruction['address'], op, to, gas, value, state.mstate.memory[meminstart.val:meminsz.val*4])) else: - self.calls.append(Call(self.nodes[key], instruction['address'], op, to, gas, value)) + self.calls.append(Call(self.nodes[key], state, instruction['address'], op, to, gas, value)) else: gas, to, meminstart, meminsz, memoutstart, memoutsz = \ get_variable(stack.pop()), get_variable(stack.pop()), get_variable(stack.pop()), get_variable(stack.pop()), get_variable(stack.pop()), get_variable(stack.pop()) - self.calls.append(Call(self.nodes[key], instruction['address'], op, to, gas)) + self.calls.append(Call(self.nodes[key], state, instruction['address'], op, to, gas)) ''' elif op == 'SSTORE': diff --git a/requirements.txt b/requirements.txt index 1262bf99..f9e5fdea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ ethereum>=2.0.4 ZODB>=5.3.0 z3-solver>=4.5 web3 -laser-ethereum==0.5.0 +laser-ethereum==0.5.1 requests BTrees py-solc diff --git a/setup.py b/setup.py index c9ac4762..4dc11906 100755 --- a/setup.py +++ b/setup.py @@ -254,7 +254,7 @@ Credit setup( name='mythril', - version='0.13.0', + version='0.13.1', description='Security analysis tool for Ethereum smart contracts', long_description=long_description, @@ -291,7 +291,7 @@ setup( 'web3', 'ZODB>=5.3.0', 'z3-solver>=4.5', - 'laser-ethereum==0.5.0', + 'laser-ethereum==0.5.1', 'requests', 'BTrees', 'py-solc'