2013-11-04
Abstract
Neurevt is a relatively new HTTP bot that already has a lot of functionalities along with an extendable and flexible infrastructure. Zhongchun Huo takes a detailed look at its infrastructure, communication protocol and encryption scheme.
Copyright © 2013 Virus Bulletin
Neurevt (also known as Beta Bot) is an HTTP bot [1] which entered the underground market around March 2013 and which is priced relatively cheaply [2]. Though still in its testing phase, the bot already has a lot of functionalities along with an extendable and flexible infrastructure. Upon installation, the bot injects itself into almost all user processes to take over the whole system. Moreover, it utilizes a mechanism that makes use of Windows messages and the registry to coordinate those injected codes. The bot communicates with its C&C server through HTTP requests. Different parts of the communication data are encrypted (mostly with RC4) separately. In this article, we will take a detailed look at this bot’s infrastructure, communication protocol and encryption schemes. (This analysis is based on samples that were collected from March to June 2013.)
Just like most malware, the installation of Neurevt starts with it copying itself to a system folder. The folder is selected according to the machine’s characteristics such as the version of Windows, the service pack installed, and whether the OS is 64-bit.
For example, on an x86 machine running Windows XP SP2, the chosen folder is %PROGRAM FILES%\COMMON FILES. The installer creates a sub-folder named ‘winlogon.{2227A280-3AEA-1069-A2DE-08002B30309D}’.
The first part of the folder name, ‘winlogon’, is obtained from the configuration of the bot, and the second part is a special GUID which makes the folder link to the ‘Printers and Faxes’ folder in Windows Explorer. This folder will act as the launching point each time the malware restarts. The installer then launches the new file and exits.
The newly launched copy creates a process of a system application, which also varies under different circumstances, and starts to inject. The injected data is within a continuous block of memory and has the following data layout:
A copy of the whole PE image of the malware process.
A block of data which contains states used by the code of the PE image in part 1.
A buffer that contains the file data of the malware.
Encrypted configuration data.
Since this is the first time the malware has injected something into another process, the injected content runs as an independent application. I refer to it as the ‘primary instance’ to distinguish it from other instances that are injected into other processes.
The primary instance searches for all running processes, and injects into those that fulfil the following conditions:
The creator of the process is not services.exe.
The user that creates the process is not NT AUTHORITY\SYSTEM.
It is not one of the following system processes:
csrss.exe
smss.exe
lsass.exe
services.exe
spoolsv.exe
winlogon.exe
It is not created by the current process.
This time, the injected data has the same layout as the primary instance. The only difference is in the ‘local cfg’ part – in which some data fields are modified to act differently since some components should be loaded in the primary instance only.
After injection, there should be one instance of the malware in every running user process. I refer to these as ‘assistant instances’. There is code in every assistant instance that monitors the status of the primary instance. Once, for whatever reason, the primary instance exits, the assistant instance will attempt to restart the malware from its launch point.
After the malware has finished its deployment, the primary instance will start to communicate with the C&C server. The whole process looks like a traditional virus infecting a file system, but instead of infecting files, the malware infects running processes.
The malware performs a system scan to gather information about the system and installed software. The gathered information will be stored in four separate flags, each of which is a single DWORD-length bit-flag.
The first flag contains information about the Windows version, installed service pack, and whether the OS is 32- or 64-bit.
The second flag contains information about the following software or vendors: .Net Framework, Java, Steam, SysInternals tools, mIRC, Hex-Rays, Immunity Inc., CodeBlocks, 7-Zip, PrestoSoft, Nmap, Perl, Visual Studio and Wireshark. It also contains information that indicates if the system:
Has battery
Has RDP records
Has UAC enabled.
The third flag contains information about the following software or vendors: Steam, EA Origin, RuneScape, Minecraft, Blizzard, League of Legends, VMware, Skype and Visual Studio.
The fourth flag contains information about installed AV software: Symantec, AVP, AVG, Avira, ESET, McAfee, Trend Micro, Avast, Microsoft Security Client, Bitdefender, BullGuard, Rising, Arcabit, Webroot, Emsisoft, F-Secure, Panda, PC Tools Internet Security and G Data AntiVirus.
The malware creates threads to perform different kinds of jobs, such as communicating with the C&C server, checking data consistency, managing messages passing among threads (components), or monitoring and infecting USB drives. These threads are like software components. In order to load the threads properly, the malware defines a function to create them. The following is the function’s definition:
HANDLE NewComponentThread( LPVOID ThreadProc, LPVOID InputBuf, int SizeOfInputBuf, int idx, int reserved, int *pThreadId, int flag );
ThreadProc is the routine that performs a particular job, while idx is the index assigned to it by the malware. 0-0x1E and 0x21 are idx values given to those unique routines for which there should be only one running thread. 0x1F and 0x20 are for multiple instance routines. If the NewComponentThread is called with idx set to these two values, the function will assign a new idx to ThreadProc, which is the first available (unassigned) number from 0x22.
The malware maintains a list to keep track of all the threads that are created by the function. Each ThreadProc takes an entry pointed to by its idx. The entry of the list has the following structure:
typedef struct THREAD_LIST_ENTRY { WORD Size; WORD Index; DWORD reserved; DWORD CreateFlag; DWORD SizeOfInputBuffer; DWORD InputBuffer; CHAR EventName1[96]; HANDLE Event1; HANDLE Event2; HANDLE ThreadHandle; DWORD ThreadId; DWORD StartAddress; DWORD StubAddress; DWORD CurrentId; };
Before actually starting the new ‘component thread’, NewComponentThread adds a short code stub and a wrapper function to ThreadProc.
The code stub will be written into ntdll’s image, at a random location within the MZ header. This random location will serve as the StartAddress when NewComponentThread calls CreateThread. So, the start address of the ‘component thread’ is within the memory range of ntdll’s image. This feature is used when the malware passes messages among its threads.
The wrapper function attempts to hide the thread from debuggers and updates the thread list before and after it calls ThreadProc. It also sets a specific TLS slot with a specific value, 1234. Since the malware’s code is always running in an injected process, the API hook will be applied to monitor and manipulate the behaviour of the host process. The specific TLS slot value is used to identify the malware’s own threads for which the API hook should not be applied.
The malware applies the Ring 3 hook in two ways.
First, the malware adds a pre-operation filter for each of the following Zw* APIs:
ZwCreateFile
ZwOpenFile
ZwDeleteFile
ZwSetInformationFile
ZwQueryDirectoryFile
ZwCreateKey
ZwOpenKey
ZwSetValueKey
ZwOpenProcess
ZwTerminateProcess
ZwCreateThread
ZwCreateThreadEx
ZwResumeThread
ZwSuspendThread
ZwSetContextThread
ZwOpenThread
ZwUnmapViewOfSection
ZwDeviceIoControlFile
ZwQueueApcThread
The filter first checks the specified TLS slot. If its value is 1234, this means that the calling thread belongs to the malware. The filter will do nothing and let the thread call the real API. If the TLS slot is not 1234, the filter examines the object (process, thread, file, registry) on which operation will be performed, if the object belongs to the malware, then the filter will return an error status to the calling thread.
The second way it applies the Ring 3 hook is by applying an inline hook on the following two groups of APIs:
getaddrinfo, GetAddrInfoW, DnsQuery_W
HttpSendRequestW, PR_Write
The malware hooks APIs in group 1 to block unwanted hosts. The host list is received from the bot server. Most of the unwanted hosts are the web servers of anti-virus software vendors.
The malware hooks the APIs in group 2 only if the injected process is one of the following browser processes:
firefox.exe
iexplore.exe
chrome.
The malware receives a list of URLs to be monitored from the bot server. If the browser sends requests to these URLs, the malware will capture the request data and send it back to the bot server.
There are multiple instances of the malware running in the system. To coordinate these instances, the malware creates a thread in each as a handler of application-defined messages. Most of the messages are sent from the primary instance after it has received something from the bot server to notify other instances to update their local data, such as the blocked host list and the monitored URL list mentioned earlier.
If a malware’s thread is about to send a message, it enumerates all the running threads in the system, searching for those that have a start address within ntdll’s image. As described in the ‘Component thread’ section, all the threads created by NewComponentThread fulfil this condition. The sending thread will call PostThreadMessage to send the message to them. Among these threads, only the message handlers (in all the malware instances) have a message queue (by calling IsGuiThread) for messages that are not associated with any window (a feature of messages sent by PostThreadMessage). So the handlers will retrieve the message and response accordingly.
Before a message is sent out, the sending thread will add a pre-defined modifier to the message identifier. This modifier is calculated based on the signature string in the bot configuration and the computer name. Its value is within the range from 0 to 31. The wParam and lParam are also modified since they often carry values with specific meanings like process id or thread id.
In the handler thread, these values will be restored by a reverse calculation before the message is handled. This means that, even if the messages passing among the malware’s threads are being monitored, it’s hard to understand their meanings.
The messages supported by the handler thread are listed in Table 1:
uMsg | Operation |
---|---|
0xECD | Update the process ID of the primary instance. |
0xECB | Update the thread ID of the thread that sends the captured HTTP request back to the bot server, also update the handle of the window created by the thread. The captured data will be sent to the window by the WM_COPYDATA message. |
0xEC9 | Perform operation (search or insert) on a pid list and a tid list. These two lists are used by hooked Zw* functions. The pids and tids in these two lists belong to the malware and will be protected. |
0xEED | Store the Control flag value in the last received bot response. |
0xEE9 | Update the local blocked host list. |
0xEE7 | Update the local monitored URL list. |
0xEC7 | Kill all of the malware’s threads in an inject instance. |
Table 1. Messages supported by the handler thread.
The malware stores shared data for all instances in registry values. The values will be created under the key HKCU\Software\CLSID\{random guid}\{hash of configuration signature string}. The following are important values used by the malware:
CS1\S02: Encrypted data that stores the last received blocked host list.
CS1\S03: Encrypted data that stores the last received monitored URL list.
CS1\S01: The last received configuration.
CG1\CF01: Value of the DWORD at offset 0x20 in the last received response.
CG1\CF02:Value of the DWORD at offset 0x24 in the last received response.
CG1\CF03: Value of the DWORD at offset 0x28 in the last received response.
CG1\BIS: Flag that indicates that the process is running on a removable disk.
CG1\BID: The first launch time.
CG1\HAL: DWORD, set to 0xEE05 after it has been installed successfully.
CG1\LCT: Time of last received bot response.
Neurevt communicates with its C&C server through HTTP. Both the request and response are encrypted with the RC4 algorithm. The communication starts as the malware on an infected machine sends its first request to the bot server. Then the communication goes on in a ‘Q & A’ manner.
Neurevt has a built-in configuration. The configuration data and the decryption code are both encrypted with RC4. The malware allocates a block of memory on the heap, and decrypts and copies the decryption code into the memory. Then it creates a thread to decrypt the configuration.
The argument passed to the thread is a pointer to the following structure:
typedef struct DECRYPT_CONFIG_STRUCT { DWORD cbSize; DWORD lpConfigData; DWORD lpKey; DWORD lpGlobalData; DWORD lpKeyForReEncrypt; DWORD ImageBase; DWORD DecryptFunc; DWORD SearchFunc; // To search the PE image for config’s hash DWORD AllocMemory; DWORD ReleaseMemory; DWORD HashString; // To calculate the config’s hash DWORD GetCriticalSection; DWORD Win32APIList; };
The fields between DecryptFunc and GetCriticalSection are functions that are used by the decryption function. First, the decryption thread will perform an integrity check by calculating the hash value of the key sequence and comparing it with a value that is embedded in the PE image. If the hash values are consistent, the thread allocates a block of memory and decrypts the configuration data into the memory.
lpKeyForReEncrypt is a pointer to a four-byte key sequence for re-encryption. It is generated randomly after the configuration data has been decrypted. The re-encryption also uses the RC4 algorithm. Its purpose is to protect the configuration data from being discovered in any memory dump. The re-encryption is done by the main thread after the decryption thread exits. Before the decryption thread exits, it stores the memory pointer and the re-encryption key in the memory block that is given by lpGlobalData.
Any access to the configuration data afterwards is carried out using the following steps:
Allocate a block of memory and copy the re encrypted data into it.
Decrypt and get the desired data.
Re-encrypt the data.
Release the memory.
Steps 2 and 3 are completed in one function. So, the configuration data remains plain text in the memory for only a short period of time.
The configuration data has a 718-byte header, which contains the fields shown in Table 2.
Offset | Type | Meaning |
---|---|---|
0x0 | WORD | Size of the configuration data, 0x2ace |
0x2 | DWORD | Magic number |
0x6 | BYTE[32] | Signature string, used for creating names, such as event, registry |
0x44 | CHAR[104] | Backup URL |
0x14e | WCHAR[128] | Name of the startup registry |
0x24e | WCHAR[128] | Name of the folder where the installer self-copies itself |
Table 2. Fields contained in the 718-byte header.
There is an array at 0x2ce of the configuration data. Each entry stores information about a C&C server. Normally there will be more than three entries in one configuration. The size of the entry is 0x280 bytes. The crucial fields are shown in Table 3.
Offset | Type | Meaning |
---|---|---|
0x0 | WORD | Entry size, 0x280 |
0x0e | DWORD | Hash of the domain name |
0x12 | WORD | Number of attempts |
0x14 | WORD | Server port |
0x16 | DWORD | (Length of domain name) xor (0xa5f0 + (index in the array)) |
0x1E | WORD | Option |
0x66 | CHAR[256] | Host |
0x166 | CHAR[256] | Path |
0x26e | BYTE[8] | RC4 key 1, for request and response header |
0x277 | BYTE[8] | RC4 key 2, for response body |
Table 3. The crucial fields.
Each request contains a 128-byte data block, which contains the fields shown in Table 4.
Offset | Type | Meaning |
---|---|---|
0x00 | BYTE[8] | Not used |
0x08 | WORD | Size of the block |
0x0A | WORD | Magic, 0xC1E5 |
0x0c | DWORD | Software flag 1 |
0x10 | DWORD | Count of CS fields in the request |
0x14 | DWORD | Unknown |
0x18 | DWORD | DWORD read from configuration, at offset 0x30 |
0x20 | DWORD | Software flag 2 |
0x24 | DWORD | Seconds since 1970 |
0x28 | DWORD | Tick count |
0x2c | DWORD | TimeZone.Bias |
0x30 | DWORD | Locale info |
0x34 | WORD | Data of registry value BK32 |
0x36 | WORD | Reserved |
0x38 | BYTE[16] | Guid |
0x48 | DWORD | Data of registry value CF01 |
0x4c | DWORD | Data of registry value CF02 |
0x50 | DWORD | Data of registry value CF03 |
0x54 | DWORD | Reserved |
0x58 | DWORD | Security software flag |
0x5c | DWORD | Software flag 3 |
0x60 | DWORD | Unknown |
0x64 | BYTE[28] | Reserved |
Table 4. Fields contained in the 128-byte data block.
The malware encrypts the data block with RC4. It uses a 12-byte key sequence, which is a combination of two parts. The first is an eight-byte sequence obtained from the chosen entry of the configuration data, at offset 0x26e. The second part is a random sequence obtained by calling CryptGenRandom. The length of the sequence is within a range from eight to 27 bytes. This part of the key and encrypted data block will be inserted into the query string.
If it is the first request sent to the bot servers, it will also contain the following three CS fields:
A full file path of installed malware
The username in NameSamCompatible format
The name of the default web browser.
These strings are encrypted separately with a loop-XOR algorithm and concatenated into a URL query string.
The bot response contains a 0x5c-byte header and a body which consists of at most eight streams (only four streams are actually used). Both the header and the body are encrypted with RC4 and they will be decrypted separately. The first four bytes in the header will be appended to the eight-byte sequence in the configuration to form a 12-byte key sequence for decrypting the header. The following four bytes in the header and another eight-byte sequence in the configuration are concatenated to form another 12-byte key sequence for the response body.
The response header has the structure shown in Table 5.
Offset | Type | Meaning |
---|---|---|
0x00 | BYTE[4] | Key1 |
0x04 | BYTE[4] | Key2 |
0x08 | DWORD | Size of the header, 0x5C |
0x0C | DWORD | Response status. 0 if error occurs |
0x10 | DWORD | Controls the sleeping time during the communication |
0x14 | BYTE[8] | Reserved |
0x1C | DWORD | Control flag |
0x20 | DWORD | To be saved into registry value CG1\CF01 |
0x24 | DWORD | To be saved into registry value CG1\CF02 |
0x28 | DWORD | To be saved into registry value CG1\CF03 |
0x2C | BYTE[16] | Reserved |
0x3C | DWORD[8] | Length array |
Table 5. Structure of response header.
The Control flag in the response header is a bit-flag which triggers different behaviours of the malware, such as invoking routines that disable security software or fake pop up warning windows to trick the user into giving permission for the malware to bypass the UAC.
Fields from 0x20 to 0x28 are three DWORDs which will be stored in the registry values. They will be copied to the bot server in the next request sent by the malware.
The length array at 0x3C has eight entries – each denotes the length of the corresponding stream in the response body.
According to the response header format, there should be eight streams in the body, but the malware only has handling routines for the first four streams.
The first stream contains bot commands sent back by the bot server. The first word of the stream is the count of the commands in the stream. Each command is stored as a null terminated string preceded by a data block. The size of the data block is 22 bytes. It is not used in any of the malware’s code.
The malware identifies the command keyword by hash value. It has a list of handling routines. Each entry in the list has the following structure:
typedef BotCmdEntry { DWORD dwHash; PVOID lpfnCmdProc; DWORD KeywordLength; }
The second stream contains a list of hosts that will be blocked. The list is used in the following hooked functions:
DnsQuery_W
GetAddrInfoW
getaddrinfo
The third stream contains a list of URLs for which the malware will monitor the HTTP requests sent. The list will be used in the following hooked APIs:
HttpSendRequestW
PR_Write
The fourth stream in the response body contains data which could be used to generate a new configuration. The stream is in a format similar to that of an INI file. The malware compiles the stream into a binary data block which is organized in a structure described in the configuration section.
The malware uses Skype to spread any text material received from the bot server. There are two bot commands that will invoke the spreading job.
The bot server sends the command along with a URL parameter pointing to a text file. Each line of the text file contains a locale-message pair which is delimited by a semicolon:
{locale name};{spam content}
The malware chooses one line according to the locale of the system’s default language and sends the message in the line to all the Skype contacts except ‘echo123’, which is the name of Skype’s echo service.
To send the message, the malware creates a new process of itself with the command line parameters set to ‘/ssp {URL sent by bot server}’. The new process sets up a communication between itself and the Skype client with the Skype API. Then it starts to send Skype commands.
The first command sent is ‘SEARCH FRIENDS’, which retrieves all the contacts of the logged-in user. For each contact, a ‘MESSAGE’ command will be sent to the Skype client to generate an IM message to send the chosen spam content.
On a UAC-enabled system, if the malware needs to elevate its privilege, it doesn’t play some trick to avoid prompting the user or disable the UAC once and for all. Instead, it will directly ‘ask’ the user for approval.
The malware creates a process of ‘Cmd.exe’ and puts the malware’s file path in the command line argument. When the prompt window pops up, it shows that ‘Cmd.exe’, which is a Windows application, is asking for privilege elevation. Careless users tend to approve the request. Then a new process of the malware will be created by ‘Cmd.exe’ and it will inherit the system privileges of ‘Cmd.exe’.
Clicking ‘show detail’ reveals the trick (Figure 18).
Under some circumstances, the malware will give a fake warning about system corruption and ask the user to approve a ‘restoration utility’ to gain a high privilege (Figure 19).
If the user denies it, the malware will continue to pop up warnings and re-prompt the user several times.
The malware prepares warnings in the following languages: Russian, Portuguese, German, French, Dutch, Arabic, Hindi, Persian, simplified Chinese, Turkish, Indonesian, Italian, Spanish, Polish, Japanese and Vietnamese.
It will choose one of them according to the system’s default language locale to avoid language inconsistency raising the user’s suspicion.
As this analysis shows, the communication protocol has reserved enough space for new functionalities to be added in the future. There are unused fields in both the request and response structure and there are four streams in the response data that don’t have any code to handle. Though Neurevt has just entered the market, the bot’s author is likely to develop it rapidly – we will undoubtedly see new features of Neurevt soon.
[1] Beta Bot – Coded in C++ – Incredibly Advanced HTTP Bot. http://www.sinister.ly/showthread.php?tid=5234.
[2] A new bot on the market: Beta Bot. http://blog.gdatasoftware.com/blog/article/a-new-bot-on-the-market-beta-bot.html.