The blog continues at suszter.com/ReversingOnWindows

February 19, 2014

Bug in Flash Player when processing PNG format

The Bug

The PNG file consists of a sequence of data structures called chunks. A chunk has a Length field that is a DWORD value. A specially crafted Length field can cause integer overflow in Flash Player leading to read out of the designated buffer. Here is the disassembly code snippet explaining the bug.

015344a0 e8f7feffff      call    FlashPlayer!WinMainSandboxed+0x1f1122 (0153439c) ;Read CHUNK.Length from attacker controlled buffer
015344a5 8bd8            mov     ebx,eax                                          ;CHUNK.Length = 0ffffffd3h
015344a7 6a04            push    4
015344a9 8d45fc          lea     eax,[ebp-4]
015344ac 50              push    eax
015344ad 8bce            mov     ecx,esi
015344bb e8dcfeffff      call    FlashPlayer!WinMainSandboxed+0x1f1122 (0153439c)
015344c0 8b4d08          mov     ecx,dword ptr [ebp+8]
015344c3 8901            mov     dword ptr [ecx],eax
015344c5 8b560c          mov     edx,dword ptr [esi+0Ch]                          ;Current Position in buffer = 29h
015344c8 8945fc          mov     dword ptr [ebp-4],eax
015344cb 8d441a04        lea     eax,[edx+ebx+4]                                  ;<-First integer overflow
                                                                                  ;TotalValue = Position + CHUNK.Length + 4
                                                                                  ;TotalValue = 29h + 0ffffffd3h + 4 = 0
015344cf 3b4610          cmp     eax,dword ptr [esi+10h]                          ;Compare TotalValue (0) to FileSize (3d0h)
015344d2 7351            jae     FlashPlayer!WinMainSandboxed+0x1f12ab (01534525) ;Unsigned evaluation. Jump is not taken
015344d4 57              push    edi
015344d5 6afc            push    0FFFFFFFCh
015344d7 58              pop     eax
015344d8 83cfff          or      edi,0FFFFFFFFh
015344db 3bd8            cmp     ebx,eax                                          ;Compare CHUNK.Length (0ffffffd3h) to hardcoded 0FFFFFFFCh
015344dd 7e26            jle     FlashPlayer!WinMainSandboxed+0x1f128b (01534505) ;Signed evaluation. Jump is taken.
[...]
01534505 8b4e14          mov     ecx,dword ptr [esi+14h]                          ;Set pointer to Buffer
01534508 03ca            add     ecx,edx                                          ;Set Current Position in Buffer
0153450a 03cb            add     ecx,ebx                                          ;<-Second integer overflow
                                                                                  ;Increment by CHUNK.Length leading to position out of the buffer backward
0153450c e88bfeffff      call    FlashPlayer!WinMainSandboxed+0x1f1122 (0153439c)
[...]
0153439c 0fb601          movzx   eax,byte ptr [ecx]                               ;<-Can read out of designated buffer
0153439f 0fb65101        movzx   edx,byte ptr [ecx+1]                             ;<-Can read out of designated buffer
015343a3 c1e008          shl     eax,8
015343a6 0bc2            or      eax,edx
015343a8 0fb65102        movzx   edx,byte ptr [ecx+2]                             ;<-Can read out of designated buffer
015343ac 0fb64903        movzx   ecx,byte ptr [ecx+3]                             ;<-Can read out of designated buffer
015343b0 c1e008          shl     eax,8
015343b3 0bc2            or      eax,edx
015343b5 c1e008          shl     eax,8
015343b8 0bc1            or      eax,ecx
015343ba c3              ret

State in the erroneous code path looks like below. The designated buffer containing the content of PNG file starts at 00e4c810 where the PNG signature is seen. Due to the bug the instruction reads the memory at 4 bytes minus the pointer to the buffer, at 00e4c80c. Note, the instruction doesn't cause access violation because the illegally accessed memory address is mapped.

0:000> t
eax=fffffffc ebx=ffffffd3 ecx=00e4c80c edx=00000029 esi=0019e134 edi=ffffffff
eip=0153439c esp=0019dbf4 ebp=0019dc08 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!WinMainSandboxed+0x1f1122:
0153439c 0fb601          movzx   eax,byte ptr [ecx]         ds:002b:00e4c80c=00
0:000> db ecx
00e4c80c  00 00 00 00 89 50 4e 47-0d 0a 1a 0a 00 00 00 0d  .....PNG........
00e4c81c  49 48 44 52 00 00 01 2c-00 00 01 2c 08 02 00 00  IHDR...,...,....
00e4c82c  00 f6 1f 19 22 ff ff ff-d3 49 44 41 54 78 9c ed  ...."....IDATx..
00e4c83c  d9 31 8a c3 40 14 44 c1-1e e3 fb 5f 59 8a 9d 09  .1..@.D...._Y...
00e4c84c  1c bc 40 55 6c b4 20 70-f2 68 98 7f b6 6b bb ce  ..@Ul. p.h...k..
00e4c85c  ef df b6 f3 e8 9f f3 ad-6f 7d fb e7 b7 9f 01 a9  ........o}......
00e4c86c  ef 4e fd 13 e0 dd 44 08-31 11 42 4c 84 10 13 21  .N....D.1.BL...!
00e4c87c  c4 bc 8e 42 cc 12 42 4c-84 10 13 21 c4 44 08 31  ...B..BL...!.D.1

Root Cause

Two incorrect sanity checks were identified.

Incorrect sanity check (015344cf) because it happens after the overflow (015344cb).
Incorrect sanity check (015344db) because signed comparison is performed on CHUNK.Length that is unsigned.

Severity

The technical severity of this bug is low because diverting execution flow is not possible. Further analysis suggests that address disclosure is not possible because the memory region can be accessed out of the designated buffer doesn't contain address.

Reproduction

Open Flash Player 12.0.0.38 (flashplayer_12_sa.exe has a size of 10,339,208) in Windbg. Then execute the following command.
0:006> bp flashplayer + 001f44a0 2
0:006> g
Open the PoC in Flash Player (send me an e-mail for a copy). Debugger breaks-in so you can step through the disassembly code and see the data-flow as explained above.

I'm aware there is a new version of Flash Player 12.0.0.44. I verified and it's affected by this bug, too.

UPDATE On 26th February an Adobe engineer confirmed via e-mail that he could reproduce the bug.

February 13, 2014

Data Flow Tracking in Flash Player: Undocumented Bytecodes and JIT

Undocumented Bytecodes

I did some analysis how the bytecodes in DoABC tag parsed, and compared the result against what I saw in the AVM2 documentation (May 2007). I found that Flash Player can parse certain bytecodes that are not mentioned in the documentation.

Bytecode Note Bytecode Note Bytecode Note Bytecode Note
0x00 RESERVED 0x40 newfunction 0x80 coerce 0xc0 increment_i
0x01 UNDOCUMENTED 0x41 call 0x81 UNDOCUMENTED 0xc1 decrement_i
0x02 nop 0x42 construct 0x82 coerce_a 0xc2 inclocal_i
0x03 throw 0x43 callmethod 0x83 UNDOCUMENTED 0xc3 declocal_i
0x04 getsuper 0x44 callstatic 0x84 UNDOCUMENTED 0xc4 negate_i
0x05 setsuper 0x45 callsuper 0x85 coerce_s 0xc5 add_i
0x06 dxns 0x46 callproperty 0x86 astype 0xc6 subtract_i
0x07 dxnslate 0x47 returnvoid 0x87 astypelate 0xc7 multiply_i
0x08 kill 0x48 returnvalue 0x88 UNDOCUMENTED 0xc8 RESERVED
0x09 label 0x49 constructsuper 0x89 UNDOCUMENTED 0xc9 RESERVED
0x0a RESERVED 0x4a constructprop 0x8a RESERVED 0xca RESERVED
0x0b RESERVED 0x4b RESERVED 0x8b RESERVED 0xcb RESERVED
0x0c ifnlt 0x4c callproplex 0x8c RESERVED 0xcc RESERVED
0x0d ifnle 0x4d RESERVED 0x8d RESERVED 0xcd RESERVED
0x0e ifngt 0x4e callsupervoid 0x8e RESERVED 0xce RESERVED
0x0f ifnge 0x4f callpropvoid 0x8f RESERVED 0xcf RESERVED
0x10 jump 0x50 UNDOCUMENTED 0x90 negate 0xd0 getlocal_0
0x11 iftrue 0x51 UNDOCUMENTED 0x91 increment 0xd1 getlocal_1
0x12 iffalse 0x52 UNDOCUMENTED 0x92 inclocal 0xd2 getlocal_2
0x13 ifeq 0x53 UNDOCUMENTED 0x93 decrement 0xd3 getlocal_3
0x14 ifne 0x54 RESERVED 0x94 declocal 0xd4 setlocal_0
0x15 iflt 0x55 newobject 0x95 typeof 0xd5 setlocal_1
0x16 ifle 0x56 newarray 0x96 not 0xd6 setlocal_2
0x17 ifgt 0x57 newactivation 0x97 bitnot 0xd7 setlocal_3
0x18 ifge 0x58 newclass 0x98 RESERVED 0xd8 RESERVED
0x19 ifstricteq 0x59 getdescendants 0x99 RESERVED 0xd9 RESERVED
0x1a ifstrictne 0x5a newcatch 0x9a RESERVED 0xda RESERVED
0x1b lookupswitch 0x5b RESERVED 0x9b RESERVED 0xdb RESERVED
0x1c pushwith 0x5c RESERVED 0x9c RESERVED 0xdc RESERVED
0x1d popscope 0x5d findpropstrict 0x9d RESERVED 0xdd RESERVED
0x1e nextname 0x5e findproperty 0x9e RESERVED 0xde RESERVED
0x1f hasnext 0x5f UNDOCUMENTED 0x9f RESERVED 0xdf RESERVED
0x20 pushnull 0x60 getlex 0xa0 add 0xe0 RESERVED
0x21 pushundefined 0x61 setproperty 0xa1 subtract 0xe1 RESERVED
0x22 RESERVED 0x62 getlocal 0xa2 multiply 0xe2 RESERVED
0x23 nextvalue 0x63 setlocal 0xa3 divide 0xe3 RESERVED
0x24 pushbyte 0x64 getglobalscope 0xa4 modulo 0xe4 RESERVED
0x25 pushshort 0x65 getscopeobject 0xa5 lshift 0xe5 RESERVED
0x26 pushtrue 0x66 getproperty 0xa6 rshift 0xe6 RESERVED
0x27 pushfalse 0x67 UNDOCUMENTED 0xa7 urshift 0xe7 RESERVED
0x28 pushnan 0x68 initproperty 0xa8 bitand 0xe8 RESERVED
0x29 pop 0x69 RESERVED 0xa9 bitor 0xe9 RESERVED
0x2a dup 0x6a deleteproperty 0xaa bitxor 0xea RESERVED
0x2b swap 0x6b RESERVED 0xab equals 0xeb RESERVED
0x2c pushstring 0x6c getslot 0xac strictequals 0xec RESERVED
0x2d pushint 0x6d setslot 0xad lessthan 0xed RESERVED
0x2e pushuint 0x6e getglobalslot 0xae lessequals 0xee RESERVED
0x2f pushdouble 0x6f setglobalslot 0xaf greaterequals 0xef debug
0x30 pushscope 0x70 convert_s 0xb0 UNDOCUMENTED 0xf0 debugline
0x31 pushnamespace 0x71 esc_xelem 0xb1 instanceof 0xf1 debugfile
0x32 hasnext2 0x72 esc_xattr 0xb2 istype 0xf2 UNDOCUMENTED
0x33 RESERVED 0x73 convert_i 0xb3 istypelate 0xf3 RESERVED
0x34 RESERVED 0x74 convert_u 0xb4 in 0xf4 RESERVED
0x35 UNDOCUMENTED 0x75 convert_d 0xb5 RESERVED 0xf5 RESERVED
0x36 UNDOCUMENTED 0x76 convert_b 0xb6 RESERVED 0xf6 RESERVED
0x37 UNDOCUMENTED 0x77 convert_o 0xb7 RESERVED 0xf7 RESERVED
0x38 UNDOCUMENTED 0x78 checkfilter 0xb8 RESERVED 0xf8 RESERVED
0x39 UNDOCUMENTED 0x79 RESERVED 0xb9 RESERVED 0xf9 RESERVED
0x3a UNDOCUMENTED 0x7a RESERVED 0xba RESERVED 0xfa RESERVED
0x3b UNDOCUMENTED 0x7b RESERVED 0xbb RESERVED 0xfb RESERVED
0x3c UNDOCUMENTED 0x7c RESERVED 0xbc RESERVED 0xfc RESERVED
0x3d UNDOCUMENTED 0x7d RESERVED 0xbd RESERVED 0xfd RESERVED
0x3e UNDOCUMENTED 0x7e RESERVED 0xbe RESERVED 0xfe RESERVED
0x3f RESERVED 0x7f RESERVED 0xbf RESERVED 0xff RESERVED

The loop and the big switch statement parsing DoABC bytecode is near 0x6087e9. Instruction near 0x58f25d also reads bytecode. The documentation certainly needs an update on Adobe's side so developers can add the currently undocumented bytecodes to their decompiler/disassembler.

JIT

After adding new functionalities to my pintool I run it against Flash Player. Here is my observation.

When executing a flash file containing DoAction tag in Flash Player no memory page allocated with or set to *EXECUTE* flag. Thus no dynamically generated code was executed with the most common method. Therefore I think DoAction works with interpreted execution. Meaning every single bytecode run on isolation rather than a set of bytes compiled&run (JIT).

When executing a flash file containing DoABC tag in Flash Player I observed increased usage of VirtualAlloc. The page was allocated with PAGE_READWRITE flag. Later on the execution the page was set to PAGE_EXECUTE_READ and the execution flow was transferred to the page. When the execution was returned to the caller the page was set back to PAGE_READWRITE. I knew this was a part of how JIT works. Change of the memory protection flags is the mitigation for DEP.

0x5205a6 is a VirtualProtect call to change the memory protection flags. When it's called with PAGE_READWRITE it's called via 0x5fc39c. When it's called with PAGE_EXECUTE_READ it's called via 0x5fc2e9.

During my experiment I figured out that instruction at 0x5d20ef calls into the JIT-compiled code. Though this might not be the only address to call JIT-compiled code from. I observed many call backs in the JIT-compiled code. One of the callback might be to give continuous feedback to the caller for example if a long loop is being executed. I observed that constants are encrypted with xor instructions to make memory spraying more difficult. This is not new but first time for me to see. This is how 0x41414141 looks like when it's encrypted.
03af1f67 b83a7c1959      mov     eax,59197C3Ah
03af1f6c 357b3d5818      xor     eax,18583D7Bh
All offsets in this post are RVAs, that is relative to Flash Player's image base. Offsets are appropriate in Flash Player 12.0.0.38 (flashplayer_12_sa.exe has a size of 10,339,208).

February 9, 2014

Data Flow Tracking in Flash Player: DefineBitsJPEG2

Here are some offsets of the standalone Flash Player 12.0.0.38 (flashplayer_12_sa.exe has a size of 10,339,208). The offsets are reached when parsing ImageData field in DefineBitsJPEG2 tag.
0x570e7  | Checks for PNG signature.
0x57124  | Checks for GIF87a signature.
0x57138  | Checks for GIF89a signature.
0x5717D  | Checks for JPEG XR (Windows Media Photo) signature.
0x5719B  | Checks for ATF (Adobe Texture Format) signature.
0x4b6fc2 | Checks for JPEG (ff d8) signature.
The first five checks performed on the permanent buffer that I wrote about in the previous post. The last check is performed on the match buffer that contains the first 0x200 bytes of ImageData in the permanent buffer. The bytes are copied from the permanent buffer to the match buffer using fast memcpy function (0x701030).

I looked at the specification of DefineBitsJPEG2 tag. It's interesting to see that swf specification version 19 says "Compressed image data is either JPEG, PNG, or GIF89a format". However as seen above there might be other file formats supported. Good to know if you want to fuzz this area.

All offsets in this post are RVAs, that is relative to Flash Player's image base.

February 4, 2014

Data Flow Tracking in Flash Player

I've been writing a Pintool that is to track the data flow between buffers in Flash Player. The tool logs when bytes from the flash file are read to the buffer, and when bytes from the buffer are copied to the subsequent buffer. The tool also logs the instruction that dereferences a position in the buffer. And, if appropriate, the tool can project the position in the buffer back to the position in the flash file.

Using Pintool can speed up the work that I would do with debuggers otherwise. When analyzing with Pintool I don't see all the non-relevant instructions that a debugger would show up but rather see more, and more relevant information. Therefore I can focus better on what's important.

I picked up an uncompressed flash file - with FWS signature in its header - and loaded it in the standalone Flash Player 12.0.0.38 (flashplayer_12_sa.exe).

When executing, the flash file is read by a loop of ReadFile() that takes 0x10000 as the number of bytes (64Kb) to read in each iteration. Once ReadFile() receives the bytes read from the file, the bytes are copied to the permanent buffer. _fastcopy_I is used to copy bytes to the permanent buffer that has RVA of 0x70c396. Note, it's reasonable to assume there might be other copy functions embedded in Flash Player for different architectures.

Many bytes in the permanent buffer are just propagated to other buffers. Otherwise, a validation is directly performed by dereferencing certain regions of the permanent buffer. I was looking at the latter as I'd inspect the data flow on a deeper level at a later date. One such validation on the permanent buffer is the following inline strlen() core, that has RVA of 0x1a922.

0040a922 8a08            mov     cl,byte ptr [eax]
0040a924 40              inc     eax
0040a925 84c9            test    cl,cl
0040a927 75f9            jne     FlashPlayer!WinMainSandboxed+0x176a8 (0040a922)


Instructions dereference the permanent buffer from 42 ditinct locations in Flash Player. I had a look at the addresses with their surrounding code and found the inline strlen() core above to be the most interesting. Note, the above inline strlen() core can be found at multiple locations in Flash Player.

There are couple of reasons I found the above code interesting. When looking at the surrounding code I didn't see sanity check of the string size. The position in the buffer can be projected back to the position in the flash file by Pintool. At this point, I suspected I had good control over the string size as I can make it to be a long string and even I can remove the ending zero in the flash file without causing Flash Player to bail out early.

Below is the screenshot of the flash file. strlen() is triggered on "movieLoader" string ending with zero. It's highlighted.

I was considering to alter the flash file in a way to overwrite the ending zero of the string, and all subsequent zeros in the following tags (DefineButton2, PlaceObject2, etc). It would mean the string would run until the end of the file hoping strlen() to read out of the bounds. Actually, when experimenting, I took the plunge to remove the last few tags and appended few "A" characters to the string until end-of-file, without ending zero. Also, adjusted the file size in the header as it was obligatory. Here is a photo of the string.

I run Flash Player and saw in the log produced by Pintool that the inline strlen() is triggered meaning it processes the altered string. This is good because the code is still reachable when reading the corrupted string. However an unexpected zero byte beyond the string causing the loop to break without Flash Player entering in an inconsistent state or reading out of bounds.

I wanted to know how the zero byte is set at the end of the string so I changed Pintool to log all write accesses on the permanent buffer. When I run Flash Player again, I didn't see in the log that zero byte is set at the end of the string. As being paranoid today, I immediately thought that might be an uninitialized value that is zero by chance but this was a guess so thought better check how the memory is allocated for the permanent buffer. I added the required code to Pintool to see how the memory is allocated and I saw it is allocated by VirtualAlloc. This explains the zero byte as memory allocated with this function is automatically initialized with zero.

I was thinking if I can construct the flash file to place the string at the end of memory page without leaving space for padding zero bytes. I was able to control the amount of memory to allocate by changing the file size field in the flash file (at offset 4). What I observed is that the size of allocated memory is always bigger than the file size in the header and so there are always padding bytes. In my experiment I saw the pattern that the amount of memory to be allocated is calculated by incrementing the file size by one and rounding up to the nearest multiple of 0x1000.

It seems defense against this string attack is consciously added by Adobe preventing to read out of bounds.
  This blog is written and maintained by Attila Suszter. Read in Feed Reader.