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”);
                    }
                }
            }
        }
    };

Advertisements
This entry was posted in browser, Exploit, Exploit Kit, Flash, Flash Exploit Analysis and tagged , , , , , , , . Bookmark the permalink.

One Response to Extend Sulo to find the CVE of Flash exploits

  1. ericporkchop says:

    I’m having trouble with ba_data and ba_info. Would it be possible to see complete source?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s