由於ZIP壓縮包文件中容許存在「../」的字符串,攻擊者能夠利用多個「../」在解壓時改變ZIP包中某個文件的存放位置,覆蓋掉應用原有的文件。若是被覆蓋掉的文件是動態連接so、dex或者odex文件,輕則產生本地拒絕服務漏洞,影響應用的可用性,重則可能形成任意代碼執行漏洞,危害用戶的設備安全和信息安全。好比近段時間發現的「寄生獸」漏洞、海豚瀏覽器遠程命令執行漏洞、三星默認輸入法遠程代碼執行漏洞等都與ZIP文件目錄遍歷有關。html
阿里聚安全的應用漏洞掃描服務,能夠檢測出應用的ZIP文件目錄遍歷風險。另外咱們發現日本計算機應急響應小組(JPCERT)給出的修復方案存在缺陷。若是使用不當(它提供的示例文檔就使用錯誤),可能起不到防止ZIP文件目錄遍歷的做用,而且國內有修復方案參考了此方案。java
在Linux/Unix系統中「../」表明的是向上級目錄跳轉,有些程序在當前工做目錄中處理到諸如用「../../../../../../../../../../../etc/hosts」表示的文件,會跳轉出當前工做目錄,跳轉到到其餘目錄中。 android
Java代碼在解壓ZIP文件時,會使用到ZipEntry類的getName()方法,若是ZIP文件中包含「../」的字符串,該方法返回值裏面原樣返回,若是沒有過濾掉getName()返回值中的「../」字符串,繼續解壓縮操做,就會在其餘目錄中建立解壓的文件。算法
如咱們構造的ZIP文件中有以下文件:瀏覽器
進行解壓的代碼以下,沒有對getName進行過濾:安全
解壓操做時的日誌:服務器
此ZIP文件存放在SD卡中,想讓解壓出來的全部文件也存在SD卡中,可是a_poc.txt文件卻存在了應用的數據目錄中:網絡
以海豚瀏覽器遠程代碼執行漏洞爲例。 oracle
海豚瀏覽器的主題設置中容許用戶經過網絡下載新的主題進行更換,主題文件實際上是一個ZIP壓縮文件。經過中間人攻擊的方法能夠替換掉這個ZIP文件。替換後的ZIP文件中有從新編譯過的libdolphin.so。此so文件重寫了JNI_OnLoad()函數:app
此so文件以「../../../../../../../../../../data/data/mobi.mgeek.TunnyBrowser/files/libdolphin.so」的形式存在惡意ZIP文件中。海豚瀏覽器解壓惡意ZIP文件後,從新的libdolphin.so就會覆蓋掉原有的so文件,從新運行海豚瀏覽器會彈出Toast提示框:
能彈出Toast說明也就能夠執行其餘代碼。
這裏分析下此漏洞產生的緣由是:
一、主題文件實際上是一個ZIP壓縮包,從服務器下載後進行解壓,可是解壓時沒有過濾getName()返回的字符串中是否有「../」:
二、動態連接庫文件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目錄的權限:
在研究中咱們發現JPCERT提供的修復方案存在缺陷。它是利用Java的File類提供的getCanonicalPath()方法過濾掉zipEntry.getName()返回的字符串中所包含的「../」,而後檢查這個字符串是不是以要解壓到的目標目錄字符串爲開頭,若是是,返回getCanonicalPath()獲取到的字符串,若是不是,則拋出異常:
可是在JPCERT給出的示例代碼中,對validateFilename()的調用對於APP來講不會達到防止任意目錄遍歷的目的:
其使用「.」,做爲要解壓到的目的目錄,「.」表示當前目錄,經測試APP進程的當前工做目錄是根目錄「/」:
查看進程狀態,獲得的APP進程的當前工做目錄cwd是連接到了根目錄:
以下的Demo,若是採用JPCERT示例中validateFilename(entry.genName(), 「.」)的調用方式,仍是會產生目錄遍歷讀到系統配置文件:
讀到的hosts文件內容:
正確的調用validateFilename()形式是傳入的要解壓到的目的目錄不要用「.」,而是指定一個絕對路徑。
對重要的ZIP壓縮包文件進行數字簽名校驗,校驗經過才進行解壓。
檢查Zip壓縮包中使用ZipEntry.getName()獲取的文件名中是否包含」../」或者」..」,檢查」../」的時候沒必要進行URI Decode(以防經過URI編碼」..%2F」來進行繞過),測試發現ZipEntry.getName()對於Zip包中有「..%2F」的文件路徑不會進行處理。
在應用上線前使用阿里聚安全的安全掃描服務,儘早發現應用的安全風險。
阿里聚安全掃描器建議修復方案:在使用java.util.zip包中ZipInputStream類的進行解壓操做時,進行檢查。示例以下
也可使用java.util.zip包中的ZipFile類,直接讀取Zip包中的全部entries,而後檢查getName()的返回值是否包含「../」:
[1] https://www.jpcert.or.jp/present/2014/20140910android-sc.pdf
2] [《海豚瀏覽器與水星瀏覽器遠程代碼執行漏洞詳解》
3] [《影響數千萬APP的安卓APP「寄生獸」漏洞技術分析》
4] [《三星默認輸入法遠程代碼執行》
[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
目錄
Android安全開發之Provider組件安全
Android安全開發之淺談密鑰硬編碼
Android安全開發之淺談網頁打開APP
Android應用安全開發之淺談加密算法的坑
做者:伊樵、呆狐@阿里聚安全,更多安全技術文章,請訪問阿里聚安全博客