
Notes de rétro-ingénierie, code source désobfusqué, IOCs et règles YARA pour le RAT Python Tourmaline ClickFix, couvrant son tunnel DNS et son C2 dead-drop blockchain.
J'ai récemment découvert un logiciel malveillant astucieusement conçu qui se fait appeler « Torumaline ».
Ce README a été très aimablement rédigé par Claude, car j'étais extrêmement occupé par mes examens au moment de la création de ce dépôt.
Exploiter et déchiffrer ce logiciel malveillant m'a pris une grande partie de mon temps de préparation aux mathématiques !
Lisez le billet de blog !
À DES FINS ÉDUCATIVES ET DE RECHERCHE UNIQUEMENT
Tous les échantillons sont fournis pour l'analyse de logiciels malveillants et la recherche défensive. Ne les exécutez pas en dehors d'un environnement isolé et sans réseau.
Tourmaline (Tourmaline.exe) est un infostealer/backdoor Windows sophistiqué à plusieurs étapes, distribué via une campagne ClickFix — une fausse fenêtre contextuelle Cloudflare « Vérifiez que vous êtes humain » qui incite les utilisateurs à exécuter manuellement une commande ou un installateur malveillant.
Le binaire est un installateur Inno Setup 6.7.0 (Révision 2, offsets 64 bits) construit sur mesure, qui embarque un runtime Python 3.11 complet et deux étapes de payload Python obfusqué. Il établit une backdoor persistante avec :
microsoft.com)tourmaline/
├── README.md # This file
├── LICENSE # Research use license
├── .gitignore
│
├── src/ # Extracted & deobfuscated source code
│ ├── stage1_loader.py # Stage 1: Original obfuscated XOR time-lock loader (main.py)
│ ├── stage2_backdoor.py # Stage 2: Deobfuscated core backdoor (from QGBdu.dxf)
│ └── decrypt_payload.py # Utility: O(1) payload key recovery & decryption
│
├── iocs/ # Indicators of Compromise
│ ├── indicators.json # Structured IOC data (IPs, hashes, domains, registry)
│ ├── indicators.csv # Flat CSV for SIEM import
│ └── rules.yar # YARA detection rules
│
└── samples/ # Malware samples (password-protected)
├── README.md # Sample archive instructions
├── Tourmaline_sample.zip # Password: infected - contains Tourmaline.exe
└── QGBdu.dxf # Raw encrypted Stage 2 payload blob
Le logiciel malveillant est distribué via ClickFix — une technique d'ingénierie sociale dans laquelle un site web compromis ou contrôlé par l'attaquant superpose une fausse page CAPTCHA Cloudflare ou de vérification du navigateur. La superposition demande à la victime de :
Win+RLa commande collée télécharge et exécute silencieusement Tourmaline.exe. L'installateur s'exécute avec /VERYSILENT /SUPPRESSMSGBOXES /NORESTART.
| Propriété | Valeur |
|---|---|
| Nom du fichier | Tourmaline.exe |
| Taille | 11 341 339 octets (~10,8 Mo) |
| MD5 | b74ac808dea2de31caf024310694ef0c |
| SHA256 | c9b390b3b7148f549df86503858d52e030b2b5e78fed0bc525ded5927f9265d6 |
| Format | Inno Setup 6.7.0 Unicode (Révision 2, offsets 64 bits) |
| Type PE | PE 32 bits, 11 sections |
| Taille de l'overlay | 10 447 387 octets |
| Offset magique Inno | 0xD9864 (890 148 octets) |
| Nom de l'application | Tourmaline |
| Version de l'application | 2.63.519 |
| GUID de l'application | {2C25872D-85FC-44C7-9B16-844E39E50A44} |
| Chemin d'installation | {commonappdata}\Tourmaline |
| Runtime embarqué | Python 3.11 (embed complet) |
| Fichiers de payload | 36 fichiers dans un flux LZMA2 solide |
Remarque : Le
innoextract 1.9standard ne peut pas extraire ce binaire — il ne prend en charge que la Révision 1. La Révision 2 utilise des offsets 64 bits et nécessite un parsing personnalisé (voirsrc/decrypt_payload.py).
Tourmaline.exe
│
├─ Inno Setup installer runs silently
│ ├─ Extracts Python 3.11 runtime to %APPDATA%\Tourmaline\
│ ├─ Extracts main.py (Stage 1 loader)
│ ├─ Extracts QGBdu.dxf (encrypted Stage 2 blob)
│ ├─ Kills any existing pythonw.exe instances
│ └─ Registers persistence (Task Scheduler / registry)
│ └─ Name: "TourmalineUpdate" / "Hardware monitoring service"
│
├─ Stage 1: main.py (XOR Time-Lock Loader)
│ ├─ Counts _n from 99,999,999 → 0 (anti-sandbox delay ~hours on slow VMs)
│ ├─ At each _n, constructs 33-byte XOR key: struct.pack('>I', _n) + hardcoded_suffix
│ ├─ Tests key against known plaintext header of QGBdu.dxf
│ └─ On match: decrypts QGBdu.dxf entirely and exec()s the result
│ └─ O(1) bypass: known-plaintext attack on first 4 bytes (see below)
│
└─ Stage 2: QGBdu.dxf → Python backdoor
├─ Reads MachineGuid from HKLM\SOFTWARE\Microsoft\Cryptography
├─ Derives mutex Global\Tourmaline_<hash>
├─ Resolves C2 IP via Ethereum dead-drop (Sepolia testnet)
├─ Establishes DNS tunnel to C2
└─ Poll-execute loop: fetches tasks, exec()s Python, returns output
Le loader (main.py) utilise un compte à rebours de 99 999 999 à 0 pour trouver la clé de déchiffrement XOR. Sur une machine réelle, cela se termine en quelques secondes (car _n commence haut et la vraie valeur est proche de 14 511 188). Dans un sandbox avec une limite de temps courte, la boucle ne se termine jamais.
Structure de la clé :
full_key = struct.pack('>I', _n) + bytes.fromhex('2b0cffe07b06ac25793b3f00cfaa2dd5881c2d6378165247539dbbdaeb')
Comme nous connaissons les 16 premiers octets du texte en clair (g1 = lambda l6, ), nous pouvons récupérer _n instantanément via une attaque XOR à texte en clair connu :
key_prefix = bytes(ciphertext[i] ^ known_plaintext[i] for i in range(4))
# Result: key_prefix = 00dd6c54 → _n = 14,511,188
Utilisez src/decrypt_payload.py pour reproduire cela.
| Paramètre | Valeur |
|---|---|
| IP C2 | 158.94.211.185 |
| Port C2 | 53 (UDP) |
| Protocole | Tunnel DNS-over-UDP personnalisé |
| Domaine usurpé | microsoft.com |
| Clé ChaCha20 | 36f555c87f71581f57d83576c88193fdc00a0173f741a3e198e2d7abdc9cda59 |
| Contrat blockchain | 0x2d7a04cca0c34005f58393f30ac725e25f19e5f5 (Sepolia) |
| Clé publique ECDSA | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEt17l3rGHHR9sYdHEvV8S+JRRCUHQQveSadlGk04xM/8WClDf/67Tyritt2+T18SY8n0xv1TKKl0AgnNFuboEEQ== |
| Nom du service | SystemService / TourmalineUpdate |
| Mutex | Global\Tourmaline_<MachineGuid-hash> |
Le logiciel malveillant appelle un contrat intelligent Ethereum sur le testnet Sepolia pour récupérer l'adresse IP C2 active. Cela signifie que l'attaquant peut changer l'IP C2 à tout moment en mettant à jour le contrat — la perturbation traditionnelle du C2 par liste de blocage d'IP est inefficace.
Contract : 0x2d7a04cca0c34005f58393f30ac725e25f19e5f5
Selector : 0xeb9fd6fe
Network : Ethereum Sepolia (chainId 11155111)
RPC : https://ethereum-sepolia-rpc.publicnode.com
Result : ChaCha20-encrypted blob containing current C2 IP
Les données renvoyées sont déchiffrées avec la clé ChaCha20 codée en dur pour révéler l'adresse C2 active. Au moment de l'analyse, celle-ci correspondait à 158.94.211.185 (vérifié via un appel RPC en direct).
Tout le trafic C2 est envoyé sous forme de requêtes DNS UDP brutes directement vers l'IP C2 sur le port 53. Les requêtes sont structurées comme <encoded_data>.microsoft.com, contournant le filtrage basé sur DNS et apparaissant comme de la télémétrie Windows légitime.
socket.getaddrinfo de Python ni aucune bibliothèque DNSLa backdoor implémente un moteur exec() Python arbitraire. Les tâches sont :
exec() dans un espace de noms persistant avec capture de sortieTypes de tâches pris en charge : exec Python, upload/download de fichiers, commande shell, auto-mise à jour.
| Type | Valeur |
|---|---|
| IP C2 | 158.94.211.185 |
| Port C2 | 53/UDP |
| Domaine usurpé | *.microsoft.com (UDP personnalisé, pas un vrai DNS) |
| RPC Ethereum | https://ethereum-sepolia-rpc.publicnode.com |
| Contrat intelligent | 0x2d7a04cca0c34005f58393f30ac725e25f19e5f5 |
| Fichier | SHA256 |
|---|---|
Tourmaline.exe | c9b390b3b7148f549df86503858d52e030b2b5e78fed0bc525ded5927f9265d6 |
Tourmaline.exe (MD5) | b74ac808dea2de31caf024310694ef0c |
| Clé | Valeur |
|---|---|
HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid | Lu pour l'empreinte de la victime |
%APPDATA%\Tourmaline\ | Répertoire d'installation |
| Planificateur de tâches | TourmalineUpdate |
| Nom d'affichage du service | Hardware monitoring service |
| Mutex | Global\Tourmaline_<hash> |
Voir iocs/rules.yar pour les règles de détection couvrant :
Si ce logiciel malveillant a été exécuté sur une machine :
taskkill /F /IM pythonw.exe%APPDATA%\Tourmaline\ ou %COMMONAPPDATA%\Tourmaline\TourmalineUpdateHKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run pour Tourmaline ou SystemService158.94.211.185:53/UDP ; bloquez tout UDP/53 sortant inattenduethereum-sepolia-rpc.publicnode.compip install pyOpenSSL cryptography
# From the repo root:
python3 src/decrypt_payload.py samples/QGBdu.dxf decrypted_stage2.py
7z x -pinfected samples/Tourmaline_sample.zip
| Champ | Valeur |
|---|---|
| Date de découverte | Septembre 2026 |
| Chercheur | Vihaan Pundir (v-pun215) |
| Vecteur de distribution | Faux CAPTCHA Cloudflare ClickFix |
| Classification | Backdoor / C2 par tunnel DNS / Dead-drop blockchain |
| Plateforme | Windows (64 bits) |
Voir LICENSE. Ce dépôt est destiné à des fins éducatives et de recherche défensive uniquement.