The Ethernaut-Solutions repository contains the solutions using Foundry and Hardhat.
Objectives
- This level requires you to take the elevator to the top floor. Set the
top
totrue
.
Analysis
Let's take a look at the level contract,
interface Building {
function isLastFloor(uint) external returns (bool);
}
contract Elevator {
bool public top;
uint public floor;
function goTo(uint _floor) public {
Building building = Building(msg.sender);
if (! building.isLastFloor(_floor)) {
floor = _floor;
top = building.isLastFloor(floor);
}
}
}
We need to set the top
to true
to clear the level. top
will set to true
if building.isLastFloor(floor)
returns true
. The Building
interface is instantiated with msg.sender
address; this implies that msg.sender
should implement the Building
interface. Note that, Elevator
contract does not implement the Building
interface. We will create a contract called ElevatorHack
that will implement the Building
interface and call the goTo()
function on the Elevator
contract so that inside the goTo()
function, msg.sender
will be the ElevatorHack
contract; doing so, our version of isLastFloor()
will be executed.
isLastFloor()
is called twice in the goTo()
function. To get past the if
condition, isLastFloor()
should return false
, so that !false
becomes true
. To set the top
to true
, isLastFloor()
should return true
.
Exploit
Let’s create a contract that will hack the level.
Go to the Remix IDE and create the following contract.
Elevator
contract implements the isLastFloor()
function of the Building interface, which the level contract will call. We will execute the hack()
function, which calls thegoTo()
function of the level contract. The goTo()
function then calls the above contract’s isLastFloor()
function. This gives us complete control over the return values from the function. As mentioned earlier, we need to return false
in the first execution to pass if
condition and true
in the second execution to set top
to true
.
We have declared the state variable count
, initialized to 0
by default. When the function isLastFloor()
is called for the first time, (count > 0)
returns false
, and executes the else
block. count++
sets count
to 1
, and the function returns false
, which passes the if
condition. The goTo()
function will call the isLastFloor()
function again for setting the top
. Since count > 0
, isLastFloor()
returns true
and sets the top
to true
.
Make sure
Metamask
is connected to thetestnet
. SelectInjected Provider
. Deploy theElevatorHack
contract.Call
hack()
function, passinginstance address
as an argument.Check value of
top
. This will returntrue
.await contract.top();
Submit the instance.
Level passed!!!😄
Key Takeaways
Exercise caution when inheriting from contracts that implement interfaces, as this can introduce additional layers of abstraction, which may make it more difficult to understand and predict the behavior of your contract. This can increase the risk of potential security vulnerabilities, as it may be more difficult to identify and address potential issues.
Be cautious when interacting with external contracts that implement an interface that your contract uses.
It is generally a good practice to limit the ability of other users to modify the storage and state of your contract. One way to do this is to use access controls, such as the
msg.sender
value, to limit which users are able to call certain functions or modify certain variables in your contract. However, it may be necessary to allow some users to implement their own interfaces or to modify the storage and state of your contract in certain circumstances. If you do need to allow this, you should ensure that you have thoroughly considered the security implications and have implemented appropriate safeguards.
The Ethernaut-Solutions repository contains the solutions using Foundry and Hardhat.
Solution using Foundry:-
Solution using Hardhat:-
More Levels