Verichains

Share this post

Another on-chain random exploit, a case study with MechMaster project

blog.verichains.io

Discover more from Verichains

Leading web3 security firm in APAC. Trusted by top blockchain customers such as BNBChain (Binance), Klaytn (Kakao Talk), Wemix (Wemade), Solana, Axie Infinity/Ronin Network (Sky Mavis)
Continue reading
Sign in

Another on-chain random exploit, a case study with MechMaster project

lifebow
May 6, 2022
Share this post

Another on-chain random exploit, a case study with MechMaster project

blog.verichains.io
Share

 Credit: lifebow, Giap Nguyen

Verichains had contacted the MechMaster team before the blog was published. The MechMaster team had planned to solve the issues.

Summary

On-chain randomness is not easy to implement because naturally blockchain is deterministic. In this case study, we analyze a typical mistake on-chain randomness implementation in a real NFT game. We also demonstrate the severe impact by arbitrarily minting high value NFT items. Finally, we give our opinions about on-chain randomness.

Why randomness?

Randomness is critical for any NFT game. It provides a general fairness and truthless for the community. If the random logic is abused, attackers are able to manipulate the game or generate unlimited high value NFTs which drain the project's pool and the community's investment.   

Why is on-chain randomness not easy?

Naturally blockchain logic is deterministic. The process should be repeatable and predictable which allows every node in the network to re-run the same logic to generate the same result. So it is hard for a node to perform a random minting NFT item and the rest of the network to reproduce or verify the result. 

An outdated recommendation for on-chain randomness takes some random source from the PoW block to generate pseudo random numbers. 

function _getRandomNumber(uint _upper) private returns (uint) {

  _seed = uint(keccak256(_seed,

                         block.blockhash(block.number — 1),

                         block.coinbase,

                         block.difficulty));

  return _seed % _upper;

}

Notice it is not a safe implementation and the behavior of those supposed random variables is different between blockchains.

Scope of the case study

Website market of Mech master:

https://market.mechmaster.io/

Verified source code on bscscan of the proxy contract published on the website. https://bscscan.com/address/0xe35f67aec4f633c01130fdc9f18286a4215c3e5f#code

The main contract which the proxy contract points to.

https://bscscan.com/address/0x37281cf9d0eda5059f41a62e969757f55c62bc1f

Explain the game on-chain logic

The game’s ERC1155 contract requires players to stake a certain $MECH token to minting random puzzles in the shape of the game's ERC1155 token.

The rarity of puzzles is distributed as below.

The analysis

Step 1: dig into the website’s dApp source code.

Notice the highlighted smart contract’s draw function called with two parameters: now and count which are totally controlled by the end-user.

Step 2: dig into the on-chain smart contract

Following the dApp calls, we identify the logic smart contract for the draw function at https://bscscan.com/address/0x37281cf9d0eda5059f41a62e969757f55c62bc1f

The smart contract has no source code but with a little effort, we can decompile into a clean version of the draw version. And focus on the random logic below.

The sha3 function is used to derive random results for the minting process. Parameters used in the calculation included:

  • block.difficulty: on Binance Smart Chain it is default = 2.

  • _blockNumber: it is actually the now value submitted by the end-user. By mistake, the decompiler assigned an incorrect name.

  • caller: address of the caller.

  • stor256: the value of contract’s storage which is extractable from blockchain data.    

Now, we guess that by controlling the now value from dApp, we are able to manipulate the smart contract to mint a rare legendary anytime. 

Step 3: Exploitation

Don't waste time to understand the minting logic, we fork the bsc mainnet and write a simple code to brute force the now value until the contract vomits legendary tokens (in the local environment).

Applying the results into bsc mainnet, we successfully mint 3 legendary tokens.

  • https://bscscan.com/tx/0x7dc44ff24ac46250547be458261699a8bb2854e2e189c557985fef4394ecf7f2

  • https://bscscan.com/tx/0xac972b895e72331a4ba79ba63140f666ea18338383ae15d2d3c3832d1f4019a6

  • https://bscscan.com/tx/0xf26bc9f07e9e2052da502fda20b55ce1019bb069a9695da3272bbc8cf042535f

What is the developer's mistake?

Looks like the developer made a common mistake when copying random code from Ethereum for BSC. The block.difficulty in Ethereum might contribute some randomness source but in BSC it is a fixed value. But even updating the source code, it is still vulnerable for advanced attacks which are out of the scope of the study-case.

To summarize our recommendations: 

Notice 1: Never assume every blockchain has the same behavior even if it is emv-compatible.

Notice 2: On-chain randomness is very difficult, don't do it by yourself unless you are very good in cryptography and blockchain security.

Share this post

Another on-chain random exploit, a case study with MechMaster project

blog.verichains.io
Share
Previous
Next
Comments
Top
New
Community

No posts

Ready for more?

© 2023 Verichains
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing