
Jellyfin Server 12.2 - Alta - Remoto tras plant .strm - Inyección de argumentos en FFmpeg - EncodingUtils -i residual de GHSA-wwwm
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · jellyfin-ffmpeg-colonslash
Clase: Inyección de argumentos en FFmpeg
Alcance: Remoto tras plantar un .strm en la biblioteca (escritor de una carpeta multimedia escaneada, luego stream sin autenticación)
Jellyfin Server v12.2 (ca0f16eb7c195f720a1493ed469a27c4657db0c6) - Jellyfin. Imagen jellyfin/jellyfin:12.2.
GHSA-wwwm-px48-fpvq / PR 17685 añadió EscapeProcessArgument en el argv de conversión y en la rama -i de file:. El protocolo que no es File sigue envolviendo la ruta sin procesar entre comillas sin ese escape (EncodingUtils.cs:14-71). ManagedFileSystem.IsPathFile es falso para :// que no sea file://. TranscodeManager sigue construyendo ProcessStartInfo.Arguments como una sola cadena.
Un .strm de una biblioteca escaneada cuya primera línea es una URL http que contiene " se clasifica como Http. Uri.TryCreate acepta esa URL. Un GET /Videos/{id}/stream sin autenticación (sin static) inicia ffmpeg. El log del laboratorio es:
-i "http://127.0.0.1/a" JELLYFIN-FFMPEG-COLONSLASH-WITNESS "x"
Control LabControl.mkv sigue siendo un solo argumento: -i file:"/media/movies/LabControl.mkv".
Un nombre de archivo POSIX con :// no llega a este sink. La barra no puede formar parte de un nombre de archivo. La planta de laboratorio a://b"…"x.mkv se almacena como /media/movies/a:/b"…"x.mkv y toma la rama File escapada. No reportes una variante de nombre de archivo con dos puntos y barra. No estoy afirmando RCE por DLL de plugin. El laboratorio solo demuestra la división de argv.
Resto #4 de la serie Jellyfin 12.2 leftovers.
| ID | aún sin CVE |
| Clase | Inyección de argumentos en FFmpeg (CWE-88) |
| Alcance | Remoto tras plantar un .strm en la biblioteca, luego stream sin autenticación |
| CWE | CWE-88 |
| CVSS | Alto: 7.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N |
| Producto | Jellyfin Server |
| Afectado | v12.2 (ca0f16eb) imagen jellyfin/jellyfin:12.2 |
| Autenticación | escritor de biblioteca para la planta; el stream no requiere autenticación |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
El mismo modelo de planta que GHSA-wwwm: escribir un .strm en una carpeta de medios escaneada (NAS / Samba / recurso compartido de invitado). Después, cualquiera que pueda abrir TCP hacia el servidor puede hacer GET /Videos/{id}/stream sin Authorization y sin static. Ese es el conjunto completo de privilegios. Nada en esta lista es un RCE por shell o por DLL de plugin.
| Eje | Método + ruta | Otorga | Procedencia |
|---|---|---|---|
| Integridad / argv | GET /Videos/{id}/stream sin autenticación tras plantar un .strm en la biblioteca | Argumentos ffmpeg adicionales fuera de las comillas de -i | Probado en laboratorio. Log de TranscodeManager |
| Reconocimiento | lo mismo más -vf drawtext=textfile= | Bytes de archivos del host en el transcode | Gadget inferido; no ejecutado |
| Integridad | -attach / -dump_attachment | Escritura con UID de jellyfin | Inferido; carga de plugin no demostrada |
| RCE | no afirmado | - | - |
ProcessBuilder. Ni RCE por DLL de plugin. La antigua cadena GHSA-866x es histórica y no se muestra aquí.:// se convierte en un directorio (a:) más un archivo. La ruta canónica no tiene ://, así que llega a la rama -i de file: escapada.level de GHSA-jh22 está cerrado.file:// de .strm se descartan (ProbeProvider conserva solo http / https / rtsp / rtp).EncoderAppPath no se puede establecer mediante la API de configuración de codificación..strm (GHSA-j2hf) sigue cerrada.Fijé jellyfin/jellyfin v12.2. Veinte búsquedas en el código fuente, luego cuatro laboratorios independientes en loopback. El PR 17685 escapó el argv de conversión y el -i de file:. Los wrappers de Http / rtsp se dejaron como "%s". La primera planta que probé fue un nombre de archivo POSIX que contenía ://. Esa planta es un directorio. El resto que se activa es un .strm cuya URL ya contiene :// y ".
Camino equivocado, en ese orden: tratar el atajo path.Contains("://") de GetFileInputArgument como un bug de nombre de archivo. La barra es un separador. La rama File ya escapa. La rama no-File no lo hace.
Hermanos en este pin: jellyfin-suggestions-idor, jellyfin-svg-gzip, jellyfin-playlist-itemids.
Cliente loopback: jellyfin-ffmpeg-colonslash-Abraxas-Labs.py. Stack: lab/ (docker-compose.yml, poc.py, run.sh). Solo laboratorio autorizado. Se comunica con loopback.
python3 jellyfin-ffmpeg-colonslash-Abraxas-Labs.py
O:
cd lab
./run.sh
Dirígete solo a http://127.0.0.1:18810. Imagen jellyfin/jellyfin:12.2. Proyecto de Compose jellyfin-ffmpeg-colonslash. Testigo JELLYFIN-FFMPEG-COLONSLASH-WITNESS.
poc.py escribe LabControl.mkv (control, un -i file:"...") y LabPlant.strm cuya URL es http://127.0.0.1/a" JELLYFIN-FFMPEG-COLONSLASH-WITNESS "x. La petición no autenticada GET /Videos/{id}/stream (sin static) registra la ruptura de comillas. Última línea:
SUCCESS jellyfin-ffmpeg-colonslash argv-split=yes plant-i=quote-break control-id=f603a52f649bcf822b333c4d5f21cc86 plant-id=d7aabf0935bb524e1667b1ff9bb945d9 JELLYFIN-FFMPEG-COLONSLASH-WITNESS
Llamar a EscapeProcessArgument en cada rama de GetInputArgument, incluidos Http y rtsp. Mantener el envoltorio, pero escapar entre comillas la cadena interna tal como ya lo hace la rama file:.