针对 Tourmaline ClickFix Python RAT 的逆向工程笔记、去混淆源代码、IOC 和 YARA 规则,涵盖其 DNS 隧道和区块链死投 C2。
我最近偶然发现了一款设计巧妙的恶意软件,它自称“Tourmaline”。
这份 README 是 Claude 非常热心地撰写的,因为在本仓库创建时我正忙于考试。
利用和破解这款恶意软件占用了我大量原本用于数学备考的时间!
阅读博客文章!
仅供教育和研究目的使用
所有样本均用于恶意软件分析和防御性研究。请勿在沙箱化、气隙隔离的环境之外执行。
Tourmaline(Tourmaline.exe)是一款复杂、多阶段的 Windows 信息窃取/后门程序,通过 ClickFix 活动传播——一个伪造的 Cloudflare“验证您是真人”浏览器弹窗,诱骗用户手动运行恶意命令或安装程序。
该二进制文件是一个自定义构建的 Inno Setup 6.7.0(修订版 2,64 位偏移) 安装程序,捆绑了完整的 Python 3.11 运行时和两个阶段的混淆 Python 载荷。它建立了一个持久化后门,具备以下特性:
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 传播——这是一种社会工程学技术,攻击者利用被入侵或受其控制的网站,覆盖一层伪造的 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(完整嵌入) |
| 载荷文件 | 36 个文件,位于 solid LZMA2 流中 |
注意: 原版
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 测试网上的以太坊智能合约来获取当前活跃的 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 查询的形式直接发送到 C2 IP 的 53 端口。查询被构造为 <encoded_data>.microsoft.com,从而绕过基于 DNS 的过滤,并伪装成合法的 Windows 遥测流量。
socket.getaddrinfo 或任何 DNS 库该后门实现了一个任意 Python exec() 引擎。任务流程如下:
exec() 执行并捕获输出支持的任务类型: Python exec、文件上传/下载、shell 命令、自更新。
| 类型 | 值 |
|---|---|
| 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 获取检测规则,覆盖:
如果此恶意软件已在某台机器上执行:
taskkill /F /IM pythonw.exe%APPDATA%\Tourmaline\ 或 %COMMONAPPDATA%\Tourmaline\TourmalineUpdateHKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run 中是否存在 Tourmaline 或 SystemService158.94.211.185:53/UDP;阻断所有意外的出站 UDP/53 流量ethereum-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
| 字段 | 值 |
|---|---|
| 发现日期 | 2026 年 9 月 |
| 研究员 | Vihaan Pundir (v-pun215) |
| 传播途径 | ClickFix 伪造 Cloudflare CAPTCHA |
| 分类 | 后门 / DNS 隧道 C2 / 区块链死投 |
| 平台 | Windows(64 位) |
参见 LICENSE。本仓库仅用于教育和防御性研究目的。