Solidity Attack Vectors #5 - Block Timestamp Manipulation Attack

Solidity Attack Vectors #5 - Block Timestamp Manipulation Attack

Table of contents

A miner can manipulate the block.timestamp which can be used to their advantage to attack a smart contract. block.timestamp can be manipulated by miners with the following constraints:

  • it cannot be stamped at an earlier time than its parent

  • it cannot be too far in the future

The Roulette contract is a game where you can win all of the Ether in the contract if you can submit a transaction at a specific time. A player needs to send 5 Ether and wins if the block.timestamp % 15 == 0.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Roulette {
    uint public pastBlockTime;

    constructor() payable {}

    function spin() external payable {
        require(msg.value == 5 ether);
        require(block.timestamp != pastBlockTime);

        pastBlockTime = block.timestamp;

        if (block.timestamp % 15 == 0) {
            (bool sent, ) = msg.sender.call{value: address(this).balance}("");
            require(sent, "Failed to send Ether");
        }
    }
}

A miner will try to manipulate the system to win the Ether in this contract by calling the spin function and submitting 5 Ether to play the game, then submit a block.timestamp for the next block that is divisible by 15. If the miner wins the next block he will win all the Ether in the smart contract.

Preventive Techniques

Don't use block.timestamp for a source of entropy and a random number. In a case where you have to use block.timestamp follow the 15 second rule: if the scale of your time-dependent event can vary by 15 seconds and maintain integrity.

Feel free to connect with me on LinkedIn and Twitter.