Esempio n. 1
0
def _mythril(bytecode):
    # import pudb; pudb.set_trace();
    logging.info('starting mythril analysis')
    max_depth = 12
    mythril = Mythril()
    address, contract_object = mythril.load_from_bytecode(bytecode)
    issues = mythril.fire_lasers(strategy="dfs", verbose_report=True,
                                 contracts=[contract_object],
                                 address=address, max_depth=max_depth,
                                 execution_timeout=30, create_timeout=30,
                                 modules=None)
    return issues.as_text()  # issues.as_json() also available
Esempio n. 2
0
def get_vulns(target_address, tx_count):
    myth = Mythril(enable_online_lookup=False, onchain_storage_access=True)

    if re.match(r"^https", rpc):
        rpchost = rpc[8:]
        rpctls = True
    else:
        rpchost = rpc[7:]
        rpctls = False

    myth.set_api_rpc(rpchost, rpctls)
    myth.load_from_address(target_address)

    report = myth.fire_lasers(
        strategy="dfs",
        modules=["ether_thief", "suicide"],
        address=target_address,
        execution_timeout=45,
        max_depth=22,
        transaction_count=tx_count,
        verbose_report=True,
    )

    nIssues = len(report.issues)

    if nIssues == 0:
        raise InvulnerableError

    vulns = []

    for i in range(0, nIssues):
        issue = report.sorted_issues()[i]

        tx = issue["debug"].replace("\n", " ").replace("'", '"')
        transactions = json.loads(tx)

        if "withdraw its balance" in issue["description"]:
            _type = VulnType.KILL_AND_WITHDRAW
            description = "Looks line anyone can kill this contract and steal its balance."
        elif "withdraw ETH" in issue["description"]:
            _type = VulnType.ETHER_THEFT
            description = "Looks like anyone can withdraw ETH from this contract."
        else:
            _type = VulnType.KILL_ONLY
            description = "Anybody can accidentally kill this contract."

        vulns.append(Vulnerability(_type, description, transactions))

    if len(vulns):
        return vulns

    raise InvulnerableError
Esempio n. 3
0
def mythril_scanner(smart_contract):
    logging.info(f'[{smart_contract}] Mythril scanner')
    mythril = Mythril(solv=None, dynld=False, solc_args=None)
    address, _ = mythril.load_from_solidity([
        smart_contract,
    ])
    logging.info(f'[{smart_contract}] address={address}')

    report = mythril.fire_lasers(strategy='dfs',
                                 address=address,
                                 modules=[],
                                 verbose_report=False,
                                 max_depth=20,
                                 execution_timeout=200)
    logging.info(f'[{smart_contract}] report={report.as_json()}')
    return report.as_json()
Esempio n. 4
0
File: karl.py Progetto: mtabz/karl
    def _run_mythril(self, contract_address=None):
        myth = Mythril(onchain_storage_access=True, enable_online_lookup=True)
        myth.set_api_rpc(rpc=self.rpc, rpctls=self.rpc_tls)

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

        return myth.fire_lasers(
            strategy="dfs",
            modules=["ether_thief", "suicide"],
            address=contract_address,
            execution_timeout=45,
            create_timeout=10,
            max_depth=22,
            transaction_count=3,
            verbose_report=True,
        )
Esempio n. 5
0
def main():
    start_meassure()
    parser = argparse.ArgumentParser(
        description='Security analysis of Ethereum smart contracts')
    parser.add_argument("solidity_file", nargs='*')

    commands = parser.add_argument_group('commands')
    commands.add_argument('-g',
                          '--graph',
                          help='generate a control flow graph')
    commands.add_argument('-V',
                          '--version',
                          action='store_true',
                          help='print the Mythril version number and exit')
    commands.add_argument(
        '-x',
        '--fire-lasers',
        action='store_true',
        help='detect vulnerabilities, use with -c, -a or solidity file(s)')

    commands.add_argument(
        '-n',
        '--annotations',
        action='store_true',
        help='detect broken annotations, used with solidity file(s)')

    commands.add_argument(
        '-cn',
        '--contract-name',
        help=
        'Specify the contract by name who\'s annotations should be checked.',
        metavar='cname')

    commands.add_argument(
        '-cf',
        '--config_file',
        help=
        'Allows to specify a config file to set configurations for the annotary analysis',
        metavar='config_file')

    commands.add_argument(
        '-t',
        '--truffle',
        action='store_true',
        help='analyze a truffle project (run from project dir)')
    commands.add_argument('-d',
                          '--disassemble',
                          action='store_true',
                          help='print disassembly')
    commands.add_argument('-j',
                          '--statespace-json',
                          help='dumps the statespace json',
                          metavar='OUTPUT_FILE')

    inputs = parser.add_argument_group('input arguments')
    inputs.add_argument('-c',
                        '--code',
                        help='hex-encoded bytecode string ("6060604052...")',
                        metavar='BYTECODE')
    inputs.add_argument('-f',
                        '--codefile',
                        help='file containing hex-encoded bytecode string',
                        metavar='BYTECODEFILE',
                        type=argparse.FileType('r'))
    inputs.add_argument('-a',
                        '--address',
                        help='pull contract from the blockchain',
                        metavar='CONTRACT_ADDRESS')
    inputs.add_argument('-l',
                        '--dynld',
                        action='store_true',
                        help='auto-load dependencies from the blockchain')

    outputs = parser.add_argument_group('output formats')
    outputs.add_argument('-o',
                         '--outform',
                         choices=['text', 'markdown', 'json'],
                         default='text',
                         help='report output format',
                         metavar='<text/markdown/json>')
    outputs.add_argument('--verbose-report',
                         action='store_true',
                         help='Include debugging information in report')

    database = parser.add_argument_group('local contracts database')
    database.add_argument('-s',
                          '--search',
                          help='search the contract database',
                          metavar='EXPRESSION')
    database.add_argument(
        '--leveldb-dir',
        help='specify leveldb directory for search or direct access operations',
        metavar='LEVELDB_PATH')

    utilities = parser.add_argument_group('utilities')
    utilities.add_argument('--hash',
                           help='calculate function signature hash',
                           metavar='SIGNATURE')
    utilities.add_argument(
        '--storage',
        help='read state variables from storage index, use with -a',
        metavar='INDEX,NUM_SLOTS,[array] / mapping,INDEX,[KEY1, KEY2...]')
    utilities.add_argument(
        '--solv',
        help=
        'specify solidity compiler version. If not present, will try to install it (Experimental)',
        metavar='SOLV')
    utilities.add_argument(
        '--contract-hash-to-address',
        help='returns corresponding address for a contract address hash',
        metavar='SHA3_TO_LOOK_FOR')

    options = parser.add_argument_group('options')
    options.add_argument(
        '-m',
        '--modules',
        help='Comma-separated list of security analysis modules',
        metavar='MODULES')
    options.add_argument('--max-depth',
                         type=int,
                         default=22,
                         help='Maximum recursion depth for symbolic execution')
    options.add_argument('--strategy',
                         choices=['dfs', 'bfs'],
                         default='dfs',
                         help='Symbolic execution strategy')
    options.add_argument(
        '--execution-timeout',
        type=int,
        default=600,
        help="The amount of seconds to spend on symbolic execution")
    options.add_argument('--create-timeout',
                         type=int,
                         default=10,
                         help="The amount of seconds to spend on "
                         "the initial contract creation")
    options.add_argument('--solc-args', help='Extra arguments for solc')
    options.add_argument('--phrack',
                         action='store_true',
                         help='Phrack-style call graph')
    options.add_argument('--enable-physics',
                         action='store_true',
                         help='enable graph physics simulation')
    options.add_argument('-v',
                         type=int,
                         help='log level (0-2)',
                         metavar='LOG_LEVEL')

    rpc = parser.add_argument_group('RPC options')
    rpc.add_argument('-i',
                     action='store_true',
                     help='Preset: Infura Node service (Mainnet)')
    rpc.add_argument('--rpc',
                     help='custom RPC settings',
                     metavar='HOST:PORT / ganache / infura-[network_name]')
    rpc.add_argument('--rpctls',
                     type=bool,
                     default=False,
                     help='RPC connection over TLS')

    # Get config values

    args = parser.parse_args()

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

    # Parse cmdline args

    if not (args.search or args.hash or args.disassemble or args.graph
            or args.fire_lasers or args.storage or args.truffle
            or args.statespace_json or args.contract_hash_to_address
            or args.annotations):
        parser.print_help()
        sys.exit()

    if args.v:
        if 0 <= args.v < 3:
            logging.basicConfig(
                level=[logging.NOTSET, logging.INFO, logging.DEBUG][args.v])
        else:
            exit_with_error(
                args.outform,
                "Invalid -v value, you can find valid values in usage")

    # -- commands --
    if args.hash:
        print(Mythril.hash_for_function_signature(args.hash))
        sys.exit()

    try:
        # the mythril object should be our main interface
        # infura = None, rpc = None, rpctls = None
        # solc_args = None, dynld = None, max_recursion_depth = 12):

        mythril = Mythril(solv=args.solv,
                          dynld=args.dynld,
                          solc_args=args.solc_args)
        if args.dynld and not (args.rpc or args.i):
            mythril.set_api_from_config_path()

        if args.address:
            # Establish RPC connection if necessary
            if args.i:
                mythril.set_api_rpc_infura()
            elif args.rpc:
                mythril.set_api_rpc(rpc=args.rpc, rpctls=args.rpctls)
            elif not args.dynld:
                mythril.set_api_rpc_localhost()
        elif args.search or args.contract_hash_to_address:
            # Open LevelDB if necessary
            mythril.set_api_leveldb(mythril.leveldb_dir if not args.leveldb_dir
                                    else args.leveldb_dir)

        if args.search:
            # Database search ops
            mythril.search_db(args.search)
            sys.exit()

        if args.contract_hash_to_address:
            # search corresponding address
            try:
                mythril.contract_hash_to_address(args.contract_hash_to_address)
            except AddressNotFoundError:
                print("Address not found.")

            sys.exit()

        if args.truffle:
            try:
                # not really pythonic atm. needs refactoring
                mythril.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()

        # Load / compile input contracts
        address = None

        if args.code:
            # Load from bytecode
            address, _ = mythril.load_from_bytecode(args.code)
        elif args.codefile:
            bytecode = ''.join(
                [l.strip() for l in args.codefile if len(l.strip()) > 0])
            address, _ = mythril.load_from_bytecode(bytecode)
        elif args.address:
            # Get bytecode from a contract address
            address, _ = mythril.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."
                )
            changed_files = args.solidity_file
            if args.annotations:
                annotary = Annotary(solc_args=args.solc_args,
                                    config_file=args.config_file)
                annotary.create_tmp_dir()
                annotary.copy_files_to_tmp(args.solidity_file)
                annotary.enter_tmp_dir()

                # create new subdirectory
                # change working direktory or append path to current source files
                # do necessary conversions of filenames
                changed_files = [
                    annotary.tmp_dir + "/" +
                    (file[file.rfind("/") + 1:] if "/" in file else file)
                    for file in args.solidity_file
                ]
            address, _ = mythril.load_from_solidity(
                changed_files)  # 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"
            )

        # Commands

        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 = mythril.get_state_variable_from_storage(
                address=address,
                params=[a.strip() for a in args.storage.strip().split(",")])
            print(storage)

        elif args.disassemble:
            easm_text = mythril.contracts[0].get_easm(
            )  # or mythril.disassemble(mythril.contracts[0])
            sys.stdout.write(easm_text)

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

            if args.graph:
                html = mythril.graph_html(
                    strategy=args.strategy,
                    contract=mythril.contracts[0],
                    address=address,
                    enable_physics=args.enable_physics,
                    phrackify=args.phrack,
                    max_depth=args.max_depth,
                    execution_timeout=args.execution_timeout,
                    create_timeout=args.create_timeout)

                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:
                report = mythril.fire_lasers(
                    strategy=args.strategy,
                    address=address,
                    modules=[
                        m.strip() for m in args.modules.strip().split(",")
                    ] if args.modules else [],
                    verbose_report=args.verbose_report,
                    max_depth=args.max_depth,
                    execution_timeout=args.execution_timeout,
                    create_timeout=args.create_timeout)
                outputs = {
                    'json': report.as_json(),
                    'text': report.as_text(),
                    'markdown': report.as_markdown()
                }
                print(outputs[args.outform])

        if args.annotations:
            if not mythril.contracts:
                exit_with_error(
                    args.outform,
                    "input files do not contain any valid contracts")
            if args.contract_name:
                annotary.set_contract_name(args.contract_name)

            annotary.provide_resources(mythril.contracts, address, mythril.eth,
                                       mythril.dynld, mythril.sigs)
            annotary.parse_annotations()
            annotary.check_annotations()
            print(annotary.get_annotation_json())
            printend()
            annotary.delete_tmp_dir()

        elif args.statespace_json:

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

            statespace = mythril.dump_statespace(
                strategy=args.strategy,
                contract=mythril.contracts[0],
                address=address,
                max_depth=args.max_depth,
                execution_timeout=args.execution_timeout,
                create_timeout=args.create_timeout)

            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()

    except CriticalError as ce:
        exit_with_error(args.outform, str(ce))
Esempio n. 6
0
def main():
    parser = argparse.ArgumentParser(
        description="Security analysis of Ethereum smart contracts")
    parser.add_argument("solidity_file", nargs="*")

    commands = parser.add_argument_group("commands")
    commands.add_argument("-g",
                          "--graph",
                          help="generate a control flow graph")
    commands.add_argument(
        "-V",
        "--version",
        action="store_true",
        help="print the Mythril version number and exit",
    )
    commands.add_argument(
        "-x",
        "--fire-lasers",
        action="store_true",
        help="detect vulnerabilities, use with -c, -a or solidity file(s)",
    )
    commands.add_argument(
        "--truffle",
        action="store_true",
        help="analyze a truffle project (run from project dir)",
    )
    commands.add_argument("-d",
                          "--disassemble",
                          action="store_true",
                          help="print disassembly")
    commands.add_argument(
        "-j",
        "--statespace-json",
        help="dumps the statespace json",
        metavar="OUTPUT_FILE",
    )

    inputs = parser.add_argument_group("input arguments")
    inputs.add_argument(
        "-c",
        "--code",
        help='hex-encoded bytecode string ("6060604052...")',
        metavar="BYTECODE",
    )
    inputs.add_argument(
        "-f",
        "--codefile",
        help="file containing hex-encoded bytecode string",
        metavar="BYTECODEFILE",
        type=argparse.FileType("r"),
    )
    inputs.add_argument(
        "-a",
        "--address",
        help="pull contract from the blockchain",
        metavar="CONTRACT_ADDRESS",
    )
    inputs.add_argument(
        "-l",
        "--dynld",
        action="store_true",
        help="auto-load dependencies from the blockchain",
    )
    inputs.add_argument(
        "--no-onchain-storage-access",
        action="store_true",
        help="turns off getting the data from onchain contracts",
    )
    inputs.add_argument(
        "--bin-runtime",
        action="store_true",
        help=
        "Only when -c or -f is used. Consider the input bytecode as binary runtime code, default being the contract creation bytecode.",
    )

    outputs = parser.add_argument_group("output formats")
    outputs.add_argument(
        "-o",
        "--outform",
        choices=["text", "markdown", "json"],
        default="text",
        help="report output format",
        metavar="<text/markdown/json>",
    )
    outputs.add_argument(
        "--verbose-report",
        action="store_true",
        help="Include debugging information in report",
    )

    database = parser.add_argument_group("local contracts database")
    database.add_argument("-s",
                          "--search",
                          help="search the contract database",
                          metavar="EXPRESSION")
    database.add_argument(
        "--leveldb-dir",
        help="specify leveldb directory for search or direct access operations",
        metavar="LEVELDB_PATH",
    )

    utilities = parser.add_argument_group("utilities")
    utilities.add_argument("--hash",
                           help="calculate function signature hash",
                           metavar="SIGNATURE")
    utilities.add_argument(
        "--storage",
        help="read state variables from storage index, use with -a",
        metavar="INDEX,NUM_SLOTS,[array] / mapping,INDEX,[KEY1, KEY2...]",
    )
    utilities.add_argument(
        "--solv",
        help=
        "specify solidity compiler version. If not present, will try to install it (Experimental)",
        metavar="SOLV",
    )
    utilities.add_argument(
        "--contract-hash-to-address",
        help="returns corresponding address for a contract address hash",
        metavar="SHA3_TO_LOOK_FOR",
    )

    options = parser.add_argument_group("options")
    options.add_argument(
        "-m",
        "--modules",
        help="Comma-separated list of security analysis modules",
        metavar="MODULES",
    )
    options.add_argument(
        "--max-depth",
        type=int,
        default=22,
        help="Maximum recursion depth for symbolic execution",
    )

    options.add_argument(
        "--strategy",
        choices=["dfs", "bfs", "naive-random", "weighted-random"],
        default="dfs",
        help="Symbolic execution strategy",
    )
    options.add_argument(
        "-t",
        "--transaction-count",
        type=int,
        default=2,
        help="Maximum number of transactions issued by laser",
    )
    options.add_argument(
        "--execution-timeout",
        type=int,
        default=600,
        help="The amount of seconds to spend on symbolic execution",
    )
    options.add_argument(
        "--create-timeout",
        type=int,
        default=10,
        help="The amount of seconds to spend on "
        "the initial contract creation",
    )
    options.add_argument("--solc-args", help="Extra arguments for solc")
    options.add_argument("--phrack",
                         action="store_true",
                         help="Phrack-style call graph")
    options.add_argument("--enable-physics",
                         action="store_true",
                         help="enable graph physics simulation")
    options.add_argument("-v",
                         type=int,
                         help="log level (0-2)",
                         metavar="LOG_LEVEL")
    options.add_argument(
        "-q",
        "--query-signature",
        action="store_true",
        help="Lookup function signatures through www.4byte.directory",
    )

    rpc = parser.add_argument_group("RPC options")

    rpc.add_argument(
        "--rpc",
        help="custom RPC settings",
        metavar="HOST:PORT / ganache / infura-[network_name]",
        default="infura-mainnet",
    )
    rpc.add_argument("--rpctls",
                     type=bool,
                     default=False,
                     help="RPC connection over TLS")
    parser.add_argument("--epic", action="store_true", help=argparse.SUPPRESS)

    # Get config values

    args = parser.parse_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

    if not (args.search or args.hash or args.disassemble or args.graph
            or args.fire_lasers or args.storage or args.truffle
            or args.statespace_json or args.contract_hash_to_address):
        parser.print_help()
        sys.exit()

    if args.v:
        if 0 <= args.v < 3:
            coloredlogs.install(
                fmt="%(name)s[%(process)d] %(levelname)s %(message)s",
                level=[logging.NOTSET, logging.INFO, logging.DEBUG][args.v],
            )
        else:
            exit_with_error(
                args.outform,
                "Invalid -v value, you can find valid values in usage")

    if args.query_signature:
        if sigs.ethereum_input_decoder == None:
            exit_with_error(
                args.outform,
                "The --query-signature function requires the python package ethereum-input-decoder",
            )

    # -- commands --
    if args.hash:
        print(Mythril.hash_for_function_signature(args.hash))
        sys.exit()

    try:
        # the mythril object should be our main interface
        # infura = None, rpc = None, rpctls = None
        # solc_args = None, dynld = None, max_recursion_depth = 12):

        mythril = Mythril(
            solv=args.solv,
            dynld=args.dynld,
            onchain_storage_access=(not args.no_onchain_storage_access),
            solc_args=args.solc_args,
            enable_online_lookup=args.query_signature,
        )
        if (args.dynld or not args.no_onchain_storage_access
                and not (args.rpc or args.i)):
            mythril.set_api_from_config_path()

        if args.address:
            # Establish RPC connection if necessary
            mythril.set_api_rpc(rpc=args.rpc, rpctls=args.rpctls)
        elif args.search or args.contract_hash_to_address:
            # Open LevelDB if necessary
            mythril.set_api_leveldb(mythril.leveldb_dir if not args.leveldb_dir
                                    else args.leveldb_dir)

        if args.search:
            # Database search ops
            mythril.search_db(args.search)
            sys.exit()

        if args.contract_hash_to_address:
            # search corresponding address
            try:
                mythril.contract_hash_to_address(args.contract_hash_to_address)
            except AddressNotFoundError:
                print("Address not found.")

            sys.exit()

        if args.truffle:
            try:
                # not really pythonic atm. needs refactoring
                mythril.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()

        # Load / compile input contracts
        address = None

        if args.code:
            # Load from bytecode
            code = args.code[2:] if args.code.startswith("0x") else args.code
            address, _ = mythril.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, _ = mythril.load_from_bytecode(bytecode, args.bin_runtime)
        elif args.address:
            # Get bytecode from a contract address
            address, _ = mythril.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, _ = mythril.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",
            )

        # Commands

        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 = mythril.get_state_variable_from_storage(
                address=address,
                params=[a.strip() for a in args.storage.strip().split(",")],
            )
            print(storage)

        elif args.disassemble:
            easm_text = mythril.contracts[0].get_easm(
            )  # or mythril.disassemble(mythril.contracts[0])
            sys.stdout.write(easm_text)

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

            if args.graph:
                html = mythril.graph_html(
                    strategy=args.strategy,
                    contract=mythril.contracts[0],
                    address=address,
                    enable_physics=args.enable_physics,
                    phrackify=args.phrack,
                    max_depth=args.max_depth,
                    execution_timeout=args.execution_timeout,
                    create_timeout=args.create_timeout,
                )

                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 = mythril.fire_lasers(
                        strategy=args.strategy,
                        address=address,
                        modules=[
                            m.strip() for m in args.modules.strip().split(",")
                        ] if args.modules else [],
                        verbose_report=args.verbose_report,
                        max_depth=args.max_depth,
                        execution_timeout=args.execution_timeout,
                        create_timeout=args.create_timeout,
                        transaction_count=args.transaction_count,
                    )
                    outputs = {
                        "json": report.as_json(),
                        "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 mythril.contracts:
                exit_with_error(
                    args.outform,
                    "input files do not contain any valid contracts")

            statespace = mythril.dump_statespace(
                strategy=args.strategy,
                contract=mythril.contracts[0],
                address=address,
                max_depth=args.max_depth,
                execution_timeout=args.execution_timeout,
                create_timeout=args.create_timeout,
            )

            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()

    except CriticalError as ce:
        exit_with_error(args.outform, str(ce))
Esempio n. 7
0
def main():
    parser = argparse.ArgumentParser(
        description='Security analysis of Ethereum smart contracts')
    parser.add_argument("solidity_file", nargs='*')

    commands = parser.add_argument_group('commands')
    commands.add_argument('-g',
                          '--graph',
                          help='generate a control flow graph')
    commands.add_argument(
        '-x',
        '--fire-lasers',
        action='store_true',
        help='detect vulnerabilities, use with -c, -a or solidity file(s)')
    commands.add_argument(
        '-t',
        '--truffle',
        action='store_true',
        help='analyze a truffle project (run from project dir)')
    commands.add_argument('-d',
                          '--disassemble',
                          action='store_true',
                          help='print disassembly')
    commands.add_argument('-j',
                          '--statespace-json',
                          help='dumps the statespace json',
                          metavar='OUTPUT_FILE')

    inputs = parser.add_argument_group('input arguments')
    inputs.add_argument('-c',
                        '--code',
                        help='hex-encoded bytecode string ("6060604052...")',
                        metavar='BYTECODE')
    inputs.add_argument('-a',
                        '--address',
                        help='pull contract from the blockchain',
                        metavar='CONTRACT_ADDRESS')
    inputs.add_argument('-l',
                        '--dynld',
                        action='store_true',
                        help='auto-load dependencies from the blockchain')

    outputs = parser.add_argument_group('output formats')
    outputs.add_argument('-o',
                         '--outform',
                         choices=['text', 'markdown', 'json'],
                         default='text',
                         help='report output format',
                         metavar='<text/json>')
    outputs.add_argument('--verbose-report',
                         action='store_true',
                         help='Include debugging information in report')

    database = parser.add_argument_group('local contracts database')
    database.add_argument('--init-db',
                          action='store_true',
                          help='initialize the contract database')
    database.add_argument('-s',
                          '--search',
                          help='search the contract database',
                          metavar='EXPRESSION')

    utilities = parser.add_argument_group('utilities')
    utilities.add_argument('--hash',
                           help='calculate function signature hash',
                           metavar='SIGNATURE')
    utilities.add_argument(
        '--storage',
        help='read state variables from storage index, use with -a',
        metavar='INDEX,NUM_SLOTS,[array] / mapping,INDEX,[KEY1, KEY2...]')
    utilities.add_argument(
        '--solv',
        help=
        'specify solidity compiler version. If not present, will try to install it (Experimental)',
        metavar='SOLV')

    options = parser.add_argument_group('options')
    options.add_argument(
        '-m',
        '--modules',
        help='Comma-separated list of security analysis modules',
        metavar='MODULES')
    options.add_argument('--max-depth',
                         type=int,
                         default=12,
                         help='Maximum recursion depth for symbolic execution')
    options.add_argument('--solc-args', help='Extra arguments for solc')
    options.add_argument('--phrack',
                         action='store_true',
                         help='Phrack-style call graph')
    options.add_argument('--enable-physics',
                         action='store_true',
                         help='enable graph physics simulation')
    options.add_argument('-v',
                         type=int,
                         help='log level (0-2)',
                         metavar='LOG_LEVEL')
    options.add_argument('--leveldb',
                         help='enable direct leveldb access operations',
                         metavar='LEVELDB_PATH')

    rpc = parser.add_argument_group('RPC options')
    rpc.add_argument('-i',
                     action='store_true',
                     help='Preset: Infura Node service (Mainnet)')
    rpc.add_argument('--rpc',
                     help='custom RPC settings',
                     metavar='HOST:PORT / ganache / infura-[network_name]')
    rpc.add_argument('--rpctls',
                     type=bool,
                     default=False,
                     help='RPC connection over TLS')
    rpc.add_argument('--ipc',
                     action='store_true',
                     help='Connect via local IPC')

    # Get config values

    args = parser.parse_args()

    # -- args sanity checks --
    # Detect unsupported combinations of command line args

    if args.dynld and not args.address:
        exit_with_error(
            args.outform,
            "Dynamic loader can be used in on-chain analysis mode only (-a).")

    # Parse cmdline args

    if not (args.search or args.init_db or args.hash or args.disassemble
            or args.graph or args.fire_lasers or args.storage or args.truffle
            or args.statespace_json):
        parser.print_help()
        sys.exit()

    if args.v:
        if 0 <= args.v < 3:
            logging.basicConfig(
                level=[logging.NOTSET, logging.INFO, logging.DEBUG][args.v])
        else:
            exit_with_error(
                args.outform,
                "Invalid -v value, you can find valid values in usage")

    # -- commands --
    if args.hash:
        print(Mythril.hash_for_function_signature(args.hash))
        sys.exit()

    try:
        # the mythril object should be our main interface
        #init_db = None, infura = None, rpc = None, rpctls = None, ipc = None,
        #solc_args = None, dynld = None, max_recursion_depth = 12):

        mythril = Mythril(solv=args.solv,
                          dynld=args.dynld,
                          solc_args=args.solc_args)

        if args.leveldb:
            # Open LevelDB if specified
            mythril.set_db_leveldb(args.leveldb)

        elif (args.address or args.init_db) and not args.leveldb:
            # Establish RPC/IPC connection if necessary
            if args.i:
                mythril.set_db_rpc_infura()
            elif args.rpc:
                mythril.set_db_rpc(rpc=args.rpc, rpctls=args.rpctls)
            elif args.ipc:
                mythril.set_db_ipc()
            else:
                mythril.set_db_rpc_localhost()

        if args.truffle:
            try:
                # not really pythonic atm. needs refactoring
                mythril.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()

        elif args.search:
            # Database search ops
            mythril.search_db(args.search)
            sys.exit()

        elif args.init_db:
            mythril.init_db()
            sys.exit()

        # Load / compile input contracts
        address = None

        if args.code:
            # Load from bytecode
            address, _ = mythril.load_from_bytecode(args.code)
        elif args.address:
            # Get bytecode from a contract address
            address, _ = mythril.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, _ = mythril.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"
            )

        # Commands

        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 = mythril.get_state_variable_from_storage(
                address=address,
                params=[a.strip() for a in args.storage.strip().split(",")])
            print(storage)

        elif args.disassemble:
            easm_text = mythril.contracts[0].get_easm(
            )  # or mythril.disassemble(mythril.contracts[0])
            sys.stdout.write(easm_text)

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

            if args.graph:
                html = mythril.graph_html(mythril.contracts[0],
                                          address=address,
                                          enable_physics=args.enable_physics,
                                          phrackify=args.phrack,
                                          max_depth=args.max_depth)

                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:
                report = mythril.fire_lasers(
                    address=address,
                    modules=[
                        m.strip() for m in args.modules.strip().split(",")
                    ] if args.modules else [],
                    verbose_report=args.verbose_report,
                    max_depth=args.max_depth)
                outputs = {
                    'json':
                    report.as_json(),
                    'text':
                    report.as_text() or
                    "The analysis was completed successfully. No issues were detected.",
                    'markdown':
                    report.as_markdown() or
                    "The analysis was completed successfully. No issues were detected."
                }
                print(outputs[args.outform])

        elif args.statespace_json:

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

            statespace = mythril.dump_statespace(mythril.contracts[0],
                                                 address=address,
                                                 max_depth=args.max_depth)

            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()

    except CriticalError as ce:
        exit_with_error(args.outform, str(ce))
Esempio n. 8
0
def mymain(file):

    #   --zyx
    cmd = [
        "solc", "--combined-json", "bin,bin-runtime,srcmap-runtime",
        '--allow-paths', "."
    ]
    cmd.append(file)
    p = Popen(cmd, stdout=PIPE, stderr=PIPE)

    stdout, stderr = p.communicate()
    ret = p.returncode
    if ret != 0:
        return "Solc experienced a fatal error (code" + str(
            ret) + ").\n\n" + str(stderr.decode('UTF-8')), 0
    #  --zyx

    parser = argparse.ArgumentParser(
        description='Security analysis of Ethereum smart contracts')
    parser.add_argument("solidity_file", nargs='*')

    commands = parser.add_argument_group('commands')
    commands.add_argument('-g',
                          '--graph',
                          help='generate a control flow graph')
    commands.add_argument('-V',
                          '--version',
                          action='store_true',
                          help='print the Mythril version number and exit')
    commands.add_argument(
        '-x',
        '--fire-lasers',
        action='store_true',
        help='detect vulnerabilities, use with -c, -a or solidity file(s)')
    commands.add_argument(
        '-t',
        '--truffle',
        action='store_true',
        help='analyze a truffle project (run from project dir)')
    commands.add_argument('-d',
                          '--disassemble',
                          action='store_true',
                          help='print disassembly')
    commands.add_argument('-j',
                          '--statespace-json',
                          help='dumps the statespace json',
                          metavar='OUTPUT_FILE')

    inputs = parser.add_argument_group('input arguments')
    inputs.add_argument('-c',
                        '--code',
                        help='hex-encoded bytecode string ("6060604052...")',
                        metavar='BYTECODE')
    inputs.add_argument('-a',
                        '--address',
                        help='pull contract from the blockchain',
                        metavar='CONTRACT_ADDRESS')
    inputs.add_argument('-l',
                        '--dynld',
                        action='store_true',
                        help='auto-load dependencies from the blockchain')

    outputs = parser.add_argument_group('output formats')
    outputs.add_argument('-o',
                         '--outform',
                         choices=['text', 'markdown', 'json'],
                         default='text',
                         help='report output format',
                         metavar='<text/json>')
    outputs.add_argument('--verbose-report',
                         action='store_true',
                         help='Include debugging information in report')

    database = parser.add_argument_group('local contracts database')
    database.add_argument('-s',
                          '--search',
                          help='search the contract database',
                          metavar='EXPRESSION')
    database.add_argument(
        '--leveldb-dir',
        help='specify leveldb directory for search or direct access operations',
        metavar='LEVELDB_PATH')

    utilities = parser.add_argument_group('utilities')
    utilities.add_argument('--hash',
                           help='calculate function signature hash',
                           metavar='SIGNATURE')
    utilities.add_argument(
        '--storage',
        help='read state variables from storage index, use with -a',
        metavar='INDEX,NUM_SLOTS,[array] / mapping,INDEX,[KEY1, KEY2...]')
    utilities.add_argument(
        '--solv',
        help=
        'specify solidity compiler version. If not present, will try to install it (Experimental)',
        metavar='SOLV')
    utilities.add_argument(
        '--contract-hash-to-address',
        help='returns corresponding address for a contract address hash',
        metavar='SHA3_TO_LOOK_FOR')

    options = parser.add_argument_group('options')
    options.add_argument(
        '-m',
        '--modules',
        help='Comma-separated list of security analysis modules',
        metavar='MODULES')
    options.add_argument('--max-depth',
                         type=int,
                         default=22,
                         help='Maximum recursion depth for symbolic execution')
    options.add_argument('--execution-timeout',
                         type=int,
                         default=600,
                         help="The amount of seconds to spend on "
                         "symbolic execution")
    outputs.add_argument('--strategy',
                         choices=['dfs', 'bfs'],
                         default='dfs',
                         help='Symbolic execution strategy')

    options.add_argument('--solc-args', help='Extra arguments for solc')
    options.add_argument('--phrack',
                         action='store_true',
                         help='Phrack-style call graph')
    options.add_argument('--enable-physics',
                         action='store_true',
                         help='enable graph physics simulation')
    options.add_argument('-v',
                         type=int,
                         help='log level (0-2)',
                         metavar='LOG_LEVEL')

    rpc = parser.add_argument_group('RPC options')
    rpc.add_argument('-i',
                     action='store_true',
                     help='Preset: Infura Node service (Mainnet)')
    rpc.add_argument('--rpc',
                     help='custom RPC settings',
                     metavar='HOST:PORT / ganache / infura-[network_name]')
    rpc.add_argument('--rpctls',
                     type=bool,
                     default=False,
                     help='RPC connection over TLS')
    rpc.add_argument('--ipc',
                     action='store_true',
                     help='Connect via local IPC')
    rpc.add_argument('--leveldb',
                     action='store_true',
                     help='Enable direct leveldb access operations')

    # Get config values
    # args = parser.parse_args()
    # print("args: "+str(type(args)))
    # print("args : " + str(args))
    # I did it --zyx
    arrr = argparse.Namespace(address=None,
                              code=None,
                              contract_hash_to_address=None,
                              disassemble=False,
                              dynld=False,
                              enable_physics=False,
                              execution_timeout=600,
                              fire_lasers=True,
                              graph=None,
                              hash=None,
                              i=False,
                              ipc=False,
                              leveldb=False,
                              leveldb_dir=None,
                              max_depth=22,
                              modules=None,
                              outform='text',
                              phrack=False,
                              rpc=None,
                              rpctls=False,
                              search=None,
                              solc_args=None,
                              solidity_file=[str(file)],
                              solv=None,
                              statespace_json=None,
                              storage=None,
                              strategy='dfs',
                              truffle=False,
                              v=None,
                              verbose_report=False,
                              version=False)
    '''
    args : Namespace(address=None, 
                    code=None, 
                    contract_hash_to_address=None, 
                    disassemble=False, 
                    dynld=False, 
                    enable_physics=False, 
                    execution_timeout=600, 
                    fire_lasers=True, 
                    graph=None, 
                    hash=None, 
                    i=False, 
                    ipc=False, 
                    leveldb=False, 
                    leveldb_dir=None, 
                    max_depth=22, 
                    modules=None, 
                    outform='text', 
                    phrack=False, 
                    rpc=None, 
                    rpctls=False, 
                    search=None, 
                    solc_args=None, 
                    solidity_file=['calls.sol'], 
                    solv=None, 
                    statespace_json=None, 
                    storage=None, 
                    strategy='dfs', 
                    truffle=False,
                    v=None, 
                    verbose_report=False, 
                    version=False)
    '''
    # the rest arrr if changed from  args  and It's me who did this --zyx
    if arrr.version:
        print("Mythril version {}".format(VERSION))
        sys.exit()

    # Parse cmdline args

    if not (arrr.search or arrr.hash or arrr.disassemble or arrr.graph
            or arrr.fire_lasers or arrr.storage or arrr.truffle
            or arrr.statespace_json or arrr.contract_hash_to_address):
        parser.print_help()
        sys.exit()

    if arrr.v:
        if 0 <= arrr.v < 3:
            logging.basicConfig(
                level=[logging.NOTSET, logging.INFO, logging.DEBUG][arrr.v])
        else:
            exit_with_error(
                arrr.outform,
                "Invalid -v value, you can find valid values in usage")

    # -- commands --
    if arrr.hash:
        print(Mythril.hash_for_function_signature(arrr.hash))
        sys.exit()

    try:
        # the mythril object should be our main interface
        # infura = None, rpc = None, rpctls = None, ipc = None,
        # solc_args = None, dynld = None, max_recursion_depth = 12):

        mythril = Mythril(solv=arrr.solv,
                          dynld=arrr.dynld,
                          solc_args=arrr.solc_args)
        if arrr.dynld and not (arrr.ipc or arrr.rpc or arrr.i):
            mythril.set_api_from_config_path()

        if arrr.address and not arrr.leveldb:
            # Establish RPC/IPC connection if necessary
            if arrr.i:
                mythril.set_api_rpc_infura()
            elif arrr.rpc:
                mythril.set_api_rpc(rpc=arrr.rpc, rpctls=arrr.rpctls)
            elif arrr.ipc:
                mythril.set_api_ipc()
            elif not arrr.dynld:
                mythril.set_api_rpc_localhost()
        elif arrr.leveldb or arrr.search or arrr.contract_hash_to_address:
            # Open LevelDB if necessary
            mythril.set_api_leveldb(mythril.leveldb_dir if not arrr.leveldb_dir
                                    else arrr.leveldb_dir)

        if arrr.search:
            # Database search ops
            mythril.search_db(arrr.search)
            sys.exit()

        if arrr.contract_hash_to_address:
            # search corresponding address
            mythril.contract_hash_to_address(arrr.contract_hash_to_address)
            sys.exit()

        if arrr.truffle:
            try:
                # not really pythonic atm. needs refactoring
                mythril.analyze_truffle_project(arrr)
            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()

        # Load / compile input contracts
        address = None

        if arrr.code:
            # Load from bytecode
            address, _ = mythril.load_from_bytecode(arrr.code)
        elif arrr.address:
            # Get bytecode from a contract address
            address, _ = mythril.load_from_address(arrr.address)
        elif arrr.solidity_file:
            # Compile Solidity source file(s)
            if arrr.graph and len(arrr.solidity_file) > 1:
                exit_with_error(
                    arrr.outform,
                    "Cannot generate call graphs from multiple input files. Please do it one at a time."
                )
            address, _ = mythril.load_from_solidity(
                arrr.solidity_file)  # list of files
        else:
            exit_with_error(
                arrr.outform,
                "No input bytecode. Please provide EVM code via -c BYTECODE, -a ADDRESS, or -i SOLIDITY_FILES"
            )

        # Commands

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

            storage = mythril.get_state_variable_from_storage(
                address=address,
                params=[a.strip() for a in arrr.storage.strip().split(",")])
            print(storage)

        elif arrr.disassemble:
            easm_text = mythril.contracts[0].get_easm(
            )  # or mythril.disassemble(mythril.contracts[0])
            sys.stdout.write(easm_text)

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

            if arrr.graph:
                html = mythril.graph_html(strategy=arrr.strategy,
                                          contract=mythril.contracts[0],
                                          address=address,
                                          enable_physics=arrr.enable_physics,
                                          phrackify=arrr.phrack,
                                          max_depth=arrr.max_depth)

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

            else:
                report = mythril.fire_lasers(
                    strategy=arrr.strategy,
                    address=address,
                    modules=[
                        m.strip() for m in arrr.modules.strip().split(",")
                    ] if arrr.modules else [],
                    verbose_report=arrr.verbose_report,
                    max_depth=arrr.max_depth,
                    execution_timeout=arrr.execution_timeout)
                outputs = {
                    'json': report.as_json(),
                    'text': report.as_text(),
                    'markdown': report.as_markdown()
                }
                #输出
                print(outputs[arrr.outform])

        elif arrr.statespace_json:

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

            statespace = mythril.dump_statespace(strategy=arrr.strategy,
                                                 contract=mythril.contracts[0],
                                                 address=address,
                                                 max_depth=arrr.max_depth)

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

        else:
            parser.print_help()

    except CriticalError as ce:
        exit_with_error(arrr.outform, str(ce))
    return mythril.zyx_lasers(
        strategy=arrr.strategy,
        address=address,
        modules=[m.strip() for m in arrr.modules.strip().split(",")]
        if arrr.modules else [],
        verbose_report=arrr.verbose_report,
        max_depth=arrr.max_depth,
        execution_timeout=arrr.execution_timeout), 1