Upload
vuongkien
View
215
Download
3
Embed Size (px)
Citation preview
Pointers and HandlesA Story of Unchecked Assumptions in the Windows Kernel
Alex [email protected]
Black Hat USA ‘08
1
About Me
Former lead kernel developer of ReactOS
Open source implementation of the Windows OS
Wrote most of the kernel (except VM and PnP)
Teaching Windows Internals with David Solomon
Writing Windows Internals, 5th Edition
Intern at Apple -- Embedded Kernel Technologies
2
About this Talk
Two pairs of bugs
Two are NULL-pointer dereferences
Two are “special” local DoS bugs
Second set related to a new class of local DoS attacks
Requires understanding of several internal NT behaviors
3
Outline
NULL Pointer Dereferences
Windows API Design
Windows GUI Subsystem
The Assumption
Exploiting
Promoting from DoS to Ring Privilege Violation
4
Outline
Protected Handle Close
Windows Object Manager
System Call PreviousMode Mechanism
The Assumption
Exploiting
Developer Guidance and Q&A
5
Windows Simplified Design
6
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
High Level API
User-Mode ComponentsApplications (Notepad.exe)
High Level Libraries (Msi.dll, Comctl32.dll, .NET)
Low Level Libraries
“base” -> Kernel32 & Parts of Advapi32.dll (Registry)
GDI/User -> User32.dll & Gdi32.dll
Subsystem Process (Csrss.exe)
Native Library (Ntdll.dll)
Kernel-Mode Components
Kernel (Ntoskrnl.exe)
HAL (Hal.dll)
Drivers (*.sys)
GUI Subsystem (Win32k.sys)
DirectX Kernel Driver (Dxgkernel.sys)
Video Drivers (atikmdag.sys, nvlddkm.sys)
Behaviors and Code Paths
Functionality “native” (built-in) to the kernel:
Translated by Ntdll.dll
Sent to kernel
Functionality specialized to Windows API:
Performed by, or through, Csrss.exe (ie: consoles)
Sent to Win32k.sys (ie: video card display access)
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS Kernel
Console API
DriversWin32 GUI Subsystem
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS KernelWin32 GUI Subsystem
Console API
Drivers
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS KernelWin32 GUI Subsystem
Console API
Drivers
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS KernelWin32 GUI Subsystem
Console API
Drivers
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS KernelWin32 GUI Subsystem
Console API
Drivers
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS KernelWin32 GUI Subsystem
Console API
Drivers
WindowsSubsystem
Windows API ArchitectureWindows
Applications
Core API
Graphics (GDI) & User
API
NTDLL
NTOS KernelWin32 GUI Subsystem
Console API
Drivers
WindowsSubsystem
Ntoskrnl.exe
Interrupts, Traps, Booting
Memory Management
Thread Scheduling
Process Management
Synchronization, Queues
Security & Auditing
I/O, PnP & Power NTOS Kernel
Process Manager
Scheduler
Registry Manager
Memory Manager
Security Manager
I/O Manager
Win32k.sys - User
Window Stations
Destops
Input & Message Queues
Windows & Classes
Menus & Hooks
GUI Process & Thread Data Win32k.sys
Message Passing
User Handles
Window Manager
Mouse & Keyboard
IME & Kbd Layouts
Monitors
Win32k.sys - GDI
Brushes, Pens, Cursors, Surfaces, Lines, DCs, Paths, Regions, etc...
ICM Color Matching
API for Video/Print Drivers
Floating Point Math Library
DirectX, OpenGL Support Win32k.sys
GDI Objects
Graphics Engine
DirectX
Displays
Fonts
Printing
NULL Pointer Dereferences
17
GUI Thread Promotion
Every Windows thread starts as a non-GUI thread
12 kb stack
As soon as the first graphics system call (ID is >= 0x1000) is made, promoted to GUI thread (PsConvertToGuiThread)
Stack grows to 60kb (KeGrowKernelStack)
Win32k.sys thread callout is called (W32ThreadCallout)
xxxCreateThreadInfo
Most internal Win32k.sys functions start by “xxx”
Perverted developers? No, “yyy” and “zzz” functions also exist!
Very different mindset from strict, organized and clear kernel naming scheme
xxxCreateThreadInfo initializes the Win32k state (stored in W32THREAD structure) for the new GUI thread
W32THREAD
W32THREAD
PQ (Input Queue)
PKL (Keyboard Layout)
PCTI (Shared Client Data)
PDESKTOP (Owner Desktop)
PSMS (SendMessage Queue)
PPI (Parent Process Info)
PMENUSTATE (Menu State)
PHOOK (Registered Hooks)
...and more!
Our Interest
xxxCreateThreadInfo can be called for threads that CSRSS owns
These threads are special
CSRSS does not belong to a desktop like other threads -- it works across all desktops
Can’t attach to their input queue
CSRSS is a trusted, privileged process -- certain Win32k system calls can only be done from within CSRSS
CSRSS Privileged APIs
NtUserSetInformationThread (Process Thread State)
NtUserSetInformationProcess (Process GUI State)
NtUserInitialize & NtUserProcessConnect (Win32k Init)
NtUserConsoleControl (Console Support)
NtGdiFullscreenControl (Full-Screen Support)
xxxRemote* APIs (Remote Control)
More...
CSRSS & Integrity Level
CSRSS overrides integrity level (can access any window handle)
Input queues owned by CSRSS are always Medium IL
Console applications’ input queues are always accessible
Allows low IL applications to send messages to high IL command prompt
CSRSS Security
CSRSS is a system process
Only administrators can access it
Administrators could modify the thread/process state with other methods (such as loading a driver or using the debugger)
Check is done by comparing the kernel process pointer (EPROCESS) of the current process, with the saved CSRSS pointer -- impossible to fake
ExhibitA
NtUserGetThreadState:
Code has ASSERT(CurrentW32Thread->Desktop != NULL);
Then dereferences Desktop to get the IME Thread ID.
The developer knew of a risk, and validated it on debug builds... but can this happen?
Exhibit B
NtUserGetDCEx(HWND Window, HRGN ClipRegion, ULONG Flags):
Backwards compatibility: if no window handle given, get the desktop’s window handle
Code blindly dereferences Win32ThreadInfo->Desktop to get the Desktop->Window
Uses Window PWND when calling _GetDCEx
Assumption
Assumption
All threads are part of a desktop
Assumption
All threads are part of a desktop
Except CSRSS-owned threads
Assumption
All threads are part of a desktop
Except CSRSS-owned threads
“but Microsoft owns that code, and it never calls NtUserGetThreadState or NtUserGetDCEx”
Assumption
All threads are part of a desktop
Except CSRSS-owned threads
DoS Exploit
hCsrss = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwCsrssId);
CreateRemoteThread(hCsrss, NULL, 0, NtUserGetThreadState, 15, 0, NULL);
This creates a new thread in CSRSS which will immediately execute NtUserGetThreadState with the thread state class which causes the dereference
Can we do better?
From DoS to Escalation
In Windows, we can allocate memory at “NULL” with NtAllocateVirtualMemory(Handle, 0x00000001, PAGE_SIZE - 1, ...);
This means we can control what the kernel reads and writes
Exploitable if kernel de-references something from the poisoned NULL pointer, then reads/writes to that value or worse -- executes!
Controlling Win32k
NtUserGetThreadState only reads a value from the Desktop pointer -- useless to us
However, NtUserGetDCEx dereferences a “Window” from the Desktop pointer.
We can pwn the PWND ;-)
No obvious modification of the Window structure was found on Server 2003 and higher
However, the PWND is cached into the DC object
Controlling the PWND
Cached PWND kept so that the owner of the DC can be requested later
GDI sometimes builds WNDOBJs based on the owner
Scope and size of code that deals with this corrupted PWND would take weeks to analyze
Possibility exists that code could be coerced into ring privilege escalation behavior
Did not spend too much time on this: Admin->Kernel escalation is only an attack against DRM
System Calls
Previous Mode
How does a kernel system call know if this is the kernel closing the handle, or an application?
Every system call updates the previous mode
If user-mode performed the call, it’s set to UserMode (1)
If Kernel-mode performed the call, it’s set to KernelMode (0)
System Calls
User-mode applications cannot call kernel functions -- must use system call functionality
Previous mode always updated
Kernel-mode code can choose to call kernel function directly if exported (or if Microsoft code within the kernel binary)
Previous mode not updated
If kernel-mode code performs system call, previous mode is updated
Previous Mode Bugs
Stale previous mode -- driver failures
Here, driver gets called from a user application (PM = 1), then directly performs a call to a kernel function (PM = 1) with kernel-mode pointers.
Call will fail because pointers will be refused -- kernel will believe this is an unprivileged app attacking the kernel.
Trusted previous mode on untrusted data -- danger
Previous Mode is Kernel
In this situation, the previous mode is 0, but the driver is still passing along the raw user data
Kernel implicitly trusts caller, does not probe parameters
Leads to code execution and privilege escalation
Or, kernel performs certain sanity checks on the premise this is a kernel mode operation
Leads to DoS -- kernel bugchecks for safety
Protected Handle Close
Object Manager
The Windows kernel manages resources as objects
Processes, Threads, Files, Keys, Tokens, Events, Mutexes, Semaphores, Timers, ALPC Ports. 30 total!
Object manager provides private header (OBJECT_HEADER) on top of each object
Owner is not aware of this and never tries to free object on its own -- Object Manager does garbage collection (prevents freeing invalid pointer)
Windows Kernel Objects
OBJECT_HEADER
Namespace Information
Quota Information
Security Information
Debug Information
Flags
Reference Count
Object Type (Superclass)
Object Body
Handle Manager
Can’t export object addresses to user mode applications
Pointers are only valid to the kernel
Can’t always export object addresses to kernel mode drivers
Structure for the object may be opaque
Kernel provides handles to the objects instead
Handle Features
Handles can be inherited from the parent
Must be inheritable
In Vista, a subset of these can be specified
Handles can be duplicated
Must have duplicate rights
Handles can be protected from close
Handle to Object Mapping
Object
Object
Object
Handle Table
Handle Entry
Handle Entry
Handle Entry
Handle Entry
Handle Entry
Object
Object
Application
SetEvent(0x4)
Handle to Object Mapping
Object
Object
Object
Handle Table
Handle Entry
Handle Entry
Handle Entry
Handle Entry
Handle Entry
Object
Object
Application
SetEvent(0x4)
Handle to Object Mapping
Object
Object
Object
Handle Table
Handle Entry
Handle Entry
Handle Entry
Handle Entry
Handle Entry
Object
Object
Application
SetEvent(0x14)
Handle to Object Mapping
Object
Object
Object
Handle Table
Handle Entry
Handle Entry
Handle Entry
Handle Entry
Handle Entry
Object
Object
Application
SetEvent(0x14)
Handle SecurityEach object has a security descriptor with ACLs
Each time a handle is created, an access mask is given
So a process can only open a resource if it has access to it
Handles are per-process (one handle table per process)
One process cannot typically touch the handles of another
Handle Isolation
Handles are isolated, but can be duplicated if process grants PROCESS_DUP_HANDLE to you
Handles created by drivers are part of the kernel’s handle table
< Vista, only administrators can access
>= Vista, kernel is protected process, no one can access
Protect From Close
Set with SetHandleInformation or NtSetInformationObject
Used for protecting against accidental close of critical handles
No ACL check done -- anyone can deprotect the handle
So mostly a debugging/reliability feature -- not security
What Happens at Close
Logic handled by ObpCloseHandleEntry
Normal cases: return STATUS_HANDLE_NOT_CLOSABLE
Process being debugged or FLG_ENABLE_CLOSE_EXCEPTIONS global flag is set or handle has debug information: RaiseException(STATUS_HANDLE_NOT_CLOSABLE)
What if Kernel Closes It?
You would expect the operation to succeed anyway -- kernel is “god”
But... remember this is a debugging/reliability feature
So we’d probably want to know if a critical handle in our driver was closed...
So what happens? Crash!KeBugCheckEx(INVALID_KERNEL_HANDLE)
Assumption
The kernel will never normally close protected handles
Microsoft code makes sure not to do this
Drivers don’t usually use this functionality
3rd-party code would crash on developer’s machines anyway, and they would fix the error
Again, assumption of controlled environment
Can it happen?
Closing a Handle
CloseHandle and NtClose when called from user-mode will set PM = 1.
Kernel will not crash if handle is protected
ZwClose when called from kernel-mode will set PM = 0
But this means it’s a driver bug
Can’t set protected bit on driver handles
Driver handles are part of system handle table
Assumption Checks Out?
Are all kernel handles in the system handle table?
No! Kernel-mode code must explicitly set OBJ_KERNEL_HANDLE
Otherwise, handle becomes part of the current process
Current process can protect the handle...
... and wait for the kernel-mode code to free it
Scenario
Find a handle that we can control, and wait for the kernel to close it.
Or better yet, have some sort of function that can coerce the kernel to close the handle immediately.
But all handle closing is done with CloseHandle
False! Window Stations and Desktops are actually managed by the Object Manager, even if they are Win32k objects
CloseWindowStationCannot normally use CloseHandle on a window station or desktop handle
Win32k wants to make sure you’re doing it right so it provides its own interface, and blocks CloseHandle calls with the OkayToClose mechanism
CloseWindowStation and CloseDesktop APIs are provided (NtUserCloseWindowStation/Desktop)
NtUserCloseWindowStation is simple wrapper around...
CloseWindowStationCannot normally use CloseHandle on a window station or desktop handle
Win32k wants to make sure you’re doing it right so it provides its own interface, and blocks CloseHandle calls with the OkayToClose mechanism
CloseWindowStation and CloseDesktop APIs are provided (NtUserCloseWindowStation/Desktop)
NtUserCloseWindowStation is simple wrapper around...
ZwClose!
CloseWindowStationCannot normally use CloseHandle on a window station or desktop handle
Win32k wants to make sure you’re doing it right so it provides its own interface, and blocks CloseHandle calls with the OkayToClose mechanism
CloseWindowStation and CloseDesktop APIs are provided (NtUserCloseWindowStation/Desktop)
NtUserCloseWindowStation is simple wrapper around...
Exploit
So all we have to do is
Create a window station with CreateWindowStation
Protect the handle with SetHandleInformation
Close it with CloseWindowStation
Bug was caught in Vista SP1 / Server 2008 timeframe
Probably due to SDL -- this is an obvious bug
Exploit, Take Two
There exists a second method to expose it, however
Protect the current window station handle
Create a new window station
Switch to it with SetProcessWindowStation
Why does this crash?
Window Station Caching
When a console application starts, it calls CSRSS
CSRSS eventually calls NtUserConsoleControl with the ConsoleWindowStationProcess (6) type
This calls PsSetProcessWin32WindowStation to cache the window station handle (used for global atom table)
When switching to a new window station, xxxSetProcessWindowStation will check if the cached copy is stale and...
Window Station Caching
When a console application starts, it calls CSRSS
CSRSS eventually calls NtUserConsoleControl with the ConsoleWindowStationProcess (6) type
This calls PsSetProcessWin32WindowStation to cache the window station handle (used for global atom table)
When switching to a new window station, xxxSetProcessWindowStation will check if the cached copy is stale and...
You guessed it -- ZwClose on the cached handle
Window Station Caching
When a console application starts, it calls CSRSS
CSRSS eventually calls NtUserConsoleControl with the ConsoleWindowStationProcess (6) type
This calls PsSetProcessWin32WindowStation to cache the window station handle (used for global atom table)
When switching to a new window station, xxxSetProcessWindowStation will check if the cached copy is stale and...
Developer Guidance
NULL Pointer Dereferences
Never dereference a member of a structure that could be NULL
Make sure nobody can call a function to dereference it after it has been deleted and/or freed
Make sure nobody can call a function to dereference it before it has been initialized
ASSERT is your friend, but isn’t perfect
Do not write your code by basing your entire security on the caller’s context -- remote thread injection exists
System Calls
Never call Nt* system calls directly from a driver
Usually not exported anyway
Stale previous mode can lead to weird failures later
Can also lead to security issues in this scenario:
User calls with buffer 0xF00
You perform some unrelated operation with Zw*
You now process 0xF00 with Nt*
System Calls
Always call Zw*
This makes sure the previous mode is never stale
But never call with user-accessible or user-owned data or parameters
Always probe and capture
Handles
Always create handles with OBJ_KERNEL_HANDLE
Unless you want to share the handle with a trusted application
In this case, make sure the application and driver are secured against non privileged access
Do not perform operations based on the data referenced by the handle
Do not close the handle with a kernel mode PM
Have the user close it -- or use ObCloseHandle
Thank You!
Slides and PoC will be at http://www.alex-ionescu.com
Email me at [email protected] for
Rants
Raves
Flames
Questions and Comments
Q & A