Analyzing CVE-2015-0350 with REVEN

Dec 10, 2018
by Luc
Categories: Technical -
Tags: Reverse Engineering - CVE - Adobe Flash - REVEN -

In this article we will show how we analyzed and tamper the PoC for CVE-2015-0350, an Adobe Flash vulnerability located in the parsing of JPEG-XR images, with the help of timeless analysis.

With REVEN v1.5.0, from an input file causing a crash, we analyze the vulnerability by instantly time-traveling to the root cause using REVEN’s memory history feature, prove the crash is user controlled using the data-tainter, analyze and modify the faulty entry bytes to overwrite the saved eip, and analyze how to control its value.

This vulnerability has been discovered by ‘scvitty’ from Google Project Zero, a PoC and the corresponding analysis are already available.


Finding the bug

The first step of our analysis is to locate the call to KiUserExceptionDispatcher in the trace and explain the crash. The page fault exception occurs because the register ebp contains the invalid value 0xfffffffb. Four instructions before we find that the ebp has just been modified by the leave instruction. The leave instruction is usually used to restore the saved ebp that has been pushed on the stack at the very beginning of the function. Basically, it is the combination of the two following instructions:

	mov esp,ebp 	  // Make esp point to the saved ebp
	pop ebp		      // Restore the saved ebp

In this case it means that the saved ebp on the stack has been overwritten.

Now let’s find out why.

We can open a new hexdump on the stack (pointed by esp) and use the memory history feature from REVEN, to find the origin of the overwrite and the faulty instruction:

By analyzing the block of instructions that is executed three times, we can see that the problem comes from an out-of-bound access to the array starting at ebp+0x2c. As a matter of fact, the index value in eax is incremented multiple times: once at each round of the loop but also with values extracted from another array starting at ebp+0x54 which are way too big for the receiving array.

The following screenshots describe the overflow step by step:

1 - First, the index is incremented using a value from the input array:

2 - Then the destination array is written at this index:

3 - At the next iteration, counting the first loop incrementation and the addition from the input array, the index value is now 5

4 - We can see in the hexdump the index 5 being overwritten:

5 - The last value fetched from the input array is 9, which will lead to the overflow:

6 - The index is now 0xf while the maximum index of the array is 0xd, hence the next write will overwrite the saved ebp:

Proving that the faulty value is user controlled

REVEN comes with a fully integrated data tainter, forward and backward, which makes it easy to prove that a value is user controlled. The next step is to taint backward one of the entries that is used to increment the index, taint it, and check if at some point the buffer containing the input file is tainted. :

To find the in-memory buffer containing the input file data, we will pick some string in it, for example “WMPHOTO”, and use the Strings widget to find where in the trace the string is manipulated.

Now that we have a view on the input file in memory, we can look at it and see tainted bytes:

This proves that the faulty data is user controlled, and most researchers would stop here and responsibly submit the bug to the editor.

Saved EIP overwrite

From the tainted values returned by REVEN, we have two options: we can either follow and analyze the taint through each and every instruction from the input buffer to the crash, in order to extract the logic to precisely understand the outcome, or, since we only have a few bytes, try to brute-force them.

A third choice would be a mix of both solutions: quickly exclude uninteresting bytes by following the taint and brute-force the last ones.

These aren’t the bytes you’re looking for

Tainted bytes from the input file are the following: one dword at offset 0x4, two dwords at offset 0x6e and one very interesting suspiciously isolated byte at offset 0x1be.

The first dword at offset 0x4 is equal to 0x20. Going through the first instructions that use this value, we can see that it is used as an offset to fetch another dword. Considering its position (offset 0x4) we can assume that it is related to basic file parsing, and even though we don’t exclude this value for sure, we can assume that it is normal that this value is tainted since the whole parsing depends on it;

The following two dwords at offset 0x6e can quickly be excluded for similar reasons:

  • Following the first one (value 0x1) in the trace, we quickly find that it is used as an offset to find the second one (right after);

  • The second one is 0x86, which is exactly the offset of the string “WMPHOTO”, which is the magic signature for this file format and it is tested right after. Again, since it is related to the basic parsing of the file, it seems normal that this value is tainted.

Pushing the right button

At this point there is only one tainted byte left. The parsing of this byte isn’t trivial to analyze, so we can just brute-force it and see what happens.

The results are immediate. Using a debugger, we see that when modifying this byte, the overwrite occurs on different parts of the stack, such as the security cookie and… the saved eip. For example, modifying 0xdf to 0xf0 triggers a crash with eip=0x1e2:

(36c.8a0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=02827340 ebx=02827000 ecx=0919456f edx=00000000 esi=0282780c edi=0012d3ec
eip=000001e2 esp=0012d354 ebp=0012d39c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
000001e2 ??              ???
2:045> kv
ChildEBP RetAddr  Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012d350 02827000 00000000 00000000 00000001 0x1e2
0012d39c 05ad64fd 02827000 00000000 00000000 0x2827000
0012d3d0 05ad41e3 00000000 0012d3ec 022061e0 pepflashplayer!PPP_ShutdownBroker+0x4d530a
0012d420 0580c743 02827000 021839d3 0000016f pepflashplayer!PPP_ShutdownBroker+0x4d2ff0

Now that we know we successfully over-wrote the saved eip, we might want to control the value we write to it.

EIP control

At first, we tried to link the 0x1e2 to some part of the data in the file, and one may notice that it’s very probably a size, but we didn’t manage to tamper with it directly. On the other hand, with some quick brute-forcing and observation of the crash value, tampering bytes that are right after the original 0xdf modify the eip value in the resulting crash dump.

After a few (actually many) iterations, we found that if we replace two bytes at offset 0x1be with f0 7f, then the next four bytes will be directly linked to the final eip. Even though we found this result manually, for the sake of this article, we will explain how the four bytes are controlled by recording a new scenario in REVEN and analyzing it, and shortly explain the CFG changes induced by these two replaced bytes.

Setting up the new input for the scenario

Here is what the original PoC file used to be:

000001a0  a3 9f 58 b1 37 36 62 a8  e4 74 1b 82 a4 13 60 d8  |..X.76b..t....`.|
000001b0  84 4c f6 40 74 8d 06 a4  12 f2 39 43 19 70 df 53  |.L.@t.....9C.p.S|
000001c0  83 08 56 b0 0a 7b 84 83  4f 05 15 98 5b 7b 32 86  |..V..{..O...[{2.|
000001d0  ec ca 5a 95 57 e0 14 a2  23 74 9c 1a 05 16 66 a1  |..Z.W...#t....f.|

And here is the modifications we have used to record the new scenario:

000001a0  a3 9f 58 b1 37 36 62 a8  e4 74 1b 82 a4 13 60 d8  |..X.76b..t....`.|
000001b0  84 4c f6 40 74 8d 06 a4  12 f2 39 43 19 70 f0 7f  |.L.@t.....9C.p..|
000001c0  ff 41 41 3f 0a 7b 84 83  4f 05 15 98 5b 7b 32 86  |.AA?.{..O...[{2.|
000001d0  ec ca 5a 95 57 e0 14 a2  23 74 9c 1a 05 16 66 a1  |..Z.W...#t....f.|

Analysis: ff 41 41 3f, a journey from file to EIP

Just like our first analysis to find the crash, we can look for the call to KiUserExceptionDispatcher in the trace. We land where the retn instruction tried to pop the value 0x3f414141 from the stack in eip, hence the page fault. In the following screenshot we opened a new hexdump on the memory pointed by esp to see the stack layout:

Once again, we can use the memory history to find the same portion of code (the three writes block) that previously overwrote the saved ebp in the first record. The result is stored on the stack at address 0x0015cf64 as the following hex dump shows, and the code responsible for its computation is visible right before:

Analyzing the code shows that the first of the four bytes is tampered whilst the three next bytes are almost left as is.

Here is a pseudo code:

    // Handle the first byte
    BitCount = first_byte >> 0x5 	          // 0xff => 0x7
    first_byte = first_byte << 0x3 	          // 0xff => 0xf8
    BitCount += 16 			                  // => 0x1d
    save_BitCount = BitCount
    i = 0
    while i < 5:
        copyMSB(result, first_byte)
        shl(result, 1)
        shl(first_byte, 1)
    // At this point, result = 0x1f, count = 0x18 = 3 * sizeof(byte)
    // Now handle the 3 nexts bytes, copied bit by bit:

    i = 0
    j = 0
    while i < 3:
        while j < 8:
            copyMSB(result, next_byte)
            shl(result, 1)
            shl(next_byte, 1)

    // At this point, result = 0x1f41413f, there is one more operation:
    offset = 1 << save_BitCount 	          // = 0x20000000 when bitcount=0x1d
    result = result + offset + 0x2
    // So the result is now 0x3f414141

Now by inverting this small algorithm, each byte of the eip can be more or less controlled, with the first byte being capped at 0x3f.

Analysis: One byte to overwrite the right place with the right value

The last missing piece of information is the impact of 0x7f byte on the CFG.

Looking at this new trace we realize that it has significantly modified the control flow of the trace, leading to the parsing of the next four bytes before returning from the function.

We must say, finding this value for this byte was a mix of perseverance, luck and intuition because looking at the source code of JPEGXR parser indicates that it went through a very specific path:

From the r_DECODE_BLOCK() an index is computed, if this index is even, then r_DECODE_ABS_LEVEL is called, in which an absolute index is computed, and finally, if this absolute index is 6, then the four next bytes are parsed and copied in the array, at the convenient position that will be read to rewrite the saved eip.

Here is the control flow from the source file:

        index = r_DECODE_FIRST_INDEX()
        if (index & 2)
            r_DECODE_ABS_LEVEL(image, str, band, context);
                abs_index = dec_abslevel_index()
                if (abs_index == 6)

Finding this path by reading the source code would have been quite complicated, so bruteforcing was the right choice in this case.

This post will not show a full exploitation chain, but feel free to have a look at it!


This analysis focused on tracking data through the execution to understand the root cause of the bug and prove the exploitability. We showed that REVEN’s Timeless Analysis allows to instantly find the faulty instructions and the associated root cause data in the input file. REVEN also helped a lot when narrowing down the taint results to a single byte, greatly improving the efficiency of fuzzing results.

Generally speaking, since REVEN allows us to instantly time-travel in memory, and that all memory accesses are indexed, dealing with unidentified crashes or unknown vulnerabilities (such as those exploited by malware) becomes very straightforward.

To go further

For more information about Reven, have a look at the following:

  • this blog entry from Cisco Talos, showing how to prove or disprove exploitability of a crash

  • this blog entry from Tetrane, showing how to analyze a Use-After-Free in Internet Explorer

  • this blog entry from Thanh Dinh TA, reversing a deeply obfuscated challenge

Or directly contact us at: contact (at) tetrane (dot) com!

Next post: Analysis of VLC Exploit Arbitrary Code Execution (CVE-2018-11529)
Previous post: Reversing DirtyC0W