Esempio n. 1
0
def get_code(disassembler: MythrilDisassembler, args: argparse.Namespace):
    address = None
    if args.code:
        # Load from bytecode
        code = args.code[2:] if args.code.startswith("0x") else args.code
        address, _ = disassembler.load_from_bytecode(code, args.bin_runtime)
    elif args.codefile:
        bytecode = "".join([l.strip() for l in args.codefile if len(l.strip()) > 0])
        bytecode = bytecode[2:] if bytecode.startswith("0x") else bytecode
        address, _ = disassembler.load_from_bytecode(bytecode, args.bin_runtime)
    elif args.address:
        # Get bytecode from a contract address
        address, _ = disassembler.load_from_address(args.address)
    elif args.solidity_file:
        # Compile Solidity source file(s)
        if args.graph and len(args.solidity_file) > 1:
            exit_with_error(
                args.outform,
                "Cannot generate call graphs from multiple input files. Please do it one at a time.",
            )
        address, _ = disassembler.load_from_solidity(
            args.solidity_file
        )  # list of files
    else:
        exit_with_error(
            args.outform,
            "No input bytecode. Please provide EVM code via -c BYTECODE, -a ADDRESS, or -i SOLIDITY_FILES",
        )
    return address
def test_get_data_from_storage_incorrect_params(params):
    config = MythrilConfig()
    config.set_api_rpc_infura()
    disassembler = MythrilDisassembler(eth=config.eth, solc_version="0.4.23")
    with pytest.raises(CriticalError):
        disassembler.get_state_variable_from_storage(
            "0x76799f77587738bfeef09452df215b63d2cfb08a", params)
Esempio n. 3
0
    def _run_mythril(self, contract_address=None):
        eth_json_rpc = EthJsonRpc(url=self.web3_rpc)

        disassembler = MythrilDisassembler(eth=eth_json_rpc,
                                           solc_version=None,
                                           enable_online_lookup=True)

        disassembler.load_from_address(contract_address)

        analyzer = MythrilAnalyzer(
            strategy="bfs",
            use_onchain_data=self.onchain_storage,
            disassembler=disassembler,
            address=contract_address,
            execution_timeout=self.timeout,
            solver_timeout=self.timeout,
            loop_bound=self.loop_bound,
            max_depth=64,
            create_timeout=10,
        )

        self.logger.info("Analyzing %s", contract_address)
        self.logger.debug("Running Mythril")

        return analyzer.fire_lasers(modules=self.modules,
                                    transaction_count=self.tx_count)
def test_get_data_from_storage(params, ans):
    config = MythrilConfig()
    config.set_api_rpc_infura()
    disassembler = MythrilDisassembler(eth=config.eth, solc_version="0.4.23")
    outtext = disassembler.get_state_variable_from_storage(
        "0x76799f77587738bfeef09452df215b63d2cfb08a", params).split("\n")
    assert outtext == ans
Esempio n. 5
0
def parse_args(parser: argparse.ArgumentParser,
               args: argparse.Namespace) -> None:
    """
    Parses the arguments
    :param parser: The parser
    :param args: The args
    """

    if args.epic:
        path = os.path.dirname(os.path.realpath(__file__))
        sys.argv.remove("--epic")
        os.system(" ".join(sys.argv) + " | python3 " + path + "/epic.py")
        sys.exit()

    if args.version:
        if args.outform == "json":
            print(json.dumps({"version_str": VERSION}))
        else:
            print("Mythril version {}".format(VERSION))
        sys.exit()

    # Parse cmdline args
    validate_args(parser, args)
    try:
        quick_commands(args)
        config = set_config(args)
        leveldb_search(config, args)
        disassembler = MythrilDisassembler(
            eth=config.eth,
            solc_version=args.solv,
            solc_args=args.solc_args,
            enable_online_lookup=args.query_signature,
        )
        if args.truffle:
            try:
                disassembler.analyze_truffle_project(args)
            except FileNotFoundError:
                print(
                    "Build directory not found. Make sure that you start the analysis from the project root, and that 'truffle compile' has executed successfully."
                )
            sys.exit()

        address = get_code(disassembler, args)
        execute_command(disassembler=disassembler,
                        address=address,
                        parser=parser,
                        args=args)
    except CriticalError as ce:
        exit_with_error(args.outform, str(ce))
    except Exception:
        exit_with_error(args.outform, traceback.format_exc())
Esempio n. 6
0
    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())
            disassembler = MythrilDisassembler()
            disassembler.contracts.append(contract)
            analyzer = MythrilAnalyzer(
                disassembler=disassembler,
                strategy="dfs",
                execution_timeout=5,
                max_depth=30,
                address=(util.get_indexed_address(0)),
            )

            html = analyzer.graph_html(transaction_count=1)
            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()
Esempio n. 7
0
def contract_hash_to_address(args: Namespace):
    """
    prints the hash from function signature
    :param args:
    :return:
    """
    print(MythrilDisassembler.hash_for_function_signature(args.func_name))
    sys.exit()
Esempio n. 8
0
def test_fire_lasers(mock_sym, mock_fire_lasers, mock_code_info):
    disassembler = MythrilDisassembler(eth=None)
    disassembler.load_from_solidity(
        [
            str(
                (
                    Path(__file__).parent.parent / "testdata/input_contracts/origin.sol"
                ).absolute()
            )
        ]
    )
    analyzer = MythrilAnalyzer(disassembler, strategy="dfs")

    issues = analyzer.fire_lasers(modules=[]).sorted_issues()
    mock_sym.assert_called()
    mock_fire_lasers.assert_called()
    mock_code_info.assert_called()
    assert len(issues) == 1
    assert issues[0]["swc-id"] == "101"
Esempio n. 9
0
def parse_args_and_execute(parser: ArgumentParser, args: Namespace) -> None:
    """
    Parses the arguments
    :param parser: The parser
    :param args: The args
    """

    if args.epic:
        path = os.path.dirname(os.path.realpath(__file__))
        sys.argv.remove("--epic")
        os.system(" ".join(sys.argv) + " | python3 " + path + "/epic.py")
        sys.exit()

    if args.command not in COMMAND_LIST or args.command is None:
        parser.print_help()
        sys.exit()

    if args.command == "version":
        if args.outform == "json":
            print(json.dumps({"version_str": VERSION}))
        else:
            print("Mythril version {}".format(VERSION))
        sys.exit()

    if args.command == "help":
        parser.print_help()
        sys.exit()

    # Parse cmdline args
    validate_args(args)
    try:
        if args.command == "function-to-hash":
            contract_hash_to_address(args)
        config = set_config(args)
        leveldb_search(config, args)
        query_signature = args.__dict__.get("query_signature", None)
        solc_json = args.__dict__.get("solc_json", None)
        solv = args.__dict__.get("solv", None)
        disassembler = MythrilDisassembler(
            eth=config.eth,
            solc_version=solv,
            solc_settings_json=solc_json,
            enable_online_lookup=query_signature,
        )

        address = load_code(disassembler, args)
        execute_command(disassembler=disassembler,
                        address=address,
                        parser=parser,
                        args=args)
    except CriticalError as ce:
        exit_with_error(args.__dict__.get("outform", "text"), str(ce))
    except Exception:
        exit_with_error(args.__dict__.get("outform", "text"),
                        traceback.format_exc())
Esempio n. 10
0
    def test_get_source_info_without_name_gets_latest_contract_info(self):
        input_file = TEST_FILES / "multi_contracts.sol"
        contract = SolidityContract(
            str(input_file),
            solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"))

        code_info = contract.get_source_info(142)

        self.assertEqual(code_info.filename, str(input_file))
        self.assertEqual(code_info.lineno, 14)
        self.assertEqual(code_info.code, "msg.sender.transfer(2 ether)")
Esempio n. 11
0
    def test_get_source_info_with_contract_name_specified_constructor(self):
        input_file = TEST_FILES / "constructor_assert.sol"
        contract = SolidityContract(
            str(input_file),
            name="AssertFail",
            solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
        )

        code_info = contract.get_source_info(70, constructor=True)

        self.assertEqual(code_info.filename, str(input_file))
        self.assertEqual(code_info.lineno, 6)
        self.assertEqual(code_info.code, "assert(var1 > 0)")
Esempio n. 12
0
    def test_get_source_info_with_contract_name_specified(self):
        input_file = TEST_FILES / "multi_contracts.sol"
        contract = SolidityContract(
            str(input_file),
            name="Transfer1",
            solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
        )

        code_info = contract.get_source_info(142)

        self.assertEqual(code_info.filename, str(input_file))
        self.assertEqual(code_info.lineno, 6)
        self.assertEqual(code_info.code, "msg.sender.transfer(1 ether)")
Esempio n. 13
0
def load_code(disassembler: MythrilDisassembler, args: Namespace):
    """
    Loads code into disassembly and returns address
    :param disassembler:
    :param args:
    :return: Address
    """

    address = None
    if args.__dict__.get("code", False):
        # Load from bytecode
        code = args.code[2:] if args.code.startswith("0x") else args.code
        address, _ = disassembler.load_from_bytecode(code, args.bin_runtime)
    elif args.__dict__.get("codefile", False):
        bytecode = "".join(
            [l.strip() for l in args.codefile if len(l.strip()) > 0])
        bytecode = bytecode[2:] if bytecode.startswith("0x") else bytecode
        address, _ = disassembler.load_from_bytecode(bytecode,
                                                     args.bin_runtime)
    elif args.__dict__.get("address", False):
        # Get bytecode from a contract address
        address, _ = disassembler.load_from_address(args.address)
    elif args.__dict__.get("solidity_file", False):
        # Compile Solidity source file(s)
        if args.command in ANALYZE_LIST and args.graph and len(
                args.solidity_file) > 1:
            exit_with_error(
                args.outform,
                "Cannot generate call graphs from multiple input files. Please do it one at a time.",
            )
        address, _ = disassembler.load_from_solidity(
            args.solidity_file)  # list of files
    else:
        exit_with_error(
            args.__dict__.get("outform", "text"),
            "No input bytecode. Please provide EVM code via -c BYTECODE, -a ADDRESS, -f BYTECODE_FILE or <SOLIDITY_FILE>",
        )
    return address
def test_sym_exec():
    contract = SolidityContract(
        str(tests.TESTDATA_INPUTS_CONTRACTS / "calls.sol"),
        solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
    )

    sym = SymExecWrapper(
        contract,
        address=(util.get_indexed_address(0)),
        strategy="dfs",
        execution_timeout=25,
    )
    issues = fire_lasers(sym)

    assert len(issues) != 0
Esempio n. 15
0
    def runTest():
        """"""
        disassembly = SolidityContract(
            "./tests/native_tests.sol",
            solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
        ).disassembly
        account = Account("0x0000000000000000000000000000000000000000", disassembly)
        accounts = {account.address: account}

        laser = svm.LaserEVM(accounts, max_depth=100, transaction_count=1)
        laser.sym_exec(account.address)

        laser_info = str(_all_info(laser))

        _test_natives(laser_info, SHA256_TEST, "SHA256")
        _test_natives(laser_info, RIPEMD160_TEST, "RIPEMD160")
        _test_natives(laser_info, ECRECOVER_TEST, "ECRECOVER")
        _test_natives(laser_info, IDENTITY_TEST, "IDENTITY")
Esempio n. 16
0
def parse_args(parser: argparse.ArgumentParser,
               args: argparse.Namespace) -> None:
    """
    Parses the arguments
    :param parser: The parser
    :param args: The args
    """

    if args.epic:
        path = os.path.dirname(os.path.realpath(__file__))
        sys.argv.remove("--epic")
        os.system(" ".join(sys.argv) + " | python3 " + path + "/epic.py")
        sys.exit()

    if args.version:
        if args.outform == "json":
            print(json.dumps({"version_str": VERSION}))
        else:
            print("Mythril version {}".format(VERSION))
        sys.exit()

    # Parse cmdline args
    validate_args(parser, args)
    try:
        quick_commands(args)
        config = set_config(args)
        leveldb_search(config, args)
        disassembler = MythrilDisassembler(
            eth=config.eth,
            solc_version=args.solv,
            solc_settings_json=args.solc_json,
            enable_online_lookup=args.query_signature,
        )

        address = get_code(disassembler, args)
        execute_command(disassembler=disassembler,
                        address=address,
                        parser=parser,
                        args=args)
    except CriticalError as ce:
        exit_with_error(args.outform, str(ce))
    except Exception:
        exit_with_error(args.outform, traceback.format_exc())
Esempio n. 17
0
    def runTest():
        """"""
        disassembly = SolidityContract(
            "./tests/native_tests.sol",
            solc_binary=MythrilDisassembler._init_solc_binary("0.5.3"),
        ).disassembly
        account = Account("0x0000000000000000000000000000000000000000",
                          disassembly)
        world_state = WorldState()
        world_state.put_account(account)
        laser = svm.LaserEVM(max_depth=100, transaction_count=1)
        laser.sym_exec(world_state=world_state,
                       target_address=account.address.value)

        laser_info = str(_all_info(laser))

        _test_natives(laser_info, SHA256_TEST, "SHA256")
        _test_natives(laser_info, RIPEMD160_TEST, "RIPEMD160")
        _test_natives(laser_info, ECRECOVER_TEST, "ECRECOVER")
        _test_natives(laser_info, IDENTITY_TEST, "IDENTITY")
def test_create():
    contract = SolidityContract(
        str(tests.TESTDATA_INPUTS_CONTRACTS / "calls.sol"),
        solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
    )

    laser_evm = svm.LaserEVM({})

    laser_evm.time = datetime.now()
    execute_contract_creation(laser_evm, contract.creation_code)

    resulting_final_state = laser_evm.open_states[0]

    for address, created_account in resulting_final_state.accounts.items():
        created_account_code = created_account.code
        actual_code = Disassembly(contract.code)

        for i in range(len(created_account_code.instruction_list)):
            found_instruction = created_account_code.instruction_list[i]
            actual_instruction = actual_code.instruction_list[i]

            assert found_instruction["opcode"] == actual_instruction["opcode"]
Esempio n. 19
0
def execute_command(
    disassembler: MythrilDisassembler,
    address: str,
    parser: argparse.ArgumentParser,
    args: argparse.Namespace,
):

    if args.storage:
        if not args.address:
            exit_with_error(
                args.outform,
                "To read storage, provide the address of a deployed contract with the -a option.",
            )

        storage = disassembler.get_state_variable_from_storage(
            address=address, params=[a.strip() for a in args.storage.strip().split(",")]
        )
        print(storage)
        return

    analyzer = MythrilAnalyzer(
        strategy=args.strategy,
        disassembler=disassembler,
        address=address,
        max_depth=args.max_depth,
        execution_timeout=args.execution_timeout,
        create_timeout=args.create_timeout,
        enable_iprof=args.enable_iprof,
        onchain_storage_access=not args.no_onchain_storage_access,
    )

    if args.disassemble:
        # or mythril.disassemble(mythril.contracts[0])

        if disassembler.contracts[0].code:
            print("Runtime Disassembly: \n" + disassembler.contracts[0].get_easm())
        if disassembler.contracts[0].creation_code:
            print("Disassembly: \n" + disassembler.contracts[0].get_creation_easm())

    elif args.graph or args.fire_lasers:
        if not disassembler.contracts:
            exit_with_error(
                args.outform, "input files do not contain any valid contracts"
            )

        if args.graph:
            html = analyzer.graph_html(
                contract=analyzer.contracts[0],
                enable_physics=args.enable_physics,
                phrackify=args.phrack,
            )

            try:
                with open(args.graph, "w") as f:
                    f.write(html)
            except Exception as e:
                exit_with_error(args.outform, "Error saving graph: " + str(e))

        else:
            try:
                report = analyzer.fire_lasers(
                    modules=[m.strip() for m in args.modules.strip().split(",")]
                    if args.modules
                    else [],
                    verbose_report=args.verbose_report,
                    transaction_count=args.transaction_count,
                )
                outputs = {
                    "json": report.as_json(),
                    "jsonv2": report.as_swc_standard_format(),
                    "text": report.as_text(),
                    "markdown": report.as_markdown(),
                }
                print(outputs[args.outform])
            except ModuleNotFoundError as e:
                exit_with_error(
                    args.outform, "Error loading analyis modules: " + format(e)
                )

    elif args.statespace_json:

        if not analyzer.contracts:
            exit_with_error(
                args.outform, "input files do not contain any valid contracts"
            )

        statespace = analyzer.dump_statespace(contract=analyzer.contracts[0])

        try:
            with open(args.statespace_json, "w") as f:
                json.dump(statespace, f)
        except Exception as e:
            exit_with_error(args.outform, "Error saving json: " + str(e))

    else:
        parser.print_help()
Esempio n. 20
0
def signature_hash(signature: Text) -> Text:
    if signature.startswith('_function'):
        return signature[-10:]
    else:
        return MythrilDisassembler.hash_for_function_signature(signature)
Esempio n. 21
0
def quick_commands(args: argparse.Namespace):
    if args.hash:
        print(MythrilDisassembler.hash_for_function_signature(args.hash))
        sys.exit()
Esempio n. 22
0
def exploits_from_mythril(
    rpcHTTP="http://localhost:8545",
    rpcWS=None,
    rpcIPC=None,
    timeout=300,
    contract="",
    account_pk="",
    strategy="bfs",
    modules=["ether_thief", "suicide"],
    transaction_count=3,
    execution_timeout=120,
    max_depth=32,
    loop_bound=3,
    disable_dependency_pruning=False,
    onchain_storage_access=True,
    enable_online_lookup=False,
):
    if re.match(r"^https", rpcHTTP):
        host, port = rpcHTTP[8:].split(":")
        rpc_tls = True
    else:
        host, port = rpcHTTP[7:].split(":")
        rpc_tls = False

    try:
        # Disassembler
        disassembler = MythrilDisassembler(
            eth=EthJsonRpc(host=host, port=port, tls=rpc_tls),
            solc_version=None,
            solc_args=None,
            enable_online_lookup=enable_online_lookup,
        )
        disassembler.load_from_address(contract)
        # Analyzer
        analyzer = MythrilAnalyzer(
            strategy=strategy,
            disassembler=disassembler,
            address=contract,
            execution_timeout=execution_timeout,
            max_depth=max_depth,
            loop_bound=loop_bound,
            disable_dependency_pruning=disable_dependency_pruning,
            onchain_storage_access=onchain_storage_access,
        )
        # Generate report
        report = analyzer.fire_lasers(
            modules=modules, transaction_count=transaction_count
        )
    except CriticalError as e:
        print(e)
        return []

    if rpcIPC is not None:
        print("Connecting to IPC: {rpc}.".format(rpc=rpcIPC))
        w3 = Web3(Web3.IPCProvider(rpcIPC, timeout=timeout))
    elif rpcWS is not None:
        print("Connecting to WebSocket: {rpc}.".format(rpc=rpcWS))
        w3 = Web3(Web3.WebsocketProvider(rpcWS, websocket_kwargs={"timeout": timeout}))
    else:
        print("Connecting to HTTP: {rpc}.".format(rpc=rpcHTTP))
        w3 = Web3(Web3.HTTPProvider(rpcHTTP, request_kwargs={"timeout": timeout}))

    exploits = []

    for ri in report.issues:
        txs = []
        issue = report.issues[ri]

        for si in issue.transaction_sequence["steps"]:
            txs.append(Tx(data=si["input"], value=si["value"], name=si["name"]))

        exploits.append(
            Exploit(
                txs=txs,
                w3=w3,
                contract=contract,
                account=private_key_to_account(account_pk),
                account_pk=account_pk,
                title=issue.title,
                description=issue.description,
                swc_id=issue.swc_id,
            )
        )

    return exploits
def test_solc_install():
    MythrilDisassembler(eth=None, solc_version="0.4.19")
Esempio n. 24
0
def execute_command(
    disassembler: MythrilDisassembler,
    address: str,
    parser: ArgumentParser,
    args: Namespace,
):
    """
    Execute command
    :param disassembler:
    :param address:
    :param parser:
    :param args:
    :return:
    """

    if args.command == "read-storage":
        storage = disassembler.get_state_variable_from_storage(
            address=address,
            params=[a.strip() for a in args.storage_slots.strip().split(",")],
        )
        print(storage)

    elif args.command in DISASSEMBLE_LIST:
        if disassembler.contracts[0].code:
            print("Runtime Disassembly: \n" +
                  disassembler.contracts[0].get_easm())
        if disassembler.contracts[0].creation_code:
            print("Disassembly: \n" +
                  disassembler.contracts[0].get_creation_easm())

    elif args.command in ANALYZE_LIST:
        analyzer = MythrilAnalyzer(
            strategy=args.strategy,
            disassembler=disassembler,
            address=address,
            max_depth=args.max_depth,
            execution_timeout=args.execution_timeout,
            loop_bound=args.loop_bound,
            create_timeout=args.create_timeout,
            enable_iprof=args.enable_iprof,
            disable_dependency_pruning=args.disable_dependency_pruning,
            onchain_storage_access=not args.no_onchain_storage_access,
        )

        if not disassembler.contracts:
            exit_with_error(args.outform,
                            "input files do not contain any valid contracts")

        if args.graph:
            html = analyzer.graph_html(
                contract=analyzer.contracts[0],
                enable_physics=args.enable_physics,
                phrackify=args.phrack,
                transaction_count=args.transaction_count,
            )

            try:
                with open(args.graph, "w") as f:
                    f.write(html)
            except Exception as e:
                exit_with_error(args.outform, "Error saving graph: " + str(e))

        elif args.statespace_json:

            if not analyzer.contracts:
                exit_with_error(
                    args.outform,
                    "input files do not contain any valid contracts")

            statespace = analyzer.dump_statespace(
                contract=analyzer.contracts[0])

            try:
                with open(args.statespace_json, "w") as f:
                    json.dump(statespace, f)
            except Exception as e:
                exit_with_error(args.outform, "Error saving json: " + str(e))

        else:
            try:
                report = analyzer.fire_lasers(
                    modules=[
                        m.strip() for m in args.modules.strip().split(",")
                    ] if args.modules else [],
                    transaction_count=args.transaction_count,
                )
                outputs = {
                    "json": report.as_json(),
                    "jsonv2": report.as_swc_standard_format(),
                    "text": report.as_text(),
                    "markdown": report.as_markdown(),
                }
                print(outputs[args.outform])
            except ModuleNotFoundError as e:
                exit_with_error(args.outform,
                                "Error loading analysis modules: " + format(e))

    else:
        parser.print_help()