Ethernaut: 14. 게이트키퍼 2
// 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;
}
}
통과해야 할 또 다른 게이트 퍼즐이 있습니다! 다시 세 개의 게이트가 있습니다.
msg.sender != tx.origin
. extcodesize
호출. require
는 게이트 키가 어떤 것인지 알려줍니다. 게이트 1
이전 퍼즐과 마찬가지로 계약을 중개인으로 사용하십시오.
게이트 2
다음은 실제 게이트입니다.
modifier gateTwo() {
uint x;
assembly { x := extcodesize(caller()) }
require(x == 0);
_;
}
extcodesize
는 기본적으로 주어진 주소의 코드 크기를 반환하며 이 경우 호출자입니다. 계약에는 코드가 있고 사용자 계정에는 코드가 없습니다. 코드 크기가 0이 되려면 계정이어야 합니다. 하지만 잠깐만, 그렇다면 첫 번째 게이트를 어떻게 통과할까요? 이 게이트의 트릭은 다음과 같습니다. extcodesize
는 constructor
에서 호출되는 경우 0을 반환합니다. 여기 내가 이 정보를 우연히 발견한 link이 있습니다.요컨대, 생성자 내에서 공격을 실행해야 합니다.
게이트 3
이 게이트의 형식은 다음과 같습니다.
modifier gateThree(bytes8 _gateKey) {
require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == uint64(0) - 1);
_;
}
이것은 단지 XOR 연산(종종 ⊕로 표시됨)이며 여기서 제어할 수 있는 매개변수는 게이트 키 하나뿐입니다. 글쎄, 우리는 그것을 어떻게 찾을 수 있습니까? XOR은 동일한 값이 자체적으로 XOR되면 취소되는 속성을 가지고 있습니다. 또한 XOR은 가환적이므로
a ⊕ b = b ⊕ a
. a ⊕ b = c
로 시작하여 양쪽을 a
로 XOR하면 a ⊕ a ⊕ b = c ⊕ a
가 되고 왼쪽은 취소되어 b = c ⊕ a
가 됩니다.한 가지 더:
(uint64(0) - 1)
원인은 Solidity에 실제로 좋지 않으며 심지어 가스 추정 오류를 유발했습니다! 결과는 기본적으로 uint64
의 가능한 최대 값이며 type(uint64).max
를 통해 찾을 수 있는 멋진 방법이 있습니다.다음과 같이 게이트 키를 안전하게 찾을 수 있습니다.
bytes8 key = bytes8(type(uint64).max ^ uint64(bytes8(keccak256(abi.encodePacked(address(this))))));
이것이 전부입니다!
Reference
이 문제에 관하여(Ethernaut: 14. 게이트키퍼 2), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/erhant/14-gatekeeper-two-5c72텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)