Get legendary items by breaking PNRG of MyCyptoChamp, an Ethereum online game (CVE-2018–12885)

Jonghyuk Song
Coinmonks
5 min readJul 31, 2018

--

Abstract

An Ethereum online game, MyCryptoChamp, has a vulnerability that it generates predictable random numbers. In this game, users can create champs and items by paying some Ether. At this time, the power of the champs and items are decided by the random numbers. The random numbers are generated using by keccak256() function with a private variable and a blockhash of block.number-1. However, they are all public, so attackers can easily access them and precompute the random number. This case is so similar with 1000 Guesss vulnerability that I described before[1]. I exploited MyCryptoChamp using the internal transactions in the way I exploited 1000 Guess. You can find more detail explanation in this article: “Attack on Pseudo-random number generator (PRNG) used in 1000 Guess, an Ethereum lottery game. (CVE-2018–12454)”. After the exploitation, I got a powerful champ with three legendary items.

Details

As I mentioned before, this vulnerability is similar to 1000 Guess’s vulnerability. If you don’t know how to read private variables and how to exploit using internal transactions, I recommend you reading my previous article[1]. There are more details.

Vulnerable codes

Figure 1. MyCryptoChamp generates random numbers with a private variable and a blockhash

randMod function is the vulnerable function of MyCryptoChamp’s smart contract. It generates random numbers using keccak256 with a private variable randNonce and blockhash(block.number-1). Both are public, so anyone can access.

Figure 2. Part of createChamp() function which generates new champs
Figure 3. Part of openLootbox() function which generates new items

Figure 2 and Figure 3 show that MyCryptoChamp generates champs and items using random numbers. Power of champs/items depends on the random numbers. Therefore, if an attacker can predict the random number, he can create most powerful champs and items.

Exploits

The way to exploit MyCryptoChamp is simple. Attackers precompute the random number by using randNonce and blockhash(block.number-1). When the desired random number is generated, attackers send transactions to MyCryptoChamp and get champs/items.

First, let’s find out how to get randNonce. we can read private variables like this[2]:

web3.eth.getStorageAt(contractAddress, position)

position is index position of a variable. We can retrieve all storage variables in smart contract using the above way. In MyCryptoChamp, randNonce is located at slot 11. Therefore, we can get randNonce when contractAddress is the address of MyCryptoChamp’s smart contract and position is 11.

Now, all we have to do is finding out blockhash(block.number-1). To know it accurately, we should know the block including the transaction that we sent. If I send a transaction at block number N, the transaction will be executed at block number N+a because it takes some time that miners select and execute transactions. However, nobody exactly knows the a. Therefore, before we send transactions, we cannot know the block number of the block executing our transactions.

Then, how we get blockhash(block.number-1) and precompute the random number?. We can know the block number of the block executing our transactions only in the smart contract. Therefore, we should deploy a smart contract and precompute the random number in that contract. This smart contract should satisfy the following conditions:

  1. generate random numbers in the same way as target contract
  2. send internal transactions to the target contract if it generates the random number that attackers want
  3. revert if it generates the random numbers that attackers don’t want

To attack MyCryptoChamp, attackers need the contract that satisfying the above conditions. Because of 3 condition, attackers should continuously send transactions until they get the random numbers they want. Therefore, some gas costs are consumed. However, in most cases, it is a tiny compared to the benefits that they will get when their attack is success.

To exploit MyCryptoChamp, I deployed the following contract:

Figure 4. The smart contract to exploit MyCryptoChamp

In createChp function, the first random number is for the attack power and the second is for the defense power. In createItm function, the first random number is for the rarity of the item and the second is for the item type. Both functions call MyCryptoChamp’s function when they generates the random numbers that attackers want. The random numbers generated in the attack contract is same with the random numbers generated in the contract of MyCryptoChamp because both are generated at the same block.

Figure 5. Champ and items that I created by exploiting the vulnerability

Figure 5. shows the champs and items that I created by exploiting this vulnerability. I created powerful champ and three legendary items.

Report

I reported it to the developers at end of June. Surprisingly, they returned back to me the gas cost that I spent on the exploit.

Now MyCryptoChamp is patched.

They removed my champ and three legendary items. Actually, I cannot find the source code of the new smart contract, so I’m not sure it is safe now.

Conclusion

Generating random numbers in Ethereum smart contract is not easy. It is not safe using private variables and block variables of past blocks. I recommend to you read my previous article if you want to know more details[1]. If there are smart contracts generating random numbers, you should check it is completely random.

Reference

[1] https://medium.com/coinmonks/attack-on-pseudo-random-number-generator-prng-used-in-1000-guess-an-ethereum-lottery-game-7b76655f953d

[2] https://web3js.readthedocs.io/en/1.0/web3-eth.html#getstorageat

Get Best Software Deals Directly In Your Inbox

--

--