In March 2014, we observed a patched Adobe Flash vulnerability (CVE-2015-0336) being exploited in the wild. Adobe released the patch on March 12, 2014, and exploit code using this vulnerability first appeared about a week later.
To help stay protected:
This blog digs deeper into the technique and tactics the attacker used to exploit this vulnerability. Understanding these techniques can help you better defend your enterprise software security infrastructure against similar exploits.
First things first, let’s talk about the vulnerability itself. The vulnerability is a “type confusion”, a common technique with ActionScript Virtual Machine. Usually, when a piece of code doesn’t verify the type of object that is passed to it, and uses it blindly without type-checking, it leads to type confusion. Type confusion can be very dangerous because a type is expressed as a layout of memory in the lower level implementation of Flash Player. Also with type confusion, wrong function pointers or data are fed into the wrong piece of code. In some circumstances this can lead to code execution.
Figure 1 shows the CVE-2015-0336 exploit code that triggers the vulnerability. This piece of code resembles the proof of concept code detailed by the finder, however, the details are somewhat different.
The first difference is the usage of an ASnative(2100,200) call instead of a NetConnection class initiation. Also, the code that triggers the confusion is different. The exploit utilizes method 8 (line 9) and calls to apply method of NetConnection function object to trigger the type confusion. The original Google Project Zero code used method 1 and a call method on this object.
Figure 1: Exploit code that triggers type confusion
The object that is passed to ASnative(2100,8) function is the _loc_2 object. The _loc2_ object is an ASnative function object that doesn’t exist. It’s just a placeholder for a function object. However, this ASnative object from line number 5 is very important in the exploitation technique, as discussed below.
The _loc2_ object is not a NetConnection object at all - its __proto__ property is set to an Object type object (_loc3_). ASnative(2100,200) is a constructor for the NetConnection object and this _loc3_ object is initialized to the NetConnection type at line 8. This makes the line 9 code to use the non-NetConnection object as a NetConnection object and treats the object’s memory layout as if it is a NetConnection object. Figure 2 shows the actual code that checks for the __proto__ property. With an updated binary, this part is fixed with more sanity checks to prevent unwanted objects passed down further into the code below.
Figure 2: The proto object check routine
The function that checks code for the proto object is actually a function that processes the ASnative(200,x) commands. It has a jump table that processes each function cases, as shown in Figure 3.
Figure 3: Jump table for function dispatch
The function number 8 falls through the jump table to the code piece as shown in Figure 4. If all the payload and vector spray code is removed from the exploit, it will crash.
Figure 4: Crash point
The actual crash point looks like this:
7061f9b2 8b4f7c mov ecx,dword ptr [edi+7Ch] ds:002b:1a1e207c=????????
It tries to access an invalid memory pointed to by the non-NetConnection object.
At this point, edi designates the start of the object as 0x1a1e2000. This value is directly controlled by the attacker. From the code below, 438181888 is actually 0x1a1e2000. This is an unexpected feature with ASnative and the ActionScript engine. You can control exactly what ASnative (edi) designates here:
var _loc2_ = _global.ASnative(2100,438181888);
The exploit author tried to find and use an instruction that writes a field inside the fake object. First, it sprays a lot of vector objects on the memory. The specific exploit that we analyzed creates more than 120,000 counts of vector.<uint> objects with a length of 0x3FE, as shown in Figure 5. After spraying these objects, it shrinks the size of each vectors to 0x1E to make some empty rooms.
Figure 5: Vector object creation
We know that the fake ASnative object points to 0x1a1e2000 and that this address is allocated by the exploit code with the vector.<uint> object from heap spray code, as shown in Figure 6.
From our testing, the allocation behavior from the ActionScript virtual machine was predictable, and we saw 100% allocation of this memory area. The attacker acquires full control over the contents of the fake NetConnection object. Whatever value it fills for the object from the vector.<uint> array will be recognized as a NetConnection object.
Figure 6: Vector.<uint> with a size of 0x1e is used as a fake NetConnection object
This fake NetConnection object is passed to various functions and treated as a real NetConnection object. The attacker figured out that they can use function 8 to achieve vector corruption. Function 8 has one of the subroutines that passes one of the NetConnection member objects located at the offset of 0x7C, as shown in Figure 6. The object’s variable at offset 0xBD8 is overwritten later by an instruction.
Figure 7 shows the data flow and how a specific memory location can be written with a value from the new function call. The overwritten location is 0x1a1e2001 - right inside the vector.length field. Now the exploit has full control over an excessive amount of memory area. With this extra power, it can perform additional vector corruption to open up full range memory control.
Figure 7: How vector corruption occurs
After the vector corruption, the exploit builds an ROP chain and shellcode by reading into the process memory and collecting the required gadget locations. The exploitation creates a FileReference object on the memory and overwrites its cancel method to the attacker-controlled code.
After that, it calls the FileReference.cancel method to pass control to malicious code.
In conclusion, this vulnerability resides in the old ActionScript 2 engine, an area that was ignored by malicious attackers for some time. However, now that a vulnerability in this legacy code has been revealed, we might see more exploited.
When the vulnerability itself is a type confusion, exploiting it is relatively easy for an attacker utilizing old methods of corrupt vector objects. The predictable behavior of vector allocation and layout implementation of Adobe Flash Player has been exploited for some time. Using the vector corruption method, an attacker can gain a reliable entry point to further exploit vulnerabilities that are otherwise not so simple to exploit.
Understanding how this exploit works helps us to be better prepared to detect and patch future exploits. Exploits such as this are usually delivered through exploit kits.
Jeong Wook Oh