понедельник, 28 ноября 2016 г.

LPE vulnerabilities exploitation on Windows 10 Anniversary Update


In our previous blog entry, we described the KASLR improvements in Windows 10 Anniversary Update. In this article we’ll discuss the possible bypasses of GDI mitigation.
In the beginning we’ll provide the basic information which is necessary to better understand of our technics and after that we'll describe bypasses. We highly recommend you read the previous post.
This article is based on our ZeroNights 2016 presentation.

Allocation of GDI and USER objects

Let’s look at the allocation USER and GDI objects.
USER objects (window, dde conversation, hook, menu… see MSDN list):

1. Allocated by HMAllocObject.
2. Can be allocated in Desktop heap, Shared heap, in pool - PagedSessionPool (pool type is 33) or PagedSessionPool|POOL_QUOTA_FAIL_INSTEAD_OF_RAISE (41) depending on object type.
3. We can read Desktop heap from usermode and USER object’s content as result.

GDI objects (bitmap, brush, pen… see MSDN list):
1. Allocated by HmgAlloc.
2. Can be allocated in PagedSessionPool. Also, there are lookaside lists with previously allocated blocks.
3. Further we’ll reference PagedSessionPool as GDI pool.

We can see that USER and GDI objects can be allocated in the same pool. Also, reading content of some USER objects can give us additional information about kernel addresses.

Desktop heap 

Desktop heap is very important in our research.
Because of this, we decided to describe features of Desktop heap briefly.
Desktop heap is created with Desktop object (CreateDesktopHeap).
Desktop heap is mapped into a process address space if a process is switched to a specified desktop.
Desktop heap contains some USER objects and structures.
We can get user mode mapping of kernel mode address (located in desktop heap) by a simple calculation
 UserModeAddress = KernelModeAddress – TEB.Win32ClientInfo.ulClientDelta  

Some Windows API are using this feature to avoid entering into kernel when reading structures.
It is very important too. We will return to this API behavior later.
Below is short list of different objects inside Desktop heap (incomplete).

Bypass methods 

Our methods are based on the following points:
Anniversary Update suppressed addresses of GDI objects, but USER objects were left intact.
We still can get addresses of USER objects from gSharedInfo from usermode.
Also, we can read content of some USER objects that were allocated in Desktop heap.
Some USER objects and structures are allocated in the GDI pool.

Discussed methods:
Arbitrary read/write via USER objects (as SURFOBJ alternative)
Leak of addresses from GDI pool.
Spray approach.

Arbitrary read/write via USER objects

We need exact address for corruption when we have vulnerability where it is possible write specific kernel address.
We could use SURFOBJ address and overwrite some SURFOBJ fields before Anniversary update. Now we can use USER objects to gain arbitrary read/write because we can get their addresses.
The main idea is very similar to arbitrary read/write with 2 SURFACEs, because we use two objects – one corrupts data pointer of another’s.
Speaking about required conditions for read/write primitive using USER objects, we have list of them:
1) We must have ability to write QWORD values because normally we write different pointers. If we will be able to write separate DWORDs only, it can lead to BSODs during replacing parts of pointer.
2) We must have ability to work under wow64 and pure x64 process.
3) We must have access to whole address space on x64.
4) Method has to be easy and stable in implementation.
5) Method should not corrupt (‘lock’, set flags etc) any data during reading or writing.

Let’s describe method itself.
Method based on using two USER objects – Window (tagWND) and Menu (tagMENU).Both objects are placed in desktop heap.
tagWND is structure which can have some “extra data”, controlled by user (via SetWindowLongPtr API).
Size of tagWND extra data is located in field tagWND.cbwndExtra.
Let's describe second object - tagMENU. Popup menu (CreatePopupMenu) can contain some items (InsertMenuItem) described by tagITEM structure.
Pointer to items array is located in tagMENU.rgItems.
It is possible to read/write different fields of menu items via WinAPI functions (GetMenuItemRect,GetMenuItemInfo/SetMenuItemInfo).
We can easily place tagWND before tagMENU in memory (because we can get addresses of both objects). If tagWND.cbwndExtra was corrupted because of some vulnerability, we can read/write tagMENU.rgItems pointer via SetWindowLongPtr. When pointer to rgItems was replaced to some arbitrary address, we can read/write it via menu items API.

Arbitrary read 

1. The most ‘obvious’ way to read content from tagITEM is GetMenuItemInfo, but there is a problem. tagITEM structure is located in desktop heap. Windows API GetMenuItemInfo is using this fact to read item’s content from usermode. So, if we replaced tagITEM pointer in tagMENU to arbitrary kernel address, we have to use another API, which can get info from kernel mode.
2. GetMenuItemRect is able to do that, but it returns result in specific format (RECT structure).
3. GetMenuItemRect reads 2 fields – tagITEM.cxItem, tagITEM.cyItem, so we can calculate 64-bit value from 2 DWORDs. Moreover, cxItem and cyItem are located sequentially in tagITEM structure which is also required for reading of x64 value.
4. GetMenuItemRect requires a Window as argument, so we need to provide one. Also, it is better to specify popup menu, because with other menu we will have some ‘excess’ internal calls. Read example is below

 GetMenuItemRect(hWnd, hMenu, 0, &rect);  
 ldw =(rect.right - rect.left); // -> low dword  
 hdw=(rect.bottom - rect.top); // -> high dword  
 result = MAKEULONGLONG(ldw, hdw);  

Arbitrary write 

Before discussing write primitive, it is important to understand behavior of necessary menu item API.
We will use SetMenuItemInfo for writing.

 BOOL WINAPI SetMenuItemInfo( HMENU hMenu, UINT uItem, BOOL fByPosition, LPMENUITEMINFO lpmii );  
 typedef struct tagMENUITEMINFO {  
 UINT cbSize;  
 UINT fMask; // MIIM_DATA , MIIM_ID …  
 UINT fType;  
 UINT fState; // -> MIIM_STATE  
 UINT wID; // -> MIIM_ID  
 HMENU hSubMenu; // -> MIIM_SUBMENU  
 HBITMAP hbmpChecked;  
 HBITMAP hbmpUnchecked;  
 ULONG_PTR dwItemData; // -> MIIM_DATA  
 LPTSTR dwTypeData; // -> MIIM_STRING  
 UINT cch;  
 HBITMAP hbmpItem;  

SetMenuItemInfo takes Menu handle and pointer to structure MENUITEMINFO.
This structure has an interesting field – fMask. Using this field we can give SetMenuItemInfo information what fields from structure have to be written. For instance, if we set MIIM_ID to fMask only wID field will be written.
Now we have required information about SetMenuItemInfo behavior and we can finally describe write primitive.
We can use API SetMenuItemInfo(MIIM_DATA) for writing content of arbitrary address.
MIIM_DATA allows to write DWORD64 on x64 system (dwItemData is ULONG_PTR). SetMenuItemInfo writes tagITEM.dwItemData field.
We will have little obstacle working from wow64. If we call “write” from wow64 process, we need to call service NtUserThunkedMenuItemInfo directly, because wow64 stub doesn’t allow to use x64 MENUITEMINFOW structure and as the result we can’t write 64 bit field (only 32-bit one).
But this obstacle is not a serious obstacle at all because we can easily call x64 code from x86 process (see Link section).
In both cases (read and write) we need to calculate arbitrary address according to tagITEM field offset.
Set arbitrary address for read (ArbAddr)

 SetWindowLongPtr(hWndCorrupted, ExtraDataIndex, (LONG_PTR) ArbAddr - 0x48);  

Where 0x48 is cxItem offset.
Set arbitrary address for write

 SetWindowLongPtr(hWndCorrupted, ExtraDataIndex, (LONG_PTR) ArbAddr - 0x38);  

Where 0x38 is dwItemData offset. You can see described above on picture below.

Also, you can see write and read tagITEM's fields on picture below.

Let’s list method’s pros and cons.
1. Easy to implement. In most cases we can use documented API (except wow64 direct NtUserThunkedMenuItemInfo call).
2. Can be used under wow64 process and pure x64 process.
3. Access to full address space on x64.
4. Works under low integrity process.
5. tagITEM and tagMENU are not changing between Windows versions. tagWND is changing, but it is not affecting this method's implementation (we have full access to object’s addresses and their content).
Cons (Limitation):
1. We need vulnerability, which can corrupt memory in desktop heap or vulnerability where we can write specific address (tagWND.cbwndExtra offset).
2. Desktop heap must be mapped in exploitation context.

As conclusion, we can say, that it is possible to use another combination of objects like tagCLS + tagMENU (because tagCLS has cbclsExtra field and we can use SetClassLongPtr function) or find another USER read/write primitive. We offered one of the easiest implementations. The main idea here to take a look at USER objects from point of bypass of latest Windows 10 mitigations.

GDI Pool addresses leaks 

If we have vulnerability where we can’t write specific address (like overflow), we can try to continue use old SURFACE technique. If we want to do that, we need to analyze different structures and objects, and find as much GDI pool pointers as possible. Then we need to select most interesting and use them during exploitation.
We found that:
We can leak addresses of some GDI structures or USER structures/data allocated in GDI pool.
We can find USER objects which are allocated in the GDI pool.

Based on both methods we can prepare memory for exploitation.
Main technique is allocation of GDI object (SURFOBJ) on place of freed object/structure which address we can get. Then we can use old SURFACE corruption technique.
Use of USER objects was described at Ekoparty 2016, we’ll show using of data and structures.

GDI structure address leak 

We can get address of specific GDI structure by reading Desktop heap.
Base information:
Every window class (RegisterClass API) described by tagCLS structure.
tagCLS contains pointer to tagDCE structure (pdce field) if class was created with CS_CLASSDC style.
tagCLS is located in Desktop Heap, so we can read it content.
tagDCE is allocated in PagedSessionPool as GDI objects (CreateCacheDC).
tagDCE has constant length – 0x60 (Win 10 x64).
Getting tagDCE pointer:
1. RegisterClass with CS_CLASSDC style.
2. Create window for this class in order to cache DC and allocate tagDCE.
3. Get pointer to tagWND, then to tagCLS (tagWND.pcls) and finally tagCLS.pdce.
We need to call DestroyWindow + UnregisterClass in order to free tagDCE.

See picture below.

We can get pdce kernel pointer by using following code

 DWORD64 pWndUMAddr = GetUserObjectKAddr(hWnd) - TEB.Win32ClientInfo.ulClientDelta;  
 DWORD64 pClassUMAddress = *(DWORD64 *)(pWndUMAddr + 0x98) - TEB.Win32ClientInfo.ulClientDelta;  
 DWORD64 pDceAddress = *(DWORD64 *)(pClassUMAddress + 0x18);  

USER structures/data allocated in GDI pool 

Some user objects are allocated in GDI pool, but they are not alone! There are many user structures allocated in GDI pool (tagPOPUPMENU, tagWND.pTransform, tagSBTRACK …).
We can get their addresses by reading object’s content from desktop heap (as USER objects contain pointers to this structures).
We also found, that tagCLS.lpszMenuName (see image above) allocated in GDI pool. This tagCLS field represents WNDCLASSEX.lpszMenuName (UNICODE).
We can easily allocate it by RegisterClass and free by UnregisterClass.
Also we can control size of this allocation.
We can easily get lpzsMenuName address by using following code (similar to pdce example above)

 DWORD64 pWndUMAddr = GetUserObjectKAddr(hWnd) - TEB.Win32ClientInfo.ulClientDelta;  
 DWORD64 pClassUMAddress = *(DWORD64 *)(pWndUMAddr + 0x98) - TEB.Win32ClientInfo.ulClientDelta;  
 DWORD64 lpszMenuName = *(DWORD64 *)(pClassUMAddress + 0x88);  

Summary: Objects in GDI pool 

We can get addresses of different object/structures allocated in GDI pool. We can divide them by type, see table below

Obviously, some USER objects/structures aren’t possible to use during exploitation because we can’t easily allocate/control them.
We made list of potentially “exploitable” objects and structures with their pros and cons.
Usage of accelerator tables and clip data was shown at Ekoparty 2016. We’ll describe alternative way. Following list is incomplete, there are other candidates.

Spray approach (tagCLS.lpszMenuName) 

Let's describe how to use tagCLS.lpszMenuName during exploitation.
1. Allocate tagCLS structure by calling RegisterClass with controlled lpszMenuName.
2. Allocate Window for class (as we don’t want to scan desktop heap) and get addresses of allocated tagCLS.lpszMenuName.
3. DestroyWindow and free tagCLS by UnregisterClass.
4. Allocate SURFOBJ (Bitmap)*.
5. Now, we can expect that SURFOBJ will be allocated on place of lpszMenuName.

*As there is lookaside list for previous GDI objects allocations, we need to fill lookaside before trying to allocate bitmap on freed space.

tagCLS.lpszMenuName spray advantages:
1. Easy allocation and destruction.
2. We can control size of tagCLS.lpszMenuName, because it is UNICODE_STRING buffer (not structure!).
3. tagCLS.lpszMenuName field allocated on GDI pool.
4. We can easily get address of field.
5. Method is working under low integrity process.
6. Big strings are allowed (> 4kb).


We can use a method of arbitrary read/write via USER objects when we can write a specific address.
We can use tagCLS.lpszMenuName spray for other vulnerabilities.
We also can use prediction of GDI objects addresses to make exploitation more stable.
We can use GDI structures(tagDCE), USER objects, USER structures allocated on GDI pool for exploitation. Choice of object will depend on vulnerability.



Link to ZeroNights presentation will be available later.

Yurii Drozdov & Liudmila Drozdova

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

Windows 10 Anniversary Update: GDI handle management and vulnerabilities exploitation


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

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


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.


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


Additional links, related to this research


Yurii Drozdov & Liudmila Drozdova

вторник, 7 июля 2015 г.

Advanced CFG Bypass on Adobe Flash Player 18 and Windows 8.1


Control Flow Guard (CFG) was introduced in Adobe Flash Player in Windows 8.1 Update 3.  But CFG-compiled flash had vulnerability which allowed attacker to bypass this protection. Core Security Team found this flaw and nice method how to use it
This flaw existed because for  JIT-generated code CFG was not enabled.
On 9th of June  this vulnerability was  fixed with Flash Player by enabling CFG for JIT-calls.

We made our analysis of this new protection and found that it is also possible to bypass it. The main goal of this research is bypass CFG and transfer control to controlled address.


We created new class in ActionScript - ExploitClass  with few functions (func1, func2, func3) for our experiments. In the ExploitClass function func1 calls func2, func2 calls func3. We use three functions for simplification only, it is possible to use only one function.

 class ExploitClass {  
 function func1(arg1, arg2, … , argn) {  
  func2(arg1, arg2, …, argn);  
 function func2(arg1, arg2, … , argn) {  
   func3(arg1, arg2, …, argn )  

 function func3(arg1, arg2, … , argn) {  

When we call one JIT-generated function from another JIT-generated function call is being checked by CFG. Every call of another JIT function in JIT-generated code looks like this

 56       push  esi  
 50       push  eax  
 51       push  ecx  
 8bc8      mov   ecx,eax  
 b840aa0f77   mov   eax, LdrpValidateUserCallTarget (770faa40)  
 ffd0      call  eax -> CFG check  
 59       pop   ecx  
 58       pop   eax  
 ffd0      call  eax -> call of validated function  
 83c410     add   esp,10h  

Block of parameters for function is being generated before CFG call and following validated function call. Let's look at instructions, which are used for saving parameters. They can look like this (instructions are provided with opcodes for clarity)

  8b5de0     mov   ebx,dword ptr [ebp-20h]  
 897d88     mov   dword ptr [ebp-78h],edi  
 8b7de8     mov   edi,dword ptr [ebp-18h]  
 89758c     mov   dword ptr [ebp-74h],esi  

or this

 8bbde8feffff  mov   edi,dword ptr [ebp-118h]  
 898df4feffff  mov   dword ptr [ebp-10Ch],ecx  
 8b8decfeffff  mov   ecx,dword ptr [ebp-114h]  
 89b5c0feffff  mov   dword ptr [ebp-140h],esi  

Our goal as we mentioned earlier, to transfer control to the controlled address.

Let's describe key points for bypassing CFG in Flash Player 18.

1. We can use one of the parameters of func2 as controlled address because arguments are saved in original state without any changes in stack and (in some cases) they are duplicated in registers.

We found that before call eax to func2 we can save one of the parameters in ecx register.

2. We found that we can't transfer control to any address (for instance, in Flash.ocx) except beginning of functions because this case is controlled by CFG. But in case of JIT functions we can transfer control to any part of any JIT function. In our situation it will be control transfer to the any part (any instruction) of func2. It is wiggle room for the exploitation.

But why it became possible?

TrendMicro's research described generation of CFGBitmap for allocated by NtAllocVirtualMemory executable memory (all bits will be set to '1').

It means, that this bug exists because of realization of CFG in Windows (nor in Flash Player). 

3.  Our idea is transfer control to any EIP-changing instruction (it can be add esp, X/ret, or as in this article - control transfer to ecx). This instruction can exist in JIT code in form of separate instruction or to be part of other instructions.

We decided to try to find any ecx-control-transfer instruction in JIT code (for instance call ecx, jmp ecx etc).

Let's describe how we got our instruction. In JIT code call ecx doesn't exist normally, but we can try to generate it via one big instruction or two separate ones. Call ecx opcode is FF D1. We found following options in our experimental module

  1. 33FF XOR EDI,EDI   
  D1F8    SAR EAX,1   
  D1E1    SHL ECX,1   
  3. 898D 70FFFFFF MOV DWORD PTR [EBP-90],ECX   
  D1EB    SHR EBX,1   
  4. 8D51 FF   LEA EDX,DWORD PTR DS:[ECX-1]   
  D1FE    SAR ESI,1   

Also, we found

  5. C785 00FFFFFF D1000000    MOV DWORD PTR [EBP-100],0D1   

Looks interesting, especially options with ebp-usage (2,3,5) as we know that Flash uses similar constructions for building  parameters block. Ideal option for us is 5, because one instruction is enough in this case.

But we noticed important thing, that different instructions(with different opcodes) can be used for saving parameters. For example

  C785 00FFFFFF 01000000    MOV DWORD PTR [EBP-100],1   
  C745 8C 01000000      MOV DWORD PTR [EBP-74],1   

First variation is good for us, because there is FF (we need FF in call ecx opcode). Why Flash select one type of instruction in one case and different in another one ?

 If in instruction  
 X> 0x80, first variant will be generated  
 X<= 0x80, second one will be generated  

We can influence on X in JIT function via local variables, number of other functions calls inside JIT function, number of their parameters.

Using this information, we need to prepare func2 before call of func3 and force flash to use fist variant of opcode (we added more local variables, additional calls etc).

We tried something like this

 function func2(arg1, arg2,...,argn) {  
   var localvar1;  
   var localvar2;  
   func3(arg1, arg2,...,0xD1,..., argn )  

in order to get MOV DWORD PTR [EBP-X],0D1 in parameters saving code. But, unfortunately, 0xD1 had been obfuscated by JIT compiler. Instead, we got something like 

 MOV EBX,18C731Eh  
 XOR EBX,18C73CFh ; EBX == 0D1h  

Seems this idea wasn't successful, but we still have two alternative ones (2,3 options) with generating of two instructions. In the end of first one will be 0xff,   and in the beginning of second one will be 0xD1. We can use parameters manipulation  (or local variables) for 0xFF generation and shr, shl, sar instructions for 0xD1. We tried to do something like this in ActionScript

 any_var1 = any_var2<<1  

But instead of desired

 D1E2       SHL EDX,1  

we got the same instruction, but with alternative opcode (probably because it is easier to generate it).
 C1E201 SHL EDX, 1  

Ok, seems it is not our option again. But let's try to find alternatives. For instance, call [ecx]. This instruction's opcode is FF 11. When we tried following code

 function func2(arg1, arg2 ..., argn) {  
   var localvar1;  
   var localvar2;  
   func3(arg1, arg2,...,0x11,... , argn)  

And we got instruction

  c78570ffffff11000000 mov dword ptr [ebp-90h],11h   

Bingo! 0x11  is not big number for JIT compiler and shouldn't be obfuscated. Obfuscation of numbers begins from 0x80. Numbers less than 0x80 will not be obfuscated.
Finally, after generation of JIT func2, we have ready call [ecx]. After exploit execution we can find address of func2 inside our object and change it to call [ecx] 's address in func2. After all manipulations after call func2 from func1 we will have transfer control to controlled address inside ecx with CFG bypass.

 126aed07 ff11      call  dword ptr [ecx]   ds:002b:42424242=????????  

in modern flash exploits we can easily put address with target address for control transfer to ecx. Then, we can transfer control to ROP using standard approach.
Following picture shows how to bypass new CFG protection in JIT code


Our approach was based on three key points.
1. It is possible to transfer control to any part of JIT-generated function despite CFG.
2. It is possible to control one of registers in func1 and fill it with one of parameters before call of func2.
3. Predictability of JIT-interpreter allows us to generate interesting control-transfer instruction.

Yurii Drozdov, Luidmila Drozdova

(C) CVR Ltd