matu3ba / win32k-mitigation

Test win32k.sys system call filtering (kernel32 + ntdll only), explicit file handle inheritance.
MIT License
0 stars 0 forks source link

Win32k mitigation

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

Usage:

# running original c code by user @mtth-bfft
zig build runc
# unused
zig build runz
# ported tests
zig build test

Zig version

Many bindings added, kernel32 and ntdll only.

Windows SHENNANIGAN

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.

Documentation of the C version

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.

Filtering mechanism

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 :

Compatibility issues

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) :

Dialog box with status 0xC0000142

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...

TODO

References

Tracing heap on Windows

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

Using AppVerifier

Select binary path Start the program Observe that there are no hints

Explicit file descriptor inheritance test

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

Least permissions process techniques

see https://github.com/matu3ba/win32k-mitigation/issues/3 and https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87151933.

SITRE Windows recommendations and rules

https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87151933

more sandboxing techniques

Look into chrome code for Windows to understand the techniqes.