
Jellyfin Server 12.2 – Hoch – Remote – SSRF – gzip data:image/png Überbleibsel von GHSA-cf3c ruft weiterhin http und file ab
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · jellyfin-svg-gzip
Klasse: SSRF + Dateilesen + memex
Reichweite: Remote (jeder authentifizierte Benutzer POST /UserImage; Rasterisierung ist unauth GET /UserImage)
Jellyfin Server 12.2 (ca0f16eb) - Jellyfin. Svg.Skia 3.7.0.
GHSA-cf3c-8m59-2vhx hat äußere http- und file-Hrefs in Profil-SVGs geschlossen. 12.2 pinnt weiterhin Svg.Skia 3.7.0. SvgDocument.ResolveExternalImages = ExternalType.None wird von ImageDrawable in 3.7.0 ignoriert. SvgSecurityValidator.ValidateDataUri gibt allow für jede Data-URI zurück, deren Medientyp nicht exakt image/svg+xml ist, noch vor dem gzip-Walk. Svg.Skia GetImageFromDataUri: mimeType.StartsWith("image/") plus gzip-Magic ruft LoadSvgz ohne erneuten Jellyfin-Scan auf. Die http(s)- / file-Hrefs des inneren SVG gehen an WebRequest.Create. Das ist die ursprüngliche GHSA-cf3c-Senke.
Jeder authentifizierte Benutzer lädt ein gzip-png-verpacktes SVG hoch. Ein unauthentifizierter GET dieses Profilbilds lässt den Server beim Rasterisieren Angreifer-http / https / file:// abrufen.
| ID | noch keine CVE |
| Klasse | SSRF + Dateilesen + memex |
| Reichweite | Remote. Jeder Benutzer POST /UserImage. Rasterisierung ist unauth GET /UserImage?userId= |
| CWE | CWE-918 / CWE-611 / CWE-400 |
| CVSS | High: 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:L |
| Produkt | Jellyfin Server |
| Betroffen | 12.2 (ca0f16eb7c195f720a1493ed469a27c4657db0c6), Image jellyfin/jellyfin:12.2, Svg.Skia 3.7.0 |
| Auth | Authentifizierter Upload. Unauthentifizierte Rasterisierung. |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Übriggebliebenes #2 der Serie Jellyfin 12.2 leftovers. Schwesterprojekt #1 Suggestions IDOR. Gleicher Pin: playlist ItemIds, FFmpeg colon-slash.
Akteur ist jeder angemeldete Jellyfin-Benutzer. Das Profilbild ist nicht nur für Admins. Die Rasterisierung benötigt keine Sitzung.
| Achse | Methode + Pfad | Gewährt | Herkunft |
|---|---|---|---|
| Recon | POST /UserImage (Content-Type: image/svg+xml, BASE64-Body), dann unauth GET /UserImage?userId= | Server-GET auf Angreifer-http/https (Loopback, RFC1918, Link-Local/IMDS) während Skia rasterisiert | Im Lab bewiesen. Oracle-Hostname oracle:8080, hits=1, Witness im Pfad |
| Recon | dasselbe | file://-Bytes werden in das PNG/WebP/JPEG gemalt, das der Anfragende herunterlädt, sofern sie als Bild dekodieren | Abgeleitet. Gleiche WebRequest.Create-Senke wie GHSA-cf3c |
| Verfügbarkeit | dasselbe, inneres file:///dev/urandom | Unbegrenztes Lesen, das auf dem Managed Heap gehalten wird. Wiederholt sich bei jedem späteren Bild-GET ohne erneuten Upload | Abgeleitet. In GHSA-cf3c gemessen |
| Integrität | keine | Kein Schreiben von Benutzerrichtlinien, keine Plugin-Installation, kein Session-Diebstahl | Geschlossen |
Bleibt geschlossen: direkte http/file-Hrefs auf dem äußeren SVG (Lab: GET blieb image/svg+xml, Orakel 0); verschachteltes data:image/svg+xml (erneut gescannt, einschließlich gzip); DTD SYSTEM; GHSA-vcmh Inline-XSS (Content-Disposition: attachment plus Rasterisierung); Plugin-Image-Traversal; Magick/PDF. Kein RCE. Kein Session-XSS. Der SSRF-Body ist das, was Skia zeichnet, nicht die rohe HTTP-Antwort.
12.2 ist der Tag, der den GHSA-cf3c-Pre-Scan bereits ausgeliefert hat. Ich habe es trotzdem dabei belassen. Patches, die einen Validator vor eine Bibliothek setzen, die weiterhin abruft, sind ein Geschenk: Man liest, was der Validator durchläuft, und fragt dann, was die Bibliothek noch parst.
Directory.Packages.props pinnt weiterhin Svg.Skia 3.7.0. Upstream wieslawsoltes/Svg.Skia#448 (behoben in #501): ImageDrawable konsultiert niemals ResolveExternalImages. Jellyfins drei ExternalType.None-Zuweisungen sind bei diesem Pin ein No-op. Renovate #17076 ist nicht im Tag enthalten.
Der Pre-Scan ist dann das einzige Gate. ValidateReference lehnt jeden Nicht-Fragment-, Nicht-data:-Href ab. Verschachteltes image/svg+xml wird erneut geparst, einschließlich gzip. Der Kommentar in SvgSecurityValidator.cs:169-173 besagt, dass jeder andere Medientyp raster ist und allow zurückgibt vor dem 0x1F 0x8B-Durchlauf. Svg.Skia 3.7.0 GetImageFromDataUri widerspricht: Jedes image/* mit gzip-Magic ist LoadSvgz.
Bereits dokumentierte Irrwege: GHSA-cf3c als geschlossen zu betrachten, weil die Negativkontrolle image/svg+xml bleibt; verschachteltes data:image/svg+xml gzip (erneut gescannt); DTD SYSTEM; GHSA-vcmh Inline-XSS; Plugin-Image-Traversal; Magick/PDF; Admin RemoteImages/Download (beabsichtigter Artwork-Abruf); eine Pixel-Bomb-DoS, die nicht dieser Rest ist; eine Reverse Shell. Theater.
python3 jellyfin-svg-gzip-Abraxas-Labs.py
oder:
cd lab
./run.sh
Nur Loopback: Jellyfin http://127.0.0.1:18780, Oracle http://127.0.0.1:18781. Image jellyfin/jellyfin:12.2. Das Oracle gibt ein 1x1-PNG zurück und protokolliert den Anforderungspfad. Der Compose-Hostname oracle:8080 ist der, den Skia abruft.
Negativ: Sende per POST ein Profil-SVG, dessen Bild-href http://oracle:8080/negative.png ist. GET bleibt image/svg+xml, Oracle 0. Positiv: gzip eines inneren SVG, dessen href http://oracle:8080/JELLYFIN-SVG-SVGSKIA-WITNESS.png ist, verpackt als data:image/png;base64 im äußeren SVG. GET ist image/png, Oracle 1.
SUCCESS jellyfin-svg-gzip gzip-png-ssrf hits=1 negative=0 JELLYFIN-SVG-SVGSKIA-WITNESS
Nur Beobachtung. Keine Exploit-Payload. Keine Reverse Shell. Autorisierte Laborumgebung. Loopback.
Durchlaufe gzip und verschachtelte data-URIs in SvgSecurityValidator, auch wenn der deklarierte Medientyp nicht image/svg+xml ist. Oder aktualisiere Svg.Skia über die Ignorierung von ImageDrawable ExternalType.None hinaus (Upstream #448 / #501; Renovate #17076 ist nicht in diesem Tag enthalten). Beides. Das Bibliotheks-Flag ist der vorgesehene Kill Switch. Der Validator sollte nicht darüber hinwegtäuschen, was die Bibliothek parsen wird.