<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/tips/11329

                    Author:[email protected]

                    0x00 前言


                    IE沙箱逃逸是IE瀏覽器安全研究的一個重要課題,其中有一類漏洞會借助ElevationPolicy設置中的白名單程序的缺陷來完成沙箱逃逸。IE在注冊表中有一個和ElevationPolicy類似的名為DragDrop策略設置,這引起了我們的注意。在本文中,筆者將以一個攻擊者的視角,嘗試各種途徑來突破IE沙箱的這一安全策略,通過分析所遇到的障礙,達到對IE沙箱拖拽安全策略進行詳細解析的目的。

                    0x01 IE沙箱的拖拽策略


                    IE沙箱逃逸技術中有一類是利用ElevationPolicy中的白名單程序的問題去執行任意代碼,在注冊表中,有一個和ElevationPolicy類似的配置,名為DragDrop,具體注冊表路徑如下:

                    HKLM\Software\Microsoft\Internet Explorer\Low Rights\DragDrop
                    

                    如下圖所示:

                    p1

                    DragDrop Policy值的含義如下:

                    0:目標窗口是無效的DropTarget,拒絕;
                    1:目標窗口是有效的DropTarget,但無法復制內容;
                    2:彈框詢問用戶,允許后將內容復制到目標窗口;
                    3:靜默允許拖拽。
                    

                    在一個干凈的Windows 8.1系統上,DragDrop目錄下默認有三個程序:iexplore.exe, explorer.exe, notepad.exe,它們的Policy值都是3。當目標程序的Policy值為2時,向目標程序窗口拖拽文件,IE會彈出一個提示框,如下圖所示:

                    p2

                    0x02 Explorer進程的拖拽問題


                    在從IE往Explorer上拖拽文件時,雖然DragDrop Policy值設置為了3,IE不會彈框,但是Explorer進程會會彈一個提示框,如下圖所示:

                    p3

                    然而,當我們從IE中向Explorer側邊欄的樹形文件夾結構中拖拽文件時,并不會彈框。這應該是Explorer程序實現上的一個疏漏。進一步設想,如果我們能夠在IE沙箱中通過程序模擬鼠標的拖拽操作,那么就能夠利用Explorer的這個問題跨越IE沙箱的安全邊界。

                    0x03 不使用鼠標完成OLE拖拽


                    OLE拖拽是一種通用的文件拖拽方式,它采用了OLE的接口設計方法來實現拖拽功能,使得拖拽的實現通用且模塊化。OLE拖拽技術包含三個基本接口:

                    1. IDropSource接口:表示拖拽操作的源對象,由源對象實現;
                    2. IDropTarget接口:表示拖拽操作的目標對象,由目標對象實現;
                    3. IDataObject接口:表示拖拽操作中傳輸的數據,由源對象實現。

                    下圖描述了一個完整的OLE拖拽操作需要實現的關鍵組件:

                    p4

                    我們要模擬鼠標拖拽,則只需要實現IDropSourceIDataObject接口。正常的OLE拖拽操作的核心是調用ole32!DoDragDrop函數,該函數原型如下:

                    #!c++
                    HRESULTDoDragDrop(
                    IDataObject*pDataObject,   // Pointer to the data object
                    IDropSource *pDropSource,   // Pointer to the source
                       DWORD     dwOKEffect,    // Effects allowed by the source
                       DWORD     *pdwEffect      // Pointer to effects on the source
                    );
                    

                    DoDragDrop的參數中包含了拖拽源對象和拖拽數據的信息,在DoDragDrop函數內部通過鼠標指針位置來獲取拖拽目標對象的信息。接下來,筆者給出一種不使用鼠標,而是用代碼模擬的方式來完成文件拖拽的方法。

                    要通過代碼模擬鼠標拖拽操作,即要將DoDragDrop函數中GUI操作的部分剝離出來,找出真正執行拖拽操作的函數,將所需要的參數直接傳遞給它來完成拖拽操作。這里以Win7上的ole32.dll 6.1.7601.18915為例,說明DoDragDrop內部的實現。

                    Ole32!DoDragDrop的主要邏輯如下:

                    #!c++
                    HRESULT __stdcallDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect)
                    {
                    CDragOperationdrgop;
                        HRESULT hr;
                    
                        CDragOperation::CDragOperation(&drgop, pDataObj, pDropSource, dwOKEffects, pdwEffect, &hr);
                        if ( hr>= 0 ){
                          while ( CDragOperation::UpdateTarget(&drgop)
                            &&CDragOperation::DragOver(&drgop)
                            &&CDragOperation::HandleMessages(&drgop) )
                            hr = CDragOperation::CompleteDrop(&drgop);
                        }
                        CDragOperation::~CDragOperation(&drgop);
                    
                        return hr;
                    }
                    

                    CDragOperation::CDragOperation是構造函數,其中重要的初始化操作包括:

                    #!c++
                    ole32!GetMarshalledInterfaceBuffer
                    ole32!ClipSetCaptureForDrag
                        -->ole32!GetPrivateClipboardWindow
                    ole32!CreateSharedDragFormats
                    

                    接下來的While循環判斷拖拽的狀態,最終由CompleteDrop完成拖拽,關鍵的函數調用如下:

                    #!c++
                    ole32!CDragOperation::UpdateTarget
                      ->ole32!CDragOperation::GetDropTarget
                           -->ole32!PrivDragDrop
                    ole32!CDragOperation::DragOver
                      -->ole32!CDropTarget::DragOver
                           -->ole32!PrivDragDrop
                    ole32!CDragOperation::CompleteDrop
                      -->ole32!CDropTarget::Drop
                           -->ole32!PrivDragDrop
                    

                    可以看到,最終實現拖拽操作的函數是ole32!PrivDragDrop,通過使用函數偏移硬編碼函數地址,可以調用到ole32.dll中的內部函數。我們定義了一個DropData函數來模擬鼠標拖拽,輸入參數為目標窗口句柄和被拖拽文件的IDataObject指針,主要邏輯如下:

                    #!c++
                    auto DropData(HWND hwndDropTarget, IDataObject* pDataObject)
                    {
                        GetPrivateClipboardWindow(CLIP_CREATEIFNOTTHERE);
                        CreateSharedDragFormats(pDataObject);
                        void *DOBuffer = nullptr;
                        HRESULT result = GetMarshalledInterfaceBuffer(IID_IDataObject, pDataObject, &DOBuffer);
                    
                        if (SUCCEEDED(result)){
                            DWORD dwEffect = 0;
                            POINTL ptl = { 0, 0 };
                            void *hDDInfo = nullptr;
                            HRESULT result = PrivDragDrop(hwndDropTarget, DRAGOP_ENTER, DOBuffer, pDataObject, MK_LBUTTON, ptl, &dwEffect, 0, &hDDInfo);
                            if (SUCCEEDED(result)){
                                HRESULT result = PrivDragDrop(hwndDropTarget, DRAGOP_OVER, 0, 0, MK_LBUTTON, ptl, &dwEffect, 0, &hDDInfo);
                                if (SUCCEEDED(result)){
                                    HWND hClip = GetPrivateClipboardWindow(CLIP_QUERY);
                                    HRESULT result = PrivDragDrop(hwndDropTarget, DRAGOP_DROP, DOBuffer, pDataObject, 0, ptl, &dwEffect, hClip, &hDDInfo);
                                }
                            }
                        }
                        return result;
                    }
                    

                    目標窗口句柄可以通過FindWindow函數獲得,將被拖拽文件封裝成一個DataObject并獲得其IDataObject接口指針的方法有兩種:

                    1. 自己編寫C++類實現IDataObject接口;
                    2. 使用現有類庫中的實現,如:MFC, Shell32中均有對拖拽接口實現的相關類。

                    筆者這里給出使用MFC類庫對文件進行封裝并獲得其IDataObject接口的方法,實現代碼如下:

                    #!c++
                    auto GetIDataObjectForFile(CStringfilePath)
                    {
                        COleDataSource* pDataSource = new COleDataSource();
                        IDataObject*    pDataObject;
                        UINT            uBuffSize = 0;
                        HGLOBAL         hgDrop;
                        DROPFILES*      pDrop;
                        TCHAR*          pszBuff;
                        FORMATETC     fmtetc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
                    
                        uBuffSize = sizeof(DROPFILES) + sizeof(TCHAR) * (lstrlen(filePath) + 2);
                        hgDrop = GlobalAlloc(GHND | GMEM_SHARE, uBuffSize);
                        if (hgDrop != nullptr){
                            pDrop = (DROPFILES*)GlobalLock(hgDrop);
                            if (pDrop != nullptr){
                                pDrop->pFiles = sizeof(DROPFILES);
                    #ifdef _UNICODE
                                pDrop->fWide = TRUE;
                    #endif
                                pszBuff = (TCHAR*)(LPBYTE(pDrop) + sizeof(DROPFILES));
                                lstrcpy(pszBuff, (LPCTSTR)filePath);
                                GlobalUnlock(hgDrop);
                                pDataSource->CacheGlobalData(CF_HDROP, hgDrop, &fmtetc);
                                pDataObject = (IDataObject *)pDataSource->GetInterface(&IID_IDataObject); 
                            }else{
                                GlobalFree(pDrop);
                                pDataObject = nullptr;
                            }
                        }else{
                            GlobalFree(hgDrop);
                            pDataObject = nullptr;
                        }
                        return pDataObject;
                    }
                    

                    0x04 IE沙箱的拖拽實現


                    當我們在IE沙箱中用鼠標進行拖拽操作時,沙箱內的IE Tab進程會通過ShdocvwBroker將數據轉發給沙箱外的主進程,在主進程中完成拖拽操作。也就是說,真正完成拖拽操作是在沙箱外的IE主進程內。兩個進程的函數調用情況大致如下:

                    IE子進程(沙箱中):

                    #!c++
                    MSHTML!CDoc::DoDrag
                    -->MSHTML!CDragDropManager::DoDrag
                    -->combase!ObjectStubless
                        --> … 發送ALPC消息給IE主進程
                    

                    IE主進程:

                    #!c++
                    … 接收IE子進程發來的ALPC消息
                        -->RPCRT4!Invoke
                    -->IEFRAME!CShdocvwBroker::PerformDoDragDrop
                                -->IEFRAME!CShdocvwBroker::PerformDoDragDropThreadProc
                                    -->ole32!DoDragDrop
                    

                    0x05 IE沙箱對拖拽操作的安全限制


                    在IE沙箱中,我們是可以直接調到Broker中的函數的。通過自己創建一個IEUserBroker,再由IEUserBroker創建一個ShdocvwBroker,我們就可以調到主進程中的IEFRAME!CShdocvwBroker::PerformDoDragDrop函數。調用的實現方法大致如下:

                    #!c++
                    typedef HRESULT(__stdcall *FuncCoCreateUserBroker)(IIEUserBroker **ppBroker);
                    IIEUserBrokerPtrCreateIEUserBroker()
                    {
                        HMODULE hMod = LoadLibraryW(L"iertutil.dll");
                        FuncCoCreateUserBrokerCoCreateUserBroker;
                        CoCreateUserBroker = (FuncCoCreateUserBroker)GetProcAddress(hMod, (LPCSTR)58);
                        if (CoCreateUserBroker)
                        {
                            IIEUserBrokerPtr broker;
                            HRESULT ret = CoCreateUserBroker(&broker);
                            return broker;
                        }
                        return nullptr;
                    }
                    
                    IIEUserBrokerPtr broker = CreateIEUserBroker();
                    IShdocvwBroker* shdocvw;
                    broker->BrokerCreateKnownObject(clsid_CIERecoveryStore, _uuidof(IRecoveryStore), (IUnknown**)&shdocvw);
                    shdocvw->PerformDoDragDrop(HWND__ *,IEDataObjectWrapper *,IEDropSourceWrapper *,ulong,ulong,ulong *,long *);
                    

                    拖拽功能最終是調用ole32!DoDragDrop函數來實現的,DoDragDrop所需的參數都可以由PerformDoDragDrop函數傳入(參考0x03章節中DoDragDrop函數的參數信息)。至此,我們已經可以從沙箱內直接走到沙箱外的ole32!DoDragDrop函數,且傳入參數可控。而要模擬鼠標拖拽操作,有兩個思路:

                    1. 使用0x02章節中所講的直接調用ole32.dll內部函數的方法;
                    2. 調用API改變鼠標位置。

                    對于第一種方法,由于我們是在沙箱內,只能通過Broker接口的代理才能從沙箱中出來,進入到IE主進程的進程空間。所以我們并不能調到主進程中dll的內部函數,進而這種方法是不可行的。

                    第二種方法,如果我們能夠改變鼠標的位置,那么在ole32!DoDragDrop函數內部通過鼠標位置獲取目標窗口信息的步驟就會成功通過,就能夠完成模擬鼠標拖拽的目標。然而實驗過程中,我們發現在IE沙箱中是無法通過API來改變鼠標指針位置的。下面來具體說明這個問題。

                    筆者想到的能夠改變鼠標指針位置的方法有兩種:

                    1. 通過SendInput函數模擬鼠標動作。SendInput函數從用戶態到內核態的函數調用關系如下所示:

                      #!c++
                      User32!SendInput
                      -->user32!NtUserSendInput
                              -->win32k.sys!NtUserSendInput
                                  -->win32k.sys!xxxSendInput
                                      -->win32k.sys!xxxMouseEventDirect
                      
                    2. 通過SetCursorPos函數改變鼠標指針位置。SetCursorPos函數從用戶態到內核態的函數調用關系如下:

                      #!c++
                      user32!SetCursorPos
                              -->user32!SetPhysicalCursorPos
                                  -->user32!NtUserCallTwoParam
                                      -->win32k.sys!NtUserCallTwoParam
                                          -->win32k.sys!zzzSetCursorPos
                                              -->win32k.sys!zzzSetCursorPosByType
                      

                    先來看SendInput,如果在IE沙箱中直接調用SendInput函數來改變鼠標指針位置的話,會返回0x5拒絕訪問錯誤,這是因為IEShims.dll中對SendInput函數做了hook,在hook函數中做了處理。具體做處理的函數位置如下:

                    #!c++
                    IEShims.dll!NS_InputQueueLowMIC::APIHook_SendInput
                        -->IEFRAME!FrameUtilExports::PreSendInput
                            -->ShimHelper::PreSendInput
                    

                    這個hook很容易繞過,我們直接調用NtUserSendInput即可,不過這個函數沒有導出,需要通過函數偏移硬編碼它的地址。

                    直接調用NtUserSendInput,該函數不返回錯誤,但是鼠標指針的位置并沒有改變。究其原因,函數調用的失敗是由于UIPI(User Interface Privilege Isolation)的限制。調用SetCursorPos函數也會出現相同的情況。

                    UIPI是從Windows Vista開始系統新加入的一項安全特性,它在Windows內核中實現,具體位置如下:

                    win32k!CheckAccessForIntegrityLevel
                    

                    在Win8.1上,這個函數的邏輯如下:

                    #!c++
                    signed int __stdcallCheckAccessForIntegrityLevelEx(
                      unsigned intCurrentProcessIntegrityLevel, 
                    intCurrentIsAppContainer, 
                      unsigned intTargetProcessIntegrityLevel, 
                    intTargetIsAppContainer)
                    {
                      signed int result;
                      if (gbEnforceUIPI&&CurrentProcessIntegrityLevel<TargetProcessIntegrityLevel )
                        result = 0;
                    esleif ( gbEnforceUIPI&&CurrentProcessIntegrityLevel == TargetProcessIntegrityLevel )
                        result = (CurrentIsAppContainer == TargetIsAppContainer || 
                    TargetIsAppContainer == -1 || 
                    CurrentIsAppContainer == -1) || 
                    SeIsParentOfChildAppContainer(
                    gSessionId, 
                    CurrentIsAppContainer, 
                    TargetIsAppContainer);
                      else
                        result = 1;
                      return result;
                    }
                    

                    這個函數首先判斷源進程和目標進程的Integrity Level,若源IL小于目標IL,則拒絕;若源IL大于目標IL,則允許。接著判斷AppContainer屬性,若源和目標的IL相等,且均運行在AppContainer中,則判斷二者是否滿足SeIsParentOfChildAppContainer函數的約束,滿足則允許,否則拒絕。

                    注:ProcessIntegrityLevel和IsAppContainer參數都是從EPROCESS->Win32Process結構中取出來的,這是一個內部結構。SeIsParentOfChildAppContainer是ntoskrnl中的一個內部函數。

                    0x06 總結


                    本文詳細解析了IE沙箱對于拖拽操作的安全策略,先后分析了IE沙箱的拖拽限制策略、Explorer進程在拖拽限制上存在的問題、ole32.dll實現拖拽的內部原理、IE在沙箱中實現拖拽操作的原理和IE沙箱對拖拽操作進行安全限制的具體位置和實現細節。IE沙箱通過在IEShims.dll中hook特定函數和借助系統的UIPI特性(Windows Vista以上)對拖拽操作進行了有效的安全限制。

                    0x07 參考資料


                    1. Understanding and Working in Protected Mode Internet Explorer https://msdn.microsoft.com/en-us/library/bb250462
                    2. OLE Drag and Drop http://www.catch22.net/tuts/ole-drag-and-drop
                    3. How to Implement Drag and Drop between Your Program and Explorer http://www.codeproject.com/Articles/840/How-to-Implement-Drag-and-Drop-Between-Your-Progra
                    4. WINDOWS VISTA UIPI https://www.coseinc.com/en/index.php?rt=download&act=publication&file=Vista_UIPI.ppt.pdf

                    0x08 致謝


                    感謝Wins0n在ole32逆向上的幫助;

                    感謝FlowerCode在思路和技術難點上的指點。

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

                                      这里只有精品视频