def test_graceful_handle_no_alive_states(self): """ If there are no alive states, or no initial states, we should not crash. issue #795 """ # initiate the blockchain m = ManticoreEVM() source_code = ''' contract Simple { function f(uint a) payable public { if (a == 65) { revert(); } } } ''' # Initiate the accounts user_account = m.create_account(balance=1000) contract_account = m.solidity_create_contract(source_code, owner=user_account, balance=0) contract_account.f(1) # it works contract_account.f(65) # it works with self.assertRaises(NoAliveStates): contract_account.f( m.SValue) # no alive states, but try to run a tx anyway
def _run_test_case_on_contract(self, contract_code, conc_txs): m2 = ManticoreEVM() owner_account = m2.create_account( balance=10**10, name="owner", address=self._main_evm.accounts.get('owner').address) attacker_account = m2.create_account( balance=10**10, name="attacker", address=self._main_evm.accounts.get('attacker').address) try: call_args = get_argument_from_create_transaction( self._main_evm, conc_txs[0]) create_value = m2.make_symbolic_value() m2.constrain(create_value == conc_txs[0].value) contract_account = solidity_create_contract_with_zero_price( m2, contract_code, owner=owner_account, args=call_args, balance=create_value, gas=0, ) except Exception as e: return m2 for conc_tx in conc_txs[1:]: try: m2.transaction( caller=conc_tx.caller, address=contract_account, value=conc_tx.value, data=conc_tx. data, # data has all needed metadata like function id ([:4]) and argument passed to function gas=0, price=0) except Exception as e: return m2 return m2
def test_parse_tx(self): m = ManticoreEVM() source_code = ''' contract C{ mapping(address => uint) balances; function test1(address to, uint val){ balances[to] = val; } } ''' user_account = m.create_account(balance=1000, name='user_account') contract_account = m.solidity_create_contract(source_code, owner=user_account, name='contract_account') calldata = binascii.unhexlify(b'9de4886f9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d') returndata = b'' md = m.get_metadata(contract_account) self.assertEqual(md.parse_tx(calldata, returndata), 'test1(899826498278242188854817720535123270925417291165, 71291600040229971300002528024956868756719167029433602173313100742126907268509)')
from manticore.ethereum import ManticoreEVM, evm from binascii import unhexlify, hexlify ################ Script ####################### # Bytecode only based analisys # No solidity, no compiler, no metadata m = ManticoreEVM() init_bytecode = unhexlify( b"608060405234801561001057600080fd5b506101cc806100206000396000f30060806040527f41000000000000000000000000000000000000000000000000000000000000006000366000818110151561003557fe5b905001357f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415610135577fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab6040518080602001828103825260088152602001807f476f7420616e204100000000000000000000000000000000000000000000000081525060200191505060405180910390a161019e565b7fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab6040518080602001828103825260128152602001807f476f7420736f6d657468696e6720656c7365000000000000000000000000000081525060200191505060405180910390a15b0000a165627a7a72305820fd5ec850d8409e19cfe593b9ee3276cc3ac12b0e3406d965317dc9c1aeb7f2670029" ) user_account = m.create_account(balance=1000) print("[+] Creating a user account", user_account) print("[+] Init bytecode:", hexlify(init_bytecode)) print("[+] EVM init assembler:") for instr in evm.EVMAsm.disassemble_all(init_bytecode[:-44]): print(hex(instr.pc), instr) contract_account = m.create_contract(owner=user_account, init=init_bytecode) print("[+] Creating a contract account", contract_account) print("[+] Now the symbolic values") symbolic_data = m.make_symbolic_buffer(320) symbolic_value = m.make_symbolic_value() m.transaction(caller=user_account, address=contract_account, data=symbolic_data, value=symbolic_value) #Let seth know we are not sending more transactions
def test(): #!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = "Raz0r" __email__ = "*****@*****.**" """ This is a solution to the PolySwarm's smart contract hacking challenge done with manticore. Please refer to https://raz0r.name/writeups/polyswarm-smart-contract-hacking-challenge-writeup/ for a complete walk through. """ import binascii from manticore.ethereum import ManticoreEVM, ABI m = ManticoreEVM() m.context["solved"] = False # Set up accounts with original addresses owner_account = m.create_account( balance=1000, name="owner", address=0xBC7DDD20D5BCEB395290FD7CE3A9DA8D8B485559) attacker_account = m.create_account( balance=1000, name="attacker", address=0x762C808237A69D786A85E8784DB8C143EB70B2FB, ) cashmoney_contract = m.create_account( balance=1000, name="CashMoney", address=0x64BA926175BC69BA757EF53A6D5EF616889C9999, ) # Create WinnerLog contract using its init bytecode file = "" if __name__ == "__main__": file = "winnerlog.bin" else: file = "test_polyswarm_challenge/winnerlog.bin" with open(file, "rb") as f: bytecode = f.read() winnerlog_contract = m.create_contract( init=bytecode, owner=owner_account, name="WinnerLog", address=0x2E4D2A597A2FCBDF6CC55EB5C973E76AA19AC410, ) # Allow cashmoney_contract to call logWinner() on winnerlog_contract m.transaction( caller=owner_account, address=winnerlog_contract, data=binascii.unhexlify( b"c3e8512400000000000000000000000064ba926175bc69ba757ef53a6d5ef616889c9999" ), value=0, ) # Prepare symbready_statesand call logWinner() with that symbolic buffer symbolic_data = m.make_symbolic_buffer(64) calldata = ABI.function_call("logWinner(address,uint256,bytes)", attacker_account, 0, symbolic_data) m.transaction( caller=cashmoney_contract, address=winnerlog_contract, data=calldata, value=0, gas=10000000, ) # Look for a running state that is not reverted for state in m.ready_states: world = state.platform result = state.solve_one(symbolic_data) print("[+] FOUND: {}".format(binascii.hexlify(result))) with m.locked_context() as context: context["solved"] = True break assert m.context["solved"]
from manticore.ethereum import ManticoreEVM, ABI from manticore.core.smtlib import Operators as Props m = ManticoreEVM() ETH = 10**18 user = m.create_account(balance=1*ETH, name='user') counter = m.solidity_create_contract('Counter.sol', contract_name='Counter', owner=user) # generate symbolic initial state and argument sval0 = m.make_symbolic_value(256) sval1 = m.make_symbolic_value(256) # set initial state for st in m.ready_states: st.platform.set_storage_data(counter, 0, sval0) # call add() with symbolic argument counter.add(sval1) # check if an overflow is possible for st in m.ready_states: n = st.platform.get_storage_data(counter, 0) prop_overflow = Props.ULT(n, sval0) # if an overflow is possible, generate a concrete case that triggers it if st.can_be_true(prop_overflow): st.constrain(prop_overflow) val0, val1 = st.solve_one_n(sval0, sval1) print('shit')
from manticore.ethereum import ManticoreEVM m = ManticoreEVM() with open('example.sol') as f: source_code = f.read() user_account = m.create_account(balance=1000) contract_account = m.solidity_create_contract(source_code, owner=user_account) symbolic_var = m.make_symbolic_value() contract_account.f(symbolic_var) print("Results are in {}".format(m.workspace)) m.finalize() # stop the exploration
from manticore.utils import config from manticore.core.plugin import Plugin m = ManticoreEVM() # Disable the gas tracking consts_evm = config.get_group("evm") consts_evm.oog = "ignore" # Increase the solver timeout config.get_group("smt").defaultunsat = False config.get_group("smt").timeout = 3600 ETHER = 10 ** 18 user = m.create_account(balance=1 * ETHER) # This plugin is used to speed up the exploration and skip the require(false) paths # It won't be needed once https://github.com/trailofbits/manticore/issues/1593 is added class SkipRequire(Plugin): def will_evm_execute_instruction_callback(self, state, instruction, arguments): world = state.platform if state.platform.current_transaction.sort != 'CREATE': if instruction.semantics == "JUMPI": potential_revert = world.current_vm.read_code(world.current_vm.pc + 4) if potential_revert[0].size == 8 and potential_revert[0].value == 0xfd: state.constrain(arguments[1] == True) print(f'controller: {hex(user.address)}')
class EthPluginsTests(unittest.TestCase): def setUp(self): self.mevm = ManticoreEVM() def tearDown(self): shutil.rmtree(self.mevm.workspace) del self.mevm def test_verbose_trace(self): source_code = '''contract X {}''' self.mevm.register_plugin(VerboseTrace()) owner = self.mevm.create_account(balance=1000) # Initialize contract so it's constructor function will be traced self.mevm.solidity_create_contract(source_code, owner=owner, gas=90000) files = set(os.listdir(self.mevm.workspace)) self.assertEqual(len(files), 0) # just a sanity check? # Shall produce a verbose trace file with self.assertLogs('manticore.core.manticore', level='INFO') as cm: self.mevm.finalize() prefix = '\x1b[34mINFO:\x1b[0m:m.c.manticore' self.assertEqual(f'{prefix}:Generated testcase No. 0 - RETURN', cm.output[0]) self.assertEqual(f'{prefix}:Results in {self.mevm.workspace}', cm.output[1]) self.assertEqual( f'{prefix}:Total time: {self.mevm._last_run_stats["time_elapsed"]}', cm.output[2]) self.assertEqual(len(cm.output), 3) files = set(os.listdir(self.mevm.workspace)) expected_files = { 'global_X.runtime_visited', 'global_X_runtime.bytecode', 'test_00000000.verbose_trace', 'global_X.sol', 'global_X.runtime_asm', 'global_X.init_asm', 'global_X.init_visited', 'test_00000000.constraints', 'command.sh', 'global_X_init.bytecode', 'test_00000000.tx', 'test_00000000.pkl', 'manticore.yml', 'global.summary', 'test_00000000.summary', 'test_00000000.tx.json', 'test_00000000.logs', 'test_00000000.trace' } self.assertEqual(files, expected_files) result_vt_path = os.path.join(self.mevm.workspace, 'test_00000000.verbose_trace') expected_vt_path = os.path.join(THIS_DIR, 'data/verbose_trace_plugin_out') with open(result_vt_path) as res_fp, open(expected_vt_path) as exp_fp: res = res_fp.readlines() exp = exp_fp.readlines() self.assertEqual(len(res), len(exp)) self.assertEqual(len(res), 204) # Till line 184 the outputs shall be the same # Next there is a CODESIZE instruction that concretizes to different values each run # and as a result, the values in memory might differ. # # For some reason even setting `(set-option :random-seed 1)` in z3 doesn't help for i in range(184): self.assertEqual(res[i], exp[i], f'Difference on line {i}') till = 130 # number of chars that doesn't differ for i in range(184, 188): self.assertEqual(res[i][:till], exp[i][:till], f'Difference on line {i}') for i in range(188, 195): self.assertEqual(res[i], exp[i], f'Difference on line {i}') for i in range(195, 200): self.assertEqual(res[i][:till], exp[i][:till], f'Difference on line {i}') for i in range(200, len(res)): self.assertEqual(res[i], exp[i], f'Difference on line {i}')
class EthTests(unittest.TestCase): def setUp(self): self.mevm = ManticoreEVM() self.worksp = self.mevm.workspace def tearDown(self): self.mevm = None shutil.rmtree(self.worksp) def test_invalid_function_signature(self): source_code = ''' contract Test{ function ret(uint256) returns(uint256){ return 1; } } ''' user_account = self.mevm.create_account(balance=1000) contract_account = self.mevm.solidity_create_contract( source_code, owner=user_account) with self.assertRaises(EthereumError) as ctx: contract_account.ret(self.mevm.make_symbolic_value(), signature='(uint8)') self.assertTrue(str(ctx.exception)) def test_selfdestruct_decoupled_account_delete(self): source_code = ''' contract C{ function d( ){ selfdestruct(0); } function g() returns(uint) { return 42 ; } } contract D{ C c; constructor () { c = new C(); } function t () returns(uint){ c.d(); return c.g(); } } ''' user_account = self.mevm.create_account(balance=1000) contract_account = self.mevm.solidity_create_contract( source_code, owner=user_account, contract_name='D', gas=9000000) contract_account.t( gas=9000000 ) #this does not return nothing as it may create several states # nothing reverted and we end up with a single state self.assertEqual(self.mevm.count_states(), 1) # Check that calling t() returned a 42 # That is that calling a selfdestructed contract works as the account # is actually deleted at the end of the human tx self.assertEqual( ABI.deserialize( 'uint', to_constant(self.mevm.world.transactions[-1].return_data)), 42) def test_function_name_collision(self): source_code = ''' contract Test{ function ret(uint) returns(uint){ return 1; } function ret(uint,uint) returns(uint){ return 2; } } ''' user_account = self.mevm.create_account(balance=1000) contract_account = self.mevm.solidity_create_contract( source_code, owner=user_account) with self.assertRaises(EthereumError): contract_account.ret(self.mevm.make_symbolic_value()) def test_function_name_with_signature(self): source_code = ''' contract Test{ function ret(uint) returns(uint){ return 1; } function ret(uint,uint) returns(uint){ return 2; } } ''' user_account = self.mevm.create_account(balance=1000) contract_account = self.mevm.solidity_create_contract( source_code, owner=user_account) contract_account.ret(self.mevm.make_symbolic_value(), self.mevm.make_symbolic_value(), signature='(uint256,uint256)') z = list(self.mevm.all_states)[0].solve_one( self.mevm.transactions()[1].return_data) self.assertEqual(ABI.deserialize('(uint256)', z)[0], 2) def test_migrate_integration(self): m = self.mevm contract_src = ''' contract Overflow { uint public sellerBalance=0; function add(uint value)public returns (bool){ sellerBalance += value; } } ''' owner_account = m.create_account(balance=1000) attacker_account = m.create_account(balance=1000) contract_account = m.solidity_create_contract(contract_src, owner=owner_account, balance=0) #Some global expression `sym_add1` sym_add1 = m.make_symbolic_value(name='sym_add1') #Let's constrain it on the global fake constraintset m.constrain(sym_add1 > 0) m.constrain(sym_add1 < 10) #Symb tx 1 contract_account.add(sym_add1, caller=attacker_account) # A new!? global expression sym_add2 = m.make_symbolic_value(name='sym_add2') #constraints involve old expression. Some states may get invalidated by this. Should this be accepted? m.constrain(sym_add1 > sym_add2) #Symb tx 2 contract_account.add(sym_add2, caller=attacker_account) #random concrete tx contract_account.sellerBalance(caller=attacker_account) #another constraining on the global constraintset. Yet more running states could get unfeasible by this. m.constrain(sym_add1 > 8) for state_num, state in enumerate(m.all_states): if state.is_feasible(): self.assertTrue(state.can_be_true(sym_add1 == 9)) self.assertTrue(state.can_be_true(sym_add2 == 8)) def test_account_names(self): m = self.mevm user_account = m.create_account(name='user_account') self.assertEqual(m.accounts['user_account'], user_account) self.assertEqual(len(m.accounts), 1) user_account1 = m.create_account(name='user_account1') self.assertEqual(m.accounts['user_account1'], user_account1) self.assertEqual(len(m.accounts), 2) user_accounts = [] for i in range(10): user_accounts.append(m.create_account()) self.assertEqual(len(m.accounts), 12) for i in range(10): self.assertEqual(m.accounts['normal{:d}'.format(i)], user_accounts[i]) def test_regression_internal_tx(self): m = self.mevm owner_account = m.create_account(balance=1000) c = ''' contract C1 { function g() returns (uint) { return 1; } } contract C2 { address c; function C2(address x) { c = x; } function f() returns (uint) { return C1(c).g(); } } ''' c1 = m.solidity_create_contract(c, owner=owner_account, contract_name='C1') self.assertEqual(m.count_states(), 1) c2 = m.solidity_create_contract(c, owner=owner_account, contract_name='C2', args=[c1.address]) self.assertEqual(m.count_states(), 1) c2.f() self.assertEqual(m.count_states(), 1) c2.f() self.assertEqual(m.count_states(), 1) for state in m.all_states: world = state.platform self.assertEqual(len(world.transactions), 6) self.assertEqual(len(world.all_transactions), 6) self.assertEqual(len(world.human_transactions), 4) self.assertListEqual( ['CREATE', 'CREATE', 'CALL', 'CALL', 'CALL', 'CALL'], [x.sort for x in world.all_transactions]) for tx in world.all_transactions[-4:]: self.assertEqual(tx.result, 'RETURN') self.assertEqual( state.solve_one(tx.return_data), b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' ) def test_emit_did_execute_end_instructions(self): """ Tests whether the did_evm_execute_instruction event is fired for instructions that internally trigger an exception """ class TestDetector(Detector): def did_evm_execute_instruction_callback(self, state, instruction, arguments, result): if instruction.is_endtx: with self.locked_context('insns', dict) as d: d[instruction.semantics] = True mevm = self.mevm p = TestDetector() mevm.register_detector(p) filename = os.path.join(THIS_DIR, 'binaries/int_overflow.sol') mevm.multi_tx_analysis(filename, tx_limit=2) self.assertIn('insns', p.context) context = p.context['insns'] self.assertIn('STOP', context) self.assertIn('RETURN', context) self.assertIn('REVERT', context) def test_end_instruction_trace(self): """ Make sure that the trace files are correct, and include the end instructions. Also, make sure we produce a valid function call in trace. """ class TestPlugin(Plugin): """ Record the pcs of all end instructions encountered. Source of truth. """ def did_evm_execute_instruction_callback(self, state, instruction, arguments, result): try: world = state.platform if world.current_transaction.sort == 'CREATE': name = 'init' else: name = 'rt' # collect all end instructions based on whether they are in init or rt if instruction.is_endtx: with self.locked_context(name) as d: d.append(instruction.pc) except Exception as e: raise mevm = self.mevm p = TestPlugin() mevm.register_plugin(p) filename = os.path.join(THIS_DIR, 'binaries/int_overflow.sol') mevm.multi_tx_analysis(filename, tx_limit=1) mevm.finalize() worksp = mevm.workspace listdir = os.listdir(worksp) def get_concatenated_files(directory, suffix, init): paths = [ os.path.join(directory, f) for f in listdir if f.endswith(suffix) ] concatenated = ''.join(open(path).read() for path in paths) result = set() for x in concatenated.split('\n'): if ':' in x: address = int(x.split(':')[0], 16) pc = int(x.split(':')[1].split(' ')[0], 16) at_init = '*' in x if at_init == init: result.add(pc) return result all_init_traces = get_concatenated_files(worksp, 'trace', init=True) all_rt_traces = get_concatenated_files(worksp, 'trace', init=False) # make sure all init end insns appear somewhere in the init traces for pc in p.context['init']: self.assertIn(pc, all_init_traces) # and all rt end insns appear somewhere in the rt traces for pc in p.context['rt']: self.assertIn(pc, all_rt_traces) # Make sure the function call is correctly produced # Extract all valid function names, and make sure we have at least one existing_functions = [] with open(filename, 'r') as src: for line in src: m = re.match(r'\s*function (\w+).*', line) if m: existing_functions.append(m.group(1)) self.assertGreater(len(existing_functions), 0) tx = next(f for f in listdir if f.endswith('0.tx')) with open(os.path.join(worksp, tx), 'r') as tx_f: lines = tx_f.readlines() # implicitly assert the following doesn't throw header_idx = lines.index('Function call:\n') func_call_summary = lines[header_idx + 1] for f in existing_functions: if func_call_summary.startswith( f) or func_call_summary.startswith("Constructor"): break else: self.fail( 'Could not find a function call summary in workspace output' ) def test_graceful_handle_no_alive_states(self): """ If there are no alive states, or no initial states, we should not crash. issue #795 """ # initiate the blockchain m = self.mevm source_code = ''' contract Simple { function f(uint a) payable public { if (a == 65) { revert(); } } } ''' # Initiate the accounts user_account = m.create_account(balance=1000) contract_account = m.solidity_create_contract(source_code, owner=user_account, balance=0) contract_account.f(1) # it works contract_account.f(65) # it works with self.assertRaises(NoAliveStates): contract_account.f( 1) # no alive states, but try to run a tx anyway @unittest.skip("reason") def test_reachability(self): class StopAtFirstJump414141(Detector): def will_decode_instruction_callback(self, state, pc): TRUE = bytearray( (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)) FALSE = bytearray( (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) #print pc, state.platform.current_vm.instruction #Once this address is reached the challenge is won if pc == 0x4141414141414141414141414141414141414141: func_id = to_constant( state.platform.current_transaction.data[:4]) if func_id == function_selector("print(string)"): func_name, args = ABI.deserialize( "print(string)", state.platform.current_transaction.data) raise Return() elif func_id == function_selector("terminate(string)"): func_name, args = ABI.deserialize( "terminate(string)", state.platform.current_transaction.data) self.manticore.shutdown() raise Return(TRUE) elif func_id == function_selector("assume(bool)"): func_name, args = ABI.deserialize( "assume(bool)", state.platform.current_transaction.data) state.add(args[0]) raise Return(TRUE) elif func_id == function_selector("is_symbolic(bytes)"): func_name, args = ABI.deserialize( "is_symbolic(bytes)", state.platform.current_transaction.data) try: arg = to_constant(args[0]) except: raise Return(TRUE) raise Return(FALSE) elif func_id == function_selector("is_symbolic(uint256)"): func_name, args = ABI.deserialize( "is_symbolic(uint256)", state.platform.current_transaction.data) try: arg = to_constant(args[0]) except Exception as e: raise Return(TRUE) raise Return(FALSE) elif func_id == function_selector("shutdown(string)"): func_name, args = ABI.deserialize( "shutdown(string)", state.platform.current_transaction.data) print("Shutdown", to_constant(args[0])) self.manticore.shutdown() elif func_id == function_selector("can_be_true(bool)"): func_name, args = ABI.deserialize( "can_be_true(bool)", state.platform.current_transaction.data) result = solver.can_be_true(state.constraints, args[0] != 0) if result: raise Return(TRUE) raise Return(FALSE) raise Stop() #otherwise keep exploring mevm = self.mevm p = StopAtFirstJump414141() mevm.register_detector(p) filename = os.path.join(THIS_DIR, 'binaries/reached.sol') mevm.multi_tx_analysis(filename, tx_limit=2, contract_name='Reachable') context = p.context.get('flags', {}) self.assertTrue(context.get('found', False))
__author__ = "Raz0r" __email__ = "*****@*****.**" """ This is a solution to the PolySwarm's smart contract hacking challenge done with manticore. Please refer to https://raz0r.name/writeups/polyswarm-smart-contract-hacking-challenge-writeup/ for a complete walk through. """ import binascii from manticore.ethereum import ManticoreEVM, ABI m = ManticoreEVM() # Set up accounts with original addresses owner_account = m.create_account( balance=1000, name='owner', address=0xbc7ddd20d5bceb395290fd7ce3a9da8d8b485559) attacker_account = m.create_account( balance=1000, name='attacker', address=0x762C808237A69d786A85E8784Db8c143EB70B2fB) cashmoney_contract = m.create_account( balance=1000, name='CashMoney', address=0x64ba926175bc69ba757ef53a6d5ef616889c9999) # Create WinnerLog contract using its init bytecode with open("winnerlog.bin", "rb") as f: bytecode = f.read() winnerlog_contract = m.create_contract(
print("[+] Setting up a user account") market.ether_token_contract.approve(market.reserve_contract, 100 * ONE_ETH, caller=market.ether_token_owner) market.market_token_contract.approve(market.voting_contract, 100 * ONE_ETH, caller=market.ether_token_owner) market.ether_token_contract.approve(market.datatrust_contract, 100 * ONE_ETH, caller=market.ether_token_owner) market.reserve_contract.support(ONE_ETH, caller=market.ether_token_owner) attacker_balance = 100 * ONE_ETH attacker_account = m.create_account(balance=attacker_balance) print("[+] Creating a attacker account", hex(attacker_account.address)) user_delivery_hash = "my_delivery_hash" market.datatrust_contract.requestDelivery(user_delivery_hash, 0, caller=attacker_account) market.datatrust_contract.requestDelivery(user_delivery_hash, 1, caller=market.ether_token_owner) if (list(m.ready_states) == []): print("requestDelivery was blocked by an attacker") m.finalize()
from backend_init import register_backend from list import list consts = config.get_group("evm") consts.oog = "ignore" # This script corresponds to issue #17 raised in the ToB audit ################ Script ####################### m = ManticoreEVM() market = initialize(m, parameters={'cost': 101, 'back_p': 33, 'maker_p': 33}) backend_user = register_backend(m, market, [market.market_token_owner]) user_account = m.create_account(balance=1 * ONE_ETH) print("[+] Creating a market owner account", hex(user_account.address)) market.ether_token_contract.deposit(value=1 * ONE_ETH, caller=user_account) print("[+] Deposit called") market.ether_token_contract.approve(market.datatrust_contract.address, 1 * ONE_ETH, caller=user_account) print("[+] Approve called") listing_hash = bytes.fromhex("41") listing = m.create_account(balance=1 * ONE_ETH) print("[+] Creating a listing owner account", hex(listing.address))
and os.path.exists(CONDITION_STORE_MANAGER_JSON_PATH)): sys.stderr.write( '''Error: It does not appear as if the project is compiled! Please do so before running the Manticore tests. ''') sys.exit(1) ################ Script ####################### if __name__ == '__main__': from manticore.ethereum import ManticoreEVM from creation import create_condition_store_manager m = ManticoreEVM() owner_account = m.create_account(balance=1000, name='owner_account') print(f'[+] Created owner account ', owner_account.name_) creator_account = m.create_account(balance=1000, name='creator_owner_account') print(f'[+] Created creator account ', creator_account.name_) _, contract_account = create_condition_store_manager( m, owner_account, EPOCH_LIBRARY_JSON_PATH, CONDITION_STORE_MANAGER_JSON_PATH) contract_account.initialize(owner_account, creator_account, caller=owner_account, value=0, signature='(address,address)')
} function changeOwner(address _newOwner) public { owner = _newOwner; } function deposit() payable public { } function withdraw() onlyowner public { msg.sender.transfer(this.balance); } } ''' # Generate the accounts. Creator has 10 ethers; attacker 0 creator_account = m.create_account(balance=10*10**18) attacker_account = m.create_account(balance=0) contract_account = m.solidity_create_contract(source_code, owner=creator_account) print "Creator account: 0x%x (%d)"%(creator_account, creator_account) print "Attacker account: 0x%x (%d)"%(attacker_account, attacker_account) # Deposit 1 ether, from the creator contract_account.deposit(caller=creator_account, value=10**18) # Two raw transactions from the attacker symbolic_data = m.make_symbolic_buffer(320) m.transaction(caller=attacker_account, address=contract_account, data=symbolic_data, value=0)
def __run_manticore(self, trace): self.print('[.] Running Manticore') consts = manticoreConfig.get_group('core') consts.procs = self.procs output_path = self.__create_output_path() manticore = ManticoreEVM(workspace_url=output_path) if self.force_loop_limit: loop_delimiter = LoopDepthLimiter( loop_count_threshold=self.loop_limit) manticore.register_plugin(loop_delimiter) if self.avoid_constant_txs: filter_nohuman_constants = FilterFunctions(regexp=r'.*', depth='human', mutability='constant', include=False) manticore.register_plugin(filter_nohuman_constants) self.print('[...] Creating user accounts') for num in range(0, self.amount_user_accounts): account_name = 'user_account_' + str(num) manticore.create_account(balance=self.user_initial_balance, name=account_name) self.print('[...] Creating a contract and its library dependencies') with open(self.contract_path, 'r') as contract_file: source_code = contract_file.read() try: contract_account = manticore.solidity_create_contract( source_code, owner=manticore.get_account('user_account_0'), args=self.contract_args, contract_name=self.contract_name) except: raise Exception('Check contract arguments') if contract_account is None: raise Exception( 'Contract account is None, check contract arguments') self.print('[...] Calling functions in trace') function_types = {} function_signatures = manticore.get_metadata( contract_account).function_signatures for signature in function_signatures: signature_parts = signature.split('(') name = str(signature_parts[0]) types = str(signature_parts[1].replace(')', '')) function_types[name] = types for function_name in trace: if function_name == '': # FIXME, check VeriSol trace manticore.transaction(caller=manticore.make_symbolic_address(), address=contract_account, value=manticore.make_symbolic_value(), data=manticore.make_symbolic_buffer( self.fallback_data_size)) else: function_to_call = getattr(contract_account, function_name) types = function_types[function_name] if len(types) > 0: function_to_call( manticore.make_symbolic_arguments( function_types[function_name])) else: function_to_call() self.print('[...] Processing output') throw_states = [] for state in manticore.terminated_states: if str(state.context['last_exception']) == 'THROW': throw_states.append(state) if len(throw_states) == 0: raise Exception('Manticore couldn\'t confirm the counterexample') if self.verbose: for state in throw_states: manticore.generate_testcase(state) self.print('[-] Look for full output in:', manticore.workspace)
from manticore.ethereum import ManticoreEVM from manticore.ethereum.abi import ABI from manticore.core.smtlib import Operators ETHER = 10**18 m = ManticoreEVM() # initiate the blockchain # Init user_account = m.create_account() with open('token.sol', 'r') as f: contract_account = m.solidity_create_contract(f, owner=user_account) # Exploration tokens_amount = m.make_symbolic_value() wei_amount = m.make_symbolic_value() contract_account.is_valid_buy(tokens_amount, wei_amount)
from manticore.ethereum import ManticoreEVM, ABI from manticore.core.smtlib import Operators from manticore.core.smtlib.solver import Z3Solver ###### Initialization ###### m = ManticoreEVM() solver = Z3Solver.instance() with open('test22.sol') as f: source_code = f.read() # Create one user account # And deploy the contract user_account = m.create_account(balance=1000) symbolic_spender = m.create_account(balance=10) symbolic_to = m.create_account(balance=20) contract_account = m.solidity_create_contract(source_code, owner=user_account, balance=0) #symbolic_spender = m.make_symbolic_value(name="SPENDER") #symbolic_spender = m.make_symbolic_address(name="SPENDER") symbolic_val1 = 25 #m.make_symbolic_value() #m.constrain(symbolic_spender != user_account) contract_account.balanceOf(user_account) contract_account.allowance(user_account, symbolic_spender) contract_account.approve(symbolic_spender, symbolic_val1, caller=user_account) symbolic_val2 = m.make_symbolic_value()
m = ManticoreEVM() token_contract = ''' contract Token { uint public decimals_state; constructor(uint _decimals) public { decimals_state = _decimals; } function decimals() public returns (uint) { return decimals_state; } } ''' token_owner_account = m.create_account(balance=1000, name='token_owner_account') print(f'[+] Created token owner account ', token_owner_account.name_) symbolic_value = m.make_symbolic_value(name="VALUE1") token_account = m.solidity_create_contract(token_contract, owner=token_owner_account, name='token_account', args=[symbolic_value]) owner_account = m.create_account(balance=1000, name='owner_account') print(f'[+] Created owner account ', owner_account.name_) with open(DISPENSER_JSON_PATH) as f: contract_json = f.read() contract_account = m.json_create_contract(contract_json,
from manticore.ethereum import ManticoreEVM, ABI from manticore.core.smtlib import Operators, solver ###### Initialization ###### m = ManticoreEVM() with open('test4.sol') as f: source_code = f.read() bytecode = m.compile(source_code, # contract_name="GuessTheNumberChallenge" ) # Add hacker's address hacker_account = m.create_account(balance=1000 * 10**18, address=42) # bytecode = bytecode + bytes.fromhex("000000000000000000000000000000000000002a") # Create one user account # And deploy the contract user_account = m.create_account(balance=1000 * 10**18) contract_account = m.create_contract(init=bytecode, owner=user_account, balance=10**18) ###### Exploration ###### symbolic_data = m.make_symbolic_buffer(36) m.transaction(caller=hacker_account, address=contract_account,
from manticore.ethereum import ManticoreEVM m = ManticoreEVM() with open('example.sol') as f: source_code = f.read() user_account = m.create_account(balance=1 * 10**18) contract = m.solidity_create_contract(source_code, owner=user_account) symbolic_var = m.make_symbolic_value() contract.f(symbolic_var) ## Check if an execution ends with a REVERT or INVALID for state in m.terminated_states: last_tx = state.platform.transactions[-1] if last_tx.result in ['REVERT', 'INVALID']: print('Throw found {}'.format(m.workspace)) m.generate_testcase(state, 'ThrowFound')
vulnerable_contract.call(reentry_attack_string); } } } ''' # Manticore currently only allows for incrementing a nonce # So, I created this helper function to make your code look better :) def set_nonce(world,address,nonce): while world.get_nonce(address)<nonce: world.increase_nonce(address) #Initialize wallets and contracts contract_balance = ??? # set to the value of the CTF contract attacker_balance = ??? # we don't need any for this exploit creator_account = m.create_account(address=contract_creator_address,balance=contract_balance) attacker_account = m.create_account(address=from_address,balance=attacker_balance) # The 'getTransactionCount' for geth currently counts contract creation as a transaction for the created contract # A contract nonce starts at '1' (EIP 161) so this works out well for us (we don't need to change it) # The nonce for the attacker account is your current wallet's nonce, we'll use this to get the right address for # the created generic exploit # For the attacker account (us) we want the nonce to be up to date (we will create the contract in the future) set_nonce(m.get_world(),attacker_account.address,???) # get this from geth's getTransactionCount function # If you use the current nonce of the CTF creator contract in this file, you'll have to re-create the ctf level # You could also find the current nonce and count back to find it's value when this contract was created # Finding the nonce for this creator address can be difficult, another way to complete this level is to manually # change the address after an exploit has been generated. In this case, you can just leave this nonce as '1' set_nonce(m.get_world(),creator_account.address,???) # get this from geth's getTransactionCount function # create our victim contract contract_account = m.solidity_create_contract(contract_source_code, contract_name="TrustFund",
#arg_3 = ONE_ETH #arg_4 = 1 #m.make_symbolic_value(name="stk") #arg_5 = 1 #arg_6 = 1 #arg_7 = 25 #arg_8 = 50 #arg_9 = ONE_KWEI * 6 market_parameters['vote_by_d'] = 1 market_parameters['pl'] = 1 market_parameters['stk'] = 1 market = initialize(m, parameters=market_parameters) backend_balance = 100 * ONE_ETH backend_account = m.create_account(balance=backend_balance) print("[+] Creating a backend account", hex(backend_account.address)) market.ether_token_contract.deposit(value=backend_balance, caller=backend_account) market.ether_token_contract.approve(market.reserve_contract, backend_balance, caller=backend_account) market.market_token_contract.approve(market.voting_contract, backend_balance, caller=backend_account) market.reserve_contract.support(100 * ONE_GWEI, caller=backend_account) backend_url = "my_backend_url"
function named_func(uint x) returns (uint) { return 5 + x; } function() payable { if (msg.data[0] == 'A') { Log("Got an A"); } else{ Log("Got something else"); } } } ''' user_account = m.create_account(balance=1000, name='user_account') print("[+] Creating a user account", user_account.name_) contract_account = m.solidity_create_contract(source_code, owner=user_account, name='contract_account') print("[+] Creating a contract account", contract_account.name_) contract_account.named_func(1) print("[+] Now the symbolic values") symbolic_data = m.make_symbolic_buffer(320) symbolic_value = m.make_symbolic_value(name="VALUE") symbolic_address = m.make_symbolic_value(name="ADDRESS") symbolic_caller = m.make_symbolic_value(name="CALLER") m.transaction(caller=symbolic_caller, address=symbolic_address, data=symbolic_data, value=symbolic_value )
def manticore_verifier( source_code, contract_name, maxfail=None, maxt=3, maxcov=100, deployer=None, senders=None, psender=None, propre=r"crytic_.*", compile_args=None, outputspace_url=None, timeout=100, ): """ Verify solidity properties The results are dumped to stdout and to the workspace folder. $manticore-verifier property.sol --contract TestToken --smt.solver yices --maxt 4 # Owner account: 0xf3c67ffb8ab4cdd4d3243ad247d0641cd24af939 # Contract account: 0x6f4b51ac2eb017600e9263085cfa06f831132c72 # Sender_0 account: 0x97528a0c7c6592772231fd581e5b42125c1a2ff4 # PSender account: 0x97528a0c7c6592772231fd581e5b42125c1a2ff4 # Found 2 properties: crytic_test_must_revert, crytic_test_balance # Exploration will stop when some of the following happens: # * 4 human transaction sent # * Code coverage is greater than 100% meassured on target contract # * No more coverage was gained in the last transaction # * At least 2 different properties where found to be breakable. (1 for fail fast) # * 240 seconds pass # Starting exploration... Transactions done: 0. States: 1, RT Coverage: 0.0%, Failing properties: 0/2 Transactions done: 1. States: 2, RT Coverage: 55.43%, Failing properties: 0/2 Transactions done: 2. States: 8, RT Coverage: 80.48%, Failing properties: 1/2 Transactions done: 3. States: 30, RT Coverage: 80.48%, Failing properties: 1/2 No coverage progress. Stopping exploration. Coverage obtained 80.48%. (RT + prop) +-------------------------+------------+ | Property Named | Status | +-------------------------+------------+ | crytic_test_balance | failed (0) | | crytic_test_must_revert | passed | +-------------------------+------------+ Checkout testcases here:./mcore_6jdil7nh :param maxfail: stop after maxfail properties are failing. All if None :param maxcov: Stop after maxcov % coverage is obtained in the main contract :param maxt: Max transaction count to explore :param deployer: (optional) address of account used to deploy the contract :param senders: (optional) a list of calles addresses for the exploration :param psender: (optional) address from where the property is tested :param source_code: A filename or source code :param contract_name: The target contract name defined in the source code :param propre: A regular expression for selecting properties :param outputspace_url: where to put the extended result :param timeout: timeout in seconds :return: """ # Termination condition # Exploration will stop when some of the following happens: # * MAXTX human transaction sent # * Code coverage is greater than MAXCOV meassured on target contract # * No more coverage was gained in the last transaction # * At least MAXFAIL different properties where found to be breakable. (1 for fail fast) # Max transaction count to explore MAXTX = maxt # Max coverage % to get MAXCOV = maxcov # Max different properties fails MAXFAIL = maxfail config.get_group("smt").timeout = 120 config.get_group("smt").memory = 16384 config.get_group("evm").ignore_balance = True config.get_group("evm").oog = "ignore" print("# Welcome to manticore-verifier") # Main manticore manager object m = ManticoreEVM() # avoid all human level tx that are marked as constant (have no effect on the storage) filter_out_human_constants = FilterFunctions(regexp=r".*", depth="human", mutability="constant", include=False) m.register_plugin(filter_out_human_constants) filter_out_human_constants.disable() # Avoid automatically exploring property filter_no_crytic = FilterFunctions(regexp=propre, include=False) m.register_plugin(filter_no_crytic) filter_no_crytic.disable() # Only explore properties (at human level) filter_only_crytic = FilterFunctions(regexp=propre, depth="human", fallback=False, include=True) m.register_plugin(filter_only_crytic) filter_only_crytic.disable() # And now make the contract account to analyze # User accounts. Transactions trying to break the property are send from one # of this senders = (None, ) if senders is None else senders user_accounts = [] for n, address_i in enumerate(senders): user_accounts.append( m.create_account(balance=10**10, address=address_i, name=f"sender_{n}")) # the address used for deployment owner_account = m.create_account(balance=10**10, address=deployer, name="deployer") # the target contract account contract_account = m.solidity_create_contract( source_code, owner=owner_account, contract_name=contract_name, compile_args=compile_args, name="contract_account", ) # the address used for checking porperties checker_account = m.create_account(balance=10**10, address=psender, name="psender") print(f"# Owner account: 0x{int(owner_account):x}") print(f"# Contract account: 0x{int(contract_account):x}") for n, user_account in enumerate(user_accounts): print(f"# Sender_{n} account: 0x{int(user_account):x}") print(f"# PSender account: 0x{int(checker_account):x}") properties = {} md = m.get_metadata(contract_account) for func_hsh in md.function_selectors: func_name = md.get_abi(func_hsh)["name"] if re.match(propre, func_name): properties[func_name] = [] print( f"# Found {len(properties)} properties: {', '.join(properties.keys())}" ) if not properties: print("I am sorry I had to run the init bytecode for this.\n" "Good Bye.") return MAXFAIL = len(properties) if MAXFAIL is None else MAXFAIL tx_num = 0 # transactions count current_coverage = None # obtained coverge % new_coverage = 0.0 print(f"""# Exploration will stop when some of the following happens: # * {MAXTX} human transaction sent # * Code coverage is greater than {MAXCOV}% meassured on target contract # * No more coverage was gained in the last transaction # * At least {MAXFAIL} different properties where found to be breakable. (1 for fail fast) # * {timeout} seconds pass""") print("# Starting exploration...") print( f"Transactions done: {tx_num}. States: {m.count_ready_states()}, RT Coverage: {0.00}%, " f"Failing properties: 0/{len(properties)}") with m.kill_timeout(timeout=timeout): while not m.is_killed(): # check if we found a way to break more than MAXFAIL properties broken_properties = sum( int(len(x) != 0) for x in properties.values()) if broken_properties >= MAXFAIL: print( f"Found {broken_properties}/{len(properties)} failing properties. Stopping exploration." ) break # check if we sent more than MAXTX transaction if tx_num >= MAXTX: print(f"Max number of transactions reached ({tx_num})") break tx_num += 1 # check if we got enough coverage new_coverage = m.global_coverage(contract_account) if new_coverage >= MAXCOV: print( f"Current coverage({new_coverage}%) is greater than max allowed ({MAXCOV}%). Stopping exploration." ) break # check if we have made coverage progress in the last transaction if current_coverage == new_coverage: print(f"No coverage progress. Stopping exploration.") break current_coverage = new_coverage # Make sure we didn't time out before starting first transaction if m.is_killed(): print("Cancelled or timeout.") break # Explore all methods but the "crytic_" properties # Note: you may be tempted to get all valid function ids/hashes from the # metadata and to constrain the first 4 bytes of the calldata here. # This wont work because we also want to prevent the contract to call # crytic added methods as internal transactions filter_no_crytic.enable() # filter out crytic_porperties filter_out_human_constants.enable() # Exclude constant methods filter_only_crytic.disable( ) # Exclude all methods that are not property checks symbolic_data = m.make_symbolic_buffer(320) symbolic_value = m.make_symbolic_value() caller_account = m.make_symbolic_value(160) args = tuple( (caller_account == address_i for address_i in user_accounts)) m.constrain(OR(*args, False)) m.transaction( caller=caller_account, address=contract_account, value=symbolic_value, data=symbolic_data, ) # check if timeout was requested during the previous transaction if m.is_killed(): print("Cancelled or timeout.") break m.clear_terminated_states() # no interest in reverted states m.take_snapshot() # make a copy of all ready states print( f"Transactions done: {tx_num}. States: {m.count_ready_states()}, " f"RT Coverage: {m.global_coverage(contract_account):3.2f}%, " f"Failing properties: {broken_properties}/{len(properties)}") # check if timeout was requested while we were taking the snapshot if m.is_killed(): print("Cancelled or timeout.") break # And now explore all properties (and only the properties) filter_no_crytic.disable() # Allow crytic_porperties filter_out_human_constants.disable( ) # Allow them to be marked as constants filter_only_crytic.enable( ) # Exclude all methods that are not property checks symbolic_data = m.make_symbolic_buffer(4) m.transaction(caller=checker_account, address=contract_account, value=0, data=symbolic_data) for state in m.all_states: world = state.platform tx = world.human_transactions[-1] md = m.get_metadata(tx.address) """ A is _broken_ if: * is normal property * RETURN False OR: * property name ends with 'revert' * does not REVERT Property is considered to _pass_ otherwise """ N = constrain_to_known_func_ids(state) for func_id in map(bytes, state.solve_n(tx.data[:4], nsolves=N)): func_name = md.get_abi(func_id)["name"] if not func_name.endswith("revert"): # Property does not ends in "revert" # It must RETURN a 1 if tx.return_value == 1: # TODO: test when property STOPs return_data = ABI.deserialize( "bool", tx.return_data) testcase = m.generate_testcase( state, f"property {md.get_func_name(func_id)} is broken", only_if=AND(tx.data[:4] == func_id, return_data == 0), ) if testcase: properties[func_name].append(testcase.num) else: # property name ends in "revert" so it MUST revert if tx.result != "REVERT": testcase = m.generate_testcase( state, f"Some property is broken did not reverted.(MUST REVERTED)", only_if=tx.data[:4] == func_id, ) if testcase: properties[func_name].append(testcase.num) m.clear_terminated_states( ) # no interest in reverted states for now! m.goto_snapshot() else: print("Cancelled or timeout.") m.clear_terminated_states() m.clear_ready_states() m.clear_snapshot() if m.is_killed(): print("Exploration ended by CTRL+C or timeout") print(f"Coverage obtained {new_coverage:3.2f}%. (RT + prop)") x = PrettyTable() x.field_names = ["Property Named", "Status"] for name, testcases in sorted(properties.items()): result = "passed" if testcases: result = f"failed ({testcases[0]})" x.add_row((name, result)) print(x) m.clear_ready_states() workspace = os.path.abspath(m.workspace)[len(os.getcwd()) + 1:] print(f"Checkout testcases here:./{workspace}")
from manticore.ethereum import ManticoreEVM m = ManticoreEVM() # initiate the blockchain with open('unprotected.sol') as f: source_code = f.read() # Generate the accounts. Creator has 10 ethers; attacker 0 creator_account = m.create_account(balance=10*10**18) attacker_account = m.create_account(balance=10*10**18) contract_account = m.solidity_create_contract(source_code, owner=creator_account) # Deposit 1 ether, from the creator contract_account.deposit(caller=creator_account, value=10**18) # Two raw transactions from the attacker symbolic_data = m.make_symbolic_buffer(320) m.transaction(caller=attacker_account, address=contract_account, data=symbolic_data, value=0) symbolic_data = m.make_symbolic_buffer(320) m.transaction(caller=attacker_account, address=contract_account, data=symbolic_data, value=0) for state in m.running_states: # Check if the attacker can ends with some ether
class EthPluginsTests(unittest.TestCase): def setUp(self): self.mevm = ManticoreEVM() def tearDown(self): # shutil.rmtree(self.mevm.workspace) del self.mevm @unittest.skip("failing") def test_verbose_trace(self): source_code = """contract X {}""" self.mevm.register_plugin(VerboseTrace()) # owner address is hardcodded so the contract address is predictable owner = self.mevm.create_account( balance=1000, address=0xAFB6D63079413D167770DE9C3F50DB6477811BDB ) # Initialize contract so it's constructor function will be traced self.mevm.solidity_create_contract(source_code, owner=owner, gas=90000) files = set(os.listdir(self.mevm.workspace)) # self.assertEqual(len(files), 0) # just a sanity check? workspace # contains .state_id and other config files # Shall produce a verbose trace file with self.assertLogs("manticore.core.manticore", level="INFO") as cm: self.mevm.finalize() prefix = "\x1b[34mINFO:\x1b[0m:m.c.manticore" # self.assertEqual(f'{prefix}:Generated testcase No. 0 - RETURN', cm.output[0]) self.assertEqual(f"{prefix}:Results in {self.mevm.workspace}", cm.output[0]) # self.assertEqual(f'{prefix}:Total time: {self.mevm._last_run_stats["time_elapsed"]}', cm.output[2]) self.assertEqual(len(cm.output), 1) import re files = set((f for f in os.listdir(self.mevm.workspace) if re.match(r"[^.].*", f))) expected_files = { "global_X.runtime_visited", "global_X_runtime.bytecode", "test_00000000.verbose_trace", "global_X.sol", "global_X.runtime_asm", "global_X.init_asm", "global_X.init_visited", "test_00000000.constraints", "command.sh", "global_X_init.bytecode", "test_00000000.tx", "test_00000000.pkl", "manticore.yml", "global.summary", "test_00000000.summary", "test_00000000.tx.json", "test_00000000.logs", "test_00000000.trace", } self.assertEqual(files, expected_files) result_vt_path = os.path.join(self.mevm.workspace, "test_00000000.verbose_trace") expected_vt_path = os.path.join(THIS_DIR, "data/verbose_trace_plugin_out") with open(result_vt_path) as res_fp, open(expected_vt_path) as exp_fp: res = res_fp.readlines() exp = exp_fp.readlines() self.assertEqual(len(res), len(exp)) self.assertEqual(len(res), 204) # Till line 184 the outputs shall be the same # Next there is a CODESIZE instruction that concretizes to different values each run # and as a result, the values in memory might differ. # # For some reason even setting `(set-option :random-seed 1)` in z3 doesn't help for i in range(184): self.assertEqual(res[i], exp[i], f"Difference on line {i}") till = 130 # number of chars that doesn't differ for i in range(184, 188): self.assertEqual(res[i][:till], exp[i][:till], f"Difference on line {i}") for i in range(188, 195): self.assertEqual(res[i], exp[i], f"Difference on line {i}") for i in range(195, 200): self.assertEqual(res[i][:till], exp[i][:till], f"Difference on line {i}") for i in range(200, len(res)): self.assertEqual(res[i], exp[i], f"Difference on line {i}")
from manticore.core.smtlib import Operators from manticore.ethereum import ManticoreEVM, ABI # Setup m = ManticoreEVM() with open('donation.sol') as f: source_code = f.read() # Deploy user_account = m.create_account(balance=10 * 10**18) contract_account = m.solidity_create_contract(source_code, owner=user_account, balance=10**18) # Two raw transactions from the attacker hacker_account = m.create_account(balance=0) # Deposit # symbolic_balance = m.make_symbolic_value() # symbolic_eth = m.make_symbolic_value() # contract_account.donate(symbolic_balance, balance=symbolic_eth) # symbolic_data = m.make_symbolic_buffer(36) m.transaction(caller=hacker_account, address=contract_account, data=symbolic_data, value=0) # symbolic_data = m.make_symbolic_buffer(36)
def will_decode_instruction_callback(self, state, pc): world = state.platform with self.manticore.locked_context('seen_rep', dict) as reps: item = (world.current_transaction.sort == 'CREATE', world.current_transaction.address, pc) if not item in reps: reps[item] = 0 reps[item] += 1 if reps[item] > 2: state.abandon() m.register_plugin(StopAtDepth()) owner_account = m.create_account(balance=1000) user_account = m.create_account(balance=1000) target_account = m.create_account(balance=1000) contract_account = m.solidity_create_contract(source_code, owner=user_account) contract_account.set(m.make_symbolic_value(name="A"), 1) contract_account.set(m.make_symbolic_value(name="B"), 1) for st in m.all_states: flag_storage_slot = 0 flag_value = st.platform.get_storage_data(contract_account.address, flag_storage_slot) if st.can_be_true(flag_value != 0): print("Flag Found! Check ", m.workspace) st.constraints.add(flag_value != 0) m.generate_testcase(st, 'Flag Found', '')
from manticore.ethereum import ManticoreEVM from manticore.ethereum.abi import ABI from manticore.core.smtlib import Operators ETHER = 10**18 m = ManticoreEVM() # initiate the blockchain # Init user_account = m.create_account(1 * ETHER) with open('exercise_1.sol', 'r') as f: contract_account = m.solidity_create_contract(f, owner=user_account) # Exploration tokens_amount = m.make_symbolic_value() wei_amount = m.make_symbolic_value() contract_account.is_valid_buy(tokens_amount, wei_amount) # Property for state in m.ready_states: condition = Operators.AND(wei_amount == 0, tokens_amount >= 1) if m.generate_testcase(state, name="BugFound", only_if=condition): print(f'Bug found, results are in {m.workspace}')
contract_balance = ??? # Set the amount of ETH we need to send in our transaction (msg.value) to play. msg_value = ??? # Read in the contract source with open(sol_file, "r") as f: contract_src = f.read() # Instantiate manticore's Ethereum Virtual Machine m = ManticoreEVM() # m.verbosity(0) # Create an account for your wallet address on the EVM with funds to # both deploy the contract and to play the lottery user_account = m.create_account(address=from_address, balance=contract_balance+msg_value) # Create the Lottery CTF level contract on the EVM using wallet contract_account = m.solidity_create_contract( contract_src, contract_name="Lottery", owner=user_account, balance=contract_balance, args=(0,0) ) # Make symbolic buffer to hold msg.data and ask Manticore to calculate the "winning" value # 4 bytes for the function signature hash and ??? more for a uint256 sym_args = m.make_symbolic_buffer(???) # Issue a symbolic transaction to the EVM by setting msg.data to symbolic buffer
ROOT_DIR = os.path.abspath(os.path.join(os.path.join(SCRIPT_DIR, os.pardir, os.pardir, os.pardir))) BUILD_DIR = os.path.join(ROOT_DIR, 'build', 'contracts') OCEANTOKEN_JSON_PATH = os.path.join(BUILD_DIR, 'OceanToken.json') if not (os.path.exists(OCEANTOKEN_JSON_PATH)): sys.stderr.write('''Error: It does not appear as if the project is compiled! Please do so before running the Manticore tests. ''') sys.exit(1) ################ Script ####################### m = ManticoreEVM() owner_account = m.create_account(balance=1000, name='owner_account') print(f'[+] Created owner account ', owner_account.name_) minter_account = m.create_account(balance=1000, name='minter_account') print(f'[+] Created minter account ', minter_account.name_) with open(OCEANTOKEN_JSON_PATH) as f: contract_json = f.read() contract_account = m.json_create_contract(contract_json, owner=owner_account, name='contract_account') print(f'[+] Created contract ', OCEANTOKEN_JSON_PATH[len(ROOT_DIR):]) symbolic_address_1 = m.make_symbolic_value() symbolic_address_2 = m.make_symbolic_value() print(f'[+] Initialized contract ', OCEANTOKEN_JSON_PATH[len(ROOT_DIR):], 'with symbolic parameters')