FFmpeg任意文件讀取漏洞分析
6月24號的時候hackerone網站上公布了一個ffmpeg的本地文件泄露的漏洞,可以影響ffmpeg很多版本,包括3.2.2、3.2.5、3.1.2、2.6.8等等。
hackerone網站上的漏洞介紹: https://hackerone.com/reports/226756。與之相關聯的還有其他幾篇hackerone上的漏洞報告:https://hackerone.com/reports/243470、https://hackerone.com/reports/242831)
實際上這個漏洞@EmilLerner和@PavelCheremushkin最早在今年的phdays大會上就已經披露了,視頻可以在https://www.phdays.com/broadcast/搜索Attacks on video converter得到,不過是全俄語的,暫時沒有英文字母。也可以參考漏洞作者在phday大會上的ppt
作者實際上在5月份的時候就發現了該漏洞並提交,6月26號的時候公布出來,最新的ffmpeg版本3.3.2中已經修復了,但是舊的版本不保證。
背景介紹
FFmpeg是一套目前非常流行的可以用來記錄、轉換數字音頻、視頻,並能將其轉化為流的開源計算機程序。它提供了錄製、轉換以及流化音視頻的完整解決方案。目前有非常多的視音頻軟體或是視頻網站、手機 APP 都採用了這個庫,但是這個庫歷史上曝出的漏洞也非常之多。這次的漏洞是利用了ffmpeg可以處理 HLS 播放列表的功能,在 AVI 文件中的 GAB2字幕塊中嵌入了一個 HLS 文件,然後提供給ffmpeg進行轉碼,在解析的過程中把它當做一個 XBIN 的視頻流來處理,再通過 XBIN 的編解碼器把本地的文件包含進來,最後放在轉碼後的視頻文件當中。
漏洞重現方法
1) 下載腳本 https://github.com/neex/ffmpeg-avi-m3u-xbin/blob/master/gen_xbin_avi.py
2) 運行腳本:python3 gen_xbin_avi.py file:///etc/passwd sxcurity.avi
3) 訪問https://arxius.io,上傳sxcurity.avi
4) 等待上傳處理視頻
5) 錄像處理完成後,點擊播放就可以看到/etc/passwd的內容
溫故而知新
這次的漏洞實際上與之前曝出的一個 CVE 非常之類似,可以說是舊瓶裝新酒,老樹開新花。
之前漏洞的一篇分析文章:SSRF 和本地文件泄露(CVE-2016-1897/8)http://static.hx99.net/static/drops/papers-15598.html
這個漏洞實際上也是利用了ffmpeg在處理 HLS 播放列表文件的過程中,由於支持非常多的協議,如http、file、concat等等,導致可以構造惡意的url造成 SSRF 攻擊和本地文件泄露。下面這幅圖介紹了整個的攻擊流程。
官方對這個漏洞的修復是把concat協議加入了黑名單並在結構體中加入了protocol_blacklist和protocol_whitelist這兩個域。但是這次的漏洞利用了內嵌在字幕塊中的 m3u8文件成功繞過了ffmpeg的某些限制。
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/cabbage2008/article/details/50522190
針對ffmpeg 3.1.2版本的漏洞分析
先介紹一下ffmpeg是怎樣把一個輸入文件轉碼成另一種格式的視頻文件,流程圖如下:(圖片引用自http://blog.csdn.net/leixiaohua1020/article/details/25422685)
然後我們觀察一下攻擊者提供的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:0n### echoing bXBINx1a x00x0fx00x10x04x01x00x00x00x00n#EXT-X-KEY: METHOD=AES-128, URI=/dev/zero, IV=0x4c4d465e0b95223279487316ffd9ec3an#EXTINF:1,n#EXT-X-BYTERANGE: 16n/dev/zeron#EXT-X-KEY: METHOD=NONEn### echoing bx00x00x00x00x00x00x00x00x00x01x00x00x00x00xbfnn#EXT-X-KEY: METHOD=AES-128, URI=/dev/zero, IV=0x140f0f1011b5223d79597717ffd95330n
以#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 進行解密後的結果是bXBINx1a x00x0fx00x10x04x01x00x00x00x00, 和漏洞作者在注釋中給出的一樣。前四個位元組 XBIN 就是 XBIN 的文件頭。從而使得ffmpeg判定這個播放列表的格式是 XBIN。
在open_input_file的函數結尾,程序會調用av_dump_format函數列印格式信息,列印結果如下
Input #0, avi, from ../sxcurity.avi:n Duration: 00:00:05.00, start: 0.000000, bitrate: 547 kb/sn Stream #0:0: Video: xbin, pal8, 256x240, 25 tbr, 25 tbn, 25 tbcn
可以看到文件中只有一個視頻流,視頻流的格式編碼格式被識別成了xbin
在打開輸入文件和打開輸出文件之後,程序就要開始進行轉碼工作了
在進行轉碼的過程中,我們可以在get_input_packet函數上下斷點,該函數的作用是獲取輸入的一幀壓縮的視頻數據放在AVPacket中。
gdb-peda$ btn#0 get_input_packet (f=0x6dfc00, pkt=0x7fffffffe460) at ffmpeg.c:3663n#1 0x000000000042d40c in process_input (file_index=0x0) at ffmpeg.c:3798n#2 0x000000000042eed2 in transcode_step () at ffmpeg.c:4108n#3 0x000000000042effc in transcode () at ffmpeg.c:4162n#4 0x000000000042f716 in main (argc=0x4, argv=0x7fffffffea48) at ffmpeg.c:4357n#5 0x00007ffff48a543a in __libc_start_main () from /usr/lib/libc.so.6n#6 0x0000000000407ada in _start ()n
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/242831這個洞的 fix 又提出了另一種 bypass 方式https://hackerone.com/reports/243470。這個方式比起使用xbin的方法要來的更加簡潔。
這次的 POC 生成的playlist長下面這樣
#EXTM3Un#EXT-X-MEDIA-SEQUENCE:0n#EXTINF:1.0nGOD.txtn#EXTINF:1.0n/etc/passwdn#EXT-X-ENDLISTn
翻譯一下作者在評論區的說明:
首先我們有幾點需要了解關於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/FFmpeg/commit/189ff4219644532bdfa7bab28dfedaee4d6d4021
官方對這個漏洞的修復也很簡單,只是對播放列表中 file 協議的文件擴展名使用了白名單進行過濾。一定程度上緩解了攻擊,但是還是可以泄露出那些多媒體文件。
typedefstructHLSContext {n //...省略了一下結構體的內容n + char *allowed_extensions; //增添一個欄位,限制了允許的文件擴展名n } HLSContext;nstaticintopen_url(AVFormatContext *s, AVIOContext **pb, constchar *url,nAVDictionary *opts, AVDictionary *opts2, int *is_http)n {n //...n// only http(s) & file are allowedn - if (!av_strstart(proto_name, "http", NULL) && !av_strstart(proto_name, "file", NULL))n + if (av_strstart(proto_name, "file", NULL)) {n + if (strcmp(c->allowed_extensions, "ALL") && !av_match_ext(url, c->allowed_extensions)) {n + av_log(s, AV_LOG_ERROR,n + "Filename extension of %s is not a common multimedia extension, blocked for security reasons.n"n + "If you wish to override this adjust allowed_extensions, you can set it to ALL to allow alln",n + url);n + return AVERROR_INVALIDDATA;n + }n + } elseif (av_strstart(proto_name, "http", NULL)) {n + ;n + } elsenreturn AVERROR_INVALIDDATA;n +n//...n }n
總結
這個漏洞和以往的核心是m3u8文件可以根據指定url獲取圖片文字,而它裡面的http協議和file協議沒有過濾好,導致可以ssrf和讀取任意文件,以前的漏洞是利用concat可以把泄露的信息傳回給攻擊者,這個是通過播放視頻,可以把信息展示給攻擊者。以後會有可能用別的方式觸發ffmpeg的別的流程,繞過file協議的過濾。
參考資料
http://blogs.360.cn/blog/ffmpegs_security_discussion/
https://github.com/radman1/xbin
https://hackerone.com/reports/243470
http://static.hx99.net/static/drops/papers-15598.html
https://tools.ietf.org/html/draft-pantos-http-live-streaming-18#userconsent
http://blog.csdn.net/cabbage2008/article/details/50522190
http://blogs.360.cn/blog/ffmpegs_security_discussion/
XBIN 文件格式的詳細介紹:https://en.wikipedia.org/wiki/XBin
--------------------------------------------------------
* 作者:棧長、胖苗、超六@螞蟻金服,更多安全知識分享和熱點信息,請關注阿里聚安全的安全專欄和官方博客
推薦閱讀:
※使用FFmpeg在B站直播的姿勢
※想了解視頻編碼,流媒體,直播,播放器等知識,求推薦一些書籍?
※FFmpeg為什麼遲遲不啟用vaapi解碼/編碼?
※yuv和rgb各有什麼優點?