This blog was co-authored by Andrea Allievi.
A few weeks ago I came across a sample that was reading from and writing a significant amount of data to the registry. Initially, it was thought that the file may be a binary, but after some analysis it was determined that the file is a configuration file for Zeus. Within this blog post we take a look at our analysis of the data I/O in the registry.
Initial Stages of Infection The scope of this paper is the analysis of the registry write. This section is a brief overview of what happens when the malware is executed.
- Creates a copy of itself in the tmp directory
- Injects itself into explorer.exe
- Starts a thread that executes the injected code
The code injected into Explorer.exe becomes the focus of our analysis. To get started, an infinite loop was added to the entry point of the injected code and Ollydebug was used to attached to Explorer.exe. Debugging injected code in this manner was previously covered here.
After attaching the debugger and prior to continuing execution, a breakpoint is set on Advapi32.RegSetValueExW() before the large data write is made. This breakpoint is tripped multiple times by multiple threads within Explorer.exe. Most of the time the threads are related to the injected ZBot code.
It turns out that the same thread is used consistently for writing to this registry key. Several sub-keys are created to store data that the application uses at a later time.The names of the sub-keys are created using an index value that is combined with other data to appear random. For instance, the key “2f0e9h99” was created by combining a hash of the User SID with the index value 0x9A. Throughout this paper, the registry key will be referenced by either name or index.
A Series of Registry Writes
This section establishes a pattern to the registry activity that can be used to help figure out what the malware is accomplishing with the registry I/O. The registry activity centers around writing to the following key:
The “ujquuvs” is dynamically generated by the application and will change between executions.
|Ujquuvs Registry Key Prior to|
Prior to the first registry write of interest the Ujquuvs sub-key contains the values shown in the above graphic. Throughout this section we’ll see that new value names are generated and data is cycled between the keys.
One of the first chunks written to the registry value 2f0e9h99 is a binary string that is 475 bytes in length. The following graphic shows the call to the Windows Advapi32.RegSetValueExW() procedure made by the malware.
|First Registry Write to 2f0e9h99|
The above graphic displays the binary string data that was written to the registry. Although 475 bytes is a significant chunk of data written to the registry it is not what caused an alarm. The registry write I am looking for is greater than 3000 bytes.
|Second Registry Write to 2f0e9h99|
Another 475 byte write occurs, but the data is different than the first write. It is worth noting that although the data is different the first four bytes appear to be the same “5D BB 95 50” pattern. This may be a header used to distinguish the data.
The next call to RegSetDataExW will write 3800 bytes to the registry. The binary data was replaced with alphanumeric data (possibly base64). Another assumption can be made. The original binary data is encoded and then stored back to the registry.
|Alphanumeric Data Written to 2f0e9h99|
This is one of the large data writes that was flagged by the sandbox. Continuing on we see several more data writes all of which are variations of the above. The data cycles between binary strings and alphanumeric strings, and the string lengths vary. One of the largest data writes was an 7200 byte alphanumeric string.
Registry Reads Along with the registry writes there are usually corresponding registry reads. The data located in 2f0e9h99 is pulled into a buffer and manipulated by the application.
Once the data is read, decoded from alphanumeric encoding to a long list of 475 byte chunks of binary data. These chunks of data contain a hash to identify specific chunks within the list. Whenever a new chunk of data is received the data contained in 2f0e9h99 is decoded and the hash value of the received chunk of data is compared against each chunk that exists already within the registry. If these hash values match, then the that registry data chunk is replaced with the incoming data. Otherwise the data is appended to the bottom of the list.
Once the input queue is empty the calls to read or write to the registry stop. The thread has not been killed, but it is (most likely) suspended until some event occurs.
The next section combines these findings with further analysis to track down the source of the registry writes.
Walking through the executable with a debugger led us to the source of the registry writes. A thread is created and starts executing the code at address 0x41F579. From here on out this code is going to be referred to as ZBotThread_1(). This procedure is the backbone for all activity related to this registry key.
|Network Socket Loop|
After several instructions for initializing various data structures, ZBotThread_1() initializes a network socket to communicate with a remote server. Once traffic is received the IP address is verified against an IP blacklist of network address ranges that exists within a data structure used throughout the application. These IP Address ranges appear to be owned by various AV vendors (indicated here). Here is the list of blacklisted address ranges with the corresponding netmasks:
- 220.127.116.11 255.255.255.224
- 18.104.22.168 255.255.224.0
- 22.214.171.124 255.252.0.0
- 126.96.36.199 255.255.192.0
- 188.8.131.52 255.255.255.0
- 184.108.40.206 255.255.252.0
- 220.127.116.11 255.255.255.0
- 18.104.22.168 255.255.255.0
- 22.214.171.124 255.255.255.0
- 126.96.36.199 255.255.252.0
- 188.8.131.52 255.254.0.0
- 184.108.40.206 255.255.0.0
- 220.127.116.11 255.255.255.0
- 18.104.22.168 255.255.255.0
- 22.214.171.124 255.255.255.0
- 126.96.36.199 255.255.255.0
- 188.8.131.52 255.255.255.0
- 184.108.40.206 255.255.0.0
- 220.127.116.11 255.255.0.0
- 18.104.22.168 255.255.255.0
- 22.214.171.124 255.255.248.0
- 126.96.36.199 255.255.0.0
- 188.8.131.52 255.255.240.0
- 184.108.40.206 255.255.255.192
- 220.127.116.11 255.255.224.0 Once the IP address is verified the payload is decrypted and the data is initialized into the following data structure (sub_41F9C6):
Throughout this post we will refer to this as ZBOT_SOCKET_DATA. Each datagram payload contains this data structure. The lpDataBuff points to a buffer that contains the data that will eventually be written to the registry.
In addition, the dataBuffHeader[0x2C] contains the first 44 bytes of the decrypted received data. These bytes contain critical information about the entire data chunk.
After a few checks to verify the integrity of the data, ZBotThread_1 calls AnalyseSockDataAndUpdateZBot (sub_43D305). This function will take the 20 byte hash of the data contained within the data chunk header (first 44 bytes) and compares it against a list of other hashes. This list of hashes is built out of previously received datagrams. If the hash is part of the list then the data is dropped. Otherwise, the hash is appended to the end of the list.
Next, AnalyseAndFinalizeSockData (sub_41D006) is called to begin the process of adding the data to the registry. Once inside the function, the data type (dataBuffHeader+0x3) is checked. There are several different data types, but the one that is relevant for the purposes of this blog post is type 0x6. This signifies the end of the data stream and the malware can proceed to save the data to registry key 2f0e9h99
The type 0x6 code branch calls VerifyFinalSckDataAndWriteToReg (sub_436889). This function strips the 0x2C length header from the socket data before verifying the integrity using RSA1.
Finally, if the data integrity is good, the WriteSckDataToReg function is called.
Writing Socket Data to the Registry
The previously received socket data has already been written to registry key 2f0e9h99. At this point, the socket data needs to be merged with the data contained within the registry key. Before this can occur, the data is currently alphanumerically encoded (see the registry write section above). The decoded data is a series of 0x1D0 hex byte chunks. Each chunk is a ZBOT_SOCKET_DATA structure.
|Alphanumeric Encoded Data in Memory|
The hash of the socket data is compared against the hash of each chunk contained within the list of chunks. If the hashes match, then that registry data chunk is replaced with the network socket data. Otherwise the network socket data is appended to the end of the list.
Once the update is completed the registry data is (once again) alphanumerically encoded and written back to the 2f0e9h99 registry key.
It’s worth noting that our sample dropper can encode the original data in several different ways: Base64, and 3 customized XOR algorithms (see function at VA 0x4339DE for all the details).
Using the registry as a way to store and update a configuration is a clever idea. The multiple writes and reads that come with constructing the file with a registry key will raise alarms. It’s what originally grabbed our interest.
This blog post covers a small percentage of the functionality of this malware sample. Some of the functionality that we uncovered denote a high level of sophistication by the author. We strongly encourage others to download a copy and crack open their debuggers.
Sample MD5 Hashes:
Injected Code: B4A00F4C6F025C0BC12DE890A2C1742E