def run_one_check(max_call_depth, ops, contract_address, debug, read_from_blockchain): global MAX_CALL_DEPTH print('\n[ ]\033[1m Search with call depth: %d : \033[0m' % (max_call_depth), end='') initialize_params(read_from_blockchain, contract_address) clear_globals() # The amount of sent Ether to the contract is zero set_params('call_value', '', '0') MyGlobals.MAX_CALL_DEPTH = max_call_depth storage = {} stack = [] mmemory = {} data = {} trace = [] configurations = {} execute_one_block(ops, stack, 0, trace, storage, mmemory, data, configurations, ['CALL', 'SUICIDE'], ether_leak, 0, 0, debug, read_from_blockchain)
def run_one_check( max_call_depth, ops, contract_address, debug, read_from_blockchain ): print('\n[ ]\033[1m Search with call depth: %d : \033[0m' % (max_call_depth) , end = '') initialize_params(read_from_blockchain, contract_address ) clear_globals() global MAX_CALL_DEPTH MyGlobals.MAX_CALL_DEPTH = max_call_depth storage = {} stack = [] mmemory = {} data = {} trace = [] configurations = {} execute_one_block(ops,stack,0, trace, storage, mmemory, data, configurations, ['CALL','CALLCODE','DELEGATECALL','SUICIDE'], ether_lock_can_send, 0, 0, debug, read_from_blockchain )
def check_one_contract_on_ether_lock( contract_bytecode, contract_address, debug=False, read_from_blockchain=False): print('\033[94m[ ] Check if contract is GREEDY\033[0m\n') print('[ ] Contract address : %s' % contract_address) print('[ ] Contract bytecode : %s...' % contract_bytecode[:50]) print('[ ] Bytecode length : %d' % len(contract_bytecode)) print('[ ] Debug : %s' % debug) global MAX_CALL_DEPTH, symbolic_vars, symbolic_sha ops = parse_code(contract_bytecode, debug) # # # First check if Ether can be received by the contract # # MyGlobals.symbolic_vars = [] initialize_params(read_from_blockchain, contract_address) set_params('call_value', '', '100') clear_globals() # Only one function has to be called MyGlobals.MAX_CALL_DEPTH = 1 storage = {} stack = [] mmemory = {} data = {} trace = [] configurations = {} execute_one_block(ops, stack, 0, trace, storage, mmemory, data, configurations, [ 'STOP', 'RETURN'], ether_lock_can_recieve, 0, 0, debug, read_from_blockchain) print(('\033[91m[-]' if not MyGlobals.stop_search else '\033[92m[+]') + '\033[0m \033[1mContract can receive Ether\033[0m') # If it did not find, then the contract cannot receive Ether and thus it # cannot lock ether (is not bad ) if not MyGlobals.stop_search: print( '\n\033[92m[-] No lock vulnerability found because the contract cannot receive Ether \033[0m') return False # # # Then check if Ether can be released by the contract # # # If it does not have instructions that send Ether, then obviously it locks if not code_has_instruction( ops, ['CALL', 'CALLCODE', 'DELEGATECALL', 'SUICIDE']): # if debug: print( '\033[91m[-] The code does not have CALL/SUICIDE/DELEGATECALL/CALLCODE thus is greedy !\033[0m') return True if debug: print_code(contract_bytecode, ops) # Make some blockchain variables symbolic so they can take any value MyGlobals.symbolic_vars = [ 'CALLVALUE', 'CALLER', 'NUMBER', 'TIMESTAMP', 'BLOCKHASH', 'BALANCE', 'ADDRESS', 'ORIGIN', 'EXTCODESIZE'] MyGlobals.symbolic_sha = True MyGlobals.symbolic_load = True # # Search # for i in range(1, MyGlobals.max_calldepth_in_normal_search + 1): run_one_check(i, ops, contract_address, debug, read_from_blockchain) if MyGlobals.stop_search: print('\n\033[92m[+] No locking vulnerability found \033[0m') return False print('\n\n\033[91m[-] Locking vulnerability found! \033[0m') return True
def exec_contract(sol_file, c_address, owner): ''' First it calls the wHBFinder object to obtain concrete events and wHB relations between them. These events are then fuzzed to find EO bugs. ''' debug = MyGlobals.debug debug1 = MyGlobals.debug1 initialize_datastructures() c_address = Web3.toChecksumAddress(c_address) if len(c_address) < 1: print('\033[91m[-] Contract address is incorrect %s \033[0m' % c_address ) # find the compiled code from the local blockchain. web3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8666")) compiled_code = web3.eth.getCode(c_address) # get the function hashes from the Solidity code if not os.path.isfile(sol_file): print('\033[91m[-] Solidity source file %s does NOT exist\033[0m' % sol_file ) funclist1 = [] else: funclist1 = getFuncHashes(sol_file, debug) # Get the function hashes in the specific order as in bytecode. compiled_code = str(hex(int.from_bytes(compiled_code, byteorder='big'))) funclist = get_func_hashes(compiled_code) MyGlobals.functions = copy.deepcopy(funclist) # Match bytecode hashes to solidity function names funclist2 = [] for f in funclist: fnd = False for f1 in funclist1: if f1[1]==f[1]: funclist2.append( (f1[0],f1[1]) ) fnd = True break if not fnd: funclist2.append( (f[1],f[1]) ) if len(funclist2) == 0: print('Something wrong with the contract \n') return MyGlobals.functions = copy.deepcopy(funclist2) # Assuming fallback function is 11111111 (or 22222222 if the previous one is already taken by a legitimate function of the contract) found_fallback = False for each_pair in funclist2: if each_pair == '11111111': found_fallback = True break if found_fallback: funclist2.append(['fallback()', '22222222']) else: funclist2.append(['fallback()', '11111111']) # Initialize remaining private global parameters. initialize_params(c_address) # Append owners to the caller array which holds possible adddresses of caller field. MyGlobals.st['caller'].append(owner.lstrip('0x')) print('\nExecuting at blocknumber: \033[92m%d\033[0m'%(MyGlobals.STORAGE_AT_BLOCK)) code = compiled_code.replace('\n','').replace('\r','').replace(' ','').lstrip('0x') time0 = datetime.datetime.now() MyGlobals.Time_checkpoint_HB = datetime.datetime.now() c_address = Web3.toChecksumAddress(c_address) # Delegate static analysis of the bytecode to wHBFinder. whbFinderInstance = WHBFinder(code, c_address, debug, funclist2, MyGlobals.read_from_blockchain) node_list, simplified_hb = whbFinderInstance.check_one_contract() contract_bytecode = code balances = [] disasm = op_parse.parse_code(contract_bytecode, debug) c_address = hex(int(c_address, 16)).rstrip('L') c_address = Web3.toChecksumAddress(pad_address(c_address)) # Find the correct way to give input to the fuzzer. new_nodes_list, new_simplified_hb = optimize_nodes(node_list, simplified_hb, c_address, disasm, debug, MyGlobals.read_from_blockchain, MyGlobals.STORAGE_AT_BLOCK) for node in new_nodes_list: for each in funclist2: if node['name'] == each[1]: if isinstance(each[0], str): node['name'] = each[0] print_nodes_list(new_nodes_list) print('\nNew simplified HB ', new_simplified_hb, '\n') # Pass the events and wHB relations to dynamic analysis component. print('\033[92m\n.....................Now fuzzing between the nodes.....................\n\033[0m') time1 = datetime.datetime.now() # criteria is used to differetiate the buggy traces 0: balances at the end 1: storage at the end if not args.balances: criteria = global_params.CHECK_FOR_BALANCE check_all_traces( [], 4, new_nodes_list, new_simplified_hb, [], balances, c_address, contract_bytecode, disasm, criteria, debug1, MyGlobals.read_from_blockchain, MyGlobals.STORAGE_AT_BLOCK, time1, False) time2 = datetime.datetime.now() print('Printing not implemented ins for contract %s'%(c_address)) print_notimplemented() MyGlobals.notimplemented_ins.clear() print('Done printing not implemented ins') print('\nTotal time for fuzzing is ', (time2-time1).total_seconds()) print('\n Complete running time for contract ', c_address, (time2-time0).total_seconds(), '\n\n')