
Critical path traversal to RCE vulnerability in Jellyfin Media Server (CVSS 9.9). Includes proof-of-concept exploit, technical analysis, and detection tools.
A critical path traversal vulnerability in Jellyfin Media Server allows authenticated users with "Upload Subtitles" permission to upload files to arbitrary locations on disk. By exploiting the unvalidated Format field in the subtitle upload endpoint, attackers can write files to sensitive locations, extract sensitive data, escalate privileges, and ultimately execute arbitrary code as root via LD_PRELOAD injection.
Jellyfin is a free and open-source media server designed to help you manage and stream your personal media collection. It provides functionality similar to commercial media servers but with full source code transparency and community control.
Jellyfin Media Server (Port 8096)
/ | \
/ | \
Web UI REST API Media Streams
(Browser) (Authenticated) (Subtitle Upload)
|
/System/Info/Public (unauthenticated)
/Videos/{itemId}/Subtitles (vulnerable)
/Library/Collections (admin)
Client Devices > Network > Jellyfin Server > Database + Storage
|
/var/lib/jellyfin/
/etc/ld.so.preload (writable via vulnerability)
The vulnerability exists in the subtitle upload endpoint (/Videos/{itemId}/Subtitles) which accepts file uploads and stores them on disk. The critical flaw lies in the insufficient validation of the Format field parameter.
The endpoint processes subtitle uploads without properly validating or sanitizing the Format field:
POST /Videos/{itemId}/Subtitles HTTP/1.1
Content-Type: multipart/form-data
[Binary subtitle data]
Format: /../../../etc/ld.so.preload
Language: en
The Format parameter is intended to specify subtitle format (srt, vtt, ass, etc.) but instead gets treated as part of the file path:
Base Path: /var/lib/jellyfin/subtitles/
User Input: /../../../etc/ld.so.preload
Result: /var/lib/jellyfin/subtitles/../../../etc/ld.so.preload
Resolved: /etc/ld.so.preload (via path traversal)
The vulnerability chains together multiple weaknesses to achieve remote code execution as root:
Step 1: Subtitle Upload with Path Traversal
POST /Videos/{itemId}/Subtitles
Format: /../../../etc/ld.so.preload
|
v
Step 2: Arbitrary File Write
Write attacker-controlled data to /etc/ld.so.preload
|
v
Step 3: File Read via .strm Files
Create .strm files pointing to sensitive paths
Extract database contents and credentials
|
v
Step 4: Database Extraction
Access /jellyfin/jellyfin.db via .strm
Extract admin user hashes
|
v
Step 5: Admin Privilege Escalation
Reset admin password or create new admin account
|
v
Step 6: RCE via LD_PRELOAD Injection
LD_PRELOAD=/path/to/malicious.so java
Arbitrary code execution as root
Input Validation Failure:
Format Field Validation:
Expected: srt | vtt | ass | ssa | sub | subrip
Actual: /../../../etc/ld.so.preload
Result: NO VALIDATION > PATH TRAVERSAL ALLOWED
File Write Operation:
String Concatenation: "/subtitles/" + user_format + ".srt"
|
NO CANONICALIZATION: Path component not resolved before write
NO WHITELIST: Format values not restricted
NO BOUNDS CHECK: ".." sequences not filtered
|
v
Final Path: /etc/ld.so.preload (EXPLOITED)
The LD_PRELOAD technique is a powerful privilege escalation and code execution method on Linux systems:
LD_PRELOAD Injection Flow:
1. Attacker writes malicious .so (shared object) to /etc/ld.so.preload
/etc/ld.so.preload contents:
/path/to/attacker.so
2. Java process starts (Jellyfin runs on Java):
kernel > execve("java", ...) > glibc initialization
|
v
Check /etc/ld.so.preload
|
v
Load attacker.so FIRST
|
v
Execute attacker code
(BEFORE Java main())
3. Code Execution Context:
Process Owner: root (Jellyfin typically runs as root)
Permissions: Full system access
Timing: Before application initialization
Detection: Minimal (malicious code runs early)
4. Attacker Capabilities:
> Create reverse shell with full root privileges
> Extract sensitive data before application starts
> Modify Java application behavior
> Persist via cron jobs or systemd services
> Establish C2 communication
User Level Access > Path Traversal > Write /etc/ld.so.preload
|
v (Next process execution)
|
Kernel reads /etc/ld.so.preload > Loads attacker .so
|
v
Malicious code executes in root context
|
v
Full system compromise
CRITICAL - Complete information disclosure
CRITICAL - System-wide file modification
CRITICAL - Service disruption and denial
The vulnerability can be detected by checking the version string from the /System/Info/Public endpoint:
GET /System/Info/Public HTTP/1.1
Host: jellyfin-server:8096
Response:
{
"ServerName": "MyJellyfin",
"Version": "10.10.3", < Vulnerable
"ProductName": "Jellyfin",
"StartupWizardCompleted": true
}
The CVE-2026-35031_Jellyfin_RCE_detector.py script provides automated vulnerability detection.
pip install requests urllib3
python CVE-2026-35031_Jellyfin_RCE_detector.py -t 10.0.0.5:8096
python CVE-2026-35031_Jellyfin_RCE_detector.py -t http://10.0.0.0/24
python CVE-2026-35031_Jellyfin_RCE_detector.py -t targets.txt -o results.json
-t, --target HOST[:PORT] or CIDR or FILE
Single target, IP range, or file with targets
-p, --port PORT Custom port (default: 8096)
--timeout SECONDS Connection timeout (default: 10)
-o, --output FILE Save results to JSON file
-v, --verbose Enable verbose logging
--no-ssl-verify Disable SSL certificate verification
[*] CVE-2026-35031 Jellyfin RCE Detection Scanner
[*] Target: http://10.0.0.5:8096
[*] Scan Time: 2026-04-15T12:00:00Z
[*] Detection method: /System/Info/Public version check
[*] Vulnerable: Jellyfin < 10.11.7
======================================================================
Target: http://10.0.0.5:8096
Scan Time: 2026-04-15T12:00:00Z
Risk Level: CRITICAL
======================================================================
Is Jellyfin: YES
Jellyfin Version: 10.10.3
Server Name: MediaServer
Operating System: Linux
Subtitle Endpoint: Accessible
Vulnerable: YES
*** VULNERABLE: Path traversal in subtitle upload ***
*** Chains to arbitrary file write and RCE as root via ld.so.preload ***
*** Upgrade to Jellyfin 10.11.7 immediately ***
======================================================================
Summary:
Total Targets: 1
Vulnerable: 1
Patched: 0
Unknown: 0
======================================================================
The scanner performs the following checks:
/System/Info/Public endpointVersion field from JSON responseThe CVE-2026-35031_Jellyfin_RCE.nse script provides integration with Nmap for vulnerability scanning.
# Copy to Nmap scripts directory
sudo cp CVE-2026-35031_Jellyfin_RCE.nse /usr/share/nmap/scripts/
# Update Nmap database
sudo nmap --script-updatedb
# Basic scan
nmap -p 8096 --script CVE-2026-35031_Jellyfin_RCE 10.0.0.5
# Comprehensive scan with service detection
nmap -sV -p 8096 --script CVE-2026-35031_Jellyfin_RCE 10.0.0.5
# Scan entire subnet
nmap -sV -p 8096 --script CVE-2026-35031_Jellyfin_RCE 10.0.0.0/24
# Aggressive scanning with timing
nmap -sV -p- --script CVE-2026-35031_Jellyfin_RCE -T4 10.0.0.5
# Export results to XML
nmap -sV -p 8096 --script CVE-2026-35031_Jellyfin_RCE -oX results.xml 10.0.0.5
PORT STATE SERVICE VERSION
8096/tcp open http Jellyfin Media Server 10.10.3
| CVE-2026-35031_Jellyfin_RCE:
| VULNERABLE:
| Jellyfin Subtitle Path Traversal to RCE (CVE-2026-35031)
| State: VULNERABLE
| Risk level: CRITICAL
| CVSS Score: 9.9
| Jellyfin Version: 10.10.3
| Fixed Version: 10.11.7
| Description:
| Jellyfin 10.10.3 is vulnerable to CVE-2026-35031. The subtitle
| upload endpoint (/Videos/{itemId}/Subtitles) does not validate
| the Format field, allowing path traversal and arbitrary file write.
| This chains into remote code execution as root via LD_PRELOAD.
| Vulnerability Chain:
| 1. POST /Videos/{itemId}/Subtitles with Format=/../../../etc/ld.so.preload
| 2. Arbitrary file write to /etc/ld.so.preload
| 3. Database extraction via .strm files
| 4. Admin privilege escalation
| 5. RCE as root via LD_PRELOAD injection
| Affected Endpoint: /Videos/{itemId}/Subtitles
| Authentication Required: YES (non-admin user)
| References:
| https://nvd.nist.gov/vuln/detail/CVE-2026-35031
| https://github.com/jellyfin/jellyfin/security/advisories/GHSA-9p5f-5x8v-x65m
|_ https://github.com/jellyfin/jellyfin/releases/tag/v10.11.7
# Custom timeout for slow networks
nmap --script CVE-2026-35031_Jellyfin_RCE --script-args timeout=30 10.0.0.5
# Debug mode for troubleshooting
nmap --script CVE-2026-35031_Jellyfin_RCE -d 10.0.0.5
# Aggressive version detection
nmap -sV --version-intensity 9 --script CVE-2026-35031_Jellyfin_RCE 10.0.0.5
Jellyfin Server Logs (/var/log/jellyfin/jellyfin.log)
[ERR] Error processing subtitle upload: Invalid path characters detected
[ERR] Exception in subtitle handling: DirectoryNotFoundException
[ERR] Unauthorized file system access attempt
[WARN] Unusual subtitle format detected: /../../../
[ERR] Security violation: Path traversal attempt blocked
System Logs (/var/log/syslog or /var/log/messages)
subtitle upload process: segmentation fault (core dumped)
kernel: [security] Attempted to load from LD_PRELOAD: /etc/ld.so.preload
ld.so.preload: permission denied or file corrupted
Java process crashed after LD_PRELOAD initialization
Unexpected behavior from root-level Java process
Modified System Files
/etc/ld.so.preload - Should not contain any paths if not configured
/lib/x86_64-linux-gnu/ - Look for suspicious .so files created recently
/var/lib/jellyfin/subtitles - Check for files outside normal naming
/etc/passwd - Verify no unauthorized access or modification
/var/lib/jellyfin/db - Database timestamps may indicate extraction
Suspicious File Paths in Subtitle Directory
/../../../etc/ld.so.preload
/../../../root/.ssh/authorized_keys
/../../../var/lib/jellyfin/jellyfin.db
../../../proc/self/environ
Suspicious HTTP Requests
POST /Videos/[0-9]+/Subtitles
- Format parameter contains: /.. or ..\ patterns
- Format parameter contains absolute paths starting with /
- Format parameter does not match known subtitle formats
Encoded Payloads:
%2e%2e%2f (URL encoded ../)
..%252f (Double encoded ../)
....// (Bypass patterns)
Outbound Connections from Jellyfin Process
Reverse shells to external IPs
Connections to known C2 infrastructure
DNS requests to anomalous domains
Sudden spike in network traffic after failed subtitle upload
Jellyfin Process Anomalies
java process executing system commands
java process spawning shell processes (/bin/bash, /bin/sh)
java process opening connections to unusual ports
java process reading system files like /etc/shadow
Unusual CPU or memory usage spikes
Child processes with different UID than parent
Jellyfin Database Changes (jellyfin.db)
New admin user created outside normal workflow
Admin password changed without admin action
API keys/tokens created unexpectedly
Unusual activity in audit logs
Upgrade Jellyfin Immediately
# Docker deployment
docker pull jellyfin/jellyfin:latest
docker-compose down
docker-compose up -d
# Package manager (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install --only-upgrade jellyfin
# Package manager (Fedora/RHEL)
sudo dnf upgrade jellyfin
Stop Jellyfin Service
sudo systemctl stop jellyfin
Check for Exploitation
# Check if ld.so.preload was modified
ls -la /etc/ld.so.preload
cat /etc/ld.so.preload
# Check subtitle directory for suspicious files
find /var/lib/jellyfin/subtitles -type f -newer /proc -ls
# Check Jellyfin data directory
find /var/lib/jellyfin -type f -newermt "2026-04-14" -ls
Restrict Subtitle Upload Permission
Jellyfin Web UI > Settings > Users
Disable "Upload Subtitles" for all non-admin users
Review all users with this permission
Network Segmentation
# Only allow trusted networks to access Jellyfin
sudo ufw allow from 192.168.1.0/24 to any port 8096
sudo ufw deny from any to any port 8096
File System Permissions
# Ensure Jellyfin runs with minimal privileges
sudo usermod -s /usr/sbin/nologin jellyfin
# Restrict ld.so.preload permissions
sudo chmod 644 /etc/ld.so.preload
sudo chmod 644 /etc/ld.so.conf
# Set proper permissions on Jellyfin directory
sudo chown -R jellyfin:jellyfin /var/lib/jellyfin
sudo chmod 750 /var/lib/jellyfin
Monitor for Exploitation Attempts
# Watch subtitle upload endpoint logs
tail -f /var/log/jellyfin/jellyfin.log | grep -i subtitle
# Monitor system logs for ld.so.preload changes
auditctl -w /etc/ld.so.preload -p wa -k ld_preload_changes
Implement Web Application Firewall (WAF)
Block requests containing:
- Path traversal patterns: ../ ..\ ..\
- Suspicious file paths: /etc/ /root/ /proc/
- Encoded variations: %2e%2e%2f
Enable Security Module
# AppArmor (Ubuntu/Debian)
sudo aa-enforce /etc/apparmor.d/usr.bin.java
# SELinux (Fedora/RHEL)
sudo semanage fcontext -a -t jellyfin_home_t "/var/lib/jellyfin(/.*)?"
sudo restorecon -R /var/lib/jellyfin
Run Jellyfin as Non-root User
# Create dedicated user if not exists
sudo useradd -r -s /usr/sbin/nologin jellyfin
# Update systemd service
sudo sed -i 's/User=.*/User=jellyfin/' /etc/systemd/system/jellyfin.service
sudo systemctl daemon-reload
sudo systemctl restart jellyfin
Implement Regular Backups
# Automated daily backups
sudo crontab -e
# 0 2 * * * /usr/local/bin/jellyfin-backup.sh
# Verify backup integrity
tar -tzf /backup/jellyfin-$(date +%Y%m%d).tar.gz > /dev/null
Enable Audit Logging
# Log all file access to sensitive locations
auditctl -w /etc/ld.so.preload -p wa -k ld_preload_audit
auditctl -w /var/lib/jellyfin -p wa -k jellyfin_audit
# Monitor process execution from Jellyfin
auditctl -a always,exit -F exe=/usr/bin/java -F arch=b64 -S execve -k jellyfin_exec
# 1. Verify Jellyfin version after upgrade
curl -s http://localhost:8096/System/Info/Public | jq .Version
# 2. Confirm no unauthorized ld.so.preload entries
cat /etc/ld.so.preload | wc -l # Should be 0 or contain only legitimate entries
# 3. Check Jellyfin process permissions
ps aux | grep jellyfin | grep -v grep
# 4. Verify subnet connectivity restrictions
sudo ufw status
# 5. Test subtitle upload with non-admin user (should work normally)
# Try uploading a legitimate .srt file and confirm it's stored correctly
Vulnerability Discovered & Documented By:
Kerem Oruc (@keraattin)
If you have improvements to this documentation or detection tools, please submit a pull request or open an issue.
Disclaimer: This document is for educational and authorized security testing purposes only. Unauthorized access to computer systems is illegal. Always obtain proper authorization before conducting security assessments.
Last Updated: 2026-04-15 | Status: PUBLISHED
| Property | Value |
|---|
| CVE ID | CVE-2026-35031 |
| CVSS Score | 9.9 (Critical) |
| CWE | CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') |
| Affected Product | Jellyfin Media Server |
| Affected Versions | < 10.11.7 |
| Fixed Version | 10.11.7 and later |
| Vulnerability Type | Path Traversal + Arbitrary File Write + RCE |
| Authentication Required | Yes (non-admin user) |
| Privileges Required | "Upload Subtitles" permission |
| Default Port | 8096/TCP |
| GitHub Advisory | GHSA-9p5f-5x8v-x65m |
| Patch Status | Available and released |
| Exploit Public | Yes |
| Version | Status | Notes |
|---|
| < 10.8.0 | Vulnerable | Original vulnerability present |
| 10.8.0 - 10.11.6 | Vulnerable | Path traversal and RCE possible |
| 10.11.7+ | Patched | Format field properly validated |
| 10.12.0+ | Patched | Latest version with security fixes |