Retour au cours

cyber / pentest-red-team

Exploitation web avancée : race conditions et logique métier

Leçon 81 exercice

Explication

Ce que vous allez apprendre

  • Comprendre le mécanisme "check-then-act" qui rend une race condition possible
  • Reproduire une race condition simple en envoyant des requêtes en parallèle
  • Corriger une opération non atomique avec une clause conditionnelle au niveau base de données
  • Reconnaître les formes courantes d'abus de logique métier
  • Expliquer pourquoi ces classes de bugs échappent aux scanners automatisés

Dans quel contexte ?

Après la désérialisation insecure (leçon précédente), une nouvelle catégorie de faille émerge sur une application de lab bancaire fictive : un endpoint de retrait d'argent semble parfaitement sécurisé — il vérifie bien le solde avant chaque retrait. Et pourtant, en envoyant vingt requêtes de retrait EN MÊME TEMPS, plusieurs d'entre elles réussissent alors qu'une seule aurait dû passer.

D'abord, il faut comprendre le mécanisme exact du bug, pas juste le symptôme

Le code vérifie le solde ("check"), puis le décrémente ("act") — mais entre ces deux étapes, un léger délai existe toujours (accès base de données, latence réseau). Si plusieurs requêtes arrivent PENDANT cette fenêtre, elles lisent toutes le même solde "encore suffisant", avant qu'aucune d'entre elles n'ait eu le temps de le mettre à jour.

C'est exactement ce que le pattern "check-then-act" désigne : deux opérations qui semblent séquentielles dans le code, mais qui ne sont pas exécutées de façon atomique par le système sous-jacent.

ÉtapeCe qui se passeProblème si non atomique
1. CheckLecture du solde actuelPlusieurs requêtes lisent la même valeur
(fenêtre de course)Latence réseau/DBToutes les requêtes passent la vérification
2. ActÉcriture du nouveau soldeChaque écriture ignore les autres en cours

Prérequis

Cette leçon suppose une compréhension de base des requêtes HTTP et des transactions en base de données ; aucune connaissance avancée en concurrence n'est nécessaire, le concept se comprend intuitivement avec l'exemple du solde bancaire.

Une fois ce bug reproduit, comment le corriger réellement ?

La solution ne consiste pas à ajouter un verrou applicatif fragile, mais à rendre l'opération atomique directement au niveau de la base de données : une seule requête UPDATE ... WHERE balance >= amount combine la vérification ET la modification en une opération indivisible, garantie par le moteur de base de données lui-même. Si le solde est insuffisant, aucune ligne n'est modifiée (rowcount == 0), sans jamais laisser de fenêtre exploitable.

Astuce

Des outils spécialisés comme Turbo Intruder (extension Burp Suite) envoient des requêtes quasi simultanées sur une seule connexion réseau, réduisant drastiquement la marge d'erreur par rapport à de simples appels curl en parallèle depuis un script bash — utile pour confirmer une race condition dont la fenêtre est très étroite.

Il reste une catégorie de bugs encore plus subtile : l'abus de logique métier

Piège fréquent

Un workflow multi-étapes qui ne revérifie pas son état à chaque étape peut être "rejoué" dans le désordre : revenir à l'étape 2 après avoir déjà validé l'étape 4, par exemple, ou appeler directement l'endpoint de confirmation de commande en sautant l'étape de paiement. Ces défauts ne sont pas des bugs de code isolés, mais des hypothèses incorrectes sur la façon dont un utilisateur légitime est censé se comporter.

Le lien avec la suite du cours

Bonne pratique

Ces classes de bugs (race conditions, abus de logique métier) échappent presque systématiquement aux scanners automatisés, car elles nécessitent de comprendre le SENS métier de l'application, pas seulement sa syntaxe. C'est précisément ce qui distingue un audit manuel expert d'un simple scan automatisé — retiens ce principe, il reviendra dans la leçon sur la rédaction de rapport, en toute fin de cours.

L'exploitation applicative maintenant couverte en profondeur, la prochaine leçon change complètement de terrain : une fois un accès initial obtenu sur un système, comment progresser de simple utilisateur vers les droits root, sous Linux.

Commandes & code

Exploitation web avancée : race conditions et logique métier

Certaines vulnérabilités ne viennent d'aucun bug de code isolé : elles exploitent un ordre d'exécution ou une hypothèse métier incorrecte.

bash
# Race condition classique : requêtes envoyées en parallèle pour dépasser une limite métier
# Exemple de lab : un endpoint de retrait qui vérifie le solde AVANT de le décrémenter
for i in $(seq 1 20); do
  curl -s -X POST http://192.168.56.10/api/withdraw        -H "Authorization: Bearer $TOKEN"        -d '{"amount": 100}' &
done
wait
# Si le check-then-act n'est pas atomique, plusieurs retraits peuvent passer avant que le solde soit à jour
python
# Le bug côté serveur (exemple simplifié, non-atomique) :
def withdraw(user, amount):
    if user.balance >= amount:      # (1) lecture
        time.sleep(0.05)            # latence réseau/DB qui élargit la fenêtre de course
        user.balance -= amount      # (2) écriture, plus tard
        return True
    return False
# Entre (1) et (2), plusieurs requêtes parallèles peuvent toutes lire un solde encore suffisant
python
# Correction : rendre l'opération atomique au niveau de la base de données
# UPDATE atomique avec condition dans la clause WHERE elle-même
def withdraw_safe(db, user_id, amount):
    result = db.execute(
        "UPDATE accounts SET balance = balance - :amount "
        "WHERE id = :user_id AND balance >= :amount",
        {"amount": amount, "user_id": user_id},
    )
    return result.rowcount == 1   # 0 ligne modifiée = solde insuffisant, rejeté atomiquement
text
# Abus de logique métier (business logic abuse) - pas un bug technique, un mauvais postulat
- Coupon appliqué plusieurs fois car aucune vérification d'usage unique côté serveur
- Étape de paiement contournée en appelant directement l'endpoint de confirmation de commande
- Prix négatif accepté car seule la validation côté client (JS) empêche les valeurs négatives
- Workflow multi-étapes rejouable : revenir à l'étape 2 après avoir validé l'étape 4
bash
# Outils dédiés au test de race conditions HTTP (envoi single-packet pour minimiser la latence réseau)
# Turbo Intruder (extension Burp Suite) envoie des requêtes quasi simultanées sur une seule connexion
# -> réduit la marge d'erreur par rapport à des curl en parallèle classiques

Résumé

  • Une race condition exploite la fenêtre entre "vérifier" et "agir" quand elle n'est pas atomique.
  • La logique métier doit être validée côté serveur à chaque étape, jamais seulement côté client.
  • La correction technique standard : opérations atomiques au niveau base de données (UPDATE conditionnel).
  • Ces classes de bugs échappent souvent aux scanners automatisés : elles demandent une analyse humaine.

Exercices pratiques

1 disponible
1

Mission : casser (puis corriger) le retrait bancaire de l'app de lab NordBank

Objectif : Démontrer une race condition sur un endpoint de retrait, puis proposer une correction atomique côté base de données.

Contexte

L'application bancaire fictive de lab NordBank vérifie le solde avant chaque retrait. Un test de charge en parallèle laisse penser qu'un check-then-act non atomique existe sur l'endpoint /api/withdraw.

Résoudre l’exercice →