Rapports analytiques · Leçon 6 sur 6
Étude de cas : une enquête de bout en bout
12 min de lecture
Les 41 leçons précédentes couvraient chacune une compétence. Les enquêtes réelles les mobilisent toutes à la fois, sous pression, avec des données incomplètes et des lecteurs qui attendent des réponses. Cette étude suit une attaque fictive, de la première alerte au rapport final, en montrant à chaque étape quelle compétence est utilisée et comment l’analyste décide du degré de confiance à accorder à chaque résultat. Tous les noms, adresses, hachages, hauteurs de bloc et montants sont inventés.
Le signalement et la question
À 09:58 UTC, le 14 mars 2026, Corvane Finance, un protocole de prêt fictif sur Ethereum, constate que son marché USDC a été vidé. Son équipe envoie à une équipe d’analyse indépendante le hachage de la transaction d’attaque, 0x9c4e…a71b, avec une demande d’une ligne : « Suivez l’argent. »
L’analyste reformule la demande en question analytique, comme l’explique la leçon De la question à l’hypothèse :
Où sont allés les actifs retirés lors de la transaction 0x9c4e…a71b, jusqu’aux blocs de coupure ; quels services de conservation en ont reçu une partie ; et quel degré de confiance accordons-nous à chaque lien ?
L’énoncé du périmètre fixe les points de départ (la transaction d’attaque), les chaînes (Ethereum, un rollup, Bitcoin), la période (les blocs Ethereum 24 790 112 à 24 812 000 et la plage Bitcoin correspondante), une limite de 10 sauts pour les chaînes de pelage Bitcoin et une règle d’arrêt à tout service de conservation. Il est consigné dans le dossier avant l’exécution de la première requête.
Étape 1 : Lire la transaction d’attaque
L’analyste récupère la transaction, son reçu et ses traces depuis un nœud d’archive (voir API de nœud et accès aux données) et consigne le hachage du bloc pour en établir la provenance. Le statut du reçu est 1 (succès), et les journaux décodés montrent ce qui a quitté le protocole :
{
"transactionHash": "0x9c4e…a71b",
"blockNumber": 24790112,
"status": "0x1",
"decodedLogs": [
{"token": "USDC", "event": "Transfer", "from": "0x5Fd0…3C2a",
"to": "0x7aE3…b19C", "value": "1912400000000"},
{"token": "WETH", "event": "Transfer", "from": "0x5Fd0…3C2a",
"to": "0x7aE3…b19C", "value": "210000000000000000000"}
]
}
0x5Fd0…3C2a est le marché USDC de Corvane et 0x7aE3…b19C, le contrat de l’attaquant. Après normalisation des unités (voir Qualité des données, normalisation et provenance) : USDC a 6 décimales, donc 1 912 400 000 000 équivaut à 1 912 400 USDC ; WETH en a 18, donc la deuxième valeur équivaut à 210 WETH. Les traces montrent un prêt éclair, une manipulation du prix dans un pool peu liquide et le vidage des fonds, l’anatomie classique abordée dans la leçon Analyser les attaques et les piratages. Une transaction ultérieure transfère la totalité des fonds du contrat au déployeur, le compte externe 0xE7c1…44aD.
Ce compte a été créé deux jours plus tôt : son premier transfert entrant, reçu avant qu’il n’ait envoyé la moindre transaction (nonce toujours à 0), était de 1,0 ETH retiré d’un mélangeur par contrat intelligent. Comme l’explique la leçon Mélangeurs, CoinJoin et obfuscation dans les graphes, ce financement ne nous apprend presque rien sur l’identité de la personne qui se cache derrière le compte. L’analyste le consigne donc comme une impasse plutôt que comme une piste.
Étape 2 : Flux de jetons en SQL
Pour repérer tous les mouvements de jetons provenant des adresses de l’attaquant, l’analyste interroge les journaux bruts à partir de la signature de l’événement, comme dans la leçon Interroger les journaux et les traces EVM :
-- Transferts ERC-20 envoyés par le contrat d'attaque ou le compte de l'attaquant
SELECT
l.block_number,
l.transaction_hash,
l.contract_address AS token,
'0x' || SUBSTR(l.topic2, 27, 40) AS recipient,
l.data AS raw_amount_hex
FROM logs l
WHERE l.block_number BETWEEN 24790112 AND 24812000
AND l.topic0 = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
AND SUBSTR(l.topic1, 27, 40) IN ('7ae3…b19c', 'e7c1…44ad') -- hexadécimal minuscule complet en pratique
ORDER BY l.block_number, l.log_index;
topic0 est le hachage keccak-256 de Transfer(address,address,uint256). topic1 et topic2 contiennent l’expéditeur et le destinataire, complétés jusqu’à 32 octets ; l’adresse correspond donc aux 40 derniers caractères hexadécimaux, à partir de la position 27, après « 0x » et les 24 zéros de remplissage. Le montant se trouve dans data sous forme hexadécimale ; la fonction qui le convertit en nombre varie selon le moteur.
Les résultats montrent trois échanges par l’intermédiaire d’un agrégateur de plateforme d’échange décentralisée en 15 minutes, qui convertissent 1 912 400 USDC en 731,2 ETH, soit une moyenne d’environ 2 615 USDC par ETH, avec l’impact sur le prix inclus (voir Échanges sur les plateformes d’échange décentralisées et flux de liquidité). Les 210 WETH sont convertis en ETH. Le compte de l’attaquant détient désormais 941,2 ETH de produits de l’attaque.
Étape 3 : Passer d’une chaîne à l’autre
Entre 10:31 et 11:05 UTC, les fonds se répartissent en trois voies (voir Traçage interchaînes) :
- 600 ETH sont déposés en six fois, à raison de 100 ETH par dépôt, dans un protocole d’échange interchaînes. Chaque dépôt contient un mémo indiquant une destination Bitcoin, bc1q…7k3f. Sur Bitcoin, les six paiements sortants du protocole vers cette adresse totalisent 22,10 BTC, et chacun contient le hachage Ethereum entrant correspondant dans une sortie OP_RETURN. Le lien est explicite dans les données des deux côtés.
- 300 ETH sont déposés en trois fois, à raison de 100 ETH par dépôt, dans un pont rapide dont les événements de dépôt n’indiquent pas de destinataire ; un fournisseur de liquidité effectue le versement sur le rollup à partir de ses propres fonds. Le seul moyen de relier les deux côtés est de comparer les montants et les heures. La requête de décompte des candidats de la leçon Confiance, incertitude et formulation ne renvoie qu’un seul versement de 99,80 à 100,00 ETH dans les 20 minutes suivant chaque dépôt ; les trois versements vont à 0x4B9e…07Fd et totalisent 299,82 ETH.
- 41,2 ETH restent dans le compte de l’attaquant, sans mouvement jusqu’aux blocs de coupure.
Étape 4 : La chaîne de pelage Bitcoin et les plateformes d’échange
À partir de bc1q…7k3f, la première transaction dépense les six sorties et lance une chaîne de pelage : neuf sauts, chacun avec un petit paiement sortant et un reliquat important. L’analyste la suit à l’aide d’une requête récursive sur les tables des entrées et sorties (voir Interroger les données UTXO) :
-- Suivre la chaîne de pelage : à chaque saut, conserver la sortie la plus importante comme monnaie rendue
WITH RECURSIVE chain (hop, txid, vout, value_sat) AS (
SELECT 1, transaction_hash, output_index, value FROM outputs
WHERE transaction_hash = 'e2d9…5a10' AND output_index = 1 -- monnaie rendue du saut 1
UNION ALL
SELECT c.hop + 1, o.transaction_hash, o.output_index, o.value
FROM chain c
JOIN inputs i ON i.spent_transaction_hash = c.txid
AND i.spent_output_index = c.vout
JOIN outputs o ON o.transaction_hash = i.transaction_hash
WHERE c.hop < 9 AND o.value = (SELECT MAX(value) FROM outputs
WHERE transaction_hash = i.transaction_hash)
)
SELECT hop, txid, value_sat / 1e8 AS value_btc FROM chain ORDER BY hop;
L’étape récursive relie chaque sortie de monnaie rendue à la transaction qui la dépense, puis conserve la sortie la plus importante de cette transaction, qui correspond au reliquat dans une chaîne de pelage. La prise en charge de WITH RECURSIVE et ses limites varient selon les moteurs. « Sortie la plus importante » n’est qu’une règle empirique ; l’analyste vérifie donc chaque saut à l’aide des indicateurs de la leçon Heuristiques de détection de la monnaie rendue : la monnaie rendue est toujours envoyée à une nouvelle adresse P2WPKH correspondant au type d’entrée, les paiements sont des montants ronds (0,75, 0,95, 1,00 BTC), contrairement à la monnaie rendue, et chaque transaction présente le même modèle de version, de taux de frais et de verrouillage, soit une empreinte de portefeuille cohérente.
| Destination des fonds issus du pelage | Sauts | Total |
|---|---|---|
| Adresses de dépôt de la plateforme d’échange K | 3 | 2,95 BTC |
| Adresses de dépôt de la plateforme d’échange O | 2 | 1,80 BTC |
| Adresses non regroupées, non dépensées | 4 | 3,10 BTC |
| Reliquat après le saut 9, non dépensé | – | 14,248 BTC |
| Frais sur les neuf sauts | – | environ 0,002 BTC |
Les lignes totalisent les 22,10 BTC transférés à l’origine. Les attributions aux plateformes d’échange s’appuient sur ce qui s’est passé ensuite : chaque adresse de dépôt a par la suite été dépensée avec plus de 100 autres adresses dans un seul portefeuille chaud, selon le modèle de balayage décrit dans les leçons Heuristique de propriété par entrées communes et Suivre la valeur à travers les plateformes d’échange et les services. Le portefeuille chaud de la plateforme d’échange K apparaît dans une liste d’adresses de réserve publiée par la plateforme elle-même, une étiquette qu’elle s’est attribuée ; celui de la plateforme d’échange O ne porte qu’une étiquette fournie par un tiers (voir Attribution et étiquetage).
Étape 5 : Évaluer les hypothèses et exprimer le degré de confiance
Qui contrôle bc1q…7k3f ? L’analyste établit une petite matrice ACH avec trois hypothèses : l’attaquant (H1), une adresse appartenant au protocole d’échange lui-même (H2), ou un tiers, comme un bureau de gré à gré rémunéré par l’attaquant (H3). Les références OP_RETURN sont compatibles avec les trois hypothèses : elles prouvent que les fonds sont arrivés à cette adresse, mais pas qui la contrôle. Les éléments diagnostiques sont le mémo défini par l’attaquant (incompatible avec H2), l’absence d’historique de l’adresse avant ces faits (incompatible avec une adresse de protocole utilisée depuis longtemps) et le pelage immédiat vers de petits dépôts sur des plateformes d’échange (incompatible avec un bureau qui règle une transaction dans ses propres portefeuilles établis). H1 reste plausible ; H3 repose sur un seul argument comportemental, si bien que la vérification de sensibilité la maintient comme opinion minoritaire.
Les principaux jugements, selon l’échelle ICD 203 déclarée dans la section méthodologique du rapport :
- Fait : 1 912 400 USDC et 210 WETH ont quitté le marché Corvane lors de la transaction 0x9c4e…a71b, au bloc 24 790 112.
- Il est presque certain que 600 ETH des produits de l’attaque ont été convertis en 22,10 BTC versés à bc1q…7k3f. Le degré de confiance est élevé : le lien est consigné sur les deux chaînes.
- Il est très probable que 2,95 BTC aient atteint des adresses de dépôt de la plateforme d’échange K. Le degré de confiance est modéré : l’étiquette est auto-identifiée, mais le parcours repose sur la détection de la monnaie rendue sur un maximum de sept sauts.
- Il est probable que 1,80 BTC aient atteint des adresses de dépôt de la plateforme d’échange O. Le degré de confiance est faible, car l’étiquette ne provient que d’une seule source tierce.
- Il est probable que l’attaquant contrôle 0x4B9e…07Fd sur le rollup. Le degré de confiance est modéré : trois correspondances distinctes de montant et d’heure, chacune avec un seul candidat.
Les soldes restés immobiles (41,2 ETH, 299,82 ETH, 3,10 BTC et 14,248 BTC) sont présentés comme des faits valables aux blocs de coupure.
Étape 6 : Rapport, visuels et divulgation
Le rapport suit la leçon Rédiger le rapport analytique : un résumé d’une page présentant les cinq jugements, le périmètre, la méthode, un constat par jugement, les hypothèses alternatives et les limites, ainsi qu’une annexe de 35 transactions avec leurs hachages complets, leurs hauteurs de bloc et leurs hachages de bloc, générée par requête. Conformément à la leçon Visualiser les flux et les graphes, il comprend deux diagrammes de Sankey, l’un en ETH et l’autre en BTC plutôt qu’un seul mélangeant les unités, un graphe de flux où le lien avec le pont est en pointillé, et une chronologie avec une ligne par chaîne indiquant que le premier dépôt sur une plateforme d’échange a eu lieu moins de cinq heures après l’attaque.
La divulgation suit la leçon Protection des données, éthique et divulgation responsable. Les plateformes d’échange K et O sont averties dans l’heure par leurs canaux de conformité respectifs ; chacune ne reçoit que les adresses de dépôt qui la concernent, les hachages de transaction, les montants, ainsi que le degré de probabilité et de confiance du lien. L’adresse du rollup est signalée à l’opérateur du pont comme un lien probable, mais non établi. L’une des adresses de pelage non regroupées se résout, via un service de noms, en ce qui semble être le nom d’une personne : elle est exclue de tous les documents partagés et n’est inscrite que dans l’annexe à accès restreint destinée aux procédures judiciaires. L’analyste a également découvert deux forks du code de Corvane présentant la même configuration vulnérable d’oracle de prix et détenant encore des fonds ; les deux équipes en sont informées en privé, et la version publique du rapport, qui ne nomme ni personnes privées ni adresses non regroupées, n’est diffusée qu’après confirmation de la correction.
Points clés pour l’examen : transformer « suivez l’argent » en une question analytique dont le périmètre est défini ; lire le statut d’un reçu et les journaux Transfer décodés, filtrer les journaux par topic0, extraire les adresses des topics complétés et normaliser USDC (6 décimales) et WETH (18 décimales) ; comprendre pourquoi un lien par mémo et OP_RETURN est plus solide qu’une correspondance de montant et d’heure ; suivre récursivement une chaîne de pelage et confirmer la monnaie rendue au moyen de plusieurs heuristiques ; attribuer des adresses de dépôt à partir des balayages et comprendre pourquoi une étiquette auto-identifiée est préférable à une étiquette tierce ; présenter séparément la probabilité et le degré de confiance pour chaque jugement ; réconcilier chaque unité de valeur, y compris les frais et les soldes immobiles ; procéder à une divulgation minimale indiquant le degré de confiance, conserver une annexe d’identité à accès restreint et informer en privé les forks vulnérables.