Threat Research Blog

CVE-2015-0336 Nuclear EK

The Nuclear Exploit Kit (EK) has incorporated an exploit for the recently patched Adobe Flash CVE-2015-0336. Adobe patched the vulnerability on March 12, 2015 (, just seven days before Nuclear EK released a fully functioning exploit.

Update 03-20-2015: The Angler EK is now using the same exploit. Aside from string obfuscations, the CVE-2015-0336 exploit now delivered by Angler is identical to that delivered by Nuclear.

Technical Detail

The Flash file (SWF) contains 3 layers. The outer layer is an obfuscated packer whose sole purpose is to hide the exploit. SecureSWF was used to obfuscate at least one of the first two layers, possibly both. This fact is underscored if one renders the Flash file in more than 0x0 pixels:

Strings are obfuscated with extra numerals and punctuation. Upon deobfuscation, they are used to base64 decode, decrypt, and load Loader.loadBytes the second layer.

public static function retustr(i:int):String {

 if (i == 0x01) {

   // loadBytes

   return("4l..3.2.o..3.a..2d.3B4.y4..t.3..e3s4.".replace(/[0-9\.]/g, ""));


 if (i == 0x07) {

   // long string containing base64 encoded encrypted Flash file



The second layer is also similar to many previous exploits. First, the sample sprays the heap with Flash vector objects to cover around 0x1a1e3000 in memory. Every 0x80th vector is special, containing the ROP chain, a FileReference object, Shellcode, and a URL to

v = new Vector.<Object>(0x01D921);

local_1 = 0x00;

luck = 0x80

while (_local_1 < 0x01D921) {

   if ((_local_1 % luck)) {

      v[_local_1] = new Vector.<uint>(0x03FE);


   else {

      v[_local_1] = new Vector.<Object>


      v[_local_1][0x02] = 0x06336016;

      v[_local_1][0x03] = cool_ba; // ROP chain

      v[_local_1][0x04] = cool_fr; // FileReference object

      v[_local_1][0x05] = pwn_ba; // Shellcode

      v[_local_1][0x03F5] = v[_local_1];


Once the heap is prepared, it triggers CVE-2015-0336 to corrupt one of the Flash vectors at 0x1a1e2000. It then loops through the heapspray to find the corrupted vector:

while (_local_11 < 0x01D921) {

   if ((_local_11 % luck)) {

      if (v[_local_11].length != 0x1E) {

>         found = _local_11;






If found, it further corrupts the next “lucky” vector in memory to change its length to 0x7fffffff.

v[found][0x03FE] = 0x7FFFFFFF;

It uses the corrupted vector to find the base address of a library in memory (which bypasses ASLR), and populate the ROP chain dynamically (bypassing DEP). It overwrites the function table pointer of one of the FileReference objects to cause its subsequent use to call a pivot, and triggers its use:


The pivot transitions to ROP, which calls VirtualProtect to mark as RWX and transition to the shellcode.

The shellcode utilized in this sample was built with flexibility and reusability in mind. It is capable of downloading and executing multiple binaries, receiving its operating parameters from the loaded HTML via a FlashVars parameter.

var _local_2 = stage.loaderInfo.parameters["exec"];

payload = (payload + func_strdecode(_local_2.toString()));

The FlashVars parameter is expected to contain one or more sets of semicolon-delimited parameters that are XOR encrypted. Each set of parameters contains a numeric value specifying whether the binary is an executable or dll, a string that is used as a rolling XOR key to decrypt the downloaded binary, and the URL to download the binary from. Executables are executed as the user of the process in which the shellcode is executing. Dlls are simply loaded into the process. Downloaded binaries are written to disk in the directory returned by the GetTempPath API with a temporary file name.


Thank you to Henry Bernabe (FireEye) and Peleus Uhley (Adobe) for your assistance in this research.

Update 03-20-2015: And thank you Sai Vashisht (FireEye) for discovering this exploit in use by the Angler EK.