Ethernaut Lvl 9 King Walkthrough: How bad contracts can abuse withdrawals

This is a in-depth series around Zeppelin team’s smart contract security puzzles. We learn key Solidity concepts to solve the puzzles 100% on your own.

Nicole Zhu
Aug 26, 2018 · 3 min read

This levels requires you to prevent the level from regaining Kingship.


In an earlier deep dive, we discussed fallback functions .

Specifically, every smart contract can have a simple Fallback function in order to receive Ethers from other contracts and wallets.

Each time your contract sends Ethers to another contract, you are depending on the other contract’s code to handle the transaction and determine the transaction’s success.

This means your valid transactions can arbitrarily fail.

As the transaction sender, you are always susceptible to the following cases:

  • Loophole 1: The receiving contract doesn’t have a payable fallback function, cannot receive Ethers, and will throw an error upon a payable request.

Detailed Walkthrough

When you submit this King.sol instance back to level, Ethernaut will call this fallback function to regain Kingship. The key is to guarantee that Ethernaut’s transaction will fail, so you can remain King.

Notice inside this fallback function, there is a king.transfer(), which can fail if the current king is a malicious contract and refuses to withdraw.

To quickly solve this level, you can either omit the fallback function (L1) or implement a malicious fallback function (L2) in a new contract. Let’s implement the malicious fallback function so we can also include a taunting message.

Note: If you are using Remix for this level, give it the full import path:

import ‘github.com/OpenZeppelin/zeppelin-solidity/contracts/ownership/Ownable.sol’;

  1. Create a BadKing contract and seed it with at least 1 Ether in the constructor:
contract BadKing {
functionBadKing() public payable {
}
}

2. Create a function to allow this BadKing to become the recognized King in King.sol, making sure to send at least 1 Ether to surpass the current prize.

function becomeKing() public {
address(king).call.value(1000000000000000000).gas(4000000)();
}

3. Implement a payable fallback function which immediately reverts the transaction. Give it an error message (optional).

Your final contract should look like:

4. Lastly, simply submit your King.sol contract instance back to the level and wait for the transaction to fail!


Coinmonks

Coinmonks is a non-profit Crypto educational publication. Follow us on Twitter @coinmonks Our other project — https://coincodecap.com

Nicole Zhu

Written by

Engineer @ParityTech | I write about blockchain and Web3 | Tweet @nczhu

Coinmonks

Coinmonks

Coinmonks is a non-profit Crypto educational publication. Follow us on Twitter @coinmonks Our other project — https://coincodecap.com

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade