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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
From 9803800e0e8cd8e1e7695f77cfbf4e0db0abfe57 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <michael@niedermayer.cc>
Date: Thu, 16 Jan 2025 01:28:46 +0100
Subject: [PATCH] avformat/hls: Be more picky on extensions
This blocks disallowed extensions from probing
It also requires all available segments to have matching extensions to the format
mpegts is treated independent of the extension
It is recommended to set the whitelists correctly
instead of depending on extensions, but this should help a bit,
and this is easier to backport
Fixes: CVE-2023-6602 II. HLS Force TTY Demuxer
Fixes: CVE-2023-6602 IV. HLS XBIN Demuxer DoS Amplification
The other parts of CVE-2023-6602 have been fixed by prior commits
Found-by: Harvey Phillips of Amazon Element55 (element55)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
(cherry picked from commit 91d96dc8ddaebe0b6cb393f672085e6bfaf15a31)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
CVE: CVE-2023-6602 CVE-2023-6604 CVE-2023-6605
Upstream-Status: Backport [https://github.com/FFmpeg/FFmpeg/commit/9803800e0e8cd8e1e7695f77cfbf4e0db0abfe57]
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
---
doc/demuxers.texi | 7 +++++++
libavformat/hls.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+)
diff --git a/doc/demuxers.texi b/doc/demuxers.texi
index 26ae768..6e0b25e 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -365,6 +365,13 @@ segment index to start live streams at (negative values are from the end).
@item allowed_extensions
',' separated list of file extensions that hls is allowed to access.
+@item extension_picky
+This blocks disallowed extensions from probing
+It also requires all available segments to have matching extensions to the format
+except mpegts, which is always allowed.
+It is recommended to set the whitelists correctly instead of depending on extensions
+Enabled by default.
+
@item max_reload
Maximum number of times a insufficient list is attempted to be reloaded.
Default value is 1000.
diff --git a/libavformat/hls.c b/libavformat/hls.c
index d5e9b21..e1bb677 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -214,6 +214,7 @@ typedef struct HLSContext {
AVDictionary *avio_opts;
AVDictionary *seg_format_opts;
char *allowed_extensions;
+ int extension_picky;
int max_reload;
int http_persistent;
int http_multiple;
@@ -716,6 +717,40 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
return ret;
}
+static int test_segment(AVFormatContext *s, const AVInputFormat *in_fmt, struct playlist *pls, struct segment *seg)
+{
+ HLSContext *c = s->priv_data;
+ int matchA = 3;
+ int matchF = 0;
+
+ if (!c->extension_picky)
+ return 0;
+
+ if (strcmp(c->allowed_extensions, "ALL"))
+ matchA = av_match_ext (seg->url, c->allowed_extensions)
+ + 2*(ff_match_url_ext(seg->url, c->allowed_extensions) > 0);
+
+ if (!matchA) {
+ av_log(s, AV_LOG_ERROR, "URL %s is not in allowed_extensions\n", seg->url);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (in_fmt) {
+ if (in_fmt->extensions) {
+ matchF = av_match_ext( seg->url, in_fmt->extensions)
+ + 2*(ff_match_url_ext(seg->url, in_fmt->extensions) > 0);
+ } else if (!strcmp(in_fmt->name, "mpegts"))
+ matchF = 3;
+
+ if (!(matchA & matchF)) {
+ av_log(s, AV_LOG_ERROR, "detected format extension %s mismatches allowed extensions in url %s\n", in_fmt->extensions ? in_fmt->extensions : "none", seg->url);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ return 0;
+}
+
static int parse_playlist(HLSContext *c, const char *url,
struct playlist *pls, AVIOContext *in)
{
@@ -959,6 +994,14 @@ static int parse_playlist(HLSContext *c, const char *url,
goto fail;
}
+ ret = test_segment(c->ctx, pls->ctx ? pls->ctx->iformat : NULL, pls, seg);
+ if (ret < 0) {
+ av_free(seg->url);
+ av_free(seg->key);
+ av_free(seg);
+ goto fail;
+ }
+
if (duration < 0.001 * AV_TIME_BASE) {
av_log(c->ctx, AV_LOG_WARNING, "Cannot get correct #EXTINF value of segment %s,"
" set to default value to 1ms.\n", seg->url);
@@ -2040,6 +2083,11 @@ static int hls_read_header(AVFormatContext *s)
pls->ctx->interrupt_callback = s->interrupt_callback;
url = av_strdup(pls->segments[0]->url);
ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0);
+
+ for (int n = 0; n < pls->n_segments; n++)
+ if (ret >= 0)
+ ret = test_segment(s, in_fmt, pls, pls->segments[n]);
+
if (ret < 0) {
/* Free the ctx - it isn't initialized properly at this point,
* so avformat_close_input shouldn't be called. If
@@ -2467,6 +2515,8 @@ static const AVOption hls_options[] = {
OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
{.str = "3gp,aac,avi,ac3,eac3,flac,mkv,m3u8,m4a,m4s,m4v,mpg,mov,mp2,mp3,mp4,mpeg,mpegts,ogg,ogv,oga,ts,vob,wav"},
INT_MIN, INT_MAX, FLAGS},
+ {"extension_picky", "Be picky with all extensions matching",
+ OFFSET(extension_picky), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS},
{"max_reload", "Maximum number of times a insufficient list is attempted to be reloaded",
OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, FLAGS},
{"m3u8_hold_counters", "The maximum number of times to load m3u8 when it refreshes without new segments",
--
2.40.0
|