<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/papers/13505

                    Author:[email protected]

                    0x00 簡介


                    CVE-2015-7084是由于IORegistryIterator沒有考慮用戶態多線程同時調用的情況,引起Race Condition,可導致任意代碼執行。漏洞存在于XNU版本3248.20.55之前的內核上,即Mac OS X 10.11.2、iOS 9.2、watchOS 2.1、tvOS 9.1之前的系統版本上。官方修復公告https://support.apple.com/en-us/HT205637

                    0x01 漏洞背景


                    IORegistryIterator是用于XNU內核中用于遍歷訪問IO Registry Entry的一個類。在XNU版本3248.20.55之前的內核上,即Mac OS X 10.11.2、iOS 9.2、watchOS 2.1、tvOS 9.1之前的系統版本上,在操作IORegistryIterator時缺少鎖機制,用戶態進程通過多線程調用引起Race Condition,最終可實現任意代碼執行。這個漏洞是由Google Project Zero的Ian Beer報告,CVE編號CVE-2015-7084。

                    0x02 漏洞分析


                    Ian Beer在https://code.google.com/p/google-security-research/issues/detail?id=598中給出了漏洞的說明,以及一份導致Double Free的PoC代碼。

                    is_io_registry_iterator_exit_entry是IORegistryIteratorExitEntry對應的內核接口,會調用IORegistryIterator::exitEntry函數。

                    #!cpp
                    /* Routine io_registry_iterator_exit */
                    kern_return_t is_io_registry_iterator_exit_entry(
                        io_object_t iterator )
                    {
                        bool    didIt;
                        CHECK( IORegistryIterator, iterator, iter );
                        didIt = iter->exitEntry();
                        return( didIt ? kIOReturnSuccess : kIOReturnNoDevice );
                    }
                    

                    .

                    #!cpp
                    bool IORegistryIterator::exitEntry( void )
                    {
                        IORegCursor *   gone;
                    …
                    
                        if( where != &start) {
                          gone = where;   // Race Condition
                            where = gone->next;
                            IOFree( gone, sizeof(IORegCursor));  // gone可能被釋放兩次
                            return( true);
                        } else
                            return( false);
                    …
                    }
                    

                    但是由于缺乏鎖的保護,通過多線程調用IORegistryIteratorExitEntry,導致gone指向的內存區域被釋放兩次,引起崩潰。示意圖如下:

                    p1

                    0x03 漏洞利用


                    由于Double Free不易利用,Pangu Team在其博客文章http://blog.pangu.io/race_condition_bug_92/中提出了另外一種思路,可以穩定地利用Race Condition實現任意代碼執行。下面將對這種思路進行具體的分析,在已知Kernel Slide的情況下,在Mac OS X 10.11上實現提權。

                    1. 攻擊流程

                    通過觀察操作IORegistryIterator的函數enterEntry,發現其包含向單向鏈表插入節點的操作,如下:

                    #!cpp
                    void IORegistryIterator::enterEntry( const IORegistryPlane * enterPlane )
                    {
                        IORegCursor *   prev;
                    
                        prev = where;
                        where = (IORegCursor *) IOMalloc( sizeof(IORegCursor));
                        assert( where);
                    
                        if( where) {
                            where->iter = 0;
                            where->next = prev; //在鏈表頭部插入新的where節點,where->next指向舊where
                            where->current = prev->current;
                            plane = enterPlane;
                        }
                    }
                    

                    而IORegistryIterator::exitEntry中,包含移除單向鏈表頭部節點的操作,并且釋放移除的節點內存。

                    #!cpp
                    bool IORegistryIterator::exitEntry( void )
                    {
                        IORegCursor *   gone;
                    …
                    
                        if( where != &start) {
                          gone = where;   
                          where = gone->next; //從鏈表頭部移除當前where節點
                            IOFree( gone, sizeof(IORegCursor)); //釋放移除的節點內存區域
                            return( true);
                        } else
                            return( false);
                    …
                    }
                    

                    在兩個線程中分別調用IORegistryIteratorEnterEntry和IORegistryIteratorExitEntry,在特定的執行序列下,可能導致enterEntry在執行where->next = prev;時,prev指向的where區域已經被exitEntry的IOFree釋放,就會導致where->next指向被釋放的內存。

                    那么,當第二次調用exitEntry時,就會使得where指向被釋放的內存,這塊內存通過Heap Spray可以控制其中的內容。

                    #!cpp
                    bool IORegistryIterator::exitEntry( void )
                    {
                    …
                        if( where != &start) {
                          gone = where; //where->next已指向被釋放的區域
                          where = gone->next; //where指向被釋放的區域
                        }
                    …
                    }
                    

                    最后,第三次調用exitEntry時,where->iter可控,通過映射用戶空間內存iter對象虛表,可實現任意代碼執行。

                    #!cpp
                    bool IORegistryIterator::exitEntry( void )
                    {
                    …
                        if( where->iter) {  // where->iter可控
                        where->iter->release();  //可通過構造虛表,執行任意代碼
                        where->iter = 0;
                        }
                    …
                    }
                    

                    攻擊流程示意圖如下:

                    p2

                    2. Heap Spray

                    關于這個漏洞利用的關鍵是要控制where指向的被釋放的內存區域的內容。where是IORegCursor指針,由IOMalloc申請,位于kalloc.32 zone中。

                    #!cpp
                    struct IORegCursor {
                        IORegCursor          *  next;
                        IORegistryEntry      *  current;
                        OSIterator       *  iter;
                    };
                    

                    當第二次exitEntry被調用后,where指向的內存區域在kalloc.32的freelist鏈表中。我們可以通過heap spray kalloc.32,使得位于freelist中的內存重新被填充為我們控制的數據,實現控制where->iter的目的。heap spray的方法就是通過結合io_service_open_extended以及OSData,Pangu Team在其POC 2015的議題《Hacking from iOS8 to iOS9》中提到了這種heap spray的方法。

                    p3

                    通過構造特定的XML數據,包含data標簽,那么通過io_service_open_extended創建任意UserClient,在OSUnserializeXML中反序列化data數據時,就可以通過OSData占用內存,實現任意zone的heap spray。

                    #!cpp
                    object_t *
                    buildData(parser_state_t *state, object_t *o)
                    {
                        OSData *data;
                    
                        if (o->size) {
                            data = OSData::withBytes(o->data, o->size);
                        } else {
                            data = OSData::withCapacity(0);
                        }
                        if (o->idref >= 0) rememberObject(state, o->idref, data);
                    
                        if (o->size) free(o->data);
                        o->data = 0;
                        o->object = data;
                        return o;
                    };
                    

                    3. 任意代碼執行

                    在用戶空間映射兩塊內存空間,分別放置構造的iter對象以及構造的虛表。將XML中的data的第三個QWORD字段設置為構造的iter對象指針,并進行heap spray。通過heap spray成功控制where指向的內存區域內容后,where->iter可控,最終調用where->iter->release()時就會調用我們構造的虛表中的函數,實現任意代碼執行。在已知Kernel Slide的情況下,在10.10.5以及10.11上都成功實現提權,10.10.5如下:

                    p4

                    0x04 官方修復


                    在10.11.2的XNU源碼中,蘋果官方進行了修復。新添加了一個IOUserIterator,用于封裝IORegistryIterator,對reset操作等加鎖,也在enterEntry和exitEntry時加鎖,防止多線程調用引起的Race Condition。

                    #!cpp
                    /* Routine io_registry_iterator_enter */
                    kern_return_t is_io_registry_iterator_enter_entry(
                        io_object_t iterator )
                    {
                        CHECKLOCKED( IORegistryIterator, iterator, iter );
                    
                        IOLockLock(oIter->lock);
                        iter->enterEntry();
                        IOLockUnlock(oIter->lock);
                    
                        return( kIOReturnSuccess );
                    }
                    
                    /* Routine io_registry_iterator_exit */
                    kern_return_t is_io_registry_iterator_exit_entry(
                        io_object_t iterator )
                    {
                        bool    didIt;
                    
                        CHECKLOCKED( IORegistryIterator, iterator, iter );
                    
                        IOLockLock(oIter->lock);
                        didIt = iter->exitEntry();
                        IOLockUnlock(oIter->lock);
                    
                        return( didIt ? kIOReturnSuccess : kIOReturnNoDevice );
                    }
                    

                    0x05 References


                    1. https://code.google.com/p/google-security-research/issues/detail?id=598
                    2. http://blog.pangu.io/race_condition_bug_92/
                    3. http://blog.pangu.io/wp-content/uploads/2015/11/POC2015_RUXCON2015.pdf

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

                                      这里只有精品视频