Logo
Overview
Naruto vs The Chaos Scroll - Insecure Randomness Catastrophe

Naruto vs The Chaos Scroll - Insecure Randomness Catastrophe

November 28, 2025
5 min read

Naruto vs The Chaos Scroll - The Ultimate Randomness Heist

Danger (Believe it… or lose 1,000 ETH.)

Orochimaru didn’t need Rasengan.
He just needed block.timestamp.

One line of code.
One predictable number.
One village - bankrupt.

Dattebayo!


TL;DR

Vulnerability: Using block.timestamp / blockhash / block.difficulty for randomness
Weapon: Miners can manipulate or predict these values
Result:

  • Lottery winners chosen by attackers
  • Game outcomes rigged
  • Millions drained

Real losses: $10M+ across lotteries and games

Fixes:

  • Chainlink VRF (best)
  • Commit-Reveal (free)
  • Blockhash + future block (weak but common)

🎬 Story Time - The Hidden Leaf Lottery Heist

The Hidden Leaf Village launches a lottery.

Prize: 1,000 ETH

Entry: 1 ETH

Winner chosen by:

uint256 winner = uint256(
keccak256(abi.encodePacked(block.timestamp, block.difficulty, blockhash(block.number - 1), players.length))
);

Naruto enters.
Sakura enters.
1000 shinobi enter.

Then Orochimaru enters.

He doesn’t fight.

He waits.

He knows the next block’s timestamp will be ~15 seconds from now.

He runs the same keccak256(block.timestamp) locally.

Finds a future timestamp where his index wins.

Mines the block at that exact second.

Calls drawWinner().

Orochimaru wins 1,000 ETH.

Naruto: “That’s not fair, dattebayo!”

Orochimaru: “Randomness? In Solidity? Believe it.”


The Vulnerable Scroll - Full Code

HiddenLeafLottery.sol
25 collapsed lines
// SPDX-License-License: MIT
pragma solidity ^0.8.30;
contract HiddenLeafLottery {
address public hokage;
uint256 public entryFee = 1 ether;
uint256 public pool = 0;
address[] public ninjas;
bool public drawCompleted = false;
constructor() {
hokage = msg.sender;
}
modifier onlyHokage() {
require(msg.sender == hokage, "Only Hokage can call");
_;
}
function enter() external payable {
require(msg.value == entryFee, "Incorrect entry fee");
ninjas.push(msg.sender);
pool += msg.value;
}
function drawWinner() external onlyHokage {
require(!drawCompleted, "Draw already completed");
require(ninjas.length > 0, "No ninjas");
uint256 seed = uint256(
keccak256(abi.encodePacked(block.timestamp, block.difficulty, blockhash(block.number - 1), ninjas.length))
);
uint256 winnerIndex = seed % ninjas.length;
address winner = ninjas[winnerIndex];
drawCompleted = true;
(bool success,) = winner.call{value: pool}("");
require(success, "Transfer failed");
pool = 0;
}
function getPoolBalance() external view returns (uint256) {
return pool;
}
}

One line.
One village destroyed.


The Heist - Three Ways Orochimaru Wins

MethodCan Miner Do It?DifficultyReal-World Used?
block.timestampYes (~15s window)EasyYes (many lotteries)
blockhash(block.number-1)Yes (if they mine)MediumYes
Predict future blockhashNoHardNo (not possible)

Most common attack: block.timestamp manipulation


Foundry Test - Watch Orochimaru Win

HiddenLeafLotteryTest.t.sol
function testOrochimaruPredictsWinner() public {
// Naruto enters at index 0
vm.prank(naruto);
lottery.enter{value: 1 ether}();
// Orochimaru enters, becomes index 1
vm.prank(orochimaru);
lottery.enter{value: 1 ether}();
// The ninjas array is now: [Naruto, Orochimaru]
uint256 ninjaCount = 2;
uint256 targetTs = block.timestamp;
// Brute-force timestamp to make Orochimaru win
// Winner index must be 1
while (true) {
// Simulate the exact seed the contract will use
uint256 seed = uint256(
keccak256(
abi.encodePacked(
targetTs,
block.difficulty,
blockhash(block.number - 1),
ninjaCount
)
)
);
if (seed % ninjaCount == 1) {
break; // Found timestamp where Orochimaru wins
}
targetTs++;
}
// Manipulate the next block timestamp to chosen timestamp
vm.warp(targetTs);
// Hokage executes drawWinner
vm.prank(hokage);
lottery.drawWinner();
// Assert Orochimaru drained pool
assertEq(orochimaru.balance, 2 ether, "Orochimaru should win jackpot");
assertEq(lottery.getPoolBalance(), 0, "Pool should be empty");
}

forge test -vv → Orochimaru wins every time


The Three Ninja Fixes

function requestWinner() external onlyHokage {
COORDINATOR.requestRandomness(...);
}
function fulfillRandomness(uint256 randomness) internal override {
uint256 winnerIndex = randomness % ninjas.length;
// → truly random, tamper-proof
}

Pros: Unbreakable
Cons: Costs LINK

Fix 2: Commit-Reveal - The Shadow Clone Technique

// Phase 1: Everyone commits hash(secret + salt)
// Phase 2: Everyone reveals
// Final random = XOR all secrets

Pros: Free, secure
Cons: Two transactions, UX pain

Fix 3: Future Blockhash - The Chidori (Still Risky)

uint256 drawBlock = block.number + 10;
bytes32 hash = blockhash(drawBlock);

Pros: Better than timestamp
Cons: Miner who mines drawBlock can still influence


Real-World Victims

ProtocolYearLossMethod
Fomo3D2018$2M+block.timestamp
Multiple lotteries2020–2024Millionsblockhash + miner attack
RNG games2023$500k+predictable seeds

Total: Tens of millions lost to bad randomness


Auditor’s Randomness Checklist

Warning
  • Using block.timestamp, block.difficulty, or blockhash(block.number-1) alone? → Critical
  • No commit-reveal or VRF? → High
  • Randomness used for money/token minting? → Must be VRF
  • Tested miner manipulation with vm.warp and vm.roll? → Mandatory
  • Draw can be called multiple times? → Add drawCompleted flag

Quiz - Are You Hokage Material?

Exercise (Why can Orochimaru always win?)

a) He has more chakra
b) block.timestamp is predictable/manipulable
c) He uses flash loans
d) He bribes the Hokage

Answer

b) Miners control the clock

Exercise (Best production randomness in 2025?)

a) block.timestamp + msg.sender
b) Chainlink VRF
c) keccak256(tx.origin)
d) Commit-reveal

Answer

b) VRF = cryptographically proven fairness

Tip

Full repo - vulnerable + 3 fixed versions + exploit + tests:
github.com/thesandf/thesandf.xyz

Orochimaru didn’t need forbidden jutsu.

He just needed one bad randomness source.

Don’t be the Hidden Leaf Village.

Use Chainlink VRF.
Or commit-reveal.
Or at least future blockhash.

Because if you don’t…

Orochimaru will believe it - and take your ETH.

Dattebayo.