The win32k mitigation policy is a per-thread mitigation which, if enabled,
denies the thread the right to call most graphics-related system call within
wink32.sys
. There are way too many of them (~1000, compared to the kernel's
400 ones), and most of their implementations is old code, so this mitigation is
quite important since it blocks a high-value targets for vulnerability exploiters.
Usage:
# running original c code by user @mtth-bfft
zig build runc
# unused
zig build runz
# ported tests
zig build test
Many bindings added, kernel32 and ntdll only.
It looks like Virtual Memory is used up (temporary?) even in the case of the dynamic library not getting loaded. This is very unfortunate, because unsuccessful loading of the library does not return a pointer t othe library/module to free the memory region and this also makes automating testing of the feature potentially (very) cumbersome as a process with sufficient virtual memory must be reserved.
This project is just a test case to try the new win32k.sys system call filtering mitigation in Windows 10.
The win32k mitigation policy is a per-thread mitigation which, if enabled,
denies the thread the right to call most graphics-related system call within
wink32.sys
. There are way too many of them (~1000, compared to the kernel's
400 ones), and most of their implementations is old code, so this mitigation is
quite important since it blocks a high-value target for vulnerability
researchers. Unfortunately, only Microsoft Edge and Chrome use it as part of
their sandbox, because there is no supported way to use it.
When a thread first calls a graphics syscall, it gets converted to a "GUI thread". This conversion happens once, and so that's where the mitigation ended up being implemented, probably for performance considerations. The problem is, enforcing the mitigation on an existing process leaves room for many threads to already be converted and thus allowed to make arbitrary system calls.
The mitigation can be enabled in one of two ways :
PROC_THREAD_ATTRIBUTE_LIST
containing a
PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY
attribute. This will necessarily
affect all the threads.When enabled, the mitigation only allows startup of small programs compiled with no graphics imports and linked for the Console subsystem.
However, when linked against a graphics library (say, user32.dll
),
CreateProcess()
returns an error, and a message box shows an unfriendly
0xC0000142
NT status to the user
(STATUS_DLL_INIT_FAILED)
:
A detour in WinDBG shows that, right after it loaded imm32.dll
(as a
dependency of user32.dll), ntdll!_LdrpInitialize()
called
ntdll!LdrpInitializationFailure()
which, in turn, called NtRaiseHardError()
with that status code.
Since debugging processes during their startup (when nothing has been
initialized yet) is hard, I tried importing all these graphics library at
runtime (using LoadLibrary
) instead of load time (using an import in the PE
header). Results of the c version are a bit clearer:
[.] Running from Z:\win32k-mitigation.exe
[.] Child process created: pid 5000
[.] Child process started successfully
[+] Child running with filtered Win32k syscalls
[.] Trying all gdi32full dependencies:
[!] Unable to load GDI32.dll: code 8
[!] Unable to load USER32.dll: code 8
[.] Trying all user32 dependencies:
[!] Unable to load GDI32.dll: code 8
[.] Trying all gdi32 dependencies:
[!] Unable to load api-ms-win-gdi-internal-uap-l1-1-0.dll: code 8
[+] Child exiting successfully
[.] Child process exited with code 0x00000000
[.] All done, return code 0
With runtime loading, we can see exactly at which loading step LoadLibrary()
fails (with a ERROR_NOT_ENOUGH_MEMORY
code, this time (?)). It seems loading
any one of user32.dll or gdi32.dll will fail because of
api-ms-win-gdi-internal-uap-l1-1-0.dll
. This is not an actual DLL name, but
an
ApiSet,
so we have to get back to runtime debugging to sort it out. To be continued...
https://learn.microsoft.com/en-us/windows-hardware/test/wpt/memory-footprint-optimization-exercise-2
in cmd with admin capabilities
REM reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\MemoryTestApp.exe" /v TracingFlags /t REG_DWORD /d 1 /f
# reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\main_ntdll_only.exe" /v TracingFlags /t REG_DWORD /d 1 /f
# reg delete "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\main_ntdll_only.exe"
reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\child_ntdll_only.exe" /v TracingFlags /t REG_DWORD /d 1 /f
reg delete "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\child_ntdll_only.exe"
WPRUI
> Heap Usage
> VirtualAlloc Usage
WPA
> VirtualAlloc Commit Lifetime
> Lifetime by Process
Select binary path Start the program Observe that there are no hints
Goal is to prevent leaking handles into subprocesses.
Handles can be attached with a security handle to make them only inheritable in certain contexts, but this security handle can not be applied to previously created handles. In this case only providing the explicit set of handles provides a race-free way to let one process inherit the selected handles without leaking them into another parallel spawned process. See https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873
see https://github.com/matu3ba/win32k-mitigation/issues/3 and https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87151933.
https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87151933
Look into chrome code for Windows to understand the techniqes.