<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

    原文地址:http://drops.wooyun.org/papers/219

    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系統本?身認為應?用沒有修改)這兩條重要的信息。

    剩下就是放出來一張更改基帶字串的圖:

    enter image description here

    具體細節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

    enter image description here

    在linux執?行了一遍,出現錯誤。可能是apk的原因。

    索性把這poc移植到windows下,先是?用apktool 把要更改的apk給反編譯出來到一個目錄apk_test

    然后?又把apk_test打包成?一個新的apk

    把原先的apk解壓出來apk_old

    把apk_old所有?文件以zip壓縮的?方式加?入新的apk中。我本機以weibo.apk為例:

    enter image description here

    可見兩者大小發生了變化,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()
    

    enter image description here

    差不多增大了一倍,放在手機上安裝了一下,成功安裝。查看了下:

    enter image description here

    出現了多對同名文件。CRC校驗不同,查看了一下,基本上是兩個字節便產生不同。

    enter image description here

    這里我又測試了只添加簽名文件,或者dex文件等,均不能通過驗證。

    可證明其在scan list掃描目錄或者復制文件時候對同名文件處理不當。

    0x02 驗證


    證明是否可以進行更改源碼,并能使用原生簽名。我把apk圖標進行了更改。

    順便講下一般的反編譯修改:

    1. apktool或者其他工具進行反編譯包含smalijava字節碼匯編和xml圖片文件。 
    2. apkzip解壓。
    3. 反編譯dex成java文件。
    4. 查找對應修改的smali文件或者xml(一般廣告鏈接)
    5. Apktool打包成apk文件
    6. 用autosign進行簽名。
    這里沒有進行簽名直接借用原來的簽名。
    

    enter image description here

    enter image description here

    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)。

    enter image description here

    2.?由于HashMap.put在相同key的情況下,會把value更新,導致上述的HashMap在相同路徑下,存儲的一定是最后一個文件的Entry。

    enter image description here

    ?系統加載dex文件,加載的是第一個dex。???

    ??1.?查找dex的Entry用的是dexZipFindEntry。?

    enter image description here

    2.?dexZipFindEntry的實現是只要match就返回,所以返回的都是第一個文件。

    enter image description here

    Zip 可以包含兩個同名文件或者路徑,而其自身的unzip 默認方式是后一個覆蓋前一個。

    HashMap.put 的寫法應該文件也直接覆蓋(hash表的沖突處理不當果真出大問題)才算是算是符合zip 的標準。

    就是加載dex的方式則是先加載第一個,這樣確實信息不一致。

    而我之前黑盒測出來認為android 默認把兩個都加載在簽名驗證順序上出現問題的,未分析到上一層的類。

    看雪上也是討論很多帖子得到準確的原理分析,大家共同討論,集思廣益。Hack it, know it too.

    持續跟新中。

    97av