def check_annotations(contracts, address, eth, dynld, max_depth=12): logging.debug("Executing annotations check") for contract in contracts: contr_to_const = deepcopy(contract) contr_to_const.disassembly = Disassembly(contr_to_const.creation_code) contr_to_const.code = contr_to_const.creation_code dynloader = DynLoader(eth) if dynld else None glbstate = get_constr_glbstate(contr_to_const, address) sym_constructor = SymExecWrapper(contr_to_const, address, dynloader, max_depth, glbstate) sym_contract = SymExecWrapper(contract, address, dynloader, max_depth=max_depth) constructor_trace = get_construction_traces( sym_constructor ) # Todo the traces here should not contain references to storages anymore for t in constructor_trace: t.pp_trace() traces = get_transaction_traces(sym_contract) print("Start") a = process_time() print("const: " + str(len(constructor_trace)) + " trans: " + str(len(traces))) trace_chains = [] for trace in constructor_trace: comp_trace_lvls = trace.apply_up_to_trace_levels(traces, 1) if not trace_chains: trace_chains = comp_trace_lvls else: for index in range(len(trace_chains)): trace_chains[index].extend(comp_trace_lvls[index]) print() for lvl in trace_chains: print("Traces: " + str(len(lvl))) #for tt in trace_chains[-1]: # tt.pp_trace() # for trace_lvl in comp_trace_lvls: # print(len(trace_lvl)) # print(sum(map(lambda t: len(t.tran_constraints), trace_lvl))/len(trace_lvl)) # for t in trace_lvl: # print("constraints: " + str(len(t.tran_constraints))) # t.pp_trace() print(process_time() - a)
def fire_lasers( self, strategy, contracts=None, address=None, modules=None, verbose_report=False, max_depth=None, execution_timeout=None, ): all_issues = [] for contract in (contracts or self.contracts): sym = SymExecWrapper( contract, address, strategy, dynloader=DynLoader(self.eth) if self.dynld else None, max_depth=max_depth, execution_timeout=execution_timeout) issues = fire_lasers(sym, modules) if type(contract) == SolidityContract: for issue in issues: issue.add_code_info(contract) all_issues += issues # Finally, output the results report = Report(verbose_report) for issue in all_issues: report.append_issue(issue) return report
def analyze(self, address): """ Analyse a contract using mythril :param address: Address of contract to analyse :return: Findings """ # The followin code is kinda straight from mythril, thanks ;) # Setup code = self.eth.eth_getCode(address) contract = ETHContract(code, name=address) sym = SymExecWrapper(contract, address, dynloader=DynLoader(self.eth)) # Starting analysis logging.debug( "Firing lasers on contract with address: {}".format(address)) issues = fire_lasers(sym) logging.debug("Found {} issues using mythril".format(len(issues))) # Build findings findings = [] for issue in issues: findings += [ Finding("Mythril analysis", issue.title, issue.description, issue.contract, issue.pc, issue.type) ] return findings
def test_symbolic_call_calldata_to(mocker): # arrange address = "0x10" state = GlobalState(None, None, None) state.mstate.memory = ["placeholder", "calldata_bling_0"] node = Node("example") node.contract_name = "the contract name" node.function_name = "the function name" to = Variable("calldata", VarType.SYMBOLIC) call = Call(node, state, None, "Type: ", to, None) mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None) statespace = SymExecWrapper(1) mocker.patch.object(statespace, "find_storage_write") statespace.find_storage_write.return_value = "Function name" # act issues = _symbolic_call(call, state, address, statespace) # assert issue = issues[0] assert issue.address == address assert issue.contract == node.contract_name assert issue.function == node.function_name assert issue.title == "Type: to a user-supplied address" assert issue.type == "Informational" assert ( issue.description == "This contract delegates execution to a contract address obtained from calldata. " "Be aware that the called contract gets unrestricted access to this contract's state." )
def dump_statespace(self, contract: EVMContract = None) -> str: """ Returns serializable statespace of the contract :param contract: The Contract on which the analysis should be done :return: The serialized state space """ sym = SymExecWrapper( contract or self.contracts[0], self.address, self.strategy, dynloader=DynLoader( self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld, ), max_depth=self.max_depth, execution_timeout=self.execution_timeout, create_timeout=self.create_timeout, enable_iprof=self.enable_iprof, disable_dependency_pruning=self.disable_dependency_pruning, run_analysis_modules=False, enable_coverage_strategy=self.enable_coverage_strategy, custom_modules_directory=self.custom_modules_directory, ) return get_serializable_statespace(sym)
def symbolic_execute(code, address): contract = ETHContract(code) sym = SymExecWrapper(contract=contract, address=address, strategy="dfs") # populate graph object graph = MythrilSymExecGraph() for n in sym.nodes.values(): # g.add all nodes - just in case one is not connected which should not happen graph.add_node(n) for e in sym.edges: graph.add_edge(sym.nodes[e.node_from], sym.nodes[e.node_to], e) """ print(graph.iterate_graph()) print(sorted([x.uid for x in graph.nodes])) print(graph._first_added.uid) print(next(graph.get_basicblocks_by_address(0))) print(graph.get_basicblock_by_uid(0)) print(list(n.uid for n in graph.iterate_graph(graph.get_basicblock_by_uid(0)))) print(graph.find_all_paths(graph.get_basicblock_by_uid(0), graph.get_basicblock_by_uid(37))) print(graph.find_all_paths(graph.get_basicblock_by_uid(0),None)) print("streams") for stream in graph.get_streams(graph.get_basicblock_by_uid(0)): print([s.uid for s in stream]) print(list(s.get_current_instruction() for b,s in graph.get_block_and_state_by_instruction_name("PUSH",prn_cmp=lambda x,y:x.startswith(y)))) print(graph.get_stream_by_block(block=graph.get_block_by_uid(2))) """ # only work on the graph from now return graph
def dump_statespace(self, strategy, contract, address=None, max_depth=12): sym = SymExecWrapper(contract, address, strategy, dynloader=DynLoader(self.eth) if self.dynld else None, max_depth=max_depth) return get_serializable_statespace(sym)
def fire_lasers(self, contracts=None, address=None, modules=None, verbose_report=False, max_depth=12): all_issues = [] if self.dynld and self.eth is None: self.set_api_rpc_infura() for contract in (contracts or self.contracts): sym = SymExecWrapper( contract, address, dynloader=DynLoader(self.eth) if self.dynld else None, max_depth=max_depth) issues = fire_lasers(sym, modules) if type(contract) == SolidityContract: for issue in issues: issue.add_code_info(contract) all_issues += issues # Finally, output the results report = Report(verbose_report) for issue in all_issues: report.append_issue(issue) return report
def test_generate_graph(self): for input_file in TESTDATA_INPUTS.iterdir(): output_expected = TESTDATA_OUTPUTS_EXPECTED / ( input_file.name + ".graph.html" ) output_current = TESTDATA_OUTPUTS_CURRENT / ( input_file.name + ".graph.html" ) contract = EVMContract(input_file.read_text()) sym = SymExecWrapper( contract, address=(util.get_indexed_address(0)), strategy="dfs", transaction_count=1, execution_timeout=5, ) html = generate_graph(sym) output_current.write_text(html) lines_expected = re.findall( r"'label': '.*'", str(output_current.read_text()) ) lines_found = re.findall(r"'label': '.*'", str(output_current.read_text())) if not (lines_expected == lines_found): self.found_changed_files(input_file, output_expected, output_current) self.assert_and_show_changed_files()
def test_symbolic_call_storage_to(mocker): # arrange address = "0x10" active_account = Account(address) environment = Environment(active_account, None, None, None, None, None) state = GlobalState(None, environment) state.mstate.memory = ["placeholder", "calldata_bling_0"] node = Node("example") node.contract_name = "the contract name" node.function_name = "the function name" to = Variable("storage_1", VarType.SYMBOLIC) call = Call(node, state, None, "Type: ", to, None) mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None) statespace = SymExecWrapper(1) mocker.patch.object(statespace, 'find_storage_write') statespace.find_storage_write.return_value = "Function name" # act issues = _symbolic_call(call, state, address, statespace) # assert issue = issues[0] assert issue.address == address assert issue.contract == node.contract_name assert issue.function == node.function_name assert issue.title == 'Type: to a user-supplied address' assert issue.type == 'Informational' assert issue.description == 'This contract delegates execution to a contract address in storage slot 1.' \ ' This storage slot can be written to by calling the function `Function name`. ' \ 'Be aware that the called contract gets unrestricted access to this contract\'s state.'
def graph_html( self, strategy, contract, address, max_depth=None, enable_physics=False, phrackify=False, execution_timeout=None, create_timeout=None, ): sym = SymExecWrapper( contract, address, strategy, dynloader=DynLoader( self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld, ), max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout, ) return generate_graph(sym, physics=enable_physics, phrackify=phrackify)
def fire_lasers( self, modules: Optional[List[str]] = None, transaction_count: Optional[int] = None, ) -> Report: """ :param modules: The analysis modules which should be executed :param transaction_count: The amount of transactions to be executed :return: The Report class which contains the all the issues/vulnerabilities """ all_issues = [] # type: List[Issue] SolverStatistics().enabled = True exceptions = [] for contract in self.contracts: StartTime() # Reinitialize start time for new contracts try: sym = SymExecWrapper( contract, self.address, self.strategy, dynloader=DynLoader( self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld, ), max_depth=self.max_depth, execution_timeout=self.execution_timeout, loop_bound=self.loop_bound, create_timeout=self.create_timeout, transaction_count=transaction_count, modules=modules, compulsory_statespace=False, enable_iprof=self.enable_iprof, disable_dependency_pruning=self.disable_dependency_pruning, ) issues = fire_lasers(sym, modules) except KeyboardInterrupt: log.critical("Keyboard Interrupt") issues = retrieve_callback_issues(modules) except Exception: log.critical( "Exception occurred, aborting analysis. Please report this issue to the Mythril GitHub page.\n" + traceback.format_exc()) issues = retrieve_callback_issues(modules) exceptions.append(traceback.format_exc()) for issue in issues: issue.add_code_info(contract) all_issues += issues log.info("Solver statistics: \n{}".format(str(SolverStatistics()))) source_data = Source() source_data.get_source_from_contracts_list(self.contracts) # Finally, output the results report = Report(contracts=self.contracts, exceptions=exceptions) for issue in all_issues: report.append_issue(issue) return report
def dump_statespace( self, strategy, contract, address=None, max_depth=None, execution_timeout=None, create_timeout=None, ): sym = SymExecWrapper( contract, address, strategy, dynloader=DynLoader( self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld, ), max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout, ) return get_serializable_statespace(sym)
def slither_graph_html(self, strategy, contract, address, max_depth=None, enable_physics=False, phrackify=False, execution_timeout=None, create_timeout=None, file=None): priority = self.parse_slither(contract=contract, file=file[0]) sym = SymExecWrapper(contract, address, strategy, dynloader=DynLoader( self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld, ), max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout, priority=priority) return generate_graph(sym, physics=enable_physics, phrackify=phrackify)
def graph_html(self, strategy, contract, address, max_depth=None, enable_physics=False, phrackify=False, execution_timeout=None, create_timeout=None, transaction_count=2, enable_iprof=False): """ :param strategy: :param contract: :param address: :param max_depth: :param enable_physics: :param phrackify: :param execution_timeout: :param create_timeout: :return: """ sym = SymExecWrapper(contract, address, strategy, dynloader=DynLoader( self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld, ), max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout, transaction_count=transaction_count) return generate_graph(sym, physics=enable_physics, phrackify=phrackify)
def graph_html( self, contract: EVMContract = None, enable_physics: bool = False, phrackify: bool = False, transaction_count: Optional[int] = None, ) -> str: """ :param contract: The Contract on which the analysis should be done :param enable_physics: If true then enables the graph physics simulation :param phrackify: If true generates Phrack-style call graph :param transaction_count: The amount of transactions to be executed :return: The generated graph in html format """ sym = SymExecWrapper( contract or self.contracts[0], self.address, self.strategy, dynloader=DynLoader( self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld, ), max_depth=self.max_depth, execution_timeout=self.execution_timeout, transaction_count=transaction_count, create_timeout=self.create_timeout, enable_iprof=self.enable_iprof, disable_dependency_pruning=self.disable_dependency_pruning, run_analysis_modules=False, ) return generate_graph(sym, physics=enable_physics, phrackify=phrackify)
def runTest(self): code = "0x60606040525b603c5b60006010603e565b9050593681016040523660008237602060003683856040603f5a0204f41560545760206000f35bfe5b50565b005b73c3b2ae46792547a96b9f84405e36d0e07edcd05c5b905600a165627a7a7230582062a884f947232ada573f95940cce9c8bfb7e4e14e21df5af4e884941afb55e590029" contract = ETHContract(code) sym = SymExecWrapper(contract, "0xd0a6E6C543bC68Db5db3A191B171A77407Ff7ccf") html = generate_graph(sym) self.assertTrue("0 PUSH1 0x60\\n2 PUSH1 0x40\\n4 MSTORE\\n5 JUMPDEST" in html)
def test_sym_exec(): contract = SolidityContract( str(tests.TESTDATA_INPUTS_CONTRACTS / 'calls.sol')) sym = SymExecWrapper(contract, address=(util.get_indexed_address(0)), strategy="dfs") issues = fire_lasers(sym) assert len(issues) != 0
def dump_statespaces(self, contracts=None, address=None, max_depth=12): statespaces = [] for contract in (contracts or self.contracts): sym = SymExecWrapper(contract, address, dynloader=DynLoader(self.eth) if self.dynld else None, max_depth=max_depth) statespaces.append((contract, get_serializable_statespace(sym))) return statespaces
def _generate_report(input_file): contract = ETHContract(input_file.read_text()) sym = SymExecWrapper(contract, address=(util.get_indexed_address(0))) issues = fire_lasers(sym) report = Report() for issue in issues: issue.filename = "test-filename.sol" report.append_issue(issue) return report, input_file
def _generate_report(input_file): contract = SolidityContract(str(input_file), name=None, solc_args=None) sym = SymExecWrapper(contract, address=(util.get_indexed_address(0))) issues = fire_lasers(sym) report = Report() for issue in issues: issue.add_code_info(contract) report.append_issue(issue) return report
def graph_html(self, contract, address, max_depth=12, enable_physics=False, phrackify=False): sym = SymExecWrapper( contract, address, dynloader=DynLoader(self.eth) if self.dynld else None, max_depth=max_depth) return generate_graph(sym, physics=enable_physics, phrackify=phrackify)
def test_sym_exec(): contract = SolidityContract( str(tests.TESTDATA_INPUTS_CONTRACTS / "calls.sol"), solc_binary=Mythril._init_solc_binary("0.5.0"), ) sym = SymExecWrapper(contract, address=(util.get_indexed_address(0)), strategy="dfs") issues = fire_lasers(sym) assert len(issues) != 0
def _generate_report(input_file): contract = ETHContract(input_file.read_text(), enable_online_lookup=False) sym = SymExecWrapper( contract, address=(util.get_indexed_address(0)), strategy="dfs", execution_timeout=30, ) issues = fire_lasers(sym) report = Report() for issue in issues: issue.filename = "test-filename.sol" report.append_issue(issue) return report, input_file
def _generate_report(input_file): contract = EVMContract(input_file.read_text(), enable_online_lookup=False) sym = SymExecWrapper( contract, address=0xAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFE, strategy="dfs", execution_timeout=30, transaction_count=1, ) issues = fire_lasers(sym) report = Report(contracts=[contract]) for issue in issues: issue.filename = "test-filename.sol" report.append_issue(issue) return report, input_file
def graph_html(self, strategy, contract, address, max_depth=None, enable_physics=False, phrackify=False, execution_timeout=None, create_timeout=None): sym = SymExecWrapper( contract, address, strategy, dynloader=DynLoader(self.eth) if self.dynld else None, max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout) return generate_graph(sym, physics=enable_physics, phrackify=phrackify)
def dump_statespace(self, strategy, contract, address=None, max_depth=None, execution_timeout=None, create_timeout=None): sym = SymExecWrapper( contract, address, strategy, dynloader=DynLoader(self.eth) if self.dynld else None, max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout) return get_serializable_statespace(sym)
def fire_lasers( self, strategy, contracts=None, address=None, modules=None, verbose_report=False, max_depth=None, execution_timeout=None, create_timeout=None, max_transaction_count=None, ): all_issues = [] for contract in contracts or self.contracts: sym = SymExecWrapper( contract, address, strategy, dynloader=DynLoader( self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld, ), max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout, max_transaction_count=max_transaction_count, ) issues = fire_lasers(sym, modules) if type(contract) == SolidityContract: for issue in issues: issue.add_code_info(contract) all_issues += issues # Finally, output the results report = Report(verbose_report) for issue in all_issues: report.append_issue(issue) return report
def test_generate_graph(self): for input_file in TESTDATA_INPUTS.iterdir(): output_expected = TESTDATA_OUTPUTS_EXPECTED / (input_file.name + ".graph.html") output_current = TESTDATA_OUTPUTS_CURRENT / (input_file.name + ".graph.html") contract = SolidityContract(str(input_file), name=None, solc_args=None) sym = SymExecWrapper(contract, address=(util.get_indexed_address(0))) html = generate_graph(sym) output_current.write_text(html) if not (output_expected.read_text() == output_current.read_text()): self.found_changed_files(input_file, output_expected, output_current) self.assert_and_show_changed_files()
def dump_statespace( self, strategy, contract, address=None, max_depth=None, execution_timeout=None, create_timeout=None, enable_iprof=False, ): """ :param strategy: :param contract: :param address: :param max_depth: :param execution_timeout: :param create_timeout: :return: """ sym = SymExecWrapper( contract, address, strategy, dynloader=DynLoader( self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld, ), max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout, enable_iprof=enable_iprof, ) return get_serializable_statespace(sym)