Xloader is a stealer, the successor of FormBook. However, apart from the basic functionality, the unusual approaches to encryption and obfuscation of internal structures, code, and strings used in XLoader are also of interest. Let’s take a detailed look at the encryption of strings, functions, and C2 decoys.
Encryption in XLoader
First, we should research 3 main cryptographic algorithms used in XLoader. These are the modified algorithms: RC4, SHA1, and Xloader’s own algorithm based on a virtual machine.
The modified RC4 algorithm
The modified RC4 algorithm is a usual RC4 with additional layers of sequential subtraction before and after the RC4 call. In the code one layer of subtractions looks like this:
# transform 1 for i in range(len(encbuf) - 1, 0, -1): encbuf[i-1] -= encbuf[i] # transform 2 for i in range(0, len(encbuf) -1): encbuf[i] -= encbuf[i+1]
The ciphertext bytes are subtracted from each other in sequence from right to left. And then they go from left to right. In the XLoader code, it looks like this:
The modified SHA1 algorithm
The SHA1 modification is a regular SHA1, but every 4 bytes are inverted:
def reversed_dword_sha1(self, dat2hash): sha1Inst = SHA1.new() sha1Inst.update(dat2hash) hashed_data = sha1Inst.digest() result = b"" for i in range(5): result += hashed_data[4*i:4*i+4][::-1] return result
Xloader’s own virtual machine algorithm
The last algorithm is a virtual machine that generates one to four bytes of plaintext, depending on the current byte of the ciphertext. Usually, this algorithm is used as an additional encryption layer, which will be discussed later. The entry of the VM decryption routine looks like this:
Decrypting XLoader Strings
Next, let’s investigate how string encryption works in XLoader. All byte arrays containing encrypted strings or key information are located in special kinds of blobs.
As you can see in the screenshot above, this blob is a function that returns a pointer to itself, below this function are the bytes you are looking for.
In order to decrypt strings, first a key is generated. The key is generated from 3 parts, to which the above-described functions are applied.
Here K1_blob, K2_blob, and K3_blob are functions that return data from the blocks described above, and the string length is an argument for them.
The functions VM_Decrypt, RC4_with_sub_Layer and sha1_* are modified algorithms that we discussed earlier.
Schematically, the key generation algorithm can be represented by the following diagram.
Here E and K are the data and the key that is fed to the input of the RC4 function, respectively, and K1, K2, and K3 are the data obtained from the K1_blob, K2_blob, and K3_blob functions.
The strings themselves are also stored as a blob and are covered by two layers of encryption:
- RC4 that uses the key obtained above.
At the same time, RC4 is not used for the whole blob at once.
After removing the first layer, the encrypted strings themselves are stored in the format:
encrypted string length – encrypted string
Consequently, to decrypt the strings, we need to loop through this structure and consistently decrypt all the strings.
Below is an example of the encrypted data after stripping the first layer. Length/string pairs for the first 3 encrypted strings are highlighted in red.
The same strings after decryption:
Along with the encrypted strings, C2 decoys are also stored there. They are always located at the end of all decrypted strings, beginning and ending with the f-start and f-end strings.
Decrypting XLoader’s C2 Servers
Next, let’s see how the main C2 encryption works. The main C2 is located elsewhere in the code, so you can get it separately from the C2 decoys.
To decrypt it, as well as to decrypt the strings, 3 keys are used. The C2 decryption scheme is shown below:
- EC2 is the encrypted C2
- DC2 is the decrypted C2
The algorithm itself is a 3 times sequential application of the RC4 algorithm with 3 different keys.
Also, in newer versions of XLoader C2 decoys, which usually lie along with all the other strings, turn out to be covered by an additional layer of encryption, and, at first glance, it is completely unclear where exactly the decryption of these strings occurs.
Since XLoader has several entry points, each responsible for different non-intersecting functionality, with many functions turning out to be encrypted.
The C2 decoys are decrypted inside the XLoader injected into Explorer.exe. And in this case, it is passed to netsh.exe, which also contains XLoader via APC injection.
In order to understand how a C2 decoy is encrypted, first of all, you need to understand how the functions are encrypted.
It’s actually quite simple. RC4 is used as the encryption algorithm. This time, the key is hardcoded and written right in the code and then xored with the 4-byte gamma.
After that, you should find pointers to the start and end of the function. This is how you do it: a unique 4-byte value is placed at the beginning and end of each encrypted function. The XLoader looks for these values and gets the desired pointers.
Then the function is decrypted, control is given to it, and it similarly searches for and decrypts the next function. This happens until the function with the main functionality is decrypted and executed. So, functions should be decrypted recursively.
The key to decrypting C2 decoys consists of 2 parts and is collected separately at two different exit points. One exit point gets the 20-byte protected key, and the second gets the 4-byte gamma to decrypt the key.
Example of extracted XLoader malware configuration
Applying the above algorithms we can extract the configuration from Xloader, including C2, C2 decoys, and strings. For your convenience, we have integrated automatic extraction of the Xloader configuration into ANY.RUN interactive sandbox — just run the sample and get all the IOCs in seconds.
Examples of successfully executed samples:
Sum it up
In this article, we discussed the encryption in xLoader stealer. It is based on both add-ons to existing algorithms and self-written algorithms.
The main tricky part of the decryption process is the key generation and the fact that the XLoader functionality is split into modules that can be run in different processes. Because of this, in order to extract strings, we have to decrypt the executable code, among other things.
Fortunately, ANY.RUN is already set up to detect this malware automatically, making the relevant configuration details just a click away.
Sample with new C2 decoys encryption
Sample without C2 decoys encryption