
Jellyfin Server 12.2 — высокий уровень — удалённое выполнение после подстановки .strm — инъекция аргументов FFmpeg — EncodingUtils -i остаток от GHSA-wwwm
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · jellyfin-ffmpeg-colonslash
Класс: инъекция аргументов FFmpeg
Достижимость: Удалённо после подкладки .strm в библиотеку (запись в сканируемую папку медиатеки, затем неаутентифицированный стрим)
Jellyfin Server v12.2 (ca0f16eb7c195f720a1493ed469a27c4657db0c6) - Jellyfin. Образ jellyfin/jellyfin:12.2.
GHSA-wwwm-px48-fpvq / PR 17685 добавили EscapeProcessArgument для argv конвертации и ветки -i с file:. Для протоколов, отличных от File, путь по-прежнему оборачивается в кавычки без этого экранирования (EncodingUtils.cs:14-71). ManagedFileSystem.IsPathFile возвращает false для ://, отличного от file://. TranscodeManager по-прежнему собирает ProcessStartInfo.Arguments как одну строку.
Сканируемый .strm в библиотеке, первая строка которого — URL http, содержащий ", классифицируется как Http. Uri.TryCreate принимает такой URL. Неаутентифицированный GET /Videos/{id}/stream (без static) запускает ffmpeg. Лог лаборатории:
-i "http://127.0.0.1/a" JELLYFIN-FFMPEG-COLONSLASH-WITNESS "x"
Управляющий LabControl.mkv остаётся одним аргументом: -i file:"/media/movies/LabControl.mkv".
POSIX-имя файла с :// не попадает в этот сток. Слэш не может быть частью имени файла. Лабораторный образец a://b"…"x.mkv сохраняется как /media/movies/a:/b"…"x.mkv и идёт по экранированной ветке File. Не подавайте вариант с именем файла, содержащим двоеточие и слэш. Я не заявляю о RCE через плагин-DLL. Лаборатория доказывает только разделение argv.
Оставшийся выпуск #4 из серии Jellyfin 12.2 leftovers.
| ID | CVE пока нет |
| Класс | Инъекция аргументов FFmpeg (CWE-88) |
| Достижимость | Удалённо после подкладки .strm в библиотеку, затем неаутентифицированный стрим |
| CWE | CWE-88 |
| CVSS | Высокий: 7.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N |
| Продукт | Jellyfin Server |
| Затронуто | v12.2 (ca0f16eb), образ jellyfin/jellyfin:12.2 |
| Аутентификация | писатель библиотеки для подкладки; стрим неаутентифицирован |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Та же модель подкладки, что и в GHSA-wwwm: записать .strm в сканируемую папку медиатеки (NAS / Samba / гостевая шара). Затем любой, кто может открыть TCP-соединение с сервером, может выполнить GET /Videos/{id}/stream без Authorization и без static. Это полный набор привилегий. Ничто в этом списке не является RCE через shell или плагин-DLL.
| Ось | Метод + путь | Даёт | Происхождение |
|---|---|---|---|
| Целостность / argv | неаутентифицированный GET /Videos/{id}/stream после подкладки .strm в библиотеку | Дополнительные аргументы ffmpeg вне кавычек -i | Доказано в лаборатории. Лог TranscodeManager |
| Разведка | то же плюс -vf drawtext=textfile= | Байты файлов хоста в транскоде | Предполагаемый гаджет; не запускался |
| Целостность | -attach / -dump_attachment | Запись от имени UID jellyfin | Предполагается; загрузка плагина не показана |
| RCE | не заявляется | - | - |
ProcessBuilder. Ни RCE через плагин-DLL. Старая цепочка GHSA-866x историческая и здесь не показана.:// превращается в каталог (a:) плюс файл. Канонический Path не содержит ://, поэтому он попадает в экранированную ветку file: -i.level в GHSA-jh22 закрыт..strm / file:// отбрасываются (ProbeProvider сохраняет только http / https / rtsp / rtp).EncoderAppPath нельзя задать через API конфигурации кодирования..strm (GHSA-j2hf) остаётся закрытой.Я зафиксировал jellyfin/jellyfin v12.2. Двадцать поисков по исходникам, затем четыре независимые лаборатории на loopback. PR 17685 экранировал argv конвертации и file: -i. Обёртки Http / rtsp остались как "%s". Первая подкладка, которую я попробовал, была POSIX-именем файла, содержащим ://. Эта подкладка — каталог. Срабатывает тот выпуск, где URL в .strm уже содержит :// и ".
Неверный поворот, в таком порядке: трактовка сокращения path.Contains("://") в GetFileInputArgument как бага в имени файла. Слэш — это разделитель. Ветка File уже экранирует. Ветка non-File — нет.
Соседние находки на этой фиксации: jellyfin-suggestions-idor, jellyfin-svg-gzip, jellyfin-playlist-itemids.
Клиент loopback: jellyfin-ffmpeg-colonslash-Abraxas-Labs.py. Стек: lab/ (docker-compose.yml, poc.py, run.sh). Только авторизованная лаборатория. Он обращается к loopback.
python3 jellyfin-ffmpeg-colonslash-Abraxas-Labs.py
Или:
cd lab
./run.sh
Цель — только http://127.0.0.1:18810. Образ jellyfin/jellyfin:12.2. Проект Compose jellyfin-ffmpeg-colonslash. Свидетель JELLYFIN-FFMPEG-COLONSLASH-WITNESS.
poc.py записывает LabControl.mkv (контроль, один -i file:"...") и LabPlant.strm, URL которого равен http://127.0.0.1/a" JELLYFIN-FFMPEG-COLONSLASH-WITNESS "x. Неаутентифицированный GET /Videos/{id}/stream (без static) логирует разрыв кавычки. Последняя строка:
SUCCESS jellyfin-ffmpeg-colonslash argv-split=yes plant-i=quote-break control-id=f603a52f649bcf822b333c4d5f21cc86 plant-id=d7aabf0935bb524e1667b1ff9bb945d9 JELLYFIN-FFMPEG-COLONSLASH-WITNESS
Вызывайте EscapeProcessArgument в каждой ветке GetInputArgument, включая Http и rtsp. Сохраните обёртку, но экранируйте внутреннюю строку с помощью кавычек так же, как это уже делает ветка file:.