Skip to main content

web3-audit

Smart contract security audit — 10 DeFi bug classes (accounting desync, access control, incomplete path, off-by-one, oracle, ERC4626, reentrancy, flash loan, signature replay, proxy), pre-dive kill signals (TVL < $500K etc), Foundry PoC template, grep patterns for each class, and real Immunefi paid examples. Use for any Solidity/Rust contract audit or when deciding whether a DeFi target is worth hunting.

소스 정보

저장소
awarexone/Agentic-Bug-Hunter
최근 소스 활동
2026년 3월 17일 19:53
감지된 SKILL.md 언어
영어
스타
5,478
포크
967

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
web3-audit
description
Smart contract security audit — 10 DeFi bug classes (accounting desync, access control, incomplete path, off-by-one, oracle, ERC4626, reentrancy, flash loan, signature replay, proxy), pre-dive kill signals (TVL < $500K etc), Foundry PoC template, grep patterns for each class, and real Immunefi paid examples. Use for any Solidity/Rust contract audit or when deciding whether a DeFi target is worth hunting.
# WEB3 SMART CONTRACT AUDIT 10 bug classes. Pre-dive kill signals. Foundry PoC template. Real paid examples. --- ## PRE-DIVE KILL SIGNALS (check BEFORE any code review) > ZKsync lesson: $322M TVL + OZ audit + 750K LOC + 5 sessions = 0 findings. Large well-audited bridges are extremely hard. 1. **TVL < $500K** → max payout capped too low for effort 2. **2+ top-tier audits** (Halborn, ToB, Cyfrin, OpenZeppelin) on simple protocol → bugs already found 3. **Protocol < 500 lines, single A→B→C flow** → minimal attack surface 4. **Formula**: `max_realistic_payout = min(10% × TVL, program_cap)` — if < $10K, skip **Soft kill:** OZ/ToB/Cyfrin audit on current version + codebase > 500K LOC → expect 40+ hours for maybe 1 finding. Only proceed if bounty floor > $50K AND you have protocol-specific expertise. **Target scoring (go if >= 6/10):** - TVL > $10M: +2 - Immunefi program with Critical >= $50K: +2 - No top-tier audit on current version: +2 - < 30 days since deploy: +1 - Protocol you've hunted before: +1 - Source code + natspec comments: +1 - Upgradeable proxies: +1 --- ## THE ONE RULE > "Read ALL sibling functions. If `vote()` has a modifier, check `poke()`, `reset()`, `harvest()`. The missing modifier on the sibling IS the bug." This single rule explains 19% of all Critical findings. --- ## 1. ACCOUNTING STATE DESYNCHRONIZATION > #1 Critical bug class — 28% of all Criticals on Immunefi. ### What It Is Two state variables supposed to stay in sync. One code path updates A but forgets B. Later code reads both and makes decisions based on stale B. ``` Real Value = A - B If A updated but B isn't → Real Value appears larger → phantom value ``` ### Root Cause Patterns **Variant 1: Phantom Yield** (Yeet protocol — 35 duplicate reports) ```solidity function startUnstake(uint256 amount) external { totalSupply -= amount; // decremented BEFORE transfer // aToken.balanceOf(this) still reflects old value // yieldAmount = aToken.balanceOf - totalSupply = phantom yield } ``` **Variant 2: Fast Path Skips State Update** (Alchemix V3) ```solidity function claimRedemption(uint256 tokenId) external { if (transmuter.balance >= amount) { transmuter.transfer(user, amount); _burn(tokenId); return; // EARLY RETURN — cumulativeEarmarked, _redemptionWeight, totalDebt never updated } // Slow path: updates all state vars correctly alchemist.redeem(...); } ``` **Variant 3: Update Happens in Wrong Order** (Alchemix) ```solidity function deposit(uint256 amount) external { _shares = (amount * totalShares) / totalAssets; // calculated BEFORE deposit totalAssets += amount; // assets added AFTER shares calculated → wrong rate } ``` ### Grep Patterns ```bash # Find all accounting variables grep -rn "totalSupply\|totalShares\|totalAssets\|totalDebt\|cumulativeReward\|rewardPerShare" contracts/ # Find all early returns in claim/redeem functions grep -rn "\breturn\b" contracts/ -B3 | grep -B3 "if\b" # For each early return: which state updates in normal path are skipped? ``` --- ## 2. ACCESS CONTROL > #2 Critical — 19% of Criticals. $953M lost in 2024 alone. ### Variant 1: Missing Modifier on Sibling Function ```solidity function vote(uint256 tokenId) external onlyNewEpoch(tokenId) { // guarded function reset(uint256 tokenId) external onlyNewEpoch(tokenId) { // guarded function poke(uint256 tokenId) external { // NO GUARD → infinite FLUX inflation } ``` ### Variant 2: Wrong Check (Existence vs Ownership) ```solidity function split(uint256 tokenId, uint256 amount) external { _requireOwned(tokenId); // checks if token EXISTS, not if caller OWNS it _burn(tokenId); _mint(msg.sender, amount); // attacker steals tokens they don't own } ``` ### Variant 3: Silent Modifier (if vs require) ```solidity // VULNERABLE — non-admin silently gets through: modifier onlyAdmin() { if (msg.sender == admin) { _; // body only executes for admin, but non-admin doesn't revert } } // CORRECT: require(msg.sender == admin, "Not admin"); _; ``` ### Variant 4: Uninitialized Proxy ```solidity function initialize(address _owner) public { // MISSING: initializer modifier owner = _owner; // anyone can call → become owner } // Fix: constructor() { _disableInitializers(); } ``` ### Grep Patterns ```bash # Find sibling function families — do ALL have the same modifier set? grep -rn "function vote\|function poke\|function reset\|function update\|function claim\|function harvest" contracts/ -A2 # Ownership check: existence vs ownership? grep -rn "_requireOwned\|ownerOf\|_isApprovedOrOwner\|_checkAuthorized" contracts/ -B5 # Silent modifiers grep -rn "modifier\b" contracts/ -A8 | grep -B3 "if (" | grep -v "require\|revert" # Uninitialized initializer grep -rn "function initialize\b" contracts/ -A3 grep -rn "_disableInitializers()" contracts/ ``` ### Real Paid Examples | Protocol | Payout | Bug | |---|---|---| | Wormhole | $10M | Uninitialized UUPS proxy → anyone calls initialize() | | ZeroLend | n/a | split() uses existence check, not ownership check | | Alchemix | n/a | poke() missing onlyNewEpoch → infinite FLUX inflation | | Parity | $150M frozen | No access control on initWallet() in library | --- ## 3. INCOMPLETE CODE PATH > #3 Critical — 17% of Criticals. ### The Function Family Comparison Test ``` 1. List all state changes in function A (deposit/place/create) 2. List all state changes in function B (withdraw/update/cancel) 3. For each state change in A: does B have the corresponding reverse? 4. For each token transfer in A: does B have the corresponding refund? If A does X but B doesn't do the reverse of X → BUG. ``` ### Variant 1: Update Function Missing Refund (ThunderNFT) ```solidity function place_order(OrderInput calldata order) external { token.safeTransferFrom(msg.sender, address(this), order.price); // takes tokens orders[orderId] = order; } function update_order(OrderInput calldata updatedOrder) external { // BUG: NO REFUND for sell orders when price decreases → tokens permanently stuck orders[orderId] = updatedOrder; } ``` ### Variant 2: Partial Fill Token Stuck (Plume) ```solidity function swapForETH(uint256 amountIn) external { token.safeTransferFrom(msg.sender, address(this), amountIn); uint256 filled = dex.swap(amountIn); // partial fill possible _refundExcessEth(amountIn - filled); // BUG: refunds ETH only, not ERC20 } ``` ### Variant 3: mint() Bypasses Check That deposit() Has (MetaPool) ```solidity function deposit(uint256 assets, address receiver) public override { shares = _deposit(assets, receiver); // includes receipt validation } function mint(uint256 shares, address receiver) public override { assets = convertToAssets(shares); _mint(receiver, shares); // MISSING: _deposit() validation → mints without receiving assets } ``` ### Grep Patterns ```bash grep -rn "function place_\|function create_\|function add_\|function open_" contracts/ -A5 grep -rn "function update_\|function modify_\|function cancel_" contracts/ -A5 grep -rn "safeApprove\b" contracts/ # safeApprove without zero-reset before grep -rn "delete\b" contracts/ -B5 -A5 # delete before operation completes grep -rn "function deposit\|function mint\|function withdraw\|function redeem" contracts/ -A10 ``` --- ## 4. OFF-BY-ONE & BOUNDARY CONDITIONS > #4 High — 22% of Highs. Single character change. Massive impact. ### Root Cause ```solidity // VeChain Stargate — post-exit reward drain: function _claimableDelegationPeriods(address delegator) internal view returns (uint256) { if (endPeriod > nextClaimablePeriod) { // BUG: should be >= return 0; // exited users get nothing } return nextClaimablePeriod - lastClaimedPeriod; // rewards for period AFTER exit } ``` ### Mental Test for Every Comparison > For every `if (A > B)`: "What happens when A == B?" Is that correct? ### 6 Boundary Locations to Check 1. Period/Epoch boundaries: `>` vs `>=` at period end 2. Time-based locks: does `block.timestamp == deadline` lock or unlock? 3. Loop break conditions: `break` with `>` vs `>=` 4. Array index boundaries: `i <= array.length` (should be `i < array.length`) 5. Amount/balance boundaries: `>= amount` allows exact full withdrawal? 6. Rounding/precision: can any input produce 0 output that should be non-zero? ### Grep Patterns ```bash # Boundaries in comparisons grep -rn "Period\|Epoch\|Round\|Deadline\|period\|epoch\|deadline" contracts/ -A3 | grep "[<>][^=]" # Loop breaks grep -rn "\bbreak\b" contracts/ -B10 # Off-by-one in array access grep -rn "\.length\s*-\s*1\|i\s*<=\s*.*\.length\b" contracts/ ``` --- ## 5. ORACLE / PRICE MANIPULATION > 12% of all reports. Largest individual payouts. $117M Mango, $70M Curve. ### Bug A: Missing Staleness Check (most common) ```solidity // VULNERABLE: (, int256 price,,,) = priceFeed.latestRoundData(); return uint256(price); // If Chainlink node goes down, stale price returned indefinitely // CORRECT: (, int256 price,, uint256 updatedAt,) = priceFeed.latestRoundData(); require(block.timestamp - updatedAt <= MAX_PRICE_AGE, "Stale price"); require(price > 0, "Invalid price"); ``` ### Bug B: Missing Confidence Interval (Pyth) ```solidity // VULNERABLE: PythStructs.Price memory p = pyth.getPriceUnsafe(priceFeed); return p.price; // ignores p.conf (confidence interval) // CORRECT: require(p.conf * 10 <= uint64(p.price), "Price too uncertain"); // conf > 10% of price = untrustworthy ``` ### Bug C: TWAP Too Short (flash loan manipulatable) ```solidity // VULNERABLE: 60-second TWAP uint32[] memory secondsAgos = new uint32[](2); secondsAgos[0] = 60; secondsAgos[1] = 0; // Flash loan can shift price for entire 60s window // CORRECT: 1800s minimum TWAP (30 min) ``` ### Bug D: Single-Source Oracle ```solidity // VULNERABLE: only Uniswap spot price uint price = getUniswapSpotPrice(token); // flash loan manipulatable // CORRECT: Chainlink primary, Uniswap TWAP as fallback, require close agreement ``` ### Grep Patterns ```bash # Missing staleness check grep -rn "latestRoundData" contracts/ -A5 | grep -v "updatedAt\|timestamp" # Pyth price usage — confidence interval checked? grep -rn "getPriceUnsafe\|getPrice\b" contracts/ -A8 | grep -v "conf\|confidence" # TWAP windows — short TWAP flag grep -rn "secondsAgo\|TWAP\|cardinality" contracts/ -A5 ``` --- ## 6. ERC4626 VAULT ATTACKS ### Exchange Rate Manipulation (near-empty vault) ```solidity // VULNERABLE — first depositor attack: // 1. Attacker deposits 1 wei → gets 1 share // 2. Attacker donates large amount directly (transfer, not deposit) // 3. Exchange rate: 1 share = (1 + donation) assets // 4. Victim deposits → rounds down to 0 shares → free donation to attacker // CORRECT: virtual shares (OpenZeppelin v4.9+) function _decimalsOffset() internal view virtual override returns (uint8) { return 9; // add 1e9 virtual shares + assets to prevent manipulation } ``` ### ERC4626 Transfer (moves shares but not stake/lock records) ```solidity // VULNERABLE: shares transferred, but lock records stay with original owner // → shares stuck, can't redeem → permanent freeze (Belong pattern) function transfer(address to, uint256 amount) external override { _transfer(msg.sender, to, amount); // moves shares // MISSING: transfer lock record from msg.sender to `to` } ``` ### Grep Patterns ```bash grep -rn "function transfer\|function transferFrom" contracts/ -A15 grep -rn "function deposit\|function mint\|function withdraw\|function redeem" contracts/ -A10 ``` --- ## 7. REENTRANCY > 2016–present. CEI pattern prevents it. Still found in DeFi. ### Variants - **Single-function**: attacker re-enters same function before state updated - **Cross-function**: re-enters a sibling function with stale state - **Cross-contract**: re-enters via a callback to another protocol - **Read-only**: re-enters a view function that returns stale data used by attacker ### Root Cause Pattern ```solidity // VULNERABLE (effects after interaction): function withdraw(uint256 amount) external { require(balances[msg.sender] >= amount); (bool success,) = msg.sender.call{value: amount}(""); // INTERACTION first require(success); balances[msg.sender] -= amount; // EFFECT after → reentrancy window } // CORRECT (CEI — Checks, Effects, Interactions): function withdraw(uint256 amount) external { require(balances[msg.sender] >= amount); // CHECK balances[msg.sender] -= amount; // EFFECT (bool success,) = msg.sender.call{value: amount}(""); // INTERACTION last require(success); } ``` ### Grep Patterns ```bash # External calls before state updates grep -rn "\.call{value\|safeTransfer\|transfer(" contracts/ -B10 | grep -v "require\|revert" # Missing nonReentrant modifier on critical functions grep -rn "function withdraw\|function redeem\|function claim" contracts/ -A2 | grep -v "nonReentrant" # Storage slot for reentrancy guard
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기