
Tourmaline ClickFix Python RATのリバースエンジニアリングノート、難読化解除済みソース、IOC、YARAルール。そのDNSトンネルとブロックチェーンデッドドロップC2を網羅しています。
最近、巧妙に設計されたマルウェアで、自らを「Tourmaline」と名乗るものに偶然出会いました。
このREADMEは、このリポジトリの立ち上げ当時、私が試験で非常に忙しかったため、Claudeがとても親切に書いてくれたものです。
このマルウェアの悪用と復号には、数学の準備から膨大な時間を割くことになりました!
ブログ記事を読んでください。
教育および研究目的のみ
すべてのサンプルはマルウェア解析および防御研究のために提供されています。サンドボックス化されたエアギャップ環境外では実行しないでください。
Tourmaline(Tourmaline.exe)は、ClickFixキャンペーンを通じて配布される高度な多段階のWindowsインフォスティーラー/バックドアです。ClickFixキャンペーンとは、偽のCloudflare「Verify you are human」ブラウザポップアップによって、ユーザーを騙して悪意のあるコマンドやインストーラーを手動で実行させる手法です。
このバイナリは、完全なPython 3.11ランタイムと2段階の難読化されたPythonペイロードをバンドルした、カスタムビルドの**Inno Setup 6.7.0(リビジョン2、64ビットオフセット)**インストーラーです。以下の機能を備えた永続的なバックドアを確立します:
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
このマルウェアは ClickFix を介して配布されます。これはソーシャルエンジニアリング手法であり、侵害された、または攻撃者が制御する Web サイトが偽の Cloudflare CAPTCHA やブラウザ検証ページをオーバーレイ表示します。このオーバーレイは被害者に次の操作を指示します:
Win+R を押す貼り付けられたコマンドは Tourmaline.exe をダウンロードしてサイレント実行します。インストーラーは /VERYSILENT /SUPPRESSMSGBOXES /NORESTART を付けて実行されます。
| プロパティ | 値 |
|---|---|
| ファイル名 | Tourmaline.exe |
| サイズ | 11,341,339 バイト(約 10.8 MB) |
| MD5 | b74ac808dea2de31caf024310694ef0c |
| SHA256 | c9b390b3b7148f549df86503858d52e030b2b5e78fed0bc525ded5927f9265d6 |
| 形式 | Inno Setup 6.7.0 Unicode(リビジョン 2、64 ビットオフセット) |
| PE タイプ | 32 ビット PE、11 セクション |
| オーバーレイサイズ | 10,447,387 バイト |
| Inno マジックオフセット | 0xD9864(890,148 バイト) |
| アプリ名 | Tourmaline |
| アプリバージョン | 2.63.519 |
| アプリ GUID | {2C25872D-85FC-44C7-9B16-844E39E50A44} |
| インストールパス | {commonappdata}\Tourmaline |
| 同梱ランタイム | Python 3.11(完全な埋め込み版) |
| ペイロードファイル | ソリッド LZMA2 ストリーム内に 36 ファイル |
注記: 標準の
innoextract 1.9ではこのバイナリを抽出できません。リビジョン 1 のみをサポートしているためです。リビジョン 2 は 64 ビットオフセットを使用しており、カスタムパーサーが必要です(src/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
ローダー(main.py)は 99,999,999 からのカウントダウンを使用して XOR 復号キーを見つけます。実機ではこれが数秒で完了します(_n が高い値から始まり、真の値が 14,511,188 付近にあるためです)。短い制限時間を持つサンドボックスでは、このループは決して完了しません。
キー構造:
full_key = struct.pack('>I', _n) + bytes.fromhex('2b0cffe07b06ac25793b3f00cfaa2dd5881c2d6378165247539dbbdaeb')
平文の最初の16バイト(g1 = lambda l6, )がわかっているため、既知平文XOR攻撃により_nを即座に復元できます:
key_prefix = bytes(ciphertext[i] ^ known_plaintext[i] for i in range(4))
# Result: key_prefix = 00dd6c54 → _n = 14,511,188
src/decrypt_payload.py を使用してこれを再現します。
| パラメータ | 値 |
|---|---|
| C2 IP | 158.94.211.185 |
| C2 ポート | 53 (UDP) |
| プロトコル | カスタム DNS-over-UDP トンネル |
| 偽装ドメイン | microsoft.com |
| ChaCha20 鍵 | 36f555c87f71581f57d83576c88193fdc00a0173f741a3e198e2d7abdc9cda59 |
| ブロックチェーンコントラクト | 0x2d7a04cca0c34005f58393f30ac725e25f19e5f5 (Sepolia) |
| ECDSA 公開鍵 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEt17l3rGHHR9sYdHEvV8S+JRRCUHQQveSadlGk04xM/8WClDf/67Tyritt2+T18SY8n0xv1TKKl0AgnNFuboEEQ== |
| サービス名 | SystemService / TourmalineUpdate |
| ミューテックス | Global\Tourmaline_<MachineGuid-hash> |
マルウェアは Sepolia テストネット上の Ethereum スマートコントラクトを呼び出して、アクティブな C2 IP アドレスを取得します。これは、攻撃者がコントラクトを更新することでいつでも C2 IP を変更できることを意味します — 従来の IP ブロックリストに基づく C2 の妨害は効果がありません。
Contract : 0x2d7a04cca0c34005f58393f30ac725e25f19e5f5
Selector : 0xeb9fd6fe
Network : Ethereum Sepolia (chainId 11155111)
RPC : https://ethereum-sepolia-rpc.publicnode.com
Result : ChaCha20-encrypted blob containing current C2 IP
返されたデータはハードコードされたChaCha20キーで復号され、実際のC2アドレスが明らかになります。分析時点では、これは 158.94.211.185 に解決されました(ライブRPC呼び出しで検証済み)。
すべてのC2トラフィックは、生のUDP DNSクエリとしてポート53上のC2 IPに直接送信されます。クエリは <encoded_data>.microsoft.com として構造化され、DNSベースのフィルタリングを回避し、正規のWindowsテレメトリのように見えます。
socket.getaddrinfo やDNSライブラリは使用していませんバックドアは任意のPython exec() エンジンを実装しています。タスクは以下のとおりです:
exec() 実行サポートされるタスク種別: Python exec、ファイルアップロード/ダウンロード、シェルコマンド、自己更新。
| 種別 | 値 |
|---|---|
| C2 IP | 158.94.211.185 |
| C2ポート | 53/UDP |
| 偽装ドメイン | *.microsoft.com(カスタムUDP、実際のDNSではない) |
| Ethereum RPC | https://ethereum-sepolia-rpc.publicnode.com |
| スマートコントラクト | 0x2d7a04cca0c34005f58393f30ac725e25f19e5f5 |
| ファイル | SHA256 |
|---|---|
Tourmaline.exe | c9b390b3b7148f549df86503858d52e030b2b5e78fed0bc525ded5927f9265d6 |
Tourmaline.exe (MD5) | b74ac808dea2de31caf024310694ef0c |
| キー | 値 |
|---|---|
HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid | 被害者のフィンガープリンティングのために読み取り |
%APPDATA%\Tourmaline\ | インストールディレクトリ |
| タスクスケジューラ | TourmalineUpdate |
| サービス表示名 | Hardware monitoring service |
| ミューテックス | Global\Tourmaline_<hash> |
以下を対象とする検出ルールについては iocs/rules.yar を参照してください:
このマルウェアがマシン上で実行された場合: