Solutions to The Ethernaut CTF challenges ⛳️
- Hello Ethernaut
- Fallback
- Fallout
- Coinflip
- Telephone
- Token
- Delegation
- Force
- Vault
- King
- Re-entrancy
- Elevator
- Privacy
- Gatekeeper One
- Gatekeeper Two
- Naught Coin
- Preservation
- Recovery
- MagicNumber
- AlienCodex
- Denial
- Shop
- DEX
- DEX TWO
- Puzzle Wallet
- Motorbike
- DoubleEntryPoint
This is a warmup. Just start by calling info()
and follow the instructions
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.
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.
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.
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.
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!