Beispiel #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)
    def test_sha3_key(self):
        """Exercise solidity-like mappings, with the key being a sha3."""
        state = State(self.env)

        state_write = state.copy()
        # Arbitrary write input[1], at SHA3(input[0])
        state_write.storage_written = {
            Sha3(self.env.calldata.read(4, 32)): self.env.calldata.read(36, 32)
        }

        # Needs that: storage[SHA3(input[0])] == 43, made possible by the previous call
        state_selfdestruct = state.copy()
        state_selfdestruct.selfdestruct_to = self.env.calldata.read(36, 32)
        storage_input = claripy.BVS("storage[SHA3(input)]", 256)
        state_selfdestruct.storage_read = {
            Sha3(self.env.calldata.read(4, 32)): storage_input
        }
        state_selfdestruct.solver.add(storage_input == 0xDEADBEEF101010)

        storage = {
            55186156870478567193644641351382124067713781048612400765092754877653207859685: 0
        }
        self.assertTrue(
            self.check_states([state_write, state_selfdestruct], mock_storage=storage)
        )
        self.assertFalse(self.check_states([state_selfdestruct], mock_storage=storage))
        self.assertFalse(self.check_states([state_write]))
    def test_write_write_and_selfdestruct(self):
        state = State(self.env)
        # Anybody can set owner
        state_write1 = state.copy()
        state_write1.storage_written = {utils.bvv(0): self.env.calldata.read(4, 32)}

        # Onlyowner: set a magic constant allowing the selfdestruct bug, at an
        # user-controlled storage key.
        state_write2 = state.copy()
        read_0 = claripy.BVS("storage[0]", 256)
        state_write2.storage_read = {utils.bvv(0): read_0}
        state_write2.storage_written = {
            self.env.calldata.read(36, 32): self.env.calldata.read(4, 32)
        }
        state_write2.solver.add(read_0 == self.env.caller)

        # Suicide, when owner and magic constant set
        state_selfdestruct = state.copy()
        read_0 = claripy.BVS("storage[0]", 256)
        read_40 = claripy.BVS("storage[4]", 256)
        state_selfdestruct.storage_read = {utils.bvv(0): read_0, utils.bvv(40): read_40}
        state_selfdestruct.solver.add(self.env.caller == read_0)
        state_selfdestruct.solver.add(read_40 == 1337)
        state_selfdestruct.selfdestruct_to = self.env.caller

        states = [state_write1, state_write2, state_selfdestruct]
        random.shuffle(states)

        storage = {0: 123456789, 40: 387642}
        for s in itertools.combinations(states, 2):
            self.assertFalse(self.check_states(s, mock_storage=storage))
        self.assertTrue(self.check_states(states, mock_storage=storage))
    def test_sha3_value2(self):
        """Same as above, but we need to pass the computed SHA3."""
        state = State(self.env)

        state_write = state.copy()
        state_write.storage_written = {
            utils.bvv(0): Sha3(self.env.calldata.read(4, 32))
        }

        state_selfdestruct = state.copy()
        state_selfdestruct.selfdestruct_to = self.env.calldata.read(36, 32)
        storage_input = claripy.BVS("storage[0]", 256)
        state_selfdestruct.storage_read = {utils.bvv(0): storage_input}
        state_selfdestruct.solver.add(
            storage_input == self.env.calldata.read(4, 32))
        state_selfdestruct.solver.add(storage_input != 0)

        storage = {0: 0}
        self.assertTrue(
            self.check_states([state_write, state_selfdestruct],
                              mock_storage=storage))
        self.assertFalse(
            self.check_states([state_selfdestruct], mock_storage=storage))
        self.assertFalse(self.check_states([state_write],
                                           mock_storage=storage))
    def test_symbolic_storage(self):
        """Specific test for using a storage key that cannot be symbolized."""
        state = State(self.env)
        storage = {10: 1}

        # We write to an arbitrary address
        state_write = state.copy()
        state_write.storage_written[state_write.env.calldata.read(
            4, 32)] = state_write.env.calldata.read(36, 32)

        # We send twice what we receive, but only if we have 1 at two arbitrary
        # keys.
        state_send = state.copy()
        storage_a = claripy.BVS("storage[a]", 256)
        storage_b = claripy.BVS("storage[b]", 256)
        k_a = state_send.env.calldata.read(4, 32)
        k_b = state_send.env.calldata.read(36, 32)
        state_send.storage_read[k_a] = storage_a
        state_send.storage_read[k_b] = storage_b
        state_send.solver.add(storage_a == 1)
        state_send.solver.add(storage_b == 1)
        state_send.calls.append(self.get_call((state.env.value * 128) / 64))

        # If k_a == 10 and k_b == 10, it works!
        self.assertTrue(self.check_states([state_send], mock_storage=storage))
        state_send.solver.add(k_a != k_b)

        self.assertFalse(self.check_states([state_send], mock_storage=storage))
        self.assertFalse(self.check_states([state_write],
                                           mock_storage=storage))

        # Now we have to first write, then send.
        bug = self.check_states([state_send, state_write],
                                mock_storage=storage)
        self.assertTrue(bug)
        self.assertEqual(len(bug[1]), 2)

        # If we force k_a to be != 10, we can use k_b == 10 instead.
        state_send.solver.add(k_a != 10)
        bug = self.check_states([state_send, state_write],
                                mock_storage=storage)
        self.assertTrue(bug)
        self.assertEqual(len(bug[1]), 2)

        # If we force both, it's impossible and we have to do two writes.
        state_send.solver.add(k_b != 10)
        bug = self.check_states([state_send, state_write],
                                mock_storage=storage)
        self.assertTrue(bug)
        self.assertEqual(len(bug[1]), 3)
Beispiel #6
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))
    def test_write_and_selfdestruct(self):
        state = State(self.env)

        state_write = state.copy()
        state_write.storage_written = {utils.bvv(0): self.env.calldata.read(4, 32)}

        state_selfdestruct = state.copy()
        state_selfdestruct.selfdestruct_to = self.env.calldata.read(4, 32)
        storage_0 = claripy.BVS("storage[0]", 256)
        state_selfdestruct.storage_read = {utils.bvv(0): storage_0}
        state_selfdestruct.solver.add(storage_0 == 0xDEADBEEF0101)

        storage = {0: 0xBAD1DEA}
        self.assertTrue(
            self.check_states([state_write, state_selfdestruct], mock_storage=storage)
        )
        self.assertFalse(self.check_states([state_selfdestruct], mock_storage=storage))
        self.assertFalse(self.check_states([state_write]))
Beispiel #8
0
    def setUp(self):
        self.env = Env(b"",
                       caller=utils.DEFAULT_CALLER,
                       address=utils.DEFAULT_ADDRESS)

        self.state = State(self.env)
        self.analyzer = Analyzer(
            address=self.env.address,
            caller=self.env.caller,
            max_wei_to_send=Web3.toWei(10, "ether"),
            min_wei_to_receive=Web3.toWei(1, "milliether"),
        )
    def check_states(self, states, timeout, max_depth):
        states = [state for state in states if state.is_interesting()]
        if not states:
            return

        # Each state must have its own independent environment
        assert not self.reference_states

        # For each state, a list of equivalent state, but each in a different
        # env so that they can be stacked together.
        self.reference_states = []
        for state in states:
            self.reference_states.append(
                [with_new_env(state) for _ in range(max_depth)])
            # Add it to the paths to explore
            self.path_queue.append((State(), [self.reference_states[-1][0]]))

        # Recursive exploration
        last_path_len = 0
        time_start = time.process_time()
        while self.path_queue:
            initial_composite_state, path = self.path_queue.popleft()

            if len(path) > last_path_len:
                logger.log(
                    utils.INFO_INTERACTIVE,
                    "Now scanning paths of length %i.",
                    len(path),
                )
                last_path_len = len(path)
            if len(path) > max_depth:
                logger.debug("Over the max allowed depth, stopping.")
                return

            if DEBUG_MARK_PATH and all(
                    is_function(s, f) for s, f in zip(path, DEBUG_MARK_PATH)):
                logger.warning("DEBUG_MARK_PATH len %i", len(path))
                logger.warning("path: %s", path)
                breakpoint()

            new_composite_states = self._append_state(initial_composite_state,
                                                      path[-1])

            for composite_state in new_composite_states:
                solver = self._search_path(composite_state, path)
                if solver is not None:
                    return composite_state, path, solver

            if timeout and time.process_time() - time_start > timeout:
                logger.debug("Timeout at depth %i, stopping.", len(path))
                return
Beispiel #10
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)
    def test_sha3_value1(self):
        """Exercise comparison of two SHA3 (as values)."""
        state = State(self.env)

        state_write = state.copy()
        state_write.storage_written = {
            utils.bvv(0): Sha3(self.env.calldata.read(4, 32))
        }

        state_selfdestruct = state.copy()
        state_selfdestruct.selfdestruct_to = self.env.calldata.read(36, 32)
        storage_input = claripy.BVS("storage[0]", 256)
        state_selfdestruct.storage_read = {utils.bvv(0): storage_input}
        state_selfdestruct.solver.add(
            storage_input == Sha3(self.env.calldata.read(4, 32))
        )

        storage = {0: 0}
        self.assertTrue(
            self.check_states([state_write, state_selfdestruct], mock_storage=storage)
        )
        self.assertFalse(self.check_states([state_selfdestruct], mock_storage=storage))
        self.assertFalse(self.check_states([state_write], mock_storage=storage))
Beispiel #12
0
 def __init__(self, env):
     self.code = env.code
     logger.debug("Initializing symbolic machine with source code: %s",
                  self.code)
     # For use by heapq only. Contains couples (score, state).
     self.branch_queue = []
     self.states_seen = set()
     self.coverage = [0] * len(self.code)
     # List of all normal/good terminations of the contract
     self.outcomes = []
     # List of all the place where we didn't know how to continue execution
     self.partial_outcomes = []
     self.fuzz = True
     self.code_errors = collections.Counter()
     self.interpreter_errors = collections.Counter()
     self.add_branch(State(env))
    def test_with_new_env(self):
        env = Env(b"")
        state = State(env)

        storage_0 = claripy.BVS("storage[0]", 256)
        storage_1 = claripy.BVS("storage[0]", 256)
        storage_2 = claripy.BVS("storage[0]", 256)
        state.storage_read[utils.bvv(0)] = storage_0
        state.storage_read[utils.bvv(1)] = storage_1
        state.storage_read[utils.bvv(2)] = storage_2
        state.storage_written[utils.bvv(0)] = utils.bvv(0)
        state.storage_written[utils.bvv(1)] = utils.bvv(0)
        state.storage_written[utils.bvv(2)] = utils.bvv(0)

        state.calls.append([
            utils.bvv(1),
            storage_0 + storage_1 + storage_2,
            utils.bvv(2),
            5 * (storage_0 + storage_1 + storage_2),
        ])
        state.solver.add(storage_0 == 42)
        state.solver.add(storage_1 == 0)
        state.solver.add(storage_2 == 0)

        self.assertEqual(state.solver.eval(state.calls[0][1], 2), (42, ))

        for i in range(3):
            new_state = with_new_env(state)

            self.assertIsNot(state.env.value, new_state.env.value)
            self.assertIsNot(state.storage_read[utils.bvv(0)],
                             new_state.storage_read[utils.bvv(0)])
            self.assertIsNot(state.storage_read[utils.bvv(1)],
                             new_state.storage_read[utils.bvv(1)])
            self.assertIsNot(state.storage_read[utils.bvv(2)],
                             new_state.storage_read[utils.bvv(2)])

            self.assertNotEqual(new_state.solver.eval(state.calls[0][1], 2),
                                (42, ))
            self.assertEqual(new_state.solver.eval(new_state.calls[0][1], 2),
                             (42, ))
Beispiel #14
0
 def __init__(self, env, fuzz=True):
     self.code = env.code
     logger.debug("Initializing symbolic machine with source code: %s",
                  self.code)
     # For use by heapq only. Contains couples (score, state).
     self.branch_queue = []
     self.states_seen = set()
     self.coverage = [0] * len(self.code)
     # List of all normal/good terminations of the contract
     self.outcomes = []
     # List of all the place where we didn't know how to continue execution
     self.partial_outcomes = []
     # Do we want to enable fuzzing? (see add_for_fuzzing below)
     self.fuzz = fuzz
     # Did fuzzing got used?
     self.fuzzed = False
     # Errors that happened during execution. These are normal.
     self.code_errors = collections.Counter()
     # Errors of the interpreter / symbolic execution engine. Not cool :(
     self.interpreter_errors = collections.Counter()
     self.add_branch(State(env))
Beispiel #15
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))
 def test_call_simple(self):
     state = State(self.env)
     state.calls.append(self.get_call(self.env.balance))
     self.assertTrue(self.check_states([state]))
 def test_selfdestruct_simple(self):
     state = State(self.env)
     state.selfdestruct_to = self.env.caller
     self.assertTrue(self.check_states([state]))
 def test_simple(self):
     state = State(self.env)
     self.assertFalse(self.check_states([state]))
    def test_send_after_write(self):
        state = State(self.env)

        # We send storage[0]
        state_send = state.copy()
        storage_0 = claripy.BVS("storage[0]", 256)
        state_send.storage_read = {utils.bvv(0): storage_0}
        state_send.calls.append(self.get_call(storage_0))

        # storage[0] is 0.5 ETH
        storage = {0: Web3.toWei(0.5, "ether")}
        self.assertTrue(self.check_states([state_send], mock_storage=storage))

        # storage[0] is 0 ETH
        storage = {0: 0}
        self.assertFalse(self.check_states([state_send], mock_storage=storage))

        # storage[0] is still 0 ETH initially, but we have an arbitrary write now
        state_write = state.copy()
        state_write.storage_written = {utils.bvv(0): self.env.calldata.read(4, 32)}
        state_write.solver.add(self.env.calldata.read(0, 4) == 0x1337)
        state_write.solver.add(self.env.calldata.read(4, 32) < Web3.toWei(1, "ether"))

        self.assertFalse(self.check_states([state_write], mock_storage=storage))
        self.assertTrue(
            self.check_states([state_send, state_write], mock_storage=storage)
        )
        self.assertTrue(
            self.check_states([state_write, state_send], mock_storage=storage)
        )

        # ...arbitrary write of 1 wei only, which is too little
        state_write_0 = state_write.copy()
        state_write_0.solver.add(self.env.calldata.read(4, 32) == 1)
        self.assertFalse(
            self.check_states([state_write_0, state_send], mock_storage=storage)
        )

        # ...arbitrary write only if the block timestamp is <10, which is impossible.
        state_write_ts = state_write.copy()
        state_write_ts.solver.add(self.env.block_timestamp < 10)
        self.assertFalse(
            self.check_states([state_write_ts, state_send], mock_storage=storage)
        )

        self.assertFalse(
            self.check_states(
                [state_write_0, state_send, state_write_ts], mock_storage=storage
            )
        )

        # now we put all these state_write* together, so there is a solution.
        self.assertTrue(
            self.check_states(
                [state_write_0, state_send, state_write, state_write_ts],
                mock_storage=storage,
            )
        )
        self.assertTrue(
            self.check_states(
                [state_write_0, state_write, state_write_ts, state_send],
                mock_storage=storage,
            )
        )