There is a kind of vulnerability that uses the flaw of whitelist applications in ElevationPolicy settings to accomplish sandbox bypass. A DragDrop policy setting similar to ElevationPolicy in the IE registry attracts our attention. In this post, the writer will try every possible means to break IE sandbox from the perspective of an attacker by analyzing all obstacles ahead to detail the drag drop security policy of IE sandbox.

0x01 DragDrop Policy of IE sandbox

Among all IE sandbox bypass techniques, there’s one that uses the issue of whitelist applications in ElevationPolicy to execute arbitrary code. In the registry, there is a configuration called DragDrop similar to ElevationPolicy. The specific registry path is:

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

As shown in the figure:

Here are the meanings for values of the DragDrop policy:

0: If the target window is not valid DropTarget, reject;

1: If the target window is valid DropTarget, but cannot copy contents;

2: Use popup to ask for user’s permission. If allowed, copy contents; to the target window;

3: Allow silent drag drop.

In a clean Windows 8.1, there are 3 applications under the DragDrop directory by default: iexplore.exe, explorer.exe, notepad.exe. The policy value for each is 3. When the policy value of the target application is 2 and drag a file to the target window, IE would pop up a prompt like this:

0x02 DragDrop Issue for the Explorer process

When drag files from IE to Explorer, although the DragDrop policy value is set to 3, IE won’t pop up anything, but the Explorer process will pop a prompt like this:

Of course, when we drag files from IE to the tree folder structure of Explorer’s sidebar, no prompt will pop up. This may be an imperfection in the implementation of the Explorer application. In second thought, if we can simulate mouse operations of drag and drop in IE sandbox, we’ll be able to use this Explorer issue to cross the security boundary of the IE sandbox.

0x03 Finish OLE Drag Drop without the Mouse

OLE dragdrop is a generic file dragging method. It uses the design for OLE interface to implement the drag and drop operations, making it generic and modular. The OLE dragdrop technique includes three basic interfaces:

  • IDropSource Interface: represents the source object where the dragdrop operation is issued, implemented by source object;

  • IDropTarget Interface: represents the target object on which the dragdrop operation is taken, implemented by target object;

  • IDataObject Interface: represents the data transferred during the dragdrop operation, implemented by source object;

This figure describes the key components required by a complete OLE dragdrop operation:

To simulate the drag and drop operations of a mouse, all we need is to implement IDropSourceinterface and IDataObject interface. The core of normal OLE dragdrop operation is to call the ole32!DoDragDrop function, here is the function prototype:

1
2
3
4
5
6
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
);

Information on the source object and data of dragdrop operation is included in parameters of DoDragDrop. Within the DoDragDrop function, it uses the position of the mouse pointer to obtain information about the source object. In the following, the writer gives a method that uses code emulator instead of mouse to achieve dragdrop operation.

To simulate the dragdrop operation from a mouse by using code emulator, that is to isolate the GUI operation part from the DoDragDrop function, find the function that does dragdrop operation, pass the required parameter to it to finish the operation. In the case of ole32.dll 6.1.7601.18915 in Win7, I’ll illustrate the internal implementation of DragDrop.

Here is the main logic of Ole32!DoDragDrop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 is a constructed function. Its important initial operations include:

1
2
3
4
ole32!GetMarshalledInterfaceBuffer
ole32!ClipSetCaptureForDrag
--ole32!GetPrivateClipboardWindow
ole32!CreateSharedDragFormats

The next While loop will determine the dragdrop status. At last, CompleteDrop will complete the operation, the key function call is like this:

1
2
3
4
5
6
7
8
9
ole32!CDragOperation::UpdateTarget
-ole32!CDragOperation::GetDropTarget
--ole32!PrivDragDrop
ole32!CDragOperation::DragOver
--ole32!CDropTarget::DragOver
--ole32!PrivDragDrop
ole32!CDragOperation::CompleteDrop
--ole32!CDropTarget::Drop
--ole32!PrivDragDrop

As it can be seen, it’s the ole32!PrivDragDrop function that finally does the dragdrop operation, by using the hardcoded offset of the function address to call the internal function in ole32.dll. We define a DropData function to simulate the dropdrag operation from the mouse, the input parameters of which are the target windows handle and the IDataObject pointer of the file being dragged, the main logic is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}

The target window handle can be obtained through the FindWindow function. There are two methods to pack a DataObject and get the pointer of its IDataObject interface:

  • Write your own C++ class to implement the IDataObject interface;

  • Use the existing implementation in the class library, for instance, both MFC and Shell32 provide related class to implement the DragDrop interface.

The writer explains how to use MFC class library to pack a DataObject and get the pointer of its IDataObject interface, here is the implementation code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 Drag Drop Implementation of IE Sandbox

When we use mouse to does the dragdrop operation in IE sandbox, the IE tab process in the sandbox will transfer data to the main process outside the sandbox through ShdocvwBroker. That’s to say, the actual dragdrop operation is completed in the IE main process outside of the sandbox. The function calls for the two processes are almost like the following:

IE sub process (inside the sandbox):

1
2
3
4
MSHTML!CDoc::DoDrag
--MSHTML!CDragDropManager::DoDrag
--combase!ObjectStubless
-- … send ALPC message to OE main process

IE main process:

1
2
3
4
5
… receive the ALP message from IE sub process
--RPCRT4!Invoke
--IEFRAME!CShdocvwBroker::PerformDoDragDrop
--IEFRAME!CShdocvwBroker::PerformDoDragDropThreadProc
--ole32!DoDragDrop

0x05 Security Limits that IE Sandbox Applies to the Drag Drop Operation

In IE sandbox, we can directly call the function in Broker. By building an IEUserBroker and using the IEUserBroker to build an ShdocvwBroker, we will be able to call the IEFRAME!CShdocvwBroker::PerformDoDragDrop function in the main process. The calling method is like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef HRESULT(__stdcall *FuncCoCreateUserBroker)(IIEUserBroker **ppBroker);
IIEUserBrokerPtrCreateIEUserBroker()
{
HMODULE hMod = LoadLibraryW(Liertutil.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 *);

The DragDrop function is eventually by calling the ole32!DoDragDrop function, all the parameters that DoDragDrop requires can be passed by the PerformDoDragDrop function(refer to the parameter information of the DoDragDrop function mentioned in chapter 0x03). At this time, we already can walk through inside the sandbox to the outside ole32!DoDragDrop function and pass the controllable parameters. However, there are two principles to simulate the dragdrop operation of a mouse:

  • Use the method mentioned in chapter 0x02 to directly call the internal function in ole32.dll;

  • Call API to change the position of the mouse.

For the first method, since we are in the sandbox, we can only use the proxy for Broker interface to get out of the sandbox and get in the process space for the IE main process. So we cannot call the internal function of the dell in the main process, further, this method is not feasible.

The second method, if we can change the position of the mouse, then inside the ole32!DoDragDrop function, we can use mouse position to get information on the target window. However, during experiment, we notice that it’s not possible to change mouse position through API inside the sandbox. The next case will illustrate this problem.

The writer can think of two ways to change the mouse position:

1.Simulate mouse movements through the SendInput function. The following shows the calling connection of the SendInput function from user mode to kernel mode:

1
2
3
4
5
User32!SendInput
--user32!NtUserSendInput
--win32k.sys!NtUserSendInput
--win32k.sys!xxxSendInput
--win32k.sys!xxxMouseEventDirect

2.Change mouse position through the SetCursorPos function. The following shows the calling connection of the SetCursorPos function from user mode to kernel mode:

1
2
3
4
5
6
user32!SetCursorPos
--user32!SetPhysicalCursorPos
--user32!NtUserCallTwoParam
--win32k.sys!NtUserCallTwoParam
--win32k.sys!zzzSetCursorPos
--win32k.sys!zzzSetCursorPosByType

First is SendInput, if directly calling the SendInput function in IE sandbox, it returns 0x5 access denied error, because the SendInput function is hooked in IEShims.dll and the hook function is processed. The specific function position requires processing is:

This hook is easy to bypass, we’ll directly call NtUserSendInput, but this function has no export, that’s why it’s required to hardcode its address through function offset. Directly calling the NtUserSendInput function, it returns no error, but the position of the mouse doesn’t change. Because the failure of the call is caused by the limits of UIP(User Interface Privilege Isolation). It’s the same when calling the SetCursorPos function.

UPI is a new security feature since Windows Vista, it’s implemented in the Windows kernel, and the specific position is as the following:

1
win32k!CheckAccessForIntegrityLevel

In Win8.1, this is the logic of the function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
signed int __stdcall CheckAccessForIntegrityLevelEx(
unsigned int CurrentProcessIntegrityLevel,
int CurrentIsAppContainer,
unsigned int TargetProcessIntegrityLevel,
int TargetIsAppContainer)
{
signed int result;
if (gbEnforceUIPICurrentProcessIntegrityLevelTargetProcessIntegrityLevel )
result = 0;
esle if ( gbEnforceUIPICurrentProcessIntegrityLevel == TargetProcessIntegrityLevel )
result = (CurrentIsAppContainer == TargetIsAppContainer ||
TargetIsAppContainer == -1 ||
CurrentIsAppContainer == -1) ||
SeIsParentOfChildAppContainer(
gSessionId,
CurrentIsAppContainer,
TargetIsAppContainer);
else
result = 1;
return result;
}

This function will first determine the integrity level of the source process and the target process. If the integrity level of the source process is lower than that of the target process, reject; if the integrity level of the source process is higher than that of the target process, permit. Next it determines the property of AppContainer. If it equals to the integrity of the source process and is running in AppContainer, then determine if the two satisfy the limits by the SeIsParentOfChildAppContainer function. If it satisfies, permit; if not, reject.

Note: parameters, such as, ProcessIntegrityLevel and IsAppContainer, are extracted from the EPROCESS-Win32Process structure, this is an internal structure. SeIsParentOfChildAppContainer is an internal function in ntoskrnl.

0x06 Summary

This post details the security policy that IE sandbox applies to dragdrop operation, which analyzes the limits policy of IE sandbox for the dragdrop operation, the problems that the Explore process has on dragdrop, the internal principle how ole32.dll implement the dragdrop and how IE implements the dragdrop operation in the sandbox, as well as the position and implementation detail the security limits are put. IE sandbox usually applies effective security limits on the dragdrop operation by hooking specific function in IEShims.dll and the UPI feature in Windows (later than Windows Vista).

0x07 Reference

  1. Understanding and Working in Protected Mode Internet Explorer

  2. OLE Drag and Drop

  3. How to Implement Drag and Drop between Your Program and Explorer

  4. WINDOWS VISTA UIPI

0x08 Acknowledgement

Thanks Wins0n for helping me with ole32 reversing and FlowerCode for helping with my thoughts and solving difficulties.

Translated by WooYun Drops.