career·8 min de lecture·Par Solingo

Votre Premier Bug Bounty — Guide Pratique

De la découverte du bug au paiement. Que mettre dedans, comment justifier la sévérité, pièges à éviter.

# Votre Premier Bug Bounty — Guide Pratique

Vous avez trouvé un bug. Maintenant il faut le reporter. Voici comment maximiser vos chances de paiement.

Étape 1 : Choisir un Programme

Immunefi

Focus smart contracts. Bounties jusqu'à $10M+.

Avantages :
  • Payouts rapides (médiane 7 jours)
  • Triagers spécialisés (comprennent Solidity)
  • KYC léger

Inconvénients :

  • Compétition élevée (programmes populaires = auditeurs pro)
  • Scope parfois flou

Code4rena

Contests de 3-7 jours. Pool prize partagé.

Avantages :
  • Tous les bugs Medium+ payés (pas seulement le #1)
  • Pratique avec feedback de judges
  • Communauté active

Inconvénients :

  • Variance élevée (duplicates = share du prize)
  • Payout 2-4 semaines post-contest

Sherlock

Contests + bug bounty permanent.

Avantages :
  • Scope très clair (pas de "out-of-scope" surprises)
  • Lead judges de qualité

Inconvénients :

  • Bar de qualité élevé (judges sévères)
  • Focus protocols DeFi

Étape 2 : Lire le Scope

Lisez TOUT le scope. Les rejets #1 = out-of-scope.

Exemple scope Immunefi :

In Scope

  • contracts/core/*
  • contracts/periphery/Router.sol

Out of Scope

  • Known issues (voir GitHub issues #123, #456)
  • Front-running (pas considéré un bug)
  • Gas optimizations
  • Centralization risks (admin peut upgrader = connu)

Impacts acceptés

Critical : Perte de funds, unauthorized mint

High : Temporary freeze, incorrect accounting

Medium : ...

Red flags :

  • "Admin ruggable" (centralization) → out-of-scope
  • "Slippage front-running" → connu, pas payé
  • "Uninitialized proxy" dans tests/ → test code out-of-scope

Étape 3 : Confirmer le Bug Localement

Avant de submit, prouvez-le à vous-même.

// Bug trouvé : integer overflow dans depositFor

contract Vault {

mapping(address => uint256) public balances;

function depositFor(address user, uint256 amount) external {

balances[user] += amount; // ❌ Pas de check overflow (Solidity 0.7)

}

}

// PoC Foundry

function testOverflow() public {

vault.depositFor(alice, type(uint256).max);

vault.depositFor(alice, 1); // Wrap to 0

assertEq(vault.balances(alice), 0); // ✅ Alice a "déposé" mais balance = 0

// Impact : Alice peut drain les funds via withdraw

}

Étape 4 : Rédiger le Rapport

Structure Standard

# [SEVERITY] Title Court et Descriptif

Summary

1-2 phrases. Quoi, où, impact.

Vulnerability Details

Code vulnérable (snippet).

Explication technique.

Impact

Conséquences concrètes.

Chiffrez si possible (ex: "attacker peut voler 100% du TVL").

Proof of Concept

Code complet reproductible.

Instructions pour run.

Recommendation

Fix minimal.

Code si pertinent.

Exemple Concret

# [HIGH] Unchecked Arithmetic Allows Balance Inflation in depositFor

Summary

Le contrat Vault.sol utilise Solidity 0.7 sans SafeMath dans depositFor, permettant un overflow qui wrap la balance à 0, puis un withdraw pour drainer le vault.

Vulnerability Details

solidity

// Vault.sol L42

function depositFor(address user, uint256 amount) external {

token.transferFrom(msg.sender, address(this), amount);

balances[user] += amount; // ❌ Overflow possible

}

Un attacker peut :
  • Deposit type(uint256).max - TVL
  • Deposit 1 de plus → overflow à (1 - TVL)
  • Withdraw(1) → drains TVL du vault
  • Impact

    High : Perte de 100% du TVL du vault (actuellement $12M sur mainnet).

    Requires : attacker possède type(uint256).max tokens (impossible).

    Wait, requires est impossible → actually c'est theoretical only.

    Revised : Medium. Overflow possible mais économiquement non viable.

    Proof of Concept

    solidity

    function testDepositOverflow() public {

    // Setup

    vault = new Vault(address(token));

    token.mint(address(vault), 1000e18); // TVL

    // Attacker tente overflow

    token.mint(attacker, type(uint256).max);

    vm.startPrank(attacker);

    token.approve(address(vault), type(uint256).max);

    vault.depositFor(attacker, type(uint256).max);

    vault.depositFor(attacker, 1);

    // Balance wrapped

    assertEq(vault.balances(attacker), 0);

    }

    Run : forge test --match-test testDepositOverflow -vvv
    
    

    Recommendation

    Utiliser Solidity 0.8+ (checked arithmetic par défaut) ou SafeMath.

    solidity

    balances[user] = balances[user].add(amount); // SafeMath

    Étape 5 : Justifier la Sévérité

    Grille Standard (Immunefi)

    Critical (5.0) :
    
    • Direct theft of funds (any amount)
    • Permanent freezing of funds (any amount)
    • Protocol insolvency

    High (4.0-4.9) :

    • Theft of unclaimed yield
    • Temporary freezing of funds
    • Unauthorized minting

    Medium (3.0-3.9) :

    • Incorrect accounting (no loss)
    • Griefing (gas cost to fix)

    Low (2.0-2.9) :

    • Contract fails to deliver promised returns (no loss)
    • State not handled correctly

    Pièges de Sévérité

    // ❌ Faux Critical
    

    function withdrawAll() external onlyOwner {

    // "Owner peut rug" → Centralization, pas un bug

    }

    // ✅ Vrai Critical

    function withdraw(uint256 amount) external {

    // Manque check balances[msg.sender] >= amount

    // → N'importe qui peut withdraw n'importe quel montant

    }

    // ❌ Faux High (c'est un Medium)

    function stake(uint256 amount) external {

    totalStaked += amount; // Manque update userStake[msg.sender]

    // Impact : accounting incorrect, mais pas de perte directe

    }

    Étape 6 : Soumettre

    Checklist Pré-Soumission

    • [ ] Bug confirmé sur la bonne version du code (pas une vieille version déjà fixée)
    • [ ] PoC run localement sans erreurs
    • [ ] In-scope vérifié (contracts + impact)
    • [ ] Pas de duplicate (chercher dans disclosed bugs)
    • [ ] Sévérité justifiée avec grille du programme
    • [ ] Recommendation réaliste (pas "rewrite everything")

    Étiquette

    ✅ Bon :
    
    • "Hi, I found a potential issue in X. PoC attached."
    • Réponses rapides aux questions du triager
    • Accepter un downgrade de sévérité si justifié

    ❌ Mauvais :

    • "This is CRITICAL, pay me now"
    • Spam de follow-ups (attendre 48h min)
    • Argumenter sans cesse si jugé out-of-scope

    Étape 7 : Attentes de Paiement

    Timeline Typique

    Immunefi :
    

    J+0 : Soumission

    J+2 : Premier triage

    J+5 : Validation par protocol team

    J+7 : Payout

    Code4rena :

    J+0 : Soumission (fin du contest)

    J+7 : Judging phase

    J+14 : Résultats publiés

    J+21 : Payout

    Montants Réalistes

    Critical : $50k - $500k (top programs)
    

    High : $5k - $50k

    Medium : $1k - $5k

    Low : $100 - $1k

    (Varie énormément selon program TVL et bounty budget)

    Raisons de Rejet Courantes

  • Out-of-scope (50% des rejets) : lire le scope 3×
  • Known issue : vérifier GitHub issues + disclosed bugs
  • Invalid : bug n'existe pas, PoC casse
  • Insufficient proof : PoC manquant ou incomplet
  • Duplicate : quelqu'un d'autre a submit avant vous
  • Conseil Final

    Votre premier bounty sera probablement un Medium ou un Low. C'est OK. Vous apprenez le process.

    Les Criticals viennent avec l'expérience de lire 100+ codebases et reconnaître les patterns à risque.

    Continuez à hunt. Le prochain pourrait être le gros.

    Prêt à mettre en pratique ?

    Applique ces concepts avec des exercices interactifs sur Solingo.

    Commencer gratuitement