
Jellyfin Server 12.2 — высокий уровень — удалённо — SSRF — gzip data:image/png остаток от GHSA-cf3c по-прежнему выполняет запросы http и file
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · jellyfin-svg-gzip
Класс: SSRF + чтение файлов + memex
Доступ: Удалённо (любой аутентифицированный пользователь POST /UserImage; растеризация без аутентификации GET /UserImage)
Jellyfin Server 12.2 (ca0f16eb) - Jellyfin. Svg.Skia 3.7.0.
GHSA-cf3c-8m59-2vhx закрыл внешние http и file href в профильных SVG. 12.2 по-прежнему закрепляет Svg.Skia 3.7.0. SvgDocument.ResolveExternalImages = ExternalType.None игнорируется в ImageDrawable из 3.7.0. SvgSecurityValidator.ValidateDataUri возвращает разрешение для любого data URI, чей media type не равен в точности image/svg+xml, до прохода по gzip. Svg.Skia GetImageFromDataUri: mimeType.StartsWith("image/") плюс gzip-магия вызывают LoadSvgz без повторного сканирования Jellyfin. Внешние http(s) / file href внутреннего SVG попадают в WebRequest.Create. Это и есть исходный сток GHSA-cf3c.
Любой аутентифицированный пользователь загружает SVG, обёрнутый в gzip-png. Неаутентифицированный GET этого профильного изображения заставляет сервер запрашивать http / https / file:// атакующего во время растеризации.
| ID | CVE пока нет |
| Класс | SSRF + чтение файлов + memex |
| Доступ | Удалённо. Любой пользователь POST /UserImage. Растеризация без аутентификации GET /UserImage?userId= |
| CWE | CWE-918 / CWE-611 / CWE-400 |
| CVSS | Высокий: 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:L |
| Продукт | Jellyfin Server |
| Затронуто | 12.2 (ca0f16eb7c195f720a1493ed469a27c4657db0c6), образ jellyfin/jellyfin:12.2, Svg.Skia 3.7.0 |
| Аутентификация | Аутентифицированная загрузка. Неаутентифицированная растеризация. |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Оставшийся #2 из серии Jellyfin 12.2 leftovers. Соседний #1 Suggestions IDOR. Тот же пин: playlist ItemIds, FFmpeg colon-slash.
Актор — любой вошедший в систему пользователь Jellyfin. Профильное изображение не только для администратора. Растеризация не требует сессии.
| Ось | Метод + путь | Даёт | Происхождение |
|---|---|---|---|
| Разведка | POST /UserImage (Content-Type: image/svg+xml, тело BASE64), затем неаутентифицированный GET /UserImage?userId= | GET сервера к http/https атакующего (loopback, RFC1918, link-local/IMDS) во время растеризации Skia | Доказано в лаборатории. Имя хоста оракула oracle:8080, hits=1, свидетель в пути |
| Разведка | то же | Байты file:// отрисовываются в PNG/WebP/JPEG, который скачивает запрашивающий, если они декодируются как изображение | Предположительно. Тот же сток WebRequest.Create, что и в GHSA-cf3c |
| Доступность | то же, внутренний file:///dev/urandom | Неограниченное чтение, удерживаемое в управляемой куче. Повторяется при каждом последующем GET изображения без повторной загрузки | Предположительно. Измерено в GHSA-cf3c |
| Целостность | нет | Нет записи пользовательской политики, нет установки плагинов, нет кражи сессии | Закрыто |
Остаётся закрытым: прямые http/file href на внешнем SVG (lab: GET остался image/svg+xml, oracle 0); вложенный data:image/svg+xml (повторно просканирован, включая gzip); DTD SYSTEM; GHSA-vcmh inline XSS (Content-Disposition: attachment плюс растеризация); обход пути через изображение плагина; Magick/PDF. Не RCE. Не session XSS. Тело SSRF — это то, что отрисовывает Skia, а не сырой HTTP-ответ.
12.2 — это тег, в котором уже был выпущен pre-scan GHSA-cf3c. Я всё равно отложил его. Патчи, добавляющие валидатор перед библиотекой, которая всё ещё выполняет выборку, — это подарок: читаешь, что обходит валидатор, затем спрашиваешь, что библиотека всё ещё парсит.
Directory.Packages.props всё ещё фиксирует Svg.Skia 3.7.0. Upstream wieslawsoltes/Svg.Skia#448 (исправлено в #501): ImageDrawable никогда не обращается к ResolveExternalImages. Три присваивания ExternalType.None в Jellyfin — no-op на этой фиксации. Renovate #17076 не входит в тег.
Таким образом, pre-scan — единственный барьер. ValidateReference отклоняет любой href, не являющийся фрагментом или data:. Вложенный image/svg+xml повторно парсится, включая gzip. Комментарий в SvgSecurityValidator.cs:169-173 говорит, что любой другой медиатип является растровым и возвращает allow до проверки 0x1F 0x8B. Svg.Skia 3.7.0 GetImageFromDataUri не согласен: любой image/* с gzip-магией — это LoadSvgz.
Уже зафиксированные неверные повороты: считать GHSA-cf3c закрытым, потому что негативный контроль остаётся image/svg+xml; вложенный data:image/svg+xml gzip (повторно просканирован); DTD SYSTEM; GHSA-vcmh inline XSS; обход пути через изображение плагина; Magick/PDF; админский RemoteImages/Download (преднамеренная загрузка artwork); pixel-bomb DoS, который не является этим остатком; reverse shell. Театр.
python3 jellyfin-svg-gzip-Abraxas-Labs.py
или:
cd lab
./run.sh
Только loopback: Jellyfin http://127.0.0.1:18780, oracle http://127.0.0.1:18781. Образ jellyfin/jellyfin:12.2. Oracle возвращает PNG 1x1 и логирует путь запроса. Именно oracle:8080 (hostname в Compose) запрашивает Skia.
Негативный сценарий: POST профиля SVG, у которого href изображения — http://oracle:8080/negative.png. GET остаётся image/svg+xml, oracle 0. Позитивный сценарий: gzip внутреннего SVG, у которого href — http://oracle:8080/JELLYFIN-SVG-SVGSKIA-WITNESS.png, обёрнутый как data:image/png;base64 во внешнем SVG. GET — image/png, oracle 1.
SUCCESS jellyfin-svg-gzip gzip-png-ssrf hits=1 negative=0 JELLYFIN-SVG-SVGSKIA-WITNESS
Только свидетель. Без эксплойт-пейлоада. Без reverse shell. Авторизованная лаборатория. Loopback.
Обходите gzip и вложенные data URI в SvgSecurityValidator, даже если объявленный медиатип не image/svg+xml. Или обновите Svg.Skia за пределы игнорирования ExternalType.None в ImageDrawable (upstream #448 / #501; renovate #17076 отсутствует в этом теге). И то, и другое. Флаг библиотеки — это предполагаемый механизм отключения. Валидатор не должен лгать о том, что будет парсить библиотека.