CVE-2017-13156 Janus高危漏洞深度分析
轉載自:https://bbs.pediy.com/thread-223539.htm
一、背景介紹
近日,Android平臺被爆出“核彈級”漏洞Janus(CVE-2017-13156),該漏洞允許惡意攻擊者任意修改Android應用中的程式碼,而不會影響其簽名。
眾所周知,Android具有簽名機制。正常情況下,開發者釋出了一個應用,該應用一定需要開發者使用他的私鑰對其進行簽名。惡意攻擊者如果嘗試修改了這個應用中的任何一個檔案(包括程式碼和資源等),那麼他就必須對APK進行重新簽名,否則修改過的應用是無法安裝到任何Android裝置上的。但如果惡意攻擊者用另一把私鑰對APK簽了名,並將這個修改過的APK對使用者手機裡的已有應用升級時,就會出現簽名不一致的情況。因此,在正常情況下,Android的簽名機制起到了防篡改的作用。
但如果惡意攻擊者利用Janus漏洞,那麼惡意攻擊者就可以任意地修改一個APK中的程式碼(包括系統的內建應用),同時卻不需要對APK進行重新簽名。換句話說,用這種方式修改過的APK,Android系統會認為它的簽名和官方的簽名是一致的,但在這個APK執行時,執行的卻是惡意攻擊者的程式碼。惡意攻擊者利用這個修改過的APK,就可以用來覆蓋安裝原官方應用(包括系統的內建應用)。由此可見,該漏洞危害極大,而且影響的不僅是手機,而是所有使用Android作業系統的裝置。
目前,Google將該漏洞危險等級定義為高,其影響Android 5.1.1至8.0的所有版本。基於多年以來針對移動端漏洞的技術積累和安全對抗,安天移動安全對Janus高危漏洞進行了緊急分析,併發布技術報告,全文如下。
二、漏洞原理
ART虛擬機器在載入並執行一個檔案時,會首先判斷這個檔案的型別。如果這個檔案是一個Dex檔案,則按Dex的格式載入執行,如果是一個APK檔案,則先抽取APK中的dex檔案,然後再執行。而判斷的依據是通過檔案的頭部魔術字(Magic Code)來判斷。如果檔案的頭部魔術字是“dex”則判定該檔案為Dex檔案,如果檔案頭部的魔術字是“PK”則判定該檔案為Apk檔案。
另一方面,Android在安裝一個APK時會對APK進行簽名驗證,但卻直接預設該APK就是一個ZIP檔案(並不檢查檔案頭部的魔術字),而ZIP格式的檔案一般都是從尾部先讀取,因此只要ZIP檔案尾部的資料結構沒有被破壞,並且在讀取過程中只要沒有碰到非正常的資料,那麼整個讀取就不會有任何問題。
總而言之,Android在載入執行程式碼時,只認檔案頭,而安裝驗證簽名時只認檔案尾。
因此只要構造一個APK,從其頭部看是一個Dex檔案,從其尾部看,是一個APK檔案,就可以實施攻擊。很容易想到,將原APK中的classes.dex抽取出來,改造或替換成攻擊者想要執行的dex,並將這個dex和原APK檔案拼起來,合成一個檔案,就可以利用Janus漏洞。
當然僅僅簡單地將惡意dex放在頭部,原apk放在尾部合起來的檔案還是不能直接用來攻擊。需要稍作修正。對於頭部dex,需要修改DexHeader中的file_size,將其調整為合併後文件的大小。另外需要修改尾部Zip,修正[end of central directory record]中[central directory]的偏移和[central directory]中各[local file header]的偏移。
當然,Janus漏洞是針對APK檔案的攻擊,因此v1簽名無法抵禦這類攻擊,而v2簽名可以抵禦。
三、漏洞利用
▲圖1 攻擊檔案拼接原理
具體的漏洞利用分為3步:
1. 從裝置上取出目標應用的APK檔案,並構造用於攻擊的DEX檔案;
2. 將攻擊DEX檔案與原APK檔案簡單拼接為一個新的檔案;
3. 修復這個合併後的新檔案的ZIP格式部分和DEX格式部分,修復原理如圖1所示,需要修復檔案格式中的關鍵偏移值和資料長度值。
最後,將修復後的檔案,重新命名為APK檔案,覆蓋安裝裝置上的原應用即可。
四、漏洞修復
1. Google官方修復方案
對檔案system/core/libziparchive/zip_archive.cc打上如下patch:
2. 修復原理
開啟ZIP格式檔案時,多做了一項校驗,也就是檢測檔案的頭部是不是以‘PK’標示打頭。如果是,則進行正常的邏輯,否則認為該檔案不是一個合法的ZIP格式檔案。
五、總結
Android平臺上的應用簽名機制是Android安全的基石。Android平臺的permission機制完全依賴於應用的簽名,簽名機制一旦突破,所有基於Android permission構建的安全體系將崩潰。而Janus漏洞已經不是Android平臺的第一例簽名機制漏洞了,之前由“Bluebox”發現的Master Key漏洞和“安卓安全小分隊”(安天移動安全上海團隊前身)發現的第二個Master Key漏洞都是利用簽名機制的漏洞,其原理是利用Android的程式碼中對APK驗證不充分的缺陷,使得應用在安裝時驗證的是原dex,但執行的是另一個dex,從而達到瞞天過海、偷樑換柱的目的。
Janus漏洞的利用在原理上也類似,它將惡意dex和原apk拼接在一起,安裝驗證時驗證的是原apk的dex,而執行時卻是執行惡意dex。修復這類漏洞的原理也很簡單,就是加強安裝時的驗證,避免不合法應被安裝到系統中。針對Janus漏洞,只需簡單驗證一下apk檔案的頭部是不是‘PK’即可。如果不是,則該apk 檔案一定不是一個正常的apk檔案。
Janus漏洞再一次提示我們,即使像Google這樣的跨國科技企業也難免在簽名驗證這麼關鍵的環節上多次產生漏洞,特別是Janus漏洞從2014年就已經存在,潛伏長達3年之久,並且從Android 5.1-8.0版本都存在這個漏洞。這說明了安全問題有時是極其隱蔽的,暫時未發現安全問題,不代表安全問題不存在;更說明了安全是動態的而不是靜止的。因此安全防護是一個工程化體系化的持久戰,很難通過單次或短期投入就將安全問題一次性解決,在安全方面只有持續而堅定地投入,一旦發現風險或者漏洞就要以最快的速度及時修復,只有這樣才能保證系統安全而穩定地執行,最大程度地規避風險和損失。
附錄:技術參考
要理解Janus高危漏洞的原理,首先需要掌握一些基礎知識:ZIP檔案結構、DEX檔案結構和Android APK簽名機制。
1. DEX檔案結構
▲圖2 dex檔案格式
Dex檔案有很多部分組成(如圖2),其中Dex Header最為重要,因為Dex的其他組成部分,都需要通過這個Dex Header中的索引才能找到。
Dex Header內部結構如下:
▲圖3 dex header 結構
在Dex Header中(如圖3),file_size規定了整個dex檔案的總大小。因此,如果想在Dex檔案中隱藏一些額外的資料的話,最簡單地,就可以將這些資料追加到Dex檔案末尾,然後再將file_size調大到合適的值即可。
2. ZIP檔案結構
ZIP檔案可以通過獲得檔案尾部的End of central directory record(EOCD record)獲取central directory,遍歷central directory中的每項記錄得到的file data即為壓縮檔案的資料。
▲表1 ZIP檔案結構
如表1,讀取ZIP檔案時,會現從最後一個記錄區end of central directory record中讀取central directory的偏移,然後遍歷central directory中的每一項,獲取每個檔案的 local file header,最後通過 local file header 獲取每個檔案的內容。
End of central directory record結構體如下:
▲表2 End of central directory record結構
該結構(如表2)中的offset of start of central directory with respect to the starting disk number,指向了central directory的位置。
Central directory結構體如下:
▲表3 Central directory結構
該結構(如表3)中的relative offset of local header,指向了每個檔案的Local File Header的位置。
因此,如需在ZIP檔案中隱藏一些資料,可將這些資料簡單新增到頭部,然後修改End of central directory record中central directory的偏移。同時修改每個central directory中的Local File Header的偏移即可。
3.Android APK簽名機制
Android APK簽名機制分為兩個版本:v1和v2版本。
兩個版本的簽名區別在於,前者是對APK中的每個檔案進行簽名,如果APK中某個檔案被篡改了,那麼簽名驗證將會通不過;後者則是對整個APK檔案進行簽名,只要APK檔案的內容發生變化則簽名失效。
很顯然,v2版本要比v1更加嚴格,安全性會高很多。
但遺憾的是,Android從7.0開始才引入v2簽名。之前的所有Android系統只能驗證v1簽名的app,即使這個app也用V2簽名了。
以下是兩個版本的簽名對比:
▲表4 v1 v2簽名對比
對於android 7.0以上,系統在校驗簽名是會先檢查是否存在V2簽名方案,若存在,則通過V2簽名方案對APK進行校驗,否則使用V1簽名方案對APK進行校驗。
▲圖4 Android v2簽名流程
對於android 7.0以下的系統,不支援V2簽名方案,所以APK在簽名時最好將兩種簽名方案都支援。
例子:https://github.com/TomesVWhite/BuildFakeApk