Exemplo n.º 1
0
 def testReplace(self):
     old_env = env.Env(b"")
     new_env = old_env.clean_copy()
     state = State(new_env)
     state.storage_written[old_env.caller] = old_env.value
     state.replace(functools.partial(env.replace, old_env, state.env))
     self.assertIs(state.storage_written[new_env.caller], new_env.value)
Exemplo n.º 2
0
    def testReplace2(self):
        a = env.Env(b"")
        sa = a.value + a.caller + a.origin

        b = a.clean_copy()
        sb = b.value + b.caller + b.origin

        self.assertIsNot(sa, sb)
        self.assertIs(env.replace(a, b, sa), sb)
Exemplo n.º 3
0
    def runTest(self):
        logger.info("Compiling contract %s" % self.filename)
        p = subprocess.run(
            [
                "solc", "--optimize", "--combined-json=bin-runtime",
                self.filename
            ],
            capture_output=True,
            text=True,
        )
        self.assertEqual(p.returncode, 0,
                         "solc compilation failed:\n%s" % p.stderr)

        output = json.loads(p.stdout)

        assert "contracts" in output
        identifier, properties = list(output["contracts"].items())[0]
        bin_runtime = properties["bin-runtime"]

        logger.info("Runtime bytecode: %s", bin_runtime)
        bin_runtime = codecs.decode(bin_runtime, "hex")
        logger.info("Compiled. Symbolic execution.")

        e = env.Env(
            bin_runtime,
            address=utils.bvv(int(ADDRESS, 16)),
            caller=utils.DEFAULT_CALLER,
            origin=utils.DEFAULT_CALLER,
            balance=utils.bvv(BALANCE),
        )
        s = sm.SymbolicMachine(e)
        s.execute(timeout_sec=EXEC_TIMEOUT)

        self.assertTrue(s.outcomes)

        ra = recursive_analyzer.RecursiveAnalyzer(
            max_wei_to_send=MAX_TO_SEND,
            min_wei_to_receive=MIN_TO_RECEIVE,
            block="invalid",
        )

        # Never contact the blockchain, instead all the storage are 0
        ra.actual_storage = {}

        bug = ra.check_states(s.outcomes,
                              timeout=ANALYSIS_TIMEOUT,
                              max_depth=MAX_TRANSACTION_DEPTH)

        self.assertTrue(bug, self.filename)
Exemplo n.º 4
0
    def testSameHashIfDifferentOrder(self):
        a = State()
        b = State()
        self.assertEqual(hash(a), hash(b))

        e = env.Env(b"")

        a.solver.add(e.value == 1)
        a.solver.add(e.block_timestamp == 2)

        # Same thing, different order
        b.solver.add(e.block_timestamp == 2)
        b.solver.add(e.value == 1)

        self.assertEqual(hash(a), hash(b))
Exemplo n.º 5
0
    def test_env_replace_merge_with_recursive_hash(self):
        old_env = env.Env(b"")
        new_env = old_env.clean_copy()

        old_state = State(old_env)
        old_state.solver.add(Sha3(Sha3(old_env.caller)) == Sha3(old_env.value))

        self.assertTrue(old_state.solver.satisfiable())
        self.assertFalse(
            old_state.solver.satisfiable(
                extra_constraints=[old_env.value == 5]))

        new_state = old_state.copy()
        new_state.replace(functools.partial(env.replace, old_env, new_env))
        new_state.replace(new_state.solver.regenerate_hash_symbols())

        self.assertTrue(new_state.solver.satisfiable())
        self.assertFalse(
            new_state.solver.satisfiable(
                extra_constraints=[new_env.value == 5]))
        self.assertTrue(
            new_state.solver.satisfiable(
                extra_constraints=[old_env.value == 5]))

        new_state.solver.add(old_env.value == new_env.value)
        self.assertTrue(new_state.solver.satisfiable())
        self.assertFalse(
            new_state.solver.satisfiable(
                extra_constraints=[new_env.value == 5]))
        self.assertFalse(
            new_state.solver.satisfiable(
                extra_constraints=[old_env.value == 5]))

        old_state.solver = old_state.solver.combine([new_state.solver])
        self.assertTrue(new_state.solver.satisfiable())
        self.assertEqual(len(old_state.solver.constraints), 3)
        self.assertEqual(len(old_state.solver.hashes),
                         len(new_state.solver.hashes) * 2)
Exemplo n.º 6
0
    def testHashWorks(self):
        state = State(env.Env(b""))
        state.pc = 5
        state.memory.write(0, 1, claripy.BVV(42, 8))
        state.memory.write(10, 1, claripy.BVV(43, 8))
        state.memory.write(20, 1, claripy.BVV(44, 8))
        state_copy = state.copy()
        self.assertEqual(hash(state), hash(state_copy))

        state.pc = 6
        self.assertNotEqual(hash(state), hash(state_copy))
        state_copy.pc = 6
        self.assertEqual(hash(state), hash(state_copy))

        state.memory.write(10, 1, claripy.BVV(45, 8))
        self.assertNotEqual(hash(state), hash(state_copy))
        state_copy.memory.write(10, 1, claripy.BVV(45, 8))
        self.assertEqual(hash(state), hash(state_copy))

        state.stack_push(state.env.calldata.read(0, 1))
        self.assertNotEqual(hash(state), hash(state_copy))
        state_copy.stack_push(state_copy.env.calldata.read(0, 1))
        self.assertEqual(hash(state), hash(state_copy))
Exemplo n.º 7
0
def main():
    args = parser.parse_args()

    if args.v.isnumeric():
        coloredlogs.install(level=int(args.v))
    elif hasattr(logging, args.v.upper()):
        coloredlogs.install(level=getattr(logging, args.v.upper()))
    else:
        err_exit("Logging should be DEBUG/INFO/WARNING/ERROR.")

    try:
        logging.debug("Node working. Block %i ", w3.eth.blockNumber)
    except web3.exceptions.CannotHandleRequest:
        err_exit(
            "Seems like Web3.py can't connect to your Ethereum node.\n"
            "If you don't have one and you want to use Infura, you can set "
            "WEB3_PROVIDER_URI as follows:\n"
            "$ export WEB3_PROVIDER_URI='https://mainnet.infura.io'")

    if args.contract_addr == "-":
        # Let's read the runtime bytecode from stdin
        code = sys.stdin.read().strip("\n")
        if not code.isalnum():
            err_exit(
                "Runtime bytecode read from stdin needs to be hexadecimal.")
        code = codecs.decode(code, "hex")
        # Dummy address, dummy balance
        args.contract_addr = "0xDEADBEEF00000000000000000000000000000000"
        if not args.force_balance:
            args.force_balance = Web3.toWei(1.337, "ether")
    else:
        code = w3.eth.getCode(args.contract_addr, block_identifier=args.block)

    balance = args.force_balance or w3.eth.getBalance(
        args.contract_addr, block_identifier=args.block)

    print("Analyzing contract at %s with balance %f ether." %
          (args.contract_addr, Web3.fromWei(balance, "ether")))

    if balance < args.min_to_receive:
        err_exit("Balance is smaller than --min-to-receive: "
                 "the analyzer will never find anything.")

    if args.summarize:
        logging.info(
            "Summarizer enabled, we won't constrain the caller/origin "
            "so more of the contract can get explored. "
            "It may be slower.")
        e = env.Env(
            code,
            address=utils.bvv(int(args.contract_addr, 16)),
            balance=utils.bvv(balance),
        )
    else:
        e = env.Env(
            code,
            address=utils.bvv(int(args.contract_addr, 16)),
            caller=utils.DEFAULT_CALLER,
            origin=utils.DEFAULT_CALLER,
            balance=utils.bvv(balance),
        )

    print("Starting symbolic execution step...")

    s = sm.SymbolicMachine(e, fuzz=not args.disable_fuzzing)
    s.execute(timeout_sec=args.exec_timeout)

    print("Symbolic execution finished with coverage %i%%." %
          int(s.get_coverage() * 100))
    print("Outcomes: %i interesting. %i total and %i unfinished paths." % (
        sum(int(o.is_interesting()) for o in s.outcomes),
        len(s.outcomes),
        len(s.partial_outcomes),
    ))

    if args.summarize:
        print()
        print("Methods from the summarizer:")
        summary.HumanSummarizer(s).print_methods()

    print()
    print("Starting analysis step...")

    ra = recursive_analyzer.RecursiveAnalyzer(
        max_wei_to_send=args.max_to_send,
        min_wei_to_receive=args.min_to_receive,
        block=args.block,
    )
    bug = ra.check_states(s.outcomes,
                          timeout=args.analysis_timeout,
                          max_depth=args.max_transaction_depth)

    if bug:
        solver = bug[2]
        if logging.getLogger().isEnabledFor(logging.DEBUG):
            print("Composite state:")
            print(bug[0].debug_string())
            print()
            print()
        print("Path:")
        for i, state in enumerate(bug[1]):
            print()
            print("Transaction %i, symbolic state:" % (i + 1))
            print(state.debug_string())
            print()
            print("Transaction %i, example solution:" % (i + 1))
            print(state.env.solution_string(solver))
            print()
        print()
        print("======> Bug found! Need %i transactions. <======" % len(bug[1]))
    else:
        print("Nothing to report.")
Exemplo n.º 8
0
 def testReplace(self):
     a = env.Env(b"")
     calldata_a = a.calldata.read(0, 32)
     b = a.clean_copy()
     self.assertIs(env.replace(a, b, a.value), b.value)
     self.assertIs(env.replace(a, b, calldata_a), b.calldata.read(0, 32))