1、ZIP檔案目錄遍歷簡介
因為ZIP壓縮包檔案中允許存在“../”的字串,攻擊者可以利用多個“../”在解壓時改變ZIP包中某個檔案的存放位置,覆蓋掉應用原有的檔案。如果被覆蓋掉的檔案是動態連結so、dex或者odex檔案,輕則產生本地拒絕服務漏洞,影響應用的可用性,重則可能造成任意程式碼執行漏洞,危害使用者的裝置安全和資訊保安。比如近段時間發現的“寄生獸”漏洞、海豚瀏覽器遠端命令執行漏洞、三星預設輸入法遠端程式碼執行漏洞等都與ZIP檔案目錄遍歷有關。
阿里聚安全的應用漏洞掃描服務,可以檢測出應用的ZIP檔案目錄遍歷風險。另外我們發現日本計算機應急響應小組(JPCERT)給出的修復方案存在缺陷。如果使用不當(它提供的示例文件就使用錯誤),可能起不到防止ZIP檔案目錄遍歷的作用,並且國內有修復方案參考了此方案。
2、漏洞原理和風險示例
2.1 漏洞原理
在Linux/Unix系統中“../”代表的是向上級目錄跳轉,有些程式在當前工作目錄中處理到諸如用“../../../../../../../../../../../etc/hosts”表示的檔案,會跳轉出當前工作目錄,跳轉到到其他目錄中。
Java程式碼在解壓ZIP檔案時,會使用到ZipEntry類的getName()方法,如果ZIP檔案中包含“../”的字串,該方法返回值裡面原樣返回,如果沒有過濾掉getName()返回值中的“../”字串,繼續解壓縮操作,就會在其他目錄中建立解壓的檔案。
如我們構造的ZIP檔案中有如下檔案:
進行解壓的程式碼如下,沒有對getName進行過濾:
解壓操作時的日誌:
此ZIP檔案存放在SD卡中,想讓解壓出來的所有檔案也存在SD卡中,但是a_poc.txt檔案卻存在了應用的資料目錄中:
2.2 風險示例
以海豚瀏覽器遠端程式碼執行漏洞為例。
海豚瀏覽器的主題設定中允許使用者通過網路下載新的主題進行更換,主題檔案其實是一個ZIP壓縮檔案。通過中間人攻擊的方法可以替換掉這個ZIP檔案。替換後的ZIP檔案中有重新編譯過的libdolphin.so。此so檔案重寫了JNI_OnLoad()函式:
此so檔案以“../../../../../../../../../../data/data/mobi.mgeek.TunnyBrowser/files/libdolphin.so”的形式存在惡意ZIP檔案中。海豚瀏覽器解壓惡意ZIP檔案後,重新的libdolphin.so就會覆蓋掉原有的so檔案,重新執行海豚瀏覽器會彈出Toast提示框:
能彈出Toast說明也就可以執行其他程式碼。
這裡分析下此漏洞產生的原因是:
1、主題檔案其實是一個ZIP壓縮包,從伺服器下載後進行解壓,但是解壓時沒有過濾getName()返回的字串中是否有“../”:
2、動態連結庫檔案libdolphin.so,並沒有放在應用資料的lib目錄下,而是放在了files目錄中:
載入使用的地方是com.dolphin.browser.search.redirect包中的SearchRedirector:
應用使用的是System.load()來載入libdolphin.so而非System.loadLibrary(),在Android中,System.loadLibrary()是從應用的lib目錄中載入.so檔案,而System.load()是用某個.so檔案的絕對路徑載入,這個.so檔案可以不在應用的lib目錄中,可以在SD卡中,或者在應用的files目錄中,只要應用有讀的許可權目錄中即可。
在files目錄中,應用具有寫入許可權,通過網路中間人攻擊,同時利用ZIP檔案目錄遍歷漏洞,替換掉檔案libdolphin.so,達到遠端命令執行的目的。
應用的lib目錄是軟連結到了/data/app-lib/應用目錄,如果libdolphin.so檔案在lib目錄下就不會被覆蓋了,第三方應用在執行時沒有寫入/data/app-lib目錄的許可權:
3、JPCERT修復方案的研究
在研究中我們發現JPCERT提供的修復方案存在缺陷。它是利用Java的File類提供的getCanonicalPath()方法過濾掉zipEntry.getName()返回的字串中所包含的“../”,然後檢查這個字串是否是以要解壓到的目標目錄字串為開頭,如果是,返回getCanonicalPath()獲取到的字串,如果不是,則丟擲異常:
但是在JPCERT給出的示例程式碼中,對validateFilename()的呼叫對於APP來說不會達到防止任意目錄遍歷的目的:
其使用“.”,作為要解壓到的目的目錄,“.”表示當前目錄,經測試APP程式的當前工作目錄是根目錄“/”:
檢視程式狀態,得到的APP程式的當前工作目錄cwd是連結到了根目錄:
如下的Demo,如果採用JPCERT示例中validateFilename(entry.genName(), “.”)的呼叫方式,還是會產生目錄遍歷讀到系統配置檔案:
讀到的hosts檔案內容:
正確的呼叫validateFilename()形式是傳入的要解壓到的目的目錄不要用“.”,而是指定一個絕對路徑。
4、阿里聚安全對開發者建議
1 對重要的ZIP壓縮包檔案進行數字簽名校驗,校驗通過才進行解壓。
2 檢查Zip壓縮包中使用ZipEntry.getName()獲取的檔名中是否包含”../”或者”..”,檢查”../”的時候不必進行URI Decode(以防通過URI編碼”..%2F”來進行繞過),測試發現ZipEntry.getName()對於Zip包中有“..%2F”的檔案路徑不會進行處理。
3 在應用上線前使用阿里聚安全的安全掃描服務,儘早發現應用的安全風險。
阿里聚安全掃描器建議修復方案:
在使用java.util.zip包中ZipInputStream類的進行解壓操作時,進行檢查,示例如下:
也可以使用java.util.zip包中的ZipFile類,直接讀取Zip包中的所有entries,然後檢查getName()的返回值是否包含“../”:
5、參考
[1] https://www.jpcert.or.jp/present/2014/20140910android-sc.pdf
[2] 《海豚瀏覽器與水星瀏覽器遠端程式碼執行漏洞詳解》http://drops.wooyun.org/mobile/8293
[3] 《影響數千萬APP的安卓APP“寄生獸”漏洞技術分析》http://drops.wooyun.org/mobile/6910
[4] 《三星預設輸入法遠端程式碼執行》http://drops.wooyun.org/papers/6632
[5] http://www.oracle.com/technetwork/articles/java/compress-1565076.html
[6] http://stackoverflow.com/questions/1099300/whats-the-difference-between-getpath-getabsolutepath-and-getcanonicalpath
[7] http://stackoverflow.com/questions/7016391/difference-between-system-load-and-system-loadlibrary-in-java
6、Android安全開發系列
目錄
作者:伊樵、呆狐@阿里聚安全,更多安全技術文章,請訪問阿里聚安全部落格