A perfect Bug (CVE-2015-3077): Using type obfuscation in Flash
For some attackers, it is important that exp can be used stably. In other words, exp can cause code execution every time on a system running a specific flash version of a specific platform. One way to write such high-quality exp is to use high-quality bugs (that is, actively writing stable exp ).
This article discusses the use of this type of bug and the factors that are suitable for compiling the stable use of exp.
The Bug
Cve-2015-3077 is a type obfuscation problem that occurs in the setters method property of Adobe Flash Button and MovieClip fliters (an index array [1] containing the filter object, so that any type of filter can be confused with other types of filter. This vulnerability was reported to Adobe In May and fixed in May. The root cause of this bug is that attackers can override the constructor that initializes the filter object. The following code illustrates the problem:
var filter = new flash.filters.BlurFilter(); object.filters = [filter]; var e = flash.filters.ConvolutionFilter; flash["filters"] = []; flash["filters"]["BlurFilter"] = e; var f = object.filters; var d = f[0];
This code may be confusing because of the '[]' operator, which is required during Flash CS compilation. Logically equivalent code is as follows (compilation is not guaranteed ):
var filter = new flash.filters.BlurFilter(); object.filters = [filter]; flash.filters.BlurFilter = flash.filters.ConvolutionFilter; var f = object.filters; var d = f[0];
The above Code sets the filters attribute of the button object or a MovieClip object to BlurFilters (which of course depends on the type of the object in the Code) and then stores it in the native mode by Flash. The BlurFilter constructor is then overwritten by the ConvolutionFilter constructor. Then the getter method of filters is called to generate the initial BlurFilter for storing the ActionScript object. However, the ConvolutionFilter constructor has been overwritten. This causes the original ConvolutionFilter object to be returned as a BlurFilter object.
The final result is that the ConvolutionFilter domain can be accessed as a BlurFilter (for read/write operations), and other types of filters are similar. This provides many opportunities for modification.
Figure 1 shows the memory layout of the original object that can be mixed in 64-bit linux.
Figure 1 AS2 Filter Types
In both cases, the space occupied by the pointer is aligned with the space occupied by multiple certificates or floating points (the float * matrix of ConvolutionFilter in 1 is just aligned with the space occupied by float blurX and float blurY), which means that, the pointer can be read and written directly. Similarly, because the sequence and size of the object's domain are determined by the class definition, the position of the object is generally predictable, so reading and writing the object will not fail. These features are critical for compiling the exp for stable use.
The Exploit
This defect may trigger type obfuscation multiple times. Therefore, FilterConfuse is a function used for type obfuscation. confuse. this function also performs some cleanup operations, such as modifying the constructor of The ActionScript filter to a normal constructor, in this way, the defective function can be called multiple times without affecting the action of ActionScript outside the function (if this is not done, it is likely to crash directly ).
The first thing to do is to determine the vtable (virtual table) Address to bypass the ASLR technology. The ideal way is to use a special object for an object that contains a virtual table. A member of this object has a cross relationship with the virtual table and can be modified, however, the virtual tables of all filter objects are at the same offset. Instead, I used the BitmapData object of DisplacementMapFilter to determine the address of the virtual table.
To determine the location of the BitmapData object in the memory, I used BevelFilter to confuse the DisplacementMapFilter type. The result is that the BitmapData pointer stored in the DisplacementMapFilter and the color attribute of BevelFilter (shadwoColor, shadowAlpha, highlightColor, and hightlightAlpha, also in terms of memory distribution) it is aligned (the space occupied is the same as that used for small tasks ). These attributes are stored in two 32-bit integers (scolor, hcolor of 2). The color attributes can access the 24-bit lower values of each integer, the alpha attribute can access the 8-bit high integer value. by reading these attributes and combining bit operations, it is possible to extract the real address of the BitmapData object.
Figure 2. Retrieving the BitmapData pointer
Next, we need to read the virtual table at the top of the BitmapData object. Therefore, I use the matrix attribute of the ConvolutionFilter object. The value of this attribute is a pointer to a floating point group, and the array to which it points is allocated when the property value is set. When you obtain the value of this attribute, the returned result is an ActionScript array containing these floating point numbers. By pointing the matrix pointer to a BitmapData object, it is feasible to read the value of the object in the memory in the form of a floating point group (in fact, it is a bit similar to C, char * buf = (char *) malloc (size), int * buf2 = (int *) buf. In this way, the data in the buffer can be read and written in char or int ).
To set this pointer, I used a DisplacementMapFilter object (instead of the same object instance as the DisplacementMapFilter object used above) to confuse the ConvolutionFilter object, set the mapPoint attribute to the pointer position of the above BitmapData object. The mapPoint attribute is a point containing the x and y coordinates (the coordinate value is an integer, that is, p_x, p_y in figure 3), and is aligned with the matrix of ConvolutionFilter, this makes it easy to set its value. Then, by reading the matrix array of the ConvolutionFilter object (the object must be DisplacementBitmapFilter for Type confusion and then be obfuscated back to the ConvolutionFilter object), it is feasible to read the virtual table address from the BitmapData object.
Figure 3. Retrieving the vtable pointer value
From this point of view, because of the use of floating point, it is more difficult to use the code stably. The values of vtbale_low and vtable_high are read from the ConvolutionFilter matrix in floating point form (matrix corresponds to an array type ). However, unfortunately, not all valid pointers are valid single-precision floating-point numbers (for example, to comply with the ieee 754 standard), which means reading this value may lead to NaN (not a number) or, worse, the read value is incorrect.
The ideal solution to this problem is to obtain the values of vtable_high and vtable_low through the gettter method obtained in integer form, but there is no such getter, because filter Members are of the floating point type due to their function characteristics.
Fortunately, the AS2 virtual machine is loose in parsing floating-point numbers-it only converts the value in its memory to floating-point when it operates on it. Native operations generally do not resolve it to a floating point unless special operations are performed, such as using its arithmetic operation. This means that when the floating point data of the matrxi array is copied to vtable_low and vtable_high, it will keep its value in the memory until it is copied to the ActionScript and used, or it performs arithmetic operations in the form of native code, even if it is not a valid floating point number. Therefore, if the values of a variable are immediately mixed with different types, and the value range covers values that can be expressed by 32 bits, such as int, it can be determined that the value remains unchanged in the memory corresponding to the matrix array. Therefore, in order to avoid instability in the use of code, it is necessary to perform this type of Type obfuscation before operating any floating point in ActionScript.
To achieve this goal, I wrote a conversion class-FloatConverter (4), which uses type obfuscation in filters to implement integer to floating point and floating point to integer. It uses the color and alpha attribute of GlowFilterd to confuse the matrix attribute of ColorMatrixFilter (which contains floating point groups). It can access different bytes of int.
Figure 4. Float converter
Although this achieves stable float to int conversion, it fails to achieve stable conversion from int to float. When the color array of ColorMatrix filter is accessed in ActionScript, the entire array is copied, even if only the first element is accessed. When an array is copied, each element is converted to a Number (sometimes containing access pointers, such as calling the valueof method of the object ). Because the color array occupies more space than the entire GlowFilter object, it will be extended to the stack when the GlowFilter is used for obfuscation (that is, part of the data on the stack will also be used as the GlowFilter data ). This means that the conversion can occur on unknown values of the heap. When the value is converted to Number, if they reference invalid pointers, it may cause a crash. Therefore, for int to float conversion, I implemented a floating point conversion (5 ), it uses a different obfuscation in ConvolutionFilter and DisplacementMapFilter-direct forced type conversion will not cause any unknown values on the stack to be accessed.
Figure 5. Alternate float converter
This solves the crash caused by access to unknown heap values, but unfortunately there is also a floating point problem related to the stability of using code. This is caused by the implementation of getter of ConvolutionFilter matrix. In ActionScript2, All numeric values are stored in the Number type, while Number is the union of an int and a double pointer. The original ConvolutionFilter matrix is stored as a group of floating-point numbers, but it is copied to an ActionScript array. Therefore, it can be accessed through the getter method of matrix in ActionScript, its value is converted to double in this process. then, when float is converted to this value, they are forcibly converted back to float.
Forcibly convert float to double, and then convert the obtained double to float. Generally, the value remains unchanged unless in a special case, the single-precision floating point value is an SNaN value. According to the single-precision floating point standard, there are two types of NaN (not a number, which is actually a floating point value exception), QNaN and SNaN. When QNaN occurs, it does not do anything, but when SNaN occurs, it throws a floating point exception in some cases. In x86, forced conversion of double to float usually causes NaN to be QNaN (even if double causes SNaN) to avoid unexpected exceptions.
Therefore, the Low bit Of A pointer happens to be an SNaN value, which will be converted to a QNaN value. This means that one of the bit (the first bit of mantissa, bit 22) will be set, and this should not be set. This problem can be avoided when the vtable is read-the third byte of the pointer (containing the bit that can be reversed) can be read in Non-Alignment mode to determine its true value. Therefore, the Code reads the vtable in Non-Alignment mode (add the Bitmap pointer to 1 and then read the vtable for the second time). If the floating point is just SNaN, the int value is corrected.
With the float Conversion Implemented above, the vtable address can be converted into an integer. Now we need to use this address to obtain code execution. A simple way to move an EIP is to overwrite the vtable of an object (or the vtable of the object pointed to by the pointer ). This can be achieved by obfuscation of ConvolutionFilter matrix Array Using the DisplacementFilter BitmapData pointer type.
BitmapData objects contain a series of native objects. The ActionScript object contains a pointer to BitmapData native object, which also contains a pointer to other native objects. One of the objects is the bits object, which contains the actual bitmap bit data. This bits object contains many virtual methods. These virtual methods are usually the first method called when any operation acts on the BitmapData object. To exploit this feature, Exp spoofs a BitmapData object containing a pointer to a forged bits object, and then calls a method, this method will call Virtual Methods for forged bits objects.
You can use the setter method of the matrix attribute of ConvolutionFilter to allocate any buffer size to floating point data. The buffer location can be determined by obfuscation of the ConvolutionFilter type with DisplacementMapFilter and by using the mapPoint attribute of DisplaementMapFilter. The operation is similar to reading the vtable. Since the allocated array remains unchanged, you must first forge a vtable object and a bits object pointing to the vtable, and finally generate a forged bitmap pointing to it.
The first step is to generate a forged vtable and use ConvolutionFilter/DisplacementMapFilter to determine its location.
Figure 6. Creating a fake vtable
Then, a bitmap bits is forged in the same way.
Figure 7. Creating a fake Bitmap Bits
Finally, the forged Bitmap points to the self-generated bits.
Figure 8. Creating a fake Bitmap
Set the BitmapData object of the DisplacementMapFilter object to a pointer to a forged bitmap, use BevelFilter for obfuscation, and set the color attribute to the pointer value (the inverse process of reading the location of the original BitmapData object ), you can obtain the reference to the forged bitmap in ActionScript. This object is then obfuscated back to DisplacementMapFilter using type obfuscation. BitmapData objects can be accessed by calling the getter method of mapImage. Then, whenever the object contains a virtual method call method called (such as setPixel32), this method will call the function address determined by the forged vtale.
From this point of view, it is worth looking at the specific content in the forged vtable. The previous float conversion ignores SNaN: Write float. ConvolutionFilter. setter of matrix also converts float to double, and then converts double back to float before writing. Therefore, if a pointer value happens to be an SNaN value, when writing back to matrix array, bit 23 is set even if it is not in the original value. This can be avoided by using limited non-alignment writes.
In the memory, an SNaN pointer layout is as follows:
00: XX XX YY QQ XX ZZ 00 00
Where:
Xx: any value from 00 to 0xFF YY: bit 5 can be set. Other unrestricted QQ: Except for the 7bit, all other bit bits are 1ZZ: The 7bit is set to 1, no limit on other bit
It can be written as a 32-Bit Single-precision floating point number, as follows:
00: 00 XX XX YY04: QQ XX ZZ 0008: 00 00 00 00
This ensures that if the original pointer value is an SNaN value, all non-alignment values will not be SNaN (because if the original floating point is an SNaN value, the 5bit of YY is always 0 ). You must use two constant pointers to achieve this purpose (unless the two are known to be SNaN values), the layout will be as follows:
00: 00 XX XX YY04: QQ XX ZZ 0008: 00 XX XX XX0C: QQ XX ZZ 0010: 00 00 00 00
There is no limit on the floating point at 0 × 08. bit22 to bit31 may eventually be an SNaN value and is incorrectly written.
Therefore, it is possible to write any pointer to a floating point group. Whether it is an SNaN value or not, it can only be done once. After the original pointer is written, if the original pointer is an SNaN value, all other pointers should be an SNaN value. If the original value is not an SNaN value, all other pointers cannot be SNaN values. The exp method for processing this restriction is to write a single pointer to the Convolution matrix array.
This is easy to implement for forged Bitmap and forged Bitmap bits, because they only need to contain a pointer. The difficulty is that the forged vtable can only contain one pointer, which is difficult to create parameters for function calls and call functions.
A good solution is to move to the buffer zone with fully controllable content after vtable is called for the first time. The good news is that the paletteMap method of the BitmapData (simulated by a forged bitmap) Class creates such a buffer.
This method has four parameters (redArray, greenArray, buleArray, and alphaArray). These four parameters are arrays of The ActionScript Numbers type. When this method is called, it is converted to an integer and copied to the native int array. Then, a native function containing four pointers pointing to the array (pointing to the specific offset of the input array) is called. This method calls the virtual method to jump to the forged vtable.
As part of the initial call, array pointers are stored in r12, r13, r14, and r15 registers of x64. The Pointer Points to four controllable buffers, which is very useful. A single pointer in a forged vtable is essentially the following:
mov rdi, r13call [r12]
The buffer content pointed to by r13 is "gedit", and the buffer content corresponding to r12 is the gadget (not concerned with SNaN) that calls the system method in a Flash library ). The final result is that when the virtual function calls a forged vtable, gedit is called.
Exp so far, although there is no elegant exit (Flash player crashes when gedit exits ). The key to an elegant exit is to call functions on the four buffers multiple times. Even if it is done here, it cannot be spared from the destruction of Flash Player (for example, if the tab is closed or swf is refreshed ). The destructor of the filter object will crash because the pointer to the ConvolutionFilter matrix array is obfuscated as the pointer to the BitmapData object. These objects are distributed on different stacks. Therefore, when an object is called and deleted, but it is actually another object, it will crash. This problem cannot be solved by obfuscation of types to a good state, because type obfuscation creates a copy of the object in this bug, so the original problematic object still exists, however, it must be released. Likewise, you cannot set parameters on the original object. Because of the BitmapData object, the setter of the matrix object will try to delete the existing object before it is created. It is possible to avoid this problem when exp is running. As long as the player keeps open and obtains the references of these objects, the objects will not be released. However, when the player is destroyed, the crash still occurs. Of course, the Code executed by exp can avoid a crash. You can place them in the correct state by correcting the positive type of obfuscation objects in the memory, or point the pointer of the destructor to null. This is not implemented in this section of code.
What Makes this Bug Reliable?
Although type obfuscation is often usable, there are other factors that make cve-2015-3077 very stable.
When type obfuscation is triggered, there are usually two types: defective objects during instantiation and obfuscation types after vulnerability triggering. How object members of the original type confuse object members has a great impact on the availability and utilization stability of exp. In this case, the source defect type members (such as pointers) are arranged by obfuscation type members. Attackers can directly perform operations, and vice versa. This is the ideal situation that results in reliable execution of exp. Another common case is that the size of the original object member exceeds the size of the obfuscated object member, and their value is determined by the value of the out-of-bounds memory region. This situation is usually usable, although not stable, because it is difficult to ensure that the object heap is arranged as expected. Another possibility is that the object operation method is limited. For example, the original object member can only be set to some restricted values, and the obfuscated object is read-only, or only one specific method can be called. In this case, whether the bug can be exploited depends on the characteristics of the bug, which can also lead to information leakage bugs that need to be integrated with other bugs. This type of bug may also be able to be used stably, but the lucky bug is required, and it happens that a correct member has a correct value.
On the other hand, type obfuscation occurs at the end of the function call that causes type obfuscation. This is critical because it means that an object can be obfuscated and not modified in an unexpected way. Some types of obfuscation bugs cannot be used or cannot be used stably, because the method called after type obfuscation is a obfuscated object, and this value should be valid, this is not valid in later use. It is worth noting that in the above Code, the MovieClip object whose members are of Type obfuscation is set to 0*0 latitude. This avoids calls to the flter object from causing unstable exploitation, because the filter does not have to act on objects without pixel data.
Similarly, in this bug, the attacker does not access the object members and is not used by the software. This is another factor that affects the availability and Stability of obfuscation-type bugs. Sometimes, useless members (from the attacker's perspective) can cause a crash, if the attacker cannot correct its value. Similarly, setting MovieClip to 0*0 latitude avoids this problem, because Flash cannot use the filter method for images without pixels.
Finally, it is important to keep reference to the original object and the obfuscated object because it prevents garbage collection. When garbage collection acts on type obfuscation objects, it will often cause a crash. Garbage collection considers the object members as valid in a certain way. For example, if the pointer is invalid, it will cause a crash. attackers usually have difficulty correcting this situation, because garbage collection can occur at any time, any invalid form is also a problem. A completely reliable solution is to prevent garbage collection when the object is valid.
Conclusion
Many factors, including the layout of the original object, the members of the obfuscated object, the method in which the object is used, and the garbage collection that is vulnerable to the object, can affect the stability of Type Obfuscation Vulnerability exploitation. Cve-2015-3077 because of the combination of the above factors is a high quality bug, can be stably utilized. This code needs to trigger this bug 31 times: eight of them, the user sets and obtains the required object members, 19 to 23rd times for float conversion, this depends on the number of SNaN values. Although the number of times seems to be a little large, the utilization is stable, because each step is definite and does not rely on behaviors that may not necessarily happen.