By analysing the samples, we see that the code evolves over the years, becoming increasingly well-structured and defensive. Based on the analysis of the code of each version, it would appear that compilation timestamps match code evolutions. From that, several dated versions have been determined. To date, three major versions are known.

This post will be divided in many parts. The first part talks about general Sakula capacities and the evolution of versions 1.x. The following parts will cover versions 2.x and 3.x.

For those who are not necessarily interested by the technical stuff, you can directly access the Yara3 rules which identify all known versions 1.x of Sakula here.

Type Version Seen Target's activity
RAT Sakula 1.0 21/11/2012 -
RAT Sakula 1.1 08/12/2012 Tubular Manufacturer *
RAT Sakula 1.2 26/12/2012 -
RAT Sakula 1.3 05/02/2013 Energy
RAT Sakula 1.4 02/04/2013 -


Versions 1.x represents the development of the malware core, rapidly changing with some bug corrections and added features.

Quick overview of the malware core capacities

Sakula is a custom Remote Administration Tool which evolves with each operation.

Each version seems to be crafted especially for a target.

Its name comes from a Windows Event "Sakula" created by some components in versions 2.x.


Installation

At its execution, Sakula moves itself to a new location specified in its configuration data. It sets up persistence mechanisms through the use of Windows Services or the registry.

In case the user has administrator rights, Sakula embbeds an exploit used to bypass the UAC. The bypass is used only on Windows 7 and Windows Server 2008 R2.

To check the user's rights, Sakula first call IsUserAnAdmin. This function is supposed to return True when the user has administrator rights. But Sakula continue its exploit process only if it return False. It can be explained by the fact that isUserAnAdmin is not supported anymore since Windows XP, and that it returns systematically False on superior versions. The goal of this call is in reality to check the OS version.

Then, if IsUserAnAdmin returns False and if the OS is Windows 7 or Windows Server 2008 R2, it compares the SID of the user and the SID of the local Administrator group (S-1-5-32-544). If they match, the exploit is executed.

The exploit seems to be highly inspired from a Proof Of Concept written by Leo Davidson. The code is publicly available since 2009 on his website, as well as how it works in details (documentation).

This exploit is used by each version of Sakula from v1.1 to v3.2.


Communication

To communicate, Sakula uses several kinds of requests, depending on its version.

  • a GET request used to ask for a command from the C&C
  • a GET request used to acknowledge the received command
  • a POST request used to send back a command output

Here are some examples of each kind of request:

  • POST /newimage.asp?imageid=vybircnaqnf0123456789&type=0&resid=102256671
  • GET (1) /photo/vybircnaqnf0123456789.jpg?resid=102257796
  • GET (2) /viewphoto.asp?resid=102258125&photoid=vybircnaqnf0123456789

The resid argument is filled with the value of GetTickCount and is used by the C&C as a command sequence number. The type argument is the associated command ID. The string vybircnaqnf0123456789 is a unique ID of the infected computer. It is derivated from the machine name and its serial number. The machine name is hashed using a custom algorithm.

def hash_machine_name(machine_name, serial_number):
    return "".join([chr((ord(c) % 0x1A) + 0x61) for c in machine_name]) + str(serial_number)

Taking the machine name JOHNDOE-PC and the serial number 123456789, the unique ID should be wbuaqbrtcp123456789.

The hash can be reversed by brute forcing some characters. Here is an example of the script used for:

EDIT: I changed the code. The old one was too 'quick and dirty'. Here is a new one. (Thanks JMP)

lut = [0] * 0x100
rev_lut = [0] * 0x100

for i in range(0x100):
    lut[i] = 0x61 + (i % 0x1A)
    if ord('A') <= i <= ord('Z'):
        rev_lut[lut[i]] = i

def decode(hashed_machine_name):
    machine = hashed_machine_name.rstrip('0123456789')
    serial = hashed_machine_name[len(machine):]
    return (''.join([chr(rev_lut[ord(c)]) for c in machine]), serial)

Data sent to the C&C through POST requests are encapsulated using this specific structure:

struct PostData
{
    DWORD   command_id;                 //  0x00
    DWORD   data_length;                //  0x04
    CHAR    hash_machine_name[16];      //  0x08
    CHAR    data[...];                  //  0x18
}

At beginning, the malware starts by doing a ping-like HTTP request to its C&C, first to check if it is up or down, and then to give some basic information about the infected system.

The ping request is sent using the POST request with 0 as command ID. Sent data uses this structure:

struct PingData
{
    DWORD   custom_os_version;          //  0x00
    DWORD   delay_commands;             //  0x04
    DWORD   system_32_or_64;            //  0x08
    DWORD   year;                       //  0x0C
    DWORD   month;                      //  0x10
    DWORD   day;                        //  0x14
    DWORD   hour;                       //  0x18
    DWORD   minutes;                    //  0x1C
    DWORD   seconds;                    //  0x20
    DWORD   user_right                  //  0x24
    CHAR    user_name[100];             //  0x88
    CHAR    campaign_id[12];            //  0x94
}

The custom_os_version is a value depending on the system and generated from a custom algorithm.

Version OS
1 Windows 2000
2 Windows XP
3 Windows Server 2003
4 Windows Vista
5 Windows 7
6 Windows Server 2008
7 Windows Server 2008 R2
8 Windows 8
9 Windows Server 2012

If the C&C can be reached, the malware asks for command using the first GET request. If a command is sent back by the C&C, it uses the following structure:

struct CommandData
{
    DWORD   command_id;                 //  0x00
    CHAR    parameters;                 //  0x04
}


Commands

Sakula can handle up to 8 different commands depending on its version.

command_id = 1

Execute a command on cmd.exe. The command looks like cmd.exe /c <arg> where <arg> is an argument given in parameters. The command output is sent back to the C&C through the POST request.

command_id = 2

Drop a file somewhere in the file system (may be splitted in several requests). Command data uses this structure:

struct Command2ReceivedData
{
    DWORD   command_id;                 //  0x00
    BYTE    unknown;                    //  0x04
    DWORD   action                      //  0x06
    DWORD   unknown                     //  0x0A
    DWORD   file_data_offset;           //  0x0E
    DWORD   file_data_length;           //  0x12
    CHAR    file_path[260];             //  0x16
    CHAR    file_data[...];             //  0x11A
}

By default, the command writes file_data to the file file_path at the offset determined by file_data_offset. If the field filepath is not specified, Sakula generates a path using the %TEMP% directory and a random name matching [a-z]{6}.exe.

A mini-protocol has been implemented to allow the C&C to send files in several parts:

  • the C&C sends the first block of data with the field action equal to 1. Sakula opens the file, writes data in it and keep the file handle somewhere in a global variable.
  • the C&C sends x-2 blocks of data (action different from 1 or 3). Sakula writes them in the file.
  • the C&C sends the last block of data with the field action equal to 3. Sakula writes data, close the file and then executes it.

This feature has been mainly designed for stealthiness when uploading big files.

command_id = 3

This command is used to send a specific file to the C&C. The name of the file to send is given in parameters. The file can be sent in multiple parts using the POST request. Each request can send a maximum of 100Kb (0x19000 bytes).

The following structure is the format of the field data in the structure PostData:

struct Command3SentData
{
    BYTE    unknown;                    //  0x00   value = 0x02
    DWORD   file_size;                  //  0x04
    DWORD   file_offset;                //  0x08
    DWORD   data_length;                //  0x0C
    DWORD   file_path;                  //  0x10
    CHAR    data[...];                  //  0x114
}

In addition to the information about the transfer status, it sends a log message like %d_of_%d_for_%s_on_%s. An example of formatted log messages could be:

  • 0_of_2_for_C:\myfile.txt_on_JOHNDOE-PC
  • 1_of_2_for_C:\myfile.txt_on_JOHNDOE-PC
  • 2_of_2_for_C:\myfile.txt_on_JOHNDOE-PC

These log messages are not transfered into data of the POST request, but in the URI instead of the hashed computer name. Taking the previous example, the POST URI should look like (POST) /newimage.asp?imageid=0_of_2_for_C:\myfile.txt_on_JOHNDOE-PC&type=3&resid=102256671

command_id = 4

Execute a command given in parameters using WinExec.

command_id = 5

Update the waiting time between each request for orders to the C&C. The new value is given in parameters. This default value is defined by the configuration, but is often around 30 seconds. The new value minus 0x3E9 cannot exceed 0x5265816 milliseconds, which represents 24 hours maximum between each request.

command_id = 6

Uninstall itself by removing its autorun key and removing its file using ShellExecute on cmd.exe with ping 127.0.0.1 & del /q \"<file_path>\" as parameter.

The command ping 127.0.0.1 seems to be useless, but is in fact similar to a Sleep for 4 seconds.

command_id = 7

Sends the ID and the process filename to the C&C through the POST request with Self Process Id:<process_id>\n<process_filename> as data.

command_id = 8

Interact through a reverse shell. The command in parameters is given to the shell process and the output is sent back to the C&C using the POST request. When this command is used for the first time, the process is created using cmd.exe and the log message Create Child Cmd.exe Process Succeed!\nChild ProcessId is <process_id>\n is written before output data.


Encryption

Sakula uses the same 1-byte xor-based encryption algorithm in all of its version. It is used to encrypt/decrypt communications (POST request data), configurations and embedded exploits.

It looks like:

    def decrypt_xor(data, key):
        out = ""
        for c in data:
            if c != '\x00' and ord(c) != key:
                c = chr(ord(c) ^ key)
            out += c
        return out


Configuration

Sakula contains some kinds of information which need to be configurable. Configuration data is encrypted. Its structure changes for each version.

Some versions have the ability to load an external configuration located in a file rss.tmp in the same directory as the malware and encrypted as the internal configuration.

Configurations can contain following information:

  • C&C domain or IP address
  • Folder, files or arguments used in URI
  • The name of the service
  • The description of the service
  • The path and filename of the malware copy
  • The name of the autorun key
  • A campaign id (assumption)

The campaign ID described in the configuration is the one sent in the ping request (structure PingData). We cannot be sure that this field is used as a campaign ID, but some information supports it. First the fact that this information is configurable and present in the ping request. It allows the C&C to be able to link an infected machine with a specific target. Secondly, some values match names of potential targeted companies.


Version 1.0 to 1.4

The first versions mark the beginnings and the first tests of the attackers, with a simple code, efficient and rapidly changing from one version to another. This code is the central core of the malware on which protections are added in the following versions.


v1.0 (21/11/2012)

At this step, Sakula was just born but already implements commands 0 to 5. Its configuration is encrypted with the key 0x88 and communications with the key 0x59.

A PDB path E:\编程\C++程序\打头\打头\Release\打头.pdb is present in the sample.

At its start, Sakula does some action to set up persistence:

  • It copies itself to %WINDIR%\system32\Sweep.exe (if 32 bits)
  • It copies itself to %WINDIR%\SysWOW64\Sweep.exe (if 64 bits)
  • It creates a service pointing to its copy
    • Option SERVICE_AUTO_START (the service starts automatically during system startup)
    • Its name is Office Auto Update
    • Its description is Microsoft Office Auto Update
  • It starts the service using the command net start "Office Auto Update"
  • It removes itself using the command ping 127.0.0.1 & del /q "<malware_path>"

The configuration structure is:

struct ConfigV1_0
{
    CHAR    cc_domain[100];             //  update.microsoft.co.kr
    CHAR    uri_get1_folder[100];       //  /photo/
    CHAR    uri_get3_file[100];         //  check.asp
    CHAR    uri_get2_file[100];         //  /viewphoto.asp
    CHAR    uri_get3_arg[100];          //  imageid
    CHAR    copy_name[100];             //  Sweep.exe
    CHAR    service_name[100];          //  Office Auto Update
    CHAR    service_description[100];   //  Microsoft Office Auto Update.
    DWORD   waiting_time;               //  0x956A0 (612000 milliseconds, ~ 10 minutes)
}

On this version, the command n°5 (which updates the waiting time between requests for orders) allows a maximum waiting time of 0x83D216 ms (+0x3E9), which is approximatively 2 hours and 23 minutes. The machine name is in plain text in URI (without serial number).

The User Agent used is iexplorer.

An example of used URIs:

  • POST /check.asp?imageid=[MACHINE_NAME]&type=[CMD_ID]
  • GET (1) /photo/[MACHINE_NAME].jpg
  • GET (2) /viewphoto.asp?photoid=[MACHINE_NAME]


v1.1 (08/12/2012)

If the user has admin rights, Sakula sets up its persistence through the use of services, like the v1.0.

Instead, if the user is not administrator, it copies itself to %ALLUSERSPROFILE%\MicroMediaCCP\MicroPlayerUpdate.exe, sets up an autorun key in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ with the name CCPUpdate pointing to its copy and executes an embedded exploit to elevate its privileges (only on Windows 7 and Windows Server 2008 R2).

Sakula embeds 2 encrypted exploits in its resources, one for 32 and one for 64 bits. The type is DAT and their names are 0x65 and 0x66. Data are in plaintext. It writes them to %TEMP%\ [a-z]{8}.dat and then executes it by running the command cmd.exe /c rundll32 "<exploit_path>" followed by PlayWin32 on 32bits and Play64x on 64bits. The file name of the exploit is generated randomly using GetTickCount. Then Sakula exits, and the exploit restarts it.

The command n°6 has been added to this version, which is the command used to uninstall itself. The service is uninstalled using the command cmd.exe /c sc delete "<service_name>". The C&C has to send the special magic 0x378 in parameters for the order to be effective.

The machine is now identified with the hashed machine name (but still without the serial number).

It uses the same configuration structure than the v1.0, but filled differently.

struct ConfigV1_1
{
    CHAR    cc_domain[100];             //  web.vipreclod.com
    CHAR    uri_get1_folder[100];       //  /photo/
    CHAR    uri_get3_file[100];         //  newimage.asp
    CHAR    uri_get2_file[100];         //  /viewphoto.asp
    CHAR    uri_get3_arg[100];          //  imageid
    CHAR    copy_name[100];             //  AppleService.exe
    CHAR    service_name[100];          //  AppleService
    CHAR    service_description[100];   //  Apple Application Service.
    DWORD   waiting_time[100];          //  0x7530 (30000 milliseconds)
}

An example of used URIs:

  • POST /newimage.asp?imageid=[HASH_MACHINE_NAME]&type=[CMD_ID]
  • GET (1) /photo/[HASH_MACHINE_NAME].jpg
  • GET (2) /viewphoto.asp?photoid=[HASH_MACHINE_NAME]


v1.2 (26/12/2012)

Unlike previous versions, Sakula doesn't start as a service anymore. This feature has been removed.

There are some minor updates:

  • Configuration is encrypted with the key 0x56
  • The path of the malware copy and the autorun key are not hardcoded anymore, but specified in configuration
  • Embedded exploits are encrypted with the key 0x24
  • The PDB path has been removed
  • The unique machine identifier is the hash of the machine name concatenated to its serial number in plaintext

About the autorun key, it is created in the hive HKEY_LOCAL_MACHINE when the user has admin rights and in HKEY_CURRENT_USER otherwise.

Some changes have been made about the configuration system. Sakula is now able to load an external file containing configuration data. This file is named .\rss.tmp and is encrypted like previous configuration data. Internal or external configuration data can contain up to 5 different C&C configurations. It means that if a C&C configuration doesn't work (e.q. the C&C doesn't respond), Sakula uses the next C&C configurations.

A command sequence number has been added to each URIs with a value initiated with GetTickCount.

The configuration structure is:

struct ConfigV1_2
{
    CHAR    cc_domain;                  //  citrix.vipreclod.com
                                        //  184.22.175.13
    CHAR    uri_get1_folder[50];        //  /photo/
    CHAR    uri_get3_file[50];          //  newimage.asp
    CHAR    uri_get2_file[50];          //  /viewphoto.asp
    CHAR    uri_get3_arg[50];           //  imageid
    CHAR    copy_file_name[50];         //  MediaCenter.exe
    CHAR    autorun_key[50];            //  MicroMedia
    CHAR    copy_file_path[50];         //  %Temp%\MicroMedia
    CHAR    campaign_id[12];            //  [REMOVED]
    DWORD   waiting_time;               //  0x7530 (30000 milliseconds)
}

The command n°7 has been added to this version, it is used to send the ID and the malware process filename.

The command n°6 (uninstall itself) has been slightly modified. Instead of deleting the service, it deletes the autorun key CCPUpdate in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ .

We can note that this command is buggy. Indeed, the malware can create the autorun key in the hive KEY_CURRENT_USER or HKEY_LOCAL_MACHINE, and its value is given in configuration and is not necessarily CCPUpdate.

An example of used URIs:

  • POST /newimage.asp?imageid=[HASH_MACHINE_NAME]&type=[CMD_ID]&resid=[CMD_SEQ_NUM]
  • GET (1) /photo/[HASH_MACHINE_NAME].jpg?resid=[CMD_SEQ_NUM]
  • GET (2) /viewphoto.asp?resid=[CMD_SEQ_NUM]&photoid=[HASH_MACHINE_NAME]


v1.3 (05/02/2013)

The number of embedded configurations has been up to 6. The bug in the command n°6 has been partially corrected, it now deletes the value of autorun key given in config, but still only in the hive KEY_CURRENT_USER. The command n°7 has been added (possibility to interact throught a reverse shell).

It uses the same configuration format than the v1.2, but filled differently.

struct ConfigV1_3
{
    CHAR    cc_domain[50];              //  www.polarroute.com
                                        //  www.northpoleroute.com
    CHAR    uri_get1_folder[50];        //  /photo/
    CHAR    uri_get3_file[50];          //  newimage.asp
    CHAR    uri_get2_file[50];          //  /viewphoto.asp
    CHAR    uri_get3_arg[50];           //  imageid
    CHAR    copy_file_name[50];         //  MediaCenter.exe
    CHAR    autorun_key[50];            //  MicroMedia
    CHAR    copy_file_path[50];         //  %Temp%\MicroMedia
    CHAR    campaign_id[12];            //  [REMOVED]
    DWORD   waiting_time;               //  0x7530 (30000 milliseconds)
}

An example of used URIs:

  • POST /newimage.asp?imageid=[HASH_MACHINE_NAME]&type=[CMD_ID]&resid=[CMD_SEQ_NUM]
  • GET (1) /photo/[HASH_MACHINE_NAME].jpg?resid=[CMD_SEQ_NUM]
  • GET (2) /viewphoto.asp?resid=[CMD_SEQ_NUM]&photoid=[HASH_MACHINE_NAME]


v1.4 (02/04/2013)

The command n°5 (update the waiting time) has been slightly modified. The maximum waiting time is now equals to 0x5265816 (+0x3E9) milliseconds, (e.q. 24 hours).

Some functions have been refactored.

It uses the same configuration format than the v1.2 and v1.3 but filled differently.

struct ConfigV1_4
{
    CHAR    cc_domain[50];             //  www.savmpet.com
    CHAR    uri_get1_folder[50];       //  /photo/
    CHAR    uri_get3_file[50];         //  newimage.asp
    CHAR    uri_get2_file[50];         //  /viewphoto.asp
    CHAR    uri_get3_arg[50];          //  imageid
    CHAR    copy_file_name[50];        //  AdobeUpdate.exe
    CHAR    autorun_key[50];           //  AdobeUpdate
    CHAR    copy_file_path[50];        //  %Temp%\MicroMedia
    CHAR    campaign_id[12];           //  [REMOVED]
    DWORD   waiting_time;              //  0x7530 (30000 milliseconds)
}

An example of used URIs:

  • POST /newimage.asp?imageid=[HASH_MACHINE_NAME]&type=[CMD_ID]&resid=[CMD_SEQ_NUM]
  • GET (1) /photo/[HASH_MACHINE_NAME].jpg?resid=[CMD_SEQ_NUM]
  • GET (2) /viewphoto.asp?resid=[CMD_SEQ_NUM]&photoid=[HASH_MACHINE_NAME]

Evolutions

Versions 1.x was focused on the development of malware capacities. Around ten others superior versions has been found, updated to bypass detection mechanisms.

They will be the topic of my next post blog, so stay tuned !