Skip to content

Commit b753bac

Browse files
committed
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 91d96dc) Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
1 parent 9abf2ae commit b753bac

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed

doc/demuxers.texi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,13 @@ prefer to use #EXT-X-START if it's in playlist instead of live_start_index.
564564
@item allowed_extensions
565565
',' separated list of file extensions that hls is allowed to access.
566566

567+
@item extension_picky
568+
This blocks disallowed extensions from probing
569+
It also requires all available segments to have matching extensions to the format
570+
except mpegts, which is always allowed.
571+
It is recommended to set the whitelists correctly instead of depending on extensions
572+
Enabled by default.
573+
567574
@item max_reload
568575
Maximum number of times a insufficient list is attempted to be reloaded.
569576
Default value is 1000.

libavformat/hls.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ typedef struct HLSContext {
223223
AVDictionary *avio_opts;
224224
AVDictionary *seg_format_opts;
225225
char *allowed_extensions;
226+
int extension_picky;
226227
int max_reload;
227228
int http_persistent;
228229
int http_multiple;
@@ -731,6 +732,40 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
731732
return ret;
732733
}
733734

735+
static int test_segment(AVFormatContext *s, const AVInputFormat *in_fmt, struct playlist *pls, struct segment *seg)
736+
{
737+
HLSContext *c = s->priv_data;
738+
int matchA = 3;
739+
int matchF = 0;
740+
741+
if (!c->extension_picky)
742+
return 0;
743+
744+
if (strcmp(c->allowed_extensions, "ALL"))
745+
matchA = av_match_ext (seg->url, c->allowed_extensions)
746+
+ 2*(ff_match_url_ext(seg->url, c->allowed_extensions) > 0);
747+
748+
if (!matchA) {
749+
av_log(s, AV_LOG_ERROR, "URL %s is not in allowed_extensions\n", seg->url);
750+
return AVERROR_INVALIDDATA;
751+
}
752+
753+
if (in_fmt) {
754+
if (in_fmt->extensions) {
755+
matchF = av_match_ext( seg->url, in_fmt->extensions)
756+
+ 2*(ff_match_url_ext(seg->url, in_fmt->extensions) > 0);
757+
} else if (!strcmp(in_fmt->name, "mpegts"))
758+
matchF = 3;
759+
760+
if (!(matchA & matchF)) {
761+
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);
762+
return AVERROR_INVALIDDATA;
763+
}
764+
}
765+
766+
return 0;
767+
}
768+
734769
static int parse_playlist(HLSContext *c, const char *url,
735770
struct playlist *pls, AVIOContext *in)
736771
{
@@ -989,6 +1024,14 @@ static int parse_playlist(HLSContext *c, const char *url,
9891024
goto fail;
9901025
}
9911026

1027+
ret = test_segment(c->ctx, pls->ctx ? pls->ctx->iformat : NULL, pls, seg);
1028+
if (ret < 0) {
1029+
av_free(seg->url);
1030+
av_free(seg->key);
1031+
av_free(seg);
1032+
goto fail;
1033+
}
1034+
9921035
if (duration < 0.001 * AV_TIME_BASE) {
9931036
av_log(c->ctx, AV_LOG_WARNING, "Cannot get correct #EXTINF value of segment %s,"
9941037
" set to default value to 1ms.\n", seg->url);
@@ -2114,6 +2157,11 @@ static int hls_read_header(AVFormatContext *s)
21142157
pls->ctx->interrupt_callback = s->interrupt_callback;
21152158
url = av_strdup(pls->segments[0]->url);
21162159
ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0);
2160+
2161+
for (int n = 0; n < pls->n_segments; n++)
2162+
if (ret >= 0)
2163+
ret = test_segment(s, in_fmt, pls, pls->segments[n]);
2164+
21172165
if (ret < 0) {
21182166
/* Free the ctx - it isn't initialized properly at this point,
21192167
* so avformat_close_input shouldn't be called. If
@@ -2576,6 +2624,8 @@ static const AVOption hls_options[] = {
25762624
OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
25772625
{.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"},
25782626
INT_MIN, INT_MAX, FLAGS},
2627+
{"extension_picky", "Be picky with all extensions matching",
2628+
OFFSET(extension_picky), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS},
25792629
{"max_reload", "Maximum number of times a insufficient list is attempted to be reloaded",
25802630
OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 3}, 0, INT_MAX, FLAGS},
25812631
{"m3u8_hold_counters", "The maximum number of times to load m3u8 when it refreshes without new segments",

0 commit comments

Comments
 (0)