Ethernaut-Solutions

Solutions to The Ethernaut CTF challenges ⛳️

Contents

  1. Hello Ethernaut
  2. Fallback
  3. Fallout
  4. Coinflip
  5. Telephone
  6. Token
  7. Delegation
  8. Force
  9. Vault
  10. King
  11. Re-entrancy
  12. Elevator
  13. Privacy
  14. Gatekeeper One
  15. Gatekeeper Two
  16. Naught Coin
  17. Preservation
  18. Recovery
  19. MagicNumber
  20. AlienCodex
  21. Denial
  22. Shop
  23. DEX
  24. DEX TWO
  25. Puzzle Wallet
  26. Motorbike
  27. DoubleEntryPoint

00 - Hello Ethernaut

This is a warmup. Just start by calling info() and follow the instructions

Script

01 - Fallback

Here we have to take ownership of the contract and withdraw all the Ether.

In order to be the owner we will have to send at least 1 wei to the contract, which will trigger the receive special function:

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

We also have to satisfy the contributions[msg.sender] > 0:

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

So beforehand, we have to call the contribute, and make a small contribution to it.

After those two steps we can call the withdraw and job done.

Script | Test

02 - Fallout

In previous versions of Solidity there was no constructor function, so it had to be named with the same name as the contract.

In this case the "constructor" had a typo and was named Fal1out. Just call the function to gain ownership of the contract.

Script | Test

03 - Coinflip

For this challenge we have to guess a coin flip for 10 times in a row.

The "random" function looks like this:

uint256 blockValue = uint256(blockhash(block.number.sub(1)));

if (lastHash == blockValue) {
    revert();
}

lastHash = blockValue;
uint256 coinFlip = blockValue.div(FACTOR);
bool side = coinFlip == 1 ? true : false;

Truly random numbers cannot be generated in Solidity. So, we can create an attacker contract with the same random function, calculate the outcome and send it to the original contract. This way we can make sure the guess will always be correct.

Repeat it 10 times and we win.

Script | Test

04 - Telephone

Here we have to claim ownership of the contract. In order to do that we have to call the function:

function changeOwner(address _owner) public {
  if (tx.origin != msg.sender) {
    owner = _owner;
  }
}

To satisfy the tx.origin != msg.sender requirement we just have to make the call from another contract.

Script | Test

05 - Token

For this challenge we need to increment our tokens to over 20.

In older versions of Solidity overflows and underflows didn't revert the tx. In this case, an underflow can be achieved in the function:

function transfer(address _to, uint256 _value) public returns (bool) {
  require(balances[msg.sender] - _value >= 0);
  balances[msg.sender] -= _value;
  balances[_to] += _value;
  return true;
}

If we send a _value greater than the balance we have, there will be an underflow, leading to a huge number.

So, what we have to do is send, lets say 21 tokens to any other address, and then our balance will significantly increase!

Script | Test

06 - Delegation