FFmpeg任意文件讀取漏洞分析

6月24號的時候hackerone網站上公佈了一個ffmpeg的本地文件泄露的漏洞,能夠影響ffmpeg不少版本,包括3.2.二、3.2.五、3.1.二、2.6.8等等。html

hackerone網站上的漏洞介紹: https://hackerone.com/reports...。與之相關聯的還有其餘幾篇hackerone上的漏洞報告:https://hackerone.com/reports...https://hackerone.com/reports...python

實際上這個漏洞@EmilLerner和@PavelCheremushkin最先在今年的phdays大會上就已經披露了,視頻能夠在https://www.phdays.com/broadc...搜索Attacks on video converter獲得,不過是全俄語的,暫時沒有英文字母。也能夠參考漏洞做者在phday大會上的pptgit

做者實際上在5月份的時候就發現了該漏洞並提交,6月26號的時候公佈出來,最新的ffmpeg版本3.3.2中已經修復了,可是舊的版本不保證。github

背景介紹
FFmpeg是一套目前很是流行的能夠用來記錄、轉換數字音頻、視頻,並能將其轉化爲流的開源計算機程序。它提供了錄製、轉換以及流化音視頻的完整解決方案。目前有很是多的視音頻軟件或是視頻網站、手機 APP 都採用了這個庫,可是這個庫歷史上曝出的漏洞也很是之多。此次的漏洞是利用了ffmpeg能夠處理 HLS 播放列表的功能,在 AVI 文件中的 GAB2字幕塊中嵌入了一個 HLS 文件,而後提供給ffmpeg進行轉碼,在解析的過程當中把它當作一個 XBIN 的視頻流來處理,再經過 XBIN 的編解碼器把本地的文件包含進來,最後放在轉碼後的視頻文件當中。安全

漏洞重現方法
1) 下載腳本 https://github.com/neex/ffmpe...
2) 運行腳本:python3 gen_xbin_avi.py file:///etc/passwd sxcurity.avi
3) 訪問https://arxius.io,上傳sxcurity.avi
4) 等待上傳處理視頻
5) 錄像處理完成後,點擊播放就能夠看到/etc/passwd的內容服務器

溫故而知新
此次的漏洞實際上與以前曝出的一個 CVE 很是之相似,能夠說是舊瓶裝新酒,老樹開新花。ide

以前漏洞的一篇分析文章:
SSRF 和本地文件泄露(CVE-2016-1897/8)http://static.hx99.net/static...函數

這個漏洞實際上也是利用了ffmpeg在處理 HLS 播放列表文件的過程當中,因爲支持很是多的協議,如http、file、concat等等,致使能夠構造惡意的url形成 SSRF 攻擊和本地文件泄露。下面這幅圖介紹了整個的攻擊流程。
圖片描述網站

官方對這個漏洞的修復是把concat協議加入了黑名單並在結構體中加入了protocol_blacklist和protocol_whitelist這兩個域。可是此次的漏洞利用了內嵌在字幕塊中的 m3u8文件成功繞過了ffmpeg的某些限制。this

HLS 協議簡單介紹
HLS(HTTP Live Streaming)是蘋果公司針對iPhone、iPod、iTouch和iPad等移動設備而開發的基於HTTP協議的流媒體解決方案。在 HLS 技術中 Web 服務器向客戶端提供接近實時的音視頻流。但在使用的過程當中是使用的標準的 HTTP 協議,因此這時,只要使用 HLS 的技術,就能在普通的 HTTP 的應用上直接提供點播和直播。該技術基本原理是將視頻文件或視頻流切分紅小片(ts)並創建索引文件(m3u8)。客戶端會先向服務器請求 m3u8索引文件,而後根據索引文件裏面的url去請求真正的ts視頻文件。若是是多級的m3u8索引的話,那就會從根索引文件開始,一層一層的往下去請求子的索引文件,獲取最終的TS流文件的http請求地址與時間段。

M3U8文件中有不少TAG,每個 TAG 的詳細的做用能夠參考這篇文章:http://blog.csdn.net/cabbage2...

針對ffmpeg 3.1.2版本的漏洞分析
先介紹一下ffmpeg是怎樣把一個輸入文件轉碼成另外一種格式的視頻文件,流程圖以下:(圖片引用自http://blog.csdn.net/leixiaoh...
圖片描述

而後咱們觀察一下攻擊者提供的poc生成的sxcurity.avi文件的結構
圖片描述

最開始是 AVI 文件的文件頭,而後中間有一個 GAB2字幕的文件頭「GAB2」,在 GAB2的文件頭後面還有 HLS 播放列表的文件頭"#EXTM3U",在文件頭後面就是 HLS 的文件內容了。

此次的漏洞主要是利用了ffmpeg處理 GAB2字幕塊的時候的邏輯錯誤,因此咱們重點從ffmpeg打開輸入文件時調用 read_gab2sub()這個函數開始分析,read_gab2sub() 函數是被avformat_find_stream_info() 這個函數調用的。整個函數調用流程圖以下:
圖片描述

咱們能夠看到在爲播放列表肯定demuxer的時候探測出格式爲XBIN,可是單看sxcurity.avi文件中並無 XBIN 的文件頭,那麼ffmpeg是怎麼肯定格式是 XBIN 的呢。這就與 HLS 的解析過程密切相關了。在判斷一個播放列表的格式時候,ffmpeg會讀取播放列表的第一個segment,而後根據這個 segment 的url去請求文件內容,而後根據讀取到的文件內容來判斷格式。

咱們能夠看到sxcurity.avi裏面內嵌的 playlist 的前幾行是這樣寫的

EXT-X-MEDIA-SEQUENCE:0

echoing b'XBINx1a x00x0fx00x10x04x01x00x00x00x00'

EXT-X-KEY: METHOD=AES-128, URI=/dev/zero, IV=0x4c4d465e0b95223279487316ffd9ec3a

EXTINF:1,

EXT-X-BYTERANGE: 16

/dev/zero

EXT-X-KEY: METHOD=NONE

echoing b'x00x00x00x00x00x00x00x00x00x01x00x00x00x00xbfn'

EXT-X-KEY: METHOD=AES-128, URI=/dev/zero, IV=0x140f0f1011b5223d79597717ffd95330

以#EXT開頭的都是m3u8文件中的標籤,其餘以#開頭的是註釋,不以#開頭的是一個url

介紹一下上面出現的標籤的含義

EXT-X-MEDIA-SEQUENCE:每個media URI 在 PlayList中只有惟一的序號,相鄰之間序號+1, 一個media URI並非必需要包含的,若是沒有,默認爲0。

EXTINF: duration 指定每一個媒體段(ts)的持續時間(秒),僅對其後面的URI有效。

EXT-X-KEY表示怎麼對mediasegments進行解碼。其做用範圍是下次該tag出現前的全部mediaURI,屬性爲NONE或者 AES-128。對於AES-128的狀況,URI屬性表示一個key文件,經過URI能夠得到這個key,若是沒有IV(Initialization Vector),則使用序列號做爲IV進行編解碼;若是有IV,則將改值當成16個字節的16進制數。

那麼上面幾行代碼包括了兩個segment,每個 segment 都須要使用 AES-128進行解密,密文是從/dev/zero這個 url獲取的16個字節,其實就是16個'0', key 也是從/dev/zero這個url獲取的16個字節,16個'0',IV是本身指定的一串十六進制數。ffmpeg對第一個 segment 進行解密後的結果是b'XBINx1a x00x0fx00x10x04x01x00x00x00x00', 和漏洞做者在註釋中給出的同樣。前四個字節 XBIN 就是 XBIN 的文件頭。從而使得ffmpeg斷定這個播放列表的格式是 XBIN。

在open_input_file的函數結尾,程序會調用av_dump_format函數打印格式信息,打印結果以下
Input #0, avi, from '../sxcurity.avi':
Duration: 00:00:05.00, start: 0.000000, bitrate: 547 kb/s

Stream #0:0: Video: xbin, pal8, 256x240, 25 tbr, 25 tbn, 25 tbc

能夠看到文件中只有一個視頻流,視頻流的格式編碼格式被識別成了xbin
在打開輸入文件和打開輸出文件以後,程序就要開始進行轉碼工做了
在進行轉碼的過程當中,咱們能夠在get_input_packet函數上下斷點,該函數的做用是獲取輸入的一幀壓縮的視頻數據放在AVPacket中。

gdb-peda$ bt

0 get_input_packet (f=0x6dfc00, pkt=0x7fffffffe460) at ffmpeg.c:3663

1 0x000000000042d40c in process_input (file_index=0x0) at ffmpeg.c:3798

2 0x000000000042eed2 in transcode_step () at ffmpeg.c:4108

3 0x000000000042effc in transcode () at ffmpeg.c:4162

4 0x000000000042f716 in main (argc=0x4, argv=0x7fffffffea48) at ffmpeg.c:4357

5 0x00007ffff48a543a in __libc_start_main () from /usr/lib/libc.so.6

6 0x0000000000407ada in _start ()

get_input_packet() 調用結束以後打印pkt的內容,能夠看到裏面已經有了/etc/passwd文件的開頭的部分信息。

圖片描述

而後 ffmpeg會轉碼成mpeg2的編碼格式,最後封裝成一個 mp4文件。打開 MP4文件,咱們就能看到泄露出的文件內容了。

不轉碼的avi文件也是能夠播放的,只是他播放的文件是當前調用ffmpeg的應用,例如你用愛奇藝打開就是調用愛奇藝的播放器,而愛奇藝是用了ffmpeg代碼,就能夠讀到愛奇藝應用的的內部文件,就形成信息泄露,由於這些文件是沒有權限讀取的。

例如能夠讀到data/data/com.qiyi.video/shared_prefs/cn.com.mma.mobile.tracking.sdkconfig.xml

而轉碼的目的就是把你上傳視頻的服務器文件寫死在轉碼後的文件,就是你不管轉成.mp4,flv均可以播放的。

另外一種攻擊方式
攻擊者針對https://hackerone.com/reports...這個洞的 fix 又提出了另外一種 bypass 方式https://hackerone.com/reports...。這個方式比起使用xbin的方法要來的更加簡潔。

此次的 POC 生成的playlist長下面這樣

EXTM3U

EXT-X-MEDIA-SEQUENCE:0

EXTINF:1.0

GOD.txt

EXTINF:1.0

/etc/passwd

EXT-X-ENDLIST

翻譯一下做者在評論區的說明:
首先咱們有幾點須要瞭解關於HLS playlist 是怎麼被處理的:
1.處理一個 playlist 的時候ffmpeg把全部 segment 的內容鏈接在一塊兒而後當作一個單獨的文件來處理
2.ffmpeg會使用 playlist 第一個 segment 來決定文件的類型
3.ffmpeg用一種特殊的方式來處理.txt後綴的文件,它會嘗試將文件的內容以終端的方式打印在屏幕上

因此上面的 playlist 的處理流程是這樣的:
1.ffmpeg在 GAB2字幕塊裏面看到了#EXTM3U標籤,認定文件類型是 HLS playlist。
2.GOD.txt這個文件甚至不須要存在,可是它的名字足夠ffmpeg把文件類型檢測成txt類型了
3.ffmpeg把 playlist 的全部 segment 的內容鏈接在一塊兒,由於只有/etc/passwd這個文件是實際存在的,因此最後的內容就是/etc/passwd文件的內容
4.由於這個文件的類型是 txt 類型,因此ffmpeg繪製一個終端來打印這個文件。

須要注意的是在解析 playlist 文件的時候,每個 segment 的url協議d的白名單爲'file, crypto',因此這裏直接把/etc/passwd改爲http://vul.com:21進行ssrf攻擊是行不通的。

對官方補丁的分析
補丁連接:https://github.com/FFmpeg/FFm...
官方對這個漏洞的修復也很簡單,只是對播放列表中 file 協議的文件擴展名使用了白名單進行過濾。必定程度上緩解了攻擊,可是仍是能夠泄露出那些多媒體文件。

typedefstructHLSContext {

//...省略了一下結構體的內容
  • char *allowed_extensions; //增添一個字段,限制了容許的文件擴展名
    } HLSContext;

staticintopen_url(AVFormatContext s, AVIOContext pb, constchar url,
AVDictionary opts, AVDictionary opts2, int *is_http)
{

//...

// only http(s) & file are allowed

    • 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(s, 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 alln",
    • url);
    • return AVERROR_INVALIDDATA;
    • }
    • } elseif (av_strstart(proto_name, "http", NULL)) {
    • ;
    • } else
    1. AVERROR_INVALIDDATA;

    +
    //...
    }

    總結
    這個漏洞和以往的核心是m3u8文件能夠根據指定url獲取圖片文字,而它裏面的http協議和file協議沒有過濾好,致使能夠ssrf和讀取任意文件,之前的漏洞是利用concat能夠把泄露的信息傳回給攻擊者,這個是經過播放視頻,能夠把信息展現給攻擊者。之後會有可能用別的方式觸發ffmpeg的別的流程,繞過file協議的過濾。

    參考資料
    http://blogs.360.cn/blog/ffmp...
    https://github.com/radman1/xbin
    https://hackerone.com/reports...
    http://static.hx99.net/static...
    https://tools.ietf.org/html/d...
    http://blog.csdn.net/cabbage2...
    http://blogs.360.cn/blog/ffmp...
    XBIN 文件格式的詳細介紹:https://en.wikipedia.org/wiki...


    • 做者:棧長、胖苗、超六@螞蟻金服,更多安全知識分享和熱點信息,請關注阿里聚安全的官方博客
    相關文章
    相關標籤/搜索