This levels requires you to exploit a poorly implemented fallback
function to gain control of someone else’s smart contract.
It is best practice to implement a simple Fallback
function if you want your smart contract to generally receive Ether from other contracts and wallets.
The Fallback function enables a smart contract’s inherent ability to act like a wallet.
If I have your wallet address, I can send you Ethers without your permission. In most cases, you might want to enable this ease-of-payment feature for your smart contracts too. This way, other contracts/wallets can send Ether to your contract, without having to know your ABI or specific function names.
Note: without a fallback, or known payable functions, smart contracts can only receive Ether: i) as a mining bonus, or ii) as the backup wallet of another contract that has self-destructed.
The problem is when developers implement key logic inside the fallback function.
Such bad practices include: changing contract ownership, transferring the funds, etc. inside the fallback function:
Bad practice: you should not reassign contract ownership in a fallback function
This level demonstrates how you open up your contract to abuse, because anyone can trigger a fallback function.
Anyone can call a fallback function by:
There are two places inside Fallback.sol where, as the msg.sender
, you can become the contract’s owner:
The first option requires you to send 1000000000000000000000 wei
, or 1000 Ether
to this smart contract.
You probably don’t have ~5 hours to slowly request 1000 ethers from Ropsten faucet, which heavily throttles you after the first few requests. So let’s fallback to the fallback
option.
require(msg.value > 0 && contributions[msg.sender] > 0);
import 'github.com/OpenZeppelin/zeppelin-solidity/contracts/ownership/Ownable.sol';
3. Retrieve your existing contract instance by loading the contract via the instance
address:
Check ‘instance’ inside the console for your address. 0xb9bcfd… is my instance address.
4. Donate a nominal amount of Ether to the contract, using thecontribute
function. Make sure you are donating from your player
account address.
Note: the contribute()
function has the conditional statement:require(msg.value < 0.001 ether);
Make sure your contribution value
is less than 0.001 ether.
Check that you’re donating from your `player` wallet address
5. Finally, add some arbitrary value into the value
field and trigger the (fallback)
function.
Inside the console, check that you now own the contract by typingawait contract.owner();
You can trigger the fallback function via sending transactions through the console, to the same effect:
contract.sendTransaction({from: player,value: toWei(...)})
// Make sure you leave the "data:" field empty
Ethernaut Lvl 0 Walkthrough: ABIs, Web3, and how to abuse them_Make a smart contract do things it didn’t want to…_hackernoon.com
Ethernaut Lvl 2 Fallout Walkthrough: how simple developer errors become big mistakes_This is a in-depth series around Zeppelin team’s smart contract security puzzles. I’ll give you the direct resources…_medium.com