
ملاحظات الهندسة العكسية، والمصدر بعد إزالة التشويش، ومؤشرات الاختراق، وقواعد YARA الخاصة ببرمجية Tourmaline ClickFix Python RAT، والتي تغطي نفق DNS الخاص بها وC2 القائم على blockchain dead-drop.
صادفت مؤخرًا قطعة من البرمجيات الخبيثة المصممة ببراعة تطلق على نفسها اسم "Tourmaline".
كُتب هذا الملف التعريفي (README) بلطف شديد من قبل Claude لأنني كنت مشغولًا جدًا بالامتحانات وقت إنشاء هذا المستودع.
استغلال هذه البرمجية الخبيثة وفك تشفيرها استهلك جزءًا كبيرًا من وقتي المخصص للتحضير للرياضيات!
اقرأ تدوينة المدونة!
لأغراض تعليمية وبحثية فقط
جميع العينات مقدمة لتحليل البرمجيات الخبيثة والبحث الدفاعي. لا تنفذها خارج بيئة معزولة (sandboxed) ومعزولة عن الشبكة (air-gapped).
Tourmaline (Tourmaline.exe) هي برمجية خبيثة متقدمة متعددة المراحل لنظام Windows من نوع infostealer/backdoor تُوزَّع عبر حملة ClickFix - وهي نافذة منبثقة مزيفة في المتصفح تحاكي رسالة Cloudflare "Verify you are human" تخدع المستخدمين لتشغيل أمر أو مثبّت خبيث يدويًا.
الملف التنفيذي هو مثبّت Inno Setup 6.7.0 (Revision 2, 64-bit offsets) مخصص البناء، يضم بيئة تشغيل Python 3.11 كاملة ومرحلتين من حمولة Python المشفرة (obfuscated). وهو ينشئ بابًا خلفيًا دائمًا (persistent backdoor) يتضمن:
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 - وهي تقنية هندسة اجتماعية يُغطّي فيها موقع مخترق أو يتحكم به المهاجم صفحة CAPTCHA مزيفة من Cloudflare أو صفحة تحقق من المتصفح. تُوجّه الطبقة المتراكبة الضحية للقيام بما يلي:
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 (Revision 2، إزاحات 64-بت) |
| نوع PE | PE 32-بت، 11 قسمًا |
| حجم الطبقة المتراكبة | 10,447,387 بايت |
| إزاحة Inno السحرية | 0xD9864 (890,148 بايت) |
| اسم التطبيق | Tourmaline |
| إصدار التطبيق | 2.63.519 |
| GUID التطبيق | {2C25872D-85FC-44C7-9B16-844E39E50A44} |
| مسار التثبيت | {commonappdata}\Tourmaline |
| بيئة التشغيل المضمّنة | Python 3.11 (تضمين كامل) |
| ملفات الحمولة | 36 ملفًا في تدفق LZMA2 صلب |
ملاحظة: لا يستطيع
innoextract 1.9القياسي استخراج هذا الملف الثنائي — فهو يدعم Revision 1 فقط. يستخدم Revision 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, )، يمكننا استعادة _n فورًا عبر هجوم XOR بالنص الأصلي المعروف:
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 لإعادة إنتاج هذا.
| المعامل | القيمة |
|---|---|
| عنوان IP الخاص بـ C2 | 158.94.211.185 |
| منفذ C2 | 53 (UDP) |
| البروتوكول | نفق DNS-over-UDP مخصص |
| النطاق المنتحل | microsoft.com |
| مفتاح ChaCha20 | 36f555c87f71581f57d83576c88193fdc00a0173f741a3e198e2d7abdc9cda59 |
| عقد البلوكتشين | 0x2d7a04cca0c34005f58393f30ac725e25f19e5f5 (Sepolia) |
| المفتاح العام ECDSA | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEt17l3rGHHR9sYdHEvV8S+JRRCUHQQveSadlGk04xM/8WClDf/67Tyritt2+T18SY8n0xv1TKKl0AgnNFuboEEQ== |
| اسم الخدمة | SystemService / TourmalineUpdate |
| Mutex | Global\Tourmaline_<MachineGuid-hash> |
يستدعي البرمجية الخبيثة عقدًا ذكيًا على شبكة إيثيريوم في شبكة Sepolia التجريبية لاسترداد عنوان IP النشط الخاص بـ C2. وهذا يعني أن المهاجم يمكنه تغيير عنوان IP الخاص بـ C2 في أي وقت عبر تحديث العقد — ما يجعل تعطيل C2 التقليدي القائم على قوائم حظر عناوين IP غير فعّال.
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 خام مباشرةً إلى عنوان IP الخاص بـ C2 على المنفذ 53. تُبنى الاستعلامات على شكل <encoded_data>.microsoft.com، متجاوزةً التصفية القائمة على DNS وتبدو كبيانات تتبع قياس عن بُعد (telemetry) شرعية من Windows.
socket.getaddrinfo الخاص بـ Python ولا أي مكتبة DNS