作者:
livers
·
2013/07/11 12:38
0x00 背景
Bluebox的CTO Jeff Forristal在其官⽅方blog爆出一個漏洞叫做UNCOVERING ANDROID MASTER KEY,大致是不篡改簽名修改android程式碼。
Link:http://bluebox.com/corporate-blog/bluebox-uncovers-android-master-key/
blog:關於細節並沒有講太多,只有discrepancies in how Android applications are cryptographically verified & installed(安卓應⽤用簽名驗證和安裝的不⼀一致)essentially allowing a malicious author to trick Android into believing the app is unchanged even if it has been(讓andriod系統本⾝身認為應⽤用沒有修改)這兩條重要的資訊。
剩下就是放出來一張更改基帶字串的圖:
具體細節7月底的blackhat放出。
沒多少天7月8號國外已經有人放出poc來。微博上看到rayh4c說已經搞定。就分析了一下。
0x01 分析
POC還沒出來之前,先是看了下android的簽名機制和安裝機制。
簽名機制: 用簡單的話來講就是android把app應用的所有檔案都做了sha1(不可逆)簽名,並對這簽名用RSA(非對稱加密演算法)的私鑰進行了加密,客戶端安裝驗證時用公鑰進行解密。
從邏輯上看,這簽名機制對完整性和唯一性的校驗是完全沒問題的。主流的很多加密都類似這樣。
安裝機制:
安裝機制則較為複雜。
1.系統應用安裝――開機時完成,沒有安裝介面
2.網路下載應用安裝――透過market應用完成,沒有安裝介面
3.ADB⼯工具安裝――沒有安裝介面。
4.第三⽅方應用安裝――透過SD卡⾥裡的APK⽂檔案安裝,有安裝介面,由packageinstaller.apk應⽤用處理安裝及解除安裝過程的介面。
安裝過程:複製APK安裝包到data/app目錄下,解壓並掃描安裝包,把dex⽂檔案(Dalvik位元組碼) 儲存到dalvik-cache目錄,並data/data目錄下建立對應的應⽤用資料目錄。
到這裡看出在安裝機制上的問題可能性比較大。
回頭看⽼老外的POC:https://gist.github.com/poliva/36b0795ab79ad6f14fd8
在linux執⾏行了一遍,出現錯誤。可能是apk的原因。
索性把這poc移植到windows下,先是⽤用apktool 把要更改的apk給反編譯出來到一個目錄apk_test
然後⼜又把apk_test打包成⼀一個新的apk
把原先的apk解壓出來apk_old
把apk_old所有⽂檔案以zip壓縮的⽅方式加⼊入新的apk中。我本機以weibo.apk為例:
可見兩者大小發生了變化,apktool在反編譯過程不可避免的出現差異。並且重編譯的apk不含有簽名檔案。
按照poc的做法我用批處理匯出目錄的檔名到1.txt修改了poc.py
import zipfile
import sys
f=open('1.txt','r')
line=f.readline()
test=[]
while line:
test1=line.replace("\n","")
test.append(test1)
if not line:
break
line=f.readline()
f.close()
z = zipfile.ZipFile("livers.apk", "a")
for i in range(0,len(test)):
print test[i]
z.write(str(test[i]))
z.close()
差不多增大了一倍,放在手機上安裝了一下,成功安裝。檢視了下:
出現了多對同名檔案。CRC校驗不同,檢視了一下,基本上是兩個位元組便產生不同。
這裡我又測試了只新增簽名檔案,或者dex檔案等,均不能透過驗證。
可證明其在scan list掃描目錄或者複製檔案時候對同名檔案處理不當。
0x02 驗證
證明是否可以進行更改原始碼,並能使用原生簽名。我把apk圖示進行了更改。
順便講下一般的反編譯修改:
1. apktool或者其他工具進行反編譯包含smalijava位元組碼彙編和xml圖片檔案。
2. apkzip解壓。
3. 反編譯dex成java檔案。
4. 查詢對應修改的smali檔案或者xml(一般廣告連結)
5. Apktool打包成apk檔案
6. 用autosign進行簽名。
這裡沒有進行簽名直接借用原來的簽名。
0x03 查詢根源
我這裡下載的android 2.2的原始碼,查詢到獲取簽名資訊安裝位於frameworks\base\core\java\android\content\pm\PackageParser.java
這個檔案,public boolean collectCertificates(Package pkg, int flags)
和private Certificate[] loadCertificates(JarFile jarFile, JarEntry je, byte[] readBuffer)
這個方法是用來獲取簽名資訊的。
Enumeration entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry je = (JarEntry)entries.nextElement();
if (je.isDirectory()) continue;
if (je.getName().startsWith("META-INF/")) continue;
Certificate[] localCerts = loadCertificates(jarFile, je,
readBuffer);
。。。。。。
} else {
// Ensure all certificates match.
for (int i=0; i<certs.length; i++) {
boolean found = false;
for (int j=0; j<localCerts.length; j++) {
if (certs[i] != null &&
certs[i].equals(localCerts[j])) {
found = true;
break;
}
}
。。。。。
前面透過黑盒方式,大致推斷出安裝機制就是把重新命名檔案同時處理了,沒有覆蓋而是:
if (certs[i] != null &&certs[i].equals(localCerts[j])) {
found = true;
break;
}
兩個重名檔案都做了驗證,只要有一個透過驗證,就返回驗證透過。
0x04 後繼
我android研究不多,大多以前玩逆向的底子。大家可以多討論。 歡迎大家留言探討~!
======================================================================================================
7月11日21點更新:
沒看到看雪上已經討論的熱火朝天,讀下來來源於看雪的zmworm的原理分析應該是更準確的。
原理簡述
由於ZIP格式允許存在兩個或以上完全相同的路徑,而安卓系統沒有考慮這種場景。
在該情況下,android包管理器校驗簽名取的是最後一個檔案的hash,而執行APK載入的dex檔案卻是zip的第一個dex檔案。
包管理器驗證簽名驗的是最後一個(名字相同情況下)檔案。
1. 解析zip的所有Entry,結果存到HashMap(key為路徑,value為Entry)。
2. 由於HashMap.put在相同key的情況下,會把value更新,導致上述的HashMap在相同路徑下,儲存的一定是最後一個檔案的Entry。
系統載入dex檔案,載入的是第一個dex。
1. 查詢dex的Entry用的是dexZipFindEntry。
2. dexZipFindEntry的實現是隻要match就返回,所以返回的都是第一個檔案。
Zip 可以包含兩個同名檔案或者路徑,而其自身的unzip 預設方式是後一個覆蓋前一個。
HashMap.put 的寫法應該檔案也直接覆蓋(hash表的衝突處理不當果真出大問題)才算是算是符合zip 的標準。
就是載入dex的方式則是先載入第一個,這樣確實資訊不一致。
而我之前黑盒測出來認為android 預設把兩個都載入在簽名驗證順序上出現問題的,未分析到上一層的類。
看雪上也是討論很多帖子得到準確的原理分析,大家共同討論,集思廣益。Hack it, know it too.
持續跟新中。
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!