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)https://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 的詳細的作用可以參考這篇文章:https://blog.csdn.net/cabbage2008/article/details/50522190
針對ffmpeg 3.1.2版本的漏洞分析
先介紹一下ffmpeg是怎樣把一個輸入文件轉碼成另一種格式的視頻文件,流程圖如下:(圖片引用自https://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:0
### echoing b'XBIN\x1a \x00\x0f\x00\x10\x04\x01\x00\x00\x00\x00'
#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'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\xbf\n'#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'XBIN\x1a \x00\x0f\x00\x10\x04\x01\x00\x00\x00\x00', 和漏洞作者在注釋中給出的一樣。前四個字節 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/242831這個洞的 fix 又提出了另一種 bypass 方式https://hackerone.com/reports/243470。這個方式比起使用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改成https://vul.com:21進行ssrf攻擊是行不通的。
對官方補丁的分析
補丁鏈接:https://github.com/FFmpeg/FFmpeg/commit/189ff4219644532bdfa7bab28dfedaee4d6d4021
官方對這個漏洞的修複也很簡單,隻是對播放列表中 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 all\n",
+ url);
+ return AVERROR_INVALIDDATA;
+ }
+ } elseif (av_strstart(proto_name, "http", NULL)) {
+ ;
+ } else
return AVERROR_INVALIDDATA;
+
//...
}
總結
這個漏洞和以往的核心是m3u8文件可以根據指定url獲取圖片文字,而它裏麵的http協議和file協議沒有過濾好,導致可以ssrf和讀取任意文件,以前的漏洞是利用concat可以把泄露的信息傳回給攻擊者,這個是通過播放視頻,可以把信息展示給攻擊者。以後會有可能用別的方式觸發ffmpeg的別的流程,繞過file協議的過濾。
參考資料
https://blogs.360.cn/blog/ffmpegs_security_discussion/
https://github.com/radman1/xbin
https://hackerone.com/reports/243470
https://static.hx99.net/static/drops/papers-15598.html
https://tools.ietf.org/html/draft-pantos-http-live-streaming-18
https://blog.csdn.net/cabbage2008/article/details/50522190
https://blogs.360.cn/blog/ffmpegs_security_discussion/
XBIN 文件格式的詳細介紹:https://en.wikipedia.org/wiki/XBin
--------------------------------------------------------
* 作者:棧長、胖苗、超六@螞蟻金服,更多安全知識分享和熱點信息,請關注阿裏聚安全的官方博客
最後更新:2017-08-13 22:39:19