OpenPanel — аналитическая платформа. Во всех версиях (на момент публикации исправленный выпуск не доступен) импортер данных извлекает URL-адрес, предоставленный вызывающей стороной, с помощью простой выборки вместо существующей защиты SSRF проекта (apps/api/src/utils/safe-fetch.ts). В packages/importer/src/providers/umami.ts parseRemoteFile вызывает fetch() для config.fileUrl, который проверяется только с помощью z.string().url(), поэтому принимаются такие значения, как http://127.0.0.1:9911/ или http://169.254.169.254/latest/meta-data/; общая фабрика createFileImportConfig предоставляет вероятному поставщику то же поле.
Аутентифицированный член организации, включая «члена» по умолчанию без строк project_access, для которого предполагаемая проверка уровня доступа пропускается, поскольку getProjectAccess возвращает логическое значение true, а не объект уровня, может поэтому подключить сервер к любому адресу, доступному с него. Результирующий статус HTTP и текст состояния сохраняются как Import.errorMessage и возвращаются import.get тому же пользователю, обеспечивая оракул сканирования внутренних хостов, портов и путей; если внутренний ответ анализируется как Umami CSV, его строки воспринимаются как события и становятся доступными для чтения в аналитических представлениях злоумышленника.
Показать оригинальное описание (EN)
OpenPanel is an analytics platform. In all versions (no patched release available at time of publication), the data importer fetches a caller-supplied URL with plain fetch instead of the project's existing SSRF guard (apps/api/src/utils/safe-fetch.ts). In packages/importer/src/providers/umami.ts, parseRemoteFile calls fetch() on config.fileUrl, which is validated only by z.string().url(), so values such as http://127.0.0.1:9911/ or http://169.254.169.254/latest/meta-data/ are accepted; the shared createFileImportConfig factory gives the plausible provider the same field. An authenticated organization member — including a default 'member' with no project_access rows, for whom the intended access-level check is skipped because getProjectAccess returns boolean true rather than a level object — can therefore make the server connect to any address reachable from it. The resulting HTTP status and status text are persisted as Import.errorMessage and returned by import.get to the same user, providing a scanning oracle for internal hosts, ports and paths; if an internal response parses as Umami CSV, its rows are ingested as events and become readable in the attacker's analytics views.
Характеристики атаки
Последствия
Строка CVSS v4.0