Ethernaut 题库闯关 #13 — Gatekeeper One

Ethernaut 题库闯关连载的第13篇题解。

今天这篇是Ethernaut 题库闯关连载的第13篇,难度等级:有点难。

Ethernaut 题库闯关我已经整理为一个专栏了, 欢迎大家订阅专栏。

挑战13:GatekeeperOne

为了解决这个挑战,我们需要通过三个不同修改器, 也就是合约代码里的 3 个 "门",每一个“门”都有不同的要求, 要求使 enter 成功返回 true。

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

import '@openzeppelin/contracts/math/SafeMath.sol';

contract GatekeeperOne {

  using SafeMath for uint256;
  address public entrant;

  modifier gateOne() {
    require(msg.sender != tx.origin);
    _;
  }

  modifier gateTwo() {
    require(gasleft().mod(8191) == 0);
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
      require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
      require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
      require(uint32(uint64(_gateKey)) == uint16(tx.origin), "GatekeeperOne: invalid gateThree part three");
    _;
  }

  function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
    entrant = tx.origin;
    return true;
  }
}

本次挑战我们会应用在挑战#4 Telephone挑战#5 Token中学到的内容, 你可以在Solidity的文档中了解更多关于特殊函数gasleft()的文档(见这里这里)。

在查看解题思路之前,可以先自己想一想,自己会怎么做?

研究合约

GatekeeperOne合约本身是很短的,

完成挑战需要在这三个函数修改器里面解决三个不同的小难题;否则,合约就会被回退。

让我们把解释说明分成三个不同的部分

gateOne:msg.sendertx.origin

要破解这个门,我们必须了解msg.sendertx.origin是什么,它们之间有什么区别。

让我们看看Solidity文档对这些全局变量的描述。

  • msg.sender (address): 消息的发送者 (当前调用)
  • tx.origin (address): 交易的发送者(完整的调用链)

当交易由EOA (外部账号)发起时,它直接与智能合约交互,这两个变量将具有相同的值。但是,如果它与一个中间人合约A交互,然后通过直接(call)调用(不是delegatecall)另一个合约B,在 B 合约里这些值将是不同的,在这种情况下。

  • msg.sender将是A合约的地址

  • tx.origin将是EOA地址地址。

因为要使gateOne不被回退,我们需要让msg.sender != tx.origin,这意味着我们必须从智能合约中调用enter,而不是直接从EOA中调用。

但另外,我建议你阅读文末的进一步阅读中列出的关于tx.origin的一些安全问题和最佳实践,以及什么时候不应该使用它,尽管这不是挑战的一部分。

gateTwo: gasleft().

从Solidity文档中关于全局变量,我们知道gasleft() returns (uint256)是一个函数,用于返回交易中剩余的Gas

重要的是要知道每个Solidity指令实际上是一系列低级EVM操作码的高级表示。在执行了 "GAS "操作码(在EVM操作码文档网站上阅读更多信息)后,返回值是在执行了 "GAS "操作码后的剩余Gas量,该操作码目前花费2个Gas

事情在这里变得过于复杂,因为要通过 "gateTwo"的检查,你必须使用 level.enter{gas: exactAmountOfGas}(gateKey),并使用一个非常具体的Gas值,使 gasleft().mod(8191) 返回 0(剩余Gas必须是8191的倍数)。

![img](https://img.learnblockc...

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Ethernaut CTF
Ethernaut CTF
信奉 CODE IS LAW.