forked from FFmpeg/FFmpeg
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>
This commit is contained in:
parent
c733e2b5ed
commit
91d96dc8dd
2 changed files with 57 additions and 0 deletions
|
@ -564,6 +564,13 @@ prefer to use #EXT-X-START if it's in playlist instead of live_start_index.
|
||||||
@item allowed_extensions
|
@item allowed_extensions
|
||||||
',' separated list of file extensions that hls is allowed to access.
|
',' 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
|
@item max_reload
|
||||||
Maximum number of times a insufficient list is attempted to be reloaded.
|
Maximum number of times a insufficient list is attempted to be reloaded.
|
||||||
Default value is 1000.
|
Default value is 1000.
|
||||||
|
|
|
@ -223,6 +223,7 @@ typedef struct HLSContext {
|
||||||
AVDictionary *avio_opts;
|
AVDictionary *avio_opts;
|
||||||
AVDictionary *seg_format_opts;
|
AVDictionary *seg_format_opts;
|
||||||
char *allowed_extensions;
|
char *allowed_extensions;
|
||||||
|
int extension_picky;
|
||||||
int max_reload;
|
int max_reload;
|
||||||
int http_persistent;
|
int http_persistent;
|
||||||
int http_multiple;
|
int http_multiple;
|
||||||
|
@ -731,6 +732,40 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
|
||||||
return ret;
|
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,
|
static int parse_playlist(HLSContext *c, const char *url,
|
||||||
struct playlist *pls, AVIOContext *in)
|
struct playlist *pls, AVIOContext *in)
|
||||||
{
|
{
|
||||||
|
@ -989,6 +1024,14 @@ static int parse_playlist(HLSContext *c, const char *url,
|
||||||
goto fail;
|
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) {
|
if (duration < 0.001 * AV_TIME_BASE) {
|
||||||
av_log(c->ctx, AV_LOG_WARNING, "Cannot get correct #EXTINF value of segment %s,"
|
av_log(c->ctx, AV_LOG_WARNING, "Cannot get correct #EXTINF value of segment %s,"
|
||||||
" set to default value to 1ms.\n", seg->url);
|
" set to default value to 1ms.\n", seg->url);
|
||||||
|
@ -2114,6 +2157,11 @@ static int hls_read_header(AVFormatContext *s)
|
||||||
pls->ctx->interrupt_callback = s->interrupt_callback;
|
pls->ctx->interrupt_callback = s->interrupt_callback;
|
||||||
url = av_strdup(pls->segments[0]->url);
|
url = av_strdup(pls->segments[0]->url);
|
||||||
ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0);
|
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) {
|
if (ret < 0) {
|
||||||
/* Free the ctx - it isn't initialized properly at this point,
|
/* Free the ctx - it isn't initialized properly at this point,
|
||||||
* so avformat_close_input shouldn't be called. If
|
* so avformat_close_input shouldn't be called. If
|
||||||
|
@ -2576,6 +2624,8 @@ static const AVOption hls_options[] = {
|
||||||
OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
|
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"},
|
{.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},
|
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",
|
{"max_reload", "Maximum number of times a insufficient list is attempted to be reloaded",
|
||||||
OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 3}, 0, INT_MAX, FLAGS},
|
OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 3}, 0, INT_MAX, FLAGS},
|
||||||
{"m3u8_hold_counters", "The maximum number of times to load m3u8 when it refreshes without new segments",
|
{"m3u8_hold_counters", "The maximum number of times to load m3u8 when it refreshes without new segments",
|
||||||
|
|
Loading…
Add table
Reference in a new issue