IE沙箱拖拽安全策略解析
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
如下圖所示:
DragDrop Policy值的含義如下:
0:目標視窗是無效的DropTarget,拒絕;
1:目標視窗是有效的DropTarget,但無法複製內容;
2:彈框詢問使用者,允許後將內容複製到目標視窗;
3:靜默允許拖拽。
在一個乾淨的Windows 8.1系統上,DragDrop目錄下預設有三個程式:iexplore.exe, explorer.exe, notepad.exe,它們的Policy值都是3。當目標程式的Policy值為2時,向目標程式視窗拖拽檔案,IE會彈出一個提示框,如下圖所示:
0x02 Explorer程式的拖拽問題
在從IE往Explorer上拖拽檔案時,雖然DragDrop Policy值設定為了3,IE不會彈框,但是Explorer程式會會彈一個提示框,如下圖所示:
然而,當我們從IE中向Explorer側邊欄的樹形資料夾結構中拖拽檔案時,並不會彈框。這應該是Explorer程式實現上的一個疏漏。進一步設想,如果我們能夠在IE沙箱中透過程式模擬滑鼠的拖拽操作,那麼就能夠利用Explorer的這個問題跨越IE沙箱的安全邊界。
0x03 不使用滑鼠完成OLE拖拽
OLE拖拽是一種通用的檔案拖拽方式,它採用了OLE的介面設計方法來實現拖拽功能,使得拖拽的實現通用且模組化。OLE拖拽技術包含三個基本介面:
- IDropSource介面:表示拖拽操作的源物件,由源物件實現;
- IDropTarget介面:表示拖拽操作的目標物件,由目標物件實現;
- IDataObject介面:表示拖拽操作中傳輸的資料,由源物件實現。
下圖描述了一個完整的OLE拖拽操作需要實現的關鍵元件:
我們要模擬滑鼠拖拽,則只需要實現IDropSource
和IDataObject
介面。正常的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介面指標的方法有兩種:
- 自己編寫C++類實現IDataObject介面;
- 使用現有類庫中的實現,如: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函式,且傳入引數可控。而要模擬滑鼠拖拽操作,有兩個思路:
- 使用0x02章節中所講的直接呼叫ole32.dll內部函式的方法;
- 呼叫API改變滑鼠位置。
對於第一種方法,由於我們是在沙箱內,只能透過Broker介面的代理才能從沙箱中出來,進入到IE主程式的程式空間。所以我們並不能調到主程式中dll的內部函式,進而這種方法是不可行的。
第二種方法,如果我們能夠改變滑鼠的位置,那麼在ole32!DoDragDrop函式內部透過滑鼠位置獲取目標視窗資訊的步驟就會成功透過,就能夠完成模擬滑鼠拖拽的目標。然而實驗過程中,我們發現在IE沙箱中是無法透過API來改變滑鼠指標位置的。下面來具體說明這個問題。
筆者想到的能夠改變滑鼠指標位置的方法有兩種:
透過SendInput函式模擬滑鼠動作。SendInput函式從使用者態到核心態的函式呼叫關係如下所示:
#!c++ User32!SendInput -->user32!NtUserSendInput -->win32k.sys!NtUserSendInput -->win32k.sys!xxxSendInput -->win32k.sys!xxxMouseEventDirect
透過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 參考資料
- Understanding and Working in Protected Mode Internet Explorer https://msdn.microsoft.com/en-us/library/bb250462
- OLE Drag and Drop http://www.catch22.net/tuts/ole-drag-and-drop
- 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
- WINDOWS VISTA UIPI https://www.coseinc.com/en/index.php?rt=download&act=publication&file=Vista_UIPI.ppt.pdf
0x08 致謝
感謝Wins0n在ole32逆向上的幫助;
感謝FlowerCode在思路和技術難點上的指點。
相關文章
- 開源自己寫的一個拖拽庫,相容到IE82019-02-16
- 在補丁上戳個洞——利用已經被修復的漏洞實現IE沙箱逃逸2020-08-19
- python沙箱逃逸2020-02-16Python
- 2、安全策略2024-09-27
- Tungsten Fabric架構解析丨TF基於應用程式的安全策略2020-02-13架構
- 模板注入&沙箱逃逸2024-05-29
- 瀏覽器安全策略說之內容安全策略CSP2020-08-19瀏覽器
- PostgreSQL 行安全策略2019-08-23SQL
- 網路安全策略2021-03-09
- ie中jQuery無法解析xml檔案的解決方案2021-09-09jQueryXML
- 動手寫 js 沙箱2019-03-07JS
- 瀏覽器沙箱模型2018-06-02瀏覽器模型
- 淺析微前端沙箱2023-09-18前端
- python 棧幀沙箱逃逸2024-08-01Python
- python棧幀沙箱逃逸2024-06-11Python
- pwn 之 沙箱機制2022-04-25
- 完美拖拽2020-10-22
- UMG————拖拽2020-11-19
- 從 simplemde 寫入 + inline-attachment 圖片拖拽上傳 到 parsedown 解析2019-03-23inline
- 一行程式碼解決各種IE相容問題,IE6,IE7,IE8,IE9,IE102019-02-16行程IE9IE10
- openGauss 設定安全策略2024-04-30
- Pod安全策略:PodSecurityPolicy(PSP)2024-06-04
- Node.js 沙箱環境2018-05-14Node.js
- 從面相過程的拖拽到物件導向的拖拽再到簡易的元件拖拽2018-03-14物件元件
- vxe-table 樹表格拖拽排序,支援拖拽到空節點,直接拖拽成子級2024-12-11排序
- js拖拽技能2018-05-25JS
- 滑鼠拖拽事件2019-10-30事件
- JavaScript拖拽效果2020-03-16JavaScript
- 拖拽 驗證2022-07-12
- Javascript 實現 Textarea 自動伸縮,相容IE6、IE7、IE8、IE9...2019-01-06JavaScriptIE9
- 使用semanage管理SELinux安全策略2020-08-01Linux
- js拖拽:右下角拖拽改變圖片大小2019-01-10JS
- 詳解javascript拖拽(二)拖拽的應用及示例2018-12-24JavaScript
- 聊聊 QianKun JS 沙箱的那些事2023-09-26JS
- JVM--java沙箱安全機制2020-10-11JVMJava
- 簡單的沙箱反除錯2021-04-19除錯
- HTML5拖拽API實現vue樹形拖拽元件2018-05-10HTMLAPIVue元件
- 基於Vue實現拖拽升級(九宮格拖拽)2021-09-09Vue