Décoder le calldata : hex, base64 et XOR répété
Le défi
Une longue chaîne hexadécimale a été extraite d'une transaction de contrat intelligent suspecte et étiquetée comme « calldata EVM » par l'indexeur. Ce n'est pas vraiment du calldata - c'est un message caché sous trois couches empilées. Inversez-les dans l'atelier et soumettez le flag caché (HDNA{...}).
Ce que tu vas apprendre
- Reconnaître que des caractères exclusivement hexadécimaux indiquent une couche de transport en hexadécimal
- Retirer trois transformations empilées dans le bon ordre inverse
- Identifier un blob base64 dissimulé dans la couche hexadécimale grâce à son jeu de caractères et son padding
- Appliquer un XOR à clé répétée courte pour retrouver le texte en clair sous-jacent
- Écarter les opérations leurres dont le format d'entrée ne correspond pas aux données
Compétences testées
Prérequis
- À l'aise avec la lecture de l'hexadécimal et du base64
- Comprendre que le XOR est sa propre inverse
- Notion de base de l'apparence du calldata EVM
Comment ça marche
Les données on-chain ne sont que des octets, et les octets sont faciles à déguiser. Ici, quelqu'un a pris un texte en clair court, l'a chiffré en XOR avec une minuscule clé répétée, a encodé le résultat en base64 pour qu'il survive sous forme de texte, puis a encodé cette chaîne en hexadécimal pour qu'elle passe pour du calldata de transaction. Un vrai calldata commence par un sélecteur de fonction de 4 octets et est encodé en ABI ; ce blob n'a rien de cette structure, ce qui est le premier indice que l'étiquette « calldata » est un déguisement.
Les couches se retirent dans l'ordre inverse de leur application. La couche la plus externe est l'hexadécimal, car chaque caractère appartient à 0-9a-f - décodez-la et vous obtenez une chaîne base64 imprimable. Décodez celle-ci et vous obtenez des octets bruts encore illisibles, car ils ont été chiffrés en XOR. Le XOR est symétrique : réappliquer la même clé avec la même opération l'annule, puisque A XOR B XOR B = A. La clé ici n'est que les deux octets tx, répétés sur tout le message.
L'atelier modélise cela comme une recette où chaque opération alimente la suivante. Ajoutez From Hex, puis From Base64, puis XOR avec la clé tx, et le flag apparaît en direct dans la sortie. Les autres opérations (ROT, Vigenère, atbash, reverse, URL-decode) sont des leurres : leur format d'entrée ne correspond jamais à ce que vous avez entre les mains à chaque étape.
Erreurs fréquentes
- Faire confiance à l'étiquette « calldata ». Les données n'ont ni structure ABI ni sélecteur de fonction : ce n'a jamais été du calldata, juste des octets déguisés en calldata.
- Retirer les couches dans le mauvais ordre. Essayer le base64 avant l'hexadécimal, ou le XOR avant l'un des deux décodages, donne un résultat illisible. Toujours commencer par la couche la plus externe.
- Deviner une longue clé XOR. La clé fait deux octets ; ne faites pas de brute force sur ce que l'énoncé vous donne déjà.
- Ajouter des opérations leurres. ROT et Vigenère agissent sur des lettres, pas sur des octets bruts : vérifiez le format des données avant d'ajouter une opération.
Comment s'en protéger
Pour les défenseurs, la leçon est qu'envelopper un payload en hexadécimal et en base64 par-dessus une clé XOR statique est de l'obfuscation, pas de la protection : cela ne fait que ralentir un examen rapide, jamais un analyste déterminé. Traitez comme suspect tout champ qui « devrait » être structuré (calldata, un token, un cookie) mais qui ne l'est pas, et décodez-le.
- Vérifiez qu'un calldata on-chain correspond réellement à l'ABI du contrat avant de faire confiance à son étiquette.
- Décodez et scannez automatiquement en XOR les blobs hexadécimaux et base64 non structurés lors du tri pour révéler les messages cachés.
- Utilisez un chiffrement authentifié avec une clé gérée quand les données doivent vraiment rester confidentielles.
- Considérez tout XOR à clé répétée courte comme entièrement récupérable, par vous comme par des adversaires.