From 7e6b64a7d9a2dd64e652b3e42a741b673cda3a26 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Sat, 3 Jun 2017 21:20:04 +0200 Subject: [PATCH] avformat/hls: Check local file extensions This reduces the attack surface of local file-system information leaking. It prevents the existing exploit leading to an information leak. As well as similar hypothetical attacks. Leaks of information from files and symlinks ending in common multimedia extensions are still possible. But files with sensitive information like private keys and passwords generally do not use common multimedia filename extensions. It does not stop leaks via remote addresses in the LAN. The existing exploit depends on a specific decoder as well. It does appear though that the exploit should be possible with any decoder. The problem is that as long as sensitive information gets into the decoder, the output of the decoder becomes sensitive as well. The only obvious solution is to prevent access to sensitive information. Or to disable hls or possibly some of its feature. More complex solutions like checking the path to limit access to only subdirectories of the hls path may work as an alternative. But such solutions are fragile and tricky to implement portably and would not stop every possible attack nor would they work with all valid hls files. Developers have expressed their dislike / objected to disabling hls by default as well as disabling hls with local files. There also where objections against restricting remote url file extensions. This here is a less robust but also lower inconvenience solution. It can be applied stand alone or together with other solutions. limiting the check to local files was suggested by nevcairiel This recommits the security fix without the author name joke which was originally requested by Nicolas. Found-by: Emil Lerner and Pavel Cheremushkin Reported-by: Thierry Foucu Signed-off-by: Michael Niedermayer (cherry picked from commit 189ff4219644532bdfa7bab28dfedaee4d6d4021) Signed-off-by: Michael Niedermayer --- libavformat/hls.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/libavformat/hls.c b/libavformat/hls.c index 6c6a0029df..17e1079b05 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -164,6 +164,7 @@ struct variant { }; typedef struct HLSContext { + AVClass *class; int n_variants; struct variant **variants; int n_playlists; @@ -179,6 +180,7 @@ typedef struct HLSContext { char *user_agent; ///< holds HTTP user agent set as an AVOption to the HTTP protocol context char *cookies; ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context char *headers; ///< holds HTTP headers set as an AVOption to the HTTP protocol context + char *allowed_extensions; } HLSContext; static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) @@ -901,13 +903,23 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, } -static int check_url(const char *url) { +static int check_url(HLSContext *c, const char *url) { const char *proto_name = avio_find_protocol_name(url); if (!proto_name) return AVERROR_INVALIDDATA; - if (!av_strstart(proto_name, "http", NULL) && !av_strstart(proto_name, "file", NULL)) + if (av_strstart(proto_name, "file", NULL)) { + if (strcmp(c->allowed_extensions, "ALL") && !av_match_ext(url, c->allowed_extensions)) { + av_log(c, AV_LOG_ERROR, + "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n" + "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n", + url); + return AVERROR_INVALIDDATA; + } + } else if (av_strstart(proto_name, "http", NULL)) { + ; + } else return AVERROR_INVALIDDATA; if (!strncmp(proto_name, url, strlen(proto_name)) && url[strlen(proto_name)] == ':') @@ -945,7 +957,7 @@ static int open_input(HLSContext *c, struct playlist *pls) seg->url, seg->url_offset, pls->index); if (seg->key_type == KEY_NONE) { - ret = check_url(seg->url); + ret = check_url(c, seg->url); if (ret < 0) goto cleanup; @@ -956,7 +968,7 @@ static int open_input(HLSContext *c, struct playlist *pls) char iv[33], key[33], url[MAX_URL_SIZE]; if (strcmp(seg->key, pls->key_url)) { URLContext *uc; - ret = check_url(seg->key); + ret = check_url(c, seg->key); if (ret < 0) goto cleanup; @@ -1728,6 +1740,23 @@ static int hls_probe(AVProbeData *p) return 0; } +#define OFFSET(x) offsetof(HLSContext, x) +#define FLAGS AV_OPT_FLAG_DECODING_PARAM +static const AVOption hls_options[] = { + {"allowed_extensions", "List of file extensions that hls is allowed to access", + OFFSET(allowed_extensions), AV_OPT_TYPE_STRING, + {.str = "3gp,aac,avi,flac,mkv,m3u8,m4a,m4s,m4v,mpg,mov,mp2,mp3,mp4,mpeg,mpegts,ogg,ogv,oga,ts,vob,wav"}, + INT_MIN, INT_MAX, FLAGS}, + {NULL} +}; + +static const AVClass hls_class = { + .class_name = "hls,applehttp", + .item_name = av_default_item_name, + .option = hls_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVInputFormat ff_hls_demuxer = { .name = "hls,applehttp", .long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"), @@ -1737,4 +1766,5 @@ AVInputFormat ff_hls_demuxer = { .read_packet = hls_read_packet, .read_close = hls_close, .read_seek = hls_read_seek, + .priv_class = &hls_class, };