Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
jellyfin-svg-gzip — Jellyfin Server 12.2 - High - Remote - SSRF - gzip data:image/png leftover of GHSA-cf3c still fetches http and file | Kitploit
Tools/GitHubGitHub/abraxas/jellyfin-svg-gzip
Vulnerability AnalysisExploitationWeb Application ExploitationInformation GatheringPenetration TestingLabs & Practice
GitHubabraxas/jellyfin-svg-gzip

jellyfin-svg-gzip

Jellyfin Server 12.2 - High - Remote - SSRF - gzip data:image/png leftover of GHSA-cf3c still fetches http and file

View Repository
31 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - jellyfin-svg-gzip

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  jellyfin-svg-gzip

jellyfin-svg-gzip

Class: SSRF + file read + memex Reach: Remote (any authenticated user POST /UserImage; rasterize is unauth GET /UserImage)

Jellyfin Server 12.2 (ca0f16eb) - Jellyfin. Svg.Skia 3.7.0.

GHSA-cf3c-8m59-2vhx closed outer http and file hrefs on profile SVGs. 12.2 still pins Svg.Skia 3.7.0. SvgDocument.ResolveExternalImages = ExternalType.None is ignored by 3.7.0 ImageDrawable. SvgSecurityValidator.ValidateDataUri returns allow for any data URI whose media type is not exactly image/svg+xml, before the gzip walk. Svg.Skia GetImageFromDataUri: mimeType.StartsWith("image/") plus gzip magic calls LoadSvgz with no Jellyfin re-scan. The inner SVG's http(s) / file hrefs go to WebRequest.Create. That is the original GHSA-cf3c sink.

Any authenticated user uploads a gzip-png wrapped SVG. An unauthenticated GET of that profile image makes the server fetch attacker http / https / file:// while rasterizing.

IDno CVE yet
ClassSSRF + file read + memex
ReachRemote. Any user POST /UserImage. Rasterize is unauth GET /UserImage?userId=
CWECWE-918 / CWE-611 / CWE-400
CVSSHigh: 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:L
ProductJellyfin Server
Affected12.2 (ca0f16eb7c195f720a1493ed469a27c4657db0c6), image jellyfin/jellyfin:12.2, Svg.Skia 3.7.0
AuthAuthenticated upload. Unauthenticated rasterize.
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

Leftover #2 of the Jellyfin 12.2 leftovers series. Sibling #1 Suggestions IDOR. Same pin: playlist ItemIds, FFmpeg colon-slash.

What an attacker can do

Actor is any signed-in Jellyfin user. Profile image is not admin-only. Rasterize does not need a session.

AxisMethod + pathGrantsProvenance
ReconPOST /UserImage (Content-Type: image/svg+xml, BASE64 body) then unauth GET /UserImage?userId=Server GET of attacker http/https (loopback, RFC1918, link-local/IMDS) while Skia rasterizesLab-proven. Oracle hostname oracle:8080, hits=1, witness in path
Reconsamefile:// bytes painted into the PNG/WebP/JPEG the requester downloads, if they decode as an imageInferred. Same WebRequest.Create sink as GHSA-cf3c
Availabilitysame, inner file:///dev/urandomUnbounded read retained on the managed heap. Repeats on every later image GET without re-uploadInferred. Measured in GHSA-cf3c
IntegritynoneNo user-policy write, no plugin install, no session stealClosed

Stays closed: direct http/file hrefs on the outer SVG (lab: GET stayed image/svg+xml, oracle 0); nested data:image/svg+xml (re-scanned, including gzip); DTD SYSTEM; GHSA-vcmh inline XSS (Content-Disposition: attachment plus rasterize); plugin-image traversal; Magick/PDF. Not RCE. Not session XSS. SSRF body is what Skia paints, not the raw HTTP response.

How I found it

12.2 is the tag that already shipped the GHSA-cf3c pre-scan. I sat on it anyway. Patches that add a validator in front of a library that still fetches are a gift: you read what the validator walks, then you ask what the library still parses.

Directory.Packages.props still pins Svg.Skia 3.7.0. Upstream wieslawsoltes/Svg.Skia#448 (fixed in #501): ImageDrawable never consults ResolveExternalImages. Jellyfin's three ExternalType.None assignments are a no-op on this pin. Renovate #17076 is not in the tag.

The pre-scan is then the only gate. ValidateReference rejects every non-fragment, non-data: href. Nested image/svg+xml is re-parsed, including gzip. The comment at SvgSecurityValidator.cs:169-173 says any other media type is raster and returns allow before the 0x1F 0x8B walk. Svg.Skia 3.7.0 GetImageFromDataUri disagrees: any image/* with gzip magic is LoadSvgz.

Wrong turns already recorded: treating GHSA-cf3c as closed because the negative control stays image/svg+xml; nested data:image/svg+xml gzip (re-scanned); DTD SYSTEM; GHSA-vcmh inline XSS; plugin-image traversal; Magick/PDF; admin RemoteImages/Download (intended artwork fetch); a pixel-bomb DoS that is not this leftover; a reverse shell. Theatre.

Lab

python3 jellyfin-svg-gzip-Abraxas-Labs.py

or:

cd lab
./run.sh

Loopback only: Jellyfin http://127.0.0.1:18780, oracle http://127.0.0.1:18781. Image jellyfin/jellyfin:12.2. Oracle returns a 1x1 PNG and logs the request path. Compose hostname oracle:8080 is what Skia fetches.

Negative: POST a profile SVG whose image href is http://oracle:8080/negative.png. GET stays image/svg+xml, oracle 0. Positive: gzip of an inner SVG whose href is http://oracle:8080/JELLYFIN-SVG-SVGSKIA-WITNESS.png, wrapped as data:image/png;base64 on the outer SVG. GET is image/png, oracle 1.

SUCCESS jellyfin-svg-gzip gzip-png-ssrf hits=1 negative=0 JELLYFIN-SVG-SVGSKIA-WITNESS

Witness-only. No exploit payload. No reverse shell. Authorized lab. Loopback.

The fix

Walk gzip and nested data URIs in SvgSecurityValidator even when the declared media type is not image/svg+xml. Or bump Svg.Skia past the ImageDrawable ExternalType.None ignore (upstream #448 / #501; renovate #17076 is not in this tag). Both. The library flag is the intended kill switch. The validator should not lie about what the library will parse.

References

Download Tool