четверг, 24 ноября 2016 г.

Windows 10 Anniversary Update: GDI handle management and vulnerabilities exploitation

Introduction 


Windows 10 Anniversary Update came with a lot of new mitigations. In the links section you can find reference to Microsoft presentation about improvements.
We now interested in KASLR improvements, that are introduced for Windows 10 x64 only.
We will focus on one related to GDI objects addresses.
Now PEB.GDISharedHandleTable doesn’t disclose GDI objects addresses and this can lead to some difficulties during exploitation.
This article describes what changes were made in win32k code in order to implement this mechanism. On the next blog post we will describe bypasses of this mitigation.
This article is based on our Defcon Russia presentation (you can find link in Links section).

GDI handle management before Windows 10 Anniversary Update


GDI objects - Bitmap, Brush, Pen, DC, Font... You can find GDI objects list on MSDN (links).
GDI handle management (in this article) is process includes creation of GDI handle table, allocation of GDI handles, insertion/deletion of objects into handle table etc.
Win32k.sys contains GDI handle manager (win32kbase.sys on Windows 10).
win32k!Hmg* functions are responsible for handle management.
HmgCreate function creates GDI handle table and initializes other handle manager structures. HmgInsertObject function inserts every created GDI object into handle table.
Handle table pointer saved in win32k variable gpentHmgr in kernel mode.
Every object in handle table described by following structure

 typedef struct {  
 PVOID64 pKernelAddress;  
 USHORT wProcessId;  
 USHORT wCount;  
 USHORT wUpper;  
 USHORT wType;  
 PVOID64 pUserAddress;  
 } GDICELL64;  

The most interesting field here is pKernelAddress which represents kernel address of specific GDI object.
The handle table is being mapped to address space of every GUI process by win32k!GdiProcessCallout function.
Pointer to GDICELL array is located in PEB.GdiSharedHandleTable in usermode.
So, we could get GDI objects kernel addresses from usermode from PEB.GdiSharedHandleTable.
HmgInsertObject is good starting point for research of changes because it contains handle table access code.
Old HmgInsertObject function is relatively simple. Incomplete code

 HmgInsertObject(_BASEOBJECT *ObjectKernelAddress,   
               unsigned __int16 flags,   
               unsigned __int8 objtype) {   
        ...   
           Handle = hGetFreeHandle(objtype);   
           ENTRYOBJ::vSetup( (gpentHmgr + 0x18 * LOWORD(Handle)),   
                            ObjectKernelAddress,   
                            objtype,  
                            flags,   
                            LOWORD(Handle));   
        ...  
 }  

ENTRYOBJ::vSetup function is filling GDICELL64 structure with given parameters.
New HmgInsertObject is much more complicated (we will describe changes later).

Usage of GDI object addresses 


How attackers used GDI kernel objects addresses during exploitation ?

More stable exploitation:
1) It is possible to check if object was allocated on the right place after spray.
2) It is possible to change memory layout as necessary.

Arbitrary read/write:
1) It is possible to change different fields of Bitmap (SURFACE in kernel) and gain arbitrary read and write (and gain system privileges).
This method was described many times, one detailed description you can read from Core Security Blog (you can find link at the end of article).
Briefly, SURFACE (corruption of its substructure SURFOBJ) is one of the popular ways to achieve privilege escalation, which is working from Vista to 10.
In this technique we can use 2 SURFACES (SURFOBJ1 and SURFOBJ2 on picture) located close to each other in memory. If we can corrupt specific field in SURFOBJ1 we will be able to write SURFOBJ2 pvScan0. When pvScan0 is changed to arbitrary address we can read/write it via GetBitmapBits/SetBitmapBits API on SURFOBJ2. Picture below is shown what fields should be corrupted in SURFOBJ1 in order to gain arbitrary read/write via SURFOBJ2.

SURFACE arbitrary read write

We can use GDI objects for exploitation even if we have vulnerability in different (not win32k) system component. In fact GDI objects provide good exploitation primitive.

Windows 10 Anniversary Update 


GDI handles management was changed a lot after update of Windows 10. PEB.GdiSharedHandleTable doesn’t contain kernel addresses anymore.
Below are two pictures – first with old content of GdiSharedHandleTable and second one with new content.


New GdiSharedHandleTable


New GdiSharedHandleTable

We can see that updated handle table doesn’t contain any addresses.
Speaking about kernel-side changes, new handle management classes and functions were added in win32kbase.sys. Few Hmg* functions were added. List is below


 HmgAllocateDcAttr  
 HmgMarkLazyDelete  
 HmgPentryFromPobj  
 HmgReplaceObject  

The most interesting from them is HmgPentryFromPobj, because it references new class GdiHandleManager. New handle management classes were added – GdiHandleManager, GdiHandleEntryDirectory, GdiHandleEntryTable, EntryDataLookupTable.
Also, new syscall was added – win32kbase! NtGdiGetEntry. This syscall is being used from gdi32.dll during querying GDI handle table from different functions (like DeleteObject or ReleaseDC).
This syscall returns GDICELL64 structure corresponding to specified handle (but without kernel address – see detailed research below).
We can easily track all changes via HmgInserObject function. We also can analyze new HmgCreate function in order to better understand structure of new tables.

New handle manager structures 


Let’s describe changes in details.
First, new kernel global variable was introduced – win32kbase!gpHandleManager.
gpHandleManager initializes by function GdiHandleManager::Create. This function allocates structure of 0x20 size (pointer to this memory later is being put to gpHandleManager variable ), we will later reference this structure as GdiHandleManager. Also, another structure is being initialized inside GdiHandleManager::Create by call GdiHandleEntryDirectory::Create.
GdiHandleEntryDirectory::Create allocates and initializes another structure of size 0x810 (we will reference it as GdiHandleEntryDirectory).
Allocated pointer is being put to GdiHandleManager structure at 0x10 offset. GdiHandleEntryDirectory structure can contain 256 pointers to structures created by function GdiHandleEntryTable::_Create (we will reference it like GdiHandleEntryTable). GdiHandleEntryDirectory has 8-byte header ( it contains count of created tables and ‘busy’ flag which is being set when all tables are filled up), 256 GdiHandleEntryTable pointers and max handle count.
New GdiHandleEntryTable can be created by function GdiHandleManager::AcquireEntryIndex .
Speaking about 256 entries, in fact only first one is being used.
Size of GdiHandleEntryTable can be different depending on number of table. Size is 0x20 bytes for first table and 0x20 + 0x18 * max_handle_count (0x18 is GDICELL64 size). Also, the address of gpentHmgr is being put on 0x0 offset for first table, address of GdiHandleEntryTable+0x20 is being put on 0x0 offset for next tables. In fact, first qword of GdiHandleEntryTable is old gpentHmgr, but without GDI objects addresses. As we can see, there is no usermode mapping at all for next tables. Next interesting structure pointer is located on 0x18 offset inside GdiHandleEntryTable.
This structure is created by call GdiHandleEntryTable::EntryDataLookupTable::Create (we will reference this memory as EntryDataLookupTable). EntryDataLookupTable has size

 8 * (max_handle_count + 255)/256 + 0x10  


The default value for max_handle_count is 0x10000. In fact, EntryDataLookupTable contains 0x10 bytes header and 256 pointers to memory blocks and each block contains 256 LOOKUP_ENTRY structures. Pointer to EntryDataLookupTable + 0x10 is located on offset 0x0 inside EntryDataLookupTable. Inside LOOKUP_ENTRY structure at offset 0x8 we can finally get an object kernel address.
Now we must go a long way to reach GDI object address.
Speaking about new handle structure, now it contain object type and two 1-byte indexes (see picture), instead of one word index in old handle table.

You can see associated structures on picture below.

GDI Handle tables after Windows 10 anniversary update

Also, we defined some structures (probably they can be interesting for the reader)

 struct GdiHandleManager {  
 DWORD64 unknown;  
 DWORD max_handle_count;  
 DWORD unknown1;  
 GdiHandleEntryDirectory * Dir;  
 };  
 struct GdiHandleEntryDirectory {  
 BYTE busy_flag ;  
 BYTE unknown;  
 WORD TableCount ;  
 DWORD unknown1 ;  
 GdiHandleEntryTable * Tables[0x100] ;  
 DWORD MaxHandleCount ;  
 } ;  
 struct GdiHandleEntryTable {  
 GDICELL64 * SharedMem_or_CellData ;  
 DWORD MaxHandleCount ;  
 DWORD unknown1 ;  
 DWORD unknown2 ;  
 DWORD unknown3 ;  
 EntryDataLookupTable * GdiLookupTable ;  
 } ;  
 struct EntryDataLookupTable {  
 LookupEntryAddress *LookupTableData ;  
 DWORD MaxHandleCount ;  
 DWORD unknown1 ;  
 } ;  
 struct LookupEntryAddress {  
 LOOKUP_ENTRY *leaddress ;  
 } ;  
 struct LOOKUP_ENTRY {  
 DWORD64 unknown;  
 PVOID64 GdiObjectAddress;  
 };  

It is really big changes in comparison with old gpentHmgr structure (in fact, it contained array of GDICELLs with kernel addresses)

Old gpentHmgr structure


How to get GDI object address by handle via Windbg (x64) ? 


In order to understand all described structures better we compared two Windbg commands – for old handle table and for new one.
Windbg command looked like this (handle in this case - 0x3c05096a) before Anniversary update

 dq poi(poi(win32kbase!gpentHmgr) + 0x18*(0x3c05096a & 0xffff))  

Lower word in handle was used as index in gpentHmgr table (0x096a in example above).
Updated Windbg command (handle in this case - 0x1f0509e) is below

 dq poi(poi(poi(poi(poi((poi(poi(win32kbase!gpHandleManager)+0x10) + 8 + 0*8)) +  
 0x18)) + ((0x1f0509ea & 0xffff) / 0x100) * 8) + (0x1f0509ea & 0xff)*0x10 + 8)  

On the picture below we can see result of execution of second command (successful dump of object content) .

Handle content Windbg


What new PEB.GdiSharedHandleTable contains? 


Let’s discuss the content of updated PEB.GdiSharedHandleTable. Handle entry size wasn’t changed, it is like old GDICELL64 size – 0x18

 typedef struct {   
 PVOID64 pKernelAddress;  
 USHORT wProcessId;   
 USHORT wCount;  
 USHORT wUpper;  
 USHORT wType;  
 PVOID64 pUserAddress;  
 } GDICELL64;  

The main change – pKernelAddress contains value 0xffffffffff000000 | dword_index, where

 dword_index = [zero_byte][unused_table_index][lookup_entry_address_index]|[lookup_entry_index]  

So, no kernel addresses anymore.

Changes summary 


Object metadata (pid, object type …) and object address were saved together in GDICELL structure before Anniversary update and both were mapped to userspace.
Now, only object metadata is mapping, kernel addresses are located in kernel pool inaccessible from usermode.

LPE vulnerabilities exploitation after updates 


All these changes made exploitation more difficult. But there are few possible solutions.
GDI is good, but not only exploitation approach.
We still have USER objects (window, cursor, menu etc, see MSDN incomplete list) and we can still get their addresses! i.e. we can use USER objects in some exploits instead of GDI objects.
We can still use GDI objects (SURFOBJ): we can try to predict location of object via spray.
Next blog entry will be about bypasses of described mitigation (from our ZeroNights 2016 presentation).

We also happy to answer your question about this research, so feel free to ask us.

Links 


Our presentation from Defcon Russia DCG#7812 (30th of September 2016)

http://www.slideshare.net/DefconRussia/windows-10-gdi-68999382 

Additional links, related to this research

https://www.coresecurity.com/blog/abusing-gdi-for-ring0-exploit-primitives
https://www.blackhat.com/docs/us-16/materials/us-16-Weston-Windows-10-Mitigation-Improvements.pdf
https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms724291(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms725486(v=vs.85).aspx

Yurii Drozdov & Liudmila Drozdova

Комментариев нет:

Отправить комментарий