Ethernaut Force
Receiving Ether
One of the main use cases of the Ethereum blockchain is to be able to transfer ether between accounts. There are two types of accounts: Externally Owned Accounts (EOA) or Contract Accounts.
When it comes to sending ether directly to Contract Accounts, there are two solidity functions that can be used: send()
and transfer()
. However, there are some requirements that contracts need to follow in order to be able to receive ether via those function calls, namely containing a receive function or a payable fallback function.
Note: The reason I am not mentioning that the receive function needs to be payable, is because it is payable by default, where fallback functions are flexible on the definition.
These are the following possibilities:
- The target contract contains a payable fallback function;
- The target contract contains a receive function;
- The target contract contains either a payable fallback function or a receive function, where if the calldata attribute is empty, the receive function will be executed.
“If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through a transaction that does not represent a payable function call and throws an exception.” — Solidity Docs
The following code, illustrates the next challenge: Force. The challenge consists on trying to make the following contract balance greater than zero:
As it can be seen, theres no code at all on this contract. Even the basic receive()
and fallback()
functions are not present.
Exploitation Steps
As mentioned above, there are requirements on whether a contract account can receive ether or not. However, there are two exceptions that bypass the mentioned possible use cases:
“A contract without a receive Ether function can receive Ether as a recipient of a coinbase transaction (aka miner block reward) or as a destination of a
selfdestruct
.A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it.” — Solidity Docs
In resume, a contract without the mentioned requirements, can also receive ether as:
- A miner block reward;
- A destination of a
selfdestruct()
call; - (And to complement the answer) A transfer of ether to the target contract address, before it was deployed/created.
For this challenge, we are gonna exploit it by:
- Creating a malicious contract with a
selfdestruct()
call; - Sending some ether to the malicious contract;
- Trigger the
selfdestruct()
, which will send the ether to the challenge contract, effectively increasing its balance.
Refer below for the malicious contract code:
Running the Exploit
root@Web3 ❯ brownie run --network sepolia -i pwn.py
Brownie v1.19.3 - Python development framework for EthereumEthernautforceProject is the active project.Running 'scripts/pwn.py::main'...
Transaction sent: 0xc3eaf8d49588eaf63b0a7ed33cc5fc8e9dfdfb5992eb5e5cdbc3f6852417d264
Gas price: 3.731397437 gwei Gas limit: 115787 Nonce: 19
TheForceIsWithYou.constructor confirmed Block: 4108319 Gas used: 105261 (90.91%)
TheForceIsWithYou deployed at: 0xCe9eD838D4eF98E418bC41700aedF60a438eA9e8[+] Force contract @ 0x5152B250A129b60b8F772cF32E517F4b684b2964 with balance: 0
[+] Deployed Malicious Force contract @ 0xCe9eD838D4eF98E418bC41700aedF60a438eA9e8
[+] Topping up Malicious Force contract with 0.01 ether...
Transaction sent: 0x112212e176a28663fd56e7f04f978cd2fb59a3b65676e7fe54deeffc09888581
Gas price: 3.731397437 gwei Gas limit: 24622 Nonce: 20
Transaction confirmed Block: 4108321 Gas used: 22384 (90.91%)[+] Activating 'self-destruct' @ Malicious Force...
Transaction sent: 0xc5ab50f538262a50b529a68822d7e0030acf031ec1d4dac11c9407ee1526ee3c
Gas price: 3.076287879 gwei Gas limit: 32217 Nonce: 21
TheForceIsWithYou.forceMoney confirmed Block: 4108328 Gas used: 29289 (90.91%)[+] Force contract balance: 10000000000000000Interactive mode enabled. Use quit() to close.
Refer to the following code for the full exploit:
References
- https://web3py.readthedocs.io/en/stable/index.html
- https://sepolia.etherscan.io/
- https://ethernaut.openzeppelin.com/
Big props to @the_ethernaut for creating the content.