Windows: User Mode Font Driver Thread Permissions EoP
Platform: Windows 10 Build 10130
Class: Elevation of Privilege
The host process for the UMFD runs as a normal user but with a heavily restrictive process DACL. It’s possible execute arbitrary code within the context of the process because it’s possible to access the processes threads leading to local EoP.
NOTE: This was tested on the latest available build on Windows 10. I don’t know if the final version will change the functionality to fix this vulnerability.
When a custom font is used in Windows 10 the User Mode Font Driver comes into play. This is initialized by a call from the kernel into the user sessions winlogon process which in turn spawns a new copy of fontdrvhost.exe. The process is started inside an appcontainer heavily restricting what resources it could access if a font bug was able to compromise it. However win32k exposes some additional calls to the UMFD for its own purposes, some of which are potentially dangerous. For that reason (presumably) winlogon creates the process with a specific DACL limiting access to the process and initial thread to SYSTEM only.
There’s a few problems with this approach, firstly it’s still running in the context of the user and includes the user’s environment variables such as PATH. This might mean if any badly written code later relies on the drive mapping or PATH there could be issues. More serious however is the specified DACL only applies to the process object and the initial thread object, but not to any subsequent thread. Therefore those threads get the default DACL from the process token (which is never changed) and are marked as owned by the current user, so the DACL could be rewritten anyway. This is a problem as with write access to the threads it’s possible to change their context and redirect execution to an arbitrary location. As the token is a lowbox token this can even be done in low integrity processes such as IE PM.
The exploitation is made trickier by the fact that you can’t directly read or write the process’ memory. Still one thing you could do is redirect the thread to LoadLibraryW and pass it the known address of a string. This can either be a string in a loaded library and rely on the path environment variable to allow it to be resolved or in something like the GDI heap.
Once in the UMFD process you can then send some of the specific Win32k escape codes. For example there’s one currently called UmfdEscEngCreateFile which will open (for read or write) a couple of files in system32. The open is done in kernel mode, with no forced access check (even though an impersonation is performed) and the handle returned to user mode. This is dangerous for a number of reasons, specifically that the NTFS driver will mark the file as having create symbolic link permissions because it’s opened in kernel mode which means the caller could set a file symbolic link. Then it could reopen the file and it would be able create an arbitrary file on disk. This hasn’t been completely tested however but it’s an example of a dangerous call, of course it could just be a vestigial feature which will be removed in release builds as the code is pretty dangerous and doesn’t even work as expected.
This issue could probably be fixed in a few ways, firstly the default token DACL should be set so that it maintains the security, assuming this is possible. Also you’d probably need to set OWNER_RIGHTS SID otherwise the user could just open the thread and rewrite its DACL. Also not using the actual user’s environment would probably be a good idea although not necessarily a complete fix. Finally presumably the process mitigation to only allow signed modules could be enabled which would complicate exploitation especially in the presence of CFG.
Proof of Concept:
I’ve provided a PoC which just crashes the fontdrvhost process at a predictable address. It’s only built for 32 bit version of Windows 10 but presumably it would work on 64 bit version as well. The password for the archive is "password".
1) Copy the PoC to a directory
2) Execute the PoC, if it wasn’t already a new instance of fontdrvhost.exe should have started. You might want to attach a debugger at this point.
3) Click the Do Exploit button, if at this point the fontdrvhost process doesn’t crash open a new copy of the PoC just to kick the threads inside the process.
It’s not possible to influence the fontdrvhost process.
Thread execution redirected to an arbitrary address of 0x55555555.
Proof of Concept: