Zscaler Blog

Get the latest Zscaler blog updates in your inbox

Subscribe
Security Research

Technical Analysis of Xloader’s Code Obfuscation in Version 4.3

Key Takeaways

  • Xloader is a popular information stealing malware family that is the successor to Formbook.
  • In early 2020, Formbook was rebranded as Xloader and the threat actors moved to a malware-as-a-service (MaaS) business model, renting C2 infrastructure to customers.
  • Xloader implements different obfuscation methods and several encryption layers to protect critical parts of code and data from analysis.
  • The developers behind this malware family continue to update the code with improved obfuscation and encryption layers with each new version that is released.
  • In January 2023, Zscaler ThreatLabz identified a new variant of Xloader that identifies itself as version 4.3 with several modifications including additional obfuscation.

Introduction

Xloader is a rebranded version of the Formbook information stealing malware, which has been sold in criminal forums since 2016. The threat actors behind this malware family have been updating and improving the code regularly. In early 2020, the malware was rebranded as Xloader. In early 2022, the threat actors released Xloader version 2.9, which introduced significant improvements to obfuscate the malware code and data including the list of command-and-control (C2) servers. In October 2022, ThreatLabz identified a new Xloader version labeled as 3.9. In January 2023, the threat actors released Xloader version 4.3. Across Xloader versions, the group has modified the malware’s obfuscation techniques including adding numerous layers of encryption with code that recursively decrypts other blocks of code until reaching the core functionality that decrypts the most sensitive data (also encrypted with multiple layers).

This blog post analyzes the encryption algorithms used by Xloader to decrypt the most critical parts of the code and the most important parameters of the malware’s configuration. The analysis is performed on the latest version of Xloader 4.3. ThreatLabz has also created an IDA Python script to decrypt Xloader’s code and data to facilitate analysis.

Technical Analysis

Basic Algorithms and Structures

Formbook and Xloader have evolved along the years with new layers of obfuscation added in each new version. However, there is a set of basic algorithms that have been used since the first versions of Formbook. These algorithms are combined in different ways to decrypt other blocks of code and data. The primary algorithms that are shared between different versions of Xloader are the following:

  • Custom RC4: an RC4-based algorithm with two additional layers based on subtraction operations.
  • Custom buffer decryption algorithm: a custom algorithm used by Xloader, mainly used to decrypt the first encryption layer of the PUSHEBP data blocks (described in the following sections).
  • Custom SHA1: a SHA1 hash is calculated and the result is reversed DWORD by DWORD.

There is also a large global data structure that is used to store important information. When Xloader is executed, this structure is allocated and initialized with information from PUSHEBP data blocks, or from hardcoded values in the code. This structure contains data and encryption keys that are used by other parts of the code. Previous blog posts have referred to this structure as the ConfigObj, with fields that are used to store flags, encryption parameters, pointers, etc. The most important offsets in the ConfigObj structure are identified in Table 1.

 

Offset

Description

Size

0x00

Value 0xffffffff

0x04

0x04

Pointer to a second PE header used for process injection (e.g., explorer.exe)

0x04

0x08

Result of RtlGetProcessHeaps()

0x04

0x48

Branch ID – XLNG (XORed with 0x3c)

0x04

0x90

Pointer to an extended config (located in memory following the ConfigObj structure)

0x04

0x2DC

Decrypted content of the PUSHEBP block 2, which is an array of API hashes

0x220

0x510

Array of library and process names hashes

0x254

0x828

Seed of a random number generator (RNG) used by the malware

0x4

0x83C

Flag indicating that Xloader has generated the parameters necessary for the communications with the C2

0x4

0x970

The Xloader version number (XORed with 0x3c)

0x4

0x990

RC4 key used to decrypt other parameters

0x14

0x9A4

RC4 key used to decrypt other parameters (this key is the SHA1 of the decrypted content of the PUSHEBP block 5)

0x14

0xCE8

RC4 key used to decrypt the C2s list

0x14

Table 1. Important Xloader 4.3 ConfigObj fields.

 

Encrypted PUSHEBP Data Blocks

Throughout the Xloader code there is a set of encrypted data blocks with the structure shown in Figure 1.

Figure 1. Xloader PUSHEBP encrypted block

Figure 1. Xloader PUSHEBP encrypted block

This structure is designed to resemble the beginning of a function, but are in fact blocks of encrypted data such as encryption keys and encrypted strings. These data blocks are decrypted using a custom buffer decryption algorithm. Table 2 shows the PUSHEBP encrypted blocks that were found in Xloader 4.3.

 

PUSHEBP Block Number

Description

Size

PUSHEBP Block 1

Encrypted strings

0xA82

PUSHEBP Block 2

API CRCs

0x222

PUSHEBP Block 3

Encryption key involved in C2 communications

0x15

PUSHEBP Block 4

Encryption key used to decrypt other data

0x14

PUSHEBP Block 5

Hardcoded C2

0x78

PUSHEBP Block 6

API CRCs

0x310

Table 2. PUSHEBP encrypted block contents

 

Encrypted PUSHEBP Functions

Formbook and Xloader also contain functions that decrypt code. An example function to decrypt code (prior to Xloader 2.9) is shown in Figure 2.

Figure 2. Encrypted code in earlier versions of Xloader and Formbook

Figure 2. Encrypted code in earlier versions of Xloader and Formbook

This code starts with the well-known function preamble push ebp / mov ebp, esp, followed by a tag identifying the encrypted code (0x49909090 in Figure 2), the encrypted code, and an ending tag 0x90909090.

In older versions, the code was decrypted using a custom RC4 algorithm with a key stored in the ConfigObj structure. In Xloader version 2.9, the code retained the custom RC4 algorithm and added a layer of encryption with a second key built on the stack, as shown in Figure 3.

Figure 3. Decryptor of the PUSHEBP functions in Xloader version 2.9

Figure 3. Decryptor of the PUSHEBP functions in Xloader version 2.9

In Xloader version 4.3, there are still PUSHEBP encrypted functions. However, the tags identifying the start and the end of the encrypted code have changed, and now they appear to be random bytes. Figure 4 shows an example of an encrypted function in Xloader 4.3.

Figure 4. Encrypted PUSHEBP function (Xloader version 4.3)

Figure 4. Encrypted PUSHEBP function (Xloader version 4.3)

Figure 5 shows the code that decrypts a PUSHEBP function (e.g., the function DecryptCriticalCodeType1_Set_909090909090), which accepts two encrypted tags and an ID value. Inside the decryptor, another 0x14 byte key is constructed dynamically in the sub-function init_key_encrypted_funcs, XORed with a DWORD (XOR key 1) and XORed again with the ID value passed as an argument and another hardcoded DWORD (XOR key 2). The resulting 0x14 byte key will be used to decrypt the encrypted code using Xloader’s custom RC4 algorithm. The same RC4 key is also used to decrypt the encrypted TAG1 and TAG2, which are passed as arguments to the decryptor to derive the starting and ending tags that delimit the encrypted PUSHEBP function.

Figure 5. PUSHEBP function decryption code (Xloader version 4.3)

Figure 5. PUSHEBP function decryption code (Xloader version 4.3)

After the code is decrypted, the delimiter tags are replaced by 90 90 90 90 90 90 (NOP) opcodes. Figure 6 shows an encrypted function before and after being decrypted.

Figure 6. Example PUSHEBP function decrypted (Xloader version 4.3)

Figure 6. Example PUSHEBP function decrypted (Xloader version 4.3)

 

Encrypted NO-PUSHEBP Functions

In Xloader version 4.3, a new type of encrypted function without the push ebp / mov ebp esp preamble has also been introduced. The limits of the encrypted code are located by searching for two tags that identify the start and the end of the block. Figure 7 shows the code responsible for determining the limits of a NO-PUSHEBP encrypted function.

Figure 7. NO-PUSHEBP decryption code limit identification and layer 1 decryption (Xloader version 4.3)

Figure 7. NO-PUSHEBP decryption code limit identification and layer 1 decryption (Xloader version 4.3)

The custom Xloader RC4 algorithm is again used to decrypt the encrypted code with two layers and two different keys. The encryption key for the first layer is calculated in another function and stored in the global structure ConfigObj (the value is the result of Xloader’s custom SHA1 algorithm of the decrypted content of the PUSHEBP data block number 5). The encryption key for the second layer is built on the fly: an initial key is built on the stack and XORed with a DWORD (XOR key), producing the final key (Xloader never hardcodes exact values including for encryption keys and delimiter tags). Figure 8 shows the code involved in the decryption of the second layer for one of the encrypted NO-PUSHEBP functions.

 

Figure 8. NO-PUSHEBP layer 2 decryption (Xloader version 4.3)

Figure 8. NO-PUSHEBP layer 2 decryption (Xloader version 4.3)

After the code is decrypted, the tag before the encrypted code is replaced by the opcodes EC 8B 55 (push ebp / mov ebp esp function preamble). The tag after the encrypted code is replaced by 90 90 90 90 (NOP) opcodes.

 

Encrypted Configuration

The most important parameters of Xloader’s configuration are stored in the PUSHEBP encrypted data blocks or calculated from hardcoded constants (that are also obfuscated).

 

Encrypted Strings

The encrypted strings in Xloader are stored in the PUSHEBP data block 1. All the PUSHEBP data blocks have to be decrypted with the custom buffer decryption algorithm as explained before. Once the block is decrypted, the result is a sequential list of items that have the following format:

struct  encrypted_string {
        BYTE length;
        BYTE content[length];
}

Each string is decrypted with the custom Xloader RC4 algorithm and an encryption key stored at offset 0x990 in the ConfigObj. This RC4 key is generated in the function shown in Figure 9.

Figure 9. Generation of the RC4 key for encrypted strings (Xloader version 4.3)

Figure 9. Generation of the RC4 key for encrypted strings (Xloader version 4.3)

ThreatLabz has reproduced this algorithm to decrypt the encrypted strings in Xloader 4.3 in Python. The code is available in our GitHub repository here.

 

Encrypted C2s

The Xloader configuration contains a C2 that is stored separately from another list of C2 domains. The C2 that is stored separately was thought to be Xloader’s real C2 and the other C2s were used as decoys. However, in more recent versions of Xloader, real C2s are likely hidden among the list of decoy C2s. In fact, the author behind Xloader has made significant efforts to protect the list of C2s that were previously thought to be decoys.

 

Hardcoded C2

The code shown in Figure 10 is responsible for decrypting the hardcoded Xloader C2.

Figure 10. Hardcoded C2 decryption (Xloader version 4.3)

Figure 10. Hardcoded C2 decryption (Xloader version 4.3)

The code in Figure 10 combines a set of operations based on Xloader’s various encryption algorithms and the data stored in the PUSHEBP data blocks to generate the encryption key necessary to decrypt the hardcoded C2 (which is stored in the PUSHEBP data block 5).

 

C2 List

As previously mentioned, there is another list of C2s that may contain decoy C2s and real C2s. In Formbook and in earlier versions of Xloader, these were stored as an encrypted string with no additional layers of encryption. In Xloader 2.9, the developers introduced an additional custom RC4 layer and Base64 encoding for the C2 list as shown in Figure 11.

Figure 11. Additional encryption layer for the C2 list (Xloader version 2.9)

Figure 11. Additional encryption layer for the C2 list (Xloader version 2.9)

In Figure 11, the function StringsDecryptor2 decrypts the first layer of the encrypted strings. In version 2.9, an additional Base64 layer is decoded followed by a layer of custom RC4 decryption. In Xloader version 4.3, they have added an additional encryption layer to this C2 list. Figure 12 shows the code responsible for decrypting these new layers.

Figure 12. New encryption layer for Xloader’s C2 list (Xloader version 4.3)

Figure 12. New encryption layer for Xloader’s C2 list (Xloader version 4.3)

In the new version, the C2 list is first Base64 decoded and a custom RC4 layer is decrypted. A table of 4 byte keys is built on the stack. Each position of the table corresponds to a C2. Once decrypted, this custom RC4 layer is Base64 encoded again. After the new additional decryption layer is complete, Xloader decrypts the same layers as version 2.9: decoding the Base64 layer again and decrypting an additional custom RC4 layer with a key stored in a sub-structure of the ConfigObj. The way that this key (for the last RC4 layer) is generated has also changed in Xloader 4.3. Figure 13 shows the code generating the RC4 key for the last encryption layer of the C2 list.

Figure 13. Key generation for the final encryption layer of the C2 list (Xloader version 4.3)

Figure 13. Key generation for the final encryption layer of the C2 list (Xloader version 4.3)

As shown in Figure 13, the key is built on the stack and it is XORed with a value from the ConfigObj that was initialized previously in a different part of the code. Once this last layer is decrypted, the plaintext C2s are obtained.

 

Branch ID and Version Number

In previous versions, the Xloader branch ID and version number were sent in the registration packet to the C2. The format of the registration packet (before the last two RC4 layers) was the following:

 

XLNG

Bot ID

Version Number

Operating System

Base64(Username)

 

XLNG is the tag for the Xloader branch (FBNG was the branch ID for Formbook).

In Xloader version 4.3, the registration packet sent to the C2 includes an additional encryption layer as shown in Figure 14.

Figure 14. Xloader 4.3 Registration packet with additional PKT2 layer

Figure 14. Xloader 4.3 Registration packet with additional PKT2 layer

This new encryption layer is marked with the tag PKT2. Communications are performed in the context of explorer.exe (previously injected). However, this registration packet is built in the first injected process (a hollow process) and copied to the context of explorer together with the rest of the injected code. That first injected process exits after injecting into explorer, so the registration packet under the last encryption layer marked with the PKT2 tag is no longer in plaintext after the first injected process terminates.

The PKT2 packet is built in one of the NO-PUSHEBP encrypted functions. That function is decrypted and executed in the context of the first injected process. The code first builds a string with the same format as the registration packet in previous Xloader versions as shown in Figure 15.

Figure 15. Registration packet with the new PKT2 encryption layer

Figure 15. Registration packet with the new PKT2 encryption layer

However, as we can see in Figure 15, Xloader 4.3 introduces a NULL character separating the bot ID and the malware version number. This added NULL byte is likely a coding error.

Figure 16 shows how the first registration packet is constructed (marked with XLNG tag) and encrypted with RC4 and encoded with Base64, and then concatenated to the PKT2 tag to generate the final registration packet.

Figure 16. Xloader version 4.3 registration packet construction

Figure 16. Xloader version 4.3 registration packet construction

However, because of the coding error previously mentioned (an extra NULL character after the bot ID) the final registration data contains just two fields for the XLNG branch and bot ID as shown below:

 

XLNG

Bot ID

 

The PKT2 tag is then prepended with the RC4 and Base64 encoded data as follows:

 

PKT2

RC4_BASE64(registration_data)

 

As a result, the version_number, operating_system and user_name is never sent to the C2. This bug will likely be fixed in future versions of the malware.

Figure 16 also shows that the branch ID and version number are no longer hardcoded unlike previous versions, with the encrypted version number and branch ID decrypted with an XOR key (0x3c).

Tools

Zscaler ThreatLabz has developed an IDA script to decrypt the Xloader’s encrypted code. The code is available in our GitHub tools repository here.

Conclusion

Since its inception, the Formbook and Xloader malware families has been a prominent threat. The threat actors behind it continue to update and improve the malware code in an effort to hinder analysis. In the most recent version, the threat actors increased the level of complexity yet again with additional layers of encryption for critical parts of the code and important data. However, Zscaler researchers have been able to unravel these obfuscation layers and analyze the key components of the malware code.

Cloud of Sandbox Detection

Zscaler sandbox coverage

 

Zscaler's multilayered cloud security platform detects Xloader and Formbook indicators at various levels, as shown below:

Indicators Of Compromise (IOCs)

Variant

Version

SHA256

Botnet

XLoader

4.3

9e1b4f2d408e187ca641c0c16269069d0acabe5ae15514418726fbc720b33731

6qne

XLoader

4.3

f55ce0741ed4615bae5646c644b3a971323ac344b12693495d5749c688d5d489

6qne

XLoader

4.3

3bd86f3906f59f627bf65664d2bfacf37a29dbaafeae601baf5eeb544396f26c

6qne

XLoader

3.9

8e12b85676aaf45a93c91e2db2065151e19f184907da6d85701ac3b13d0e6052

nvp4

XLoader

3.9

6a726fb5c93adbae0f3061b40b19745587c0114deb86bd72c90acdd69242cbe0

nvp4

 

Network Indicators

Type

Domain

Harcoded C2 domain

jourmoe[.]com

C2 list domain

060jinbo[.]com

C2 list domain

10086253[.]vip

C2 list domain

117ygh9x[.]com

C2 list domain

365-8119[.]com

C2 list domain

365heji[.]com

C2 list domain

4tx[.]ru

C2 list domain

667fm[.]com

C2 list domain

991-touring[.]info

C2 list domain

abttt[.]win

C2 list domain

adacaranya[.]com

C2 list domain

allforfun[.]online

C2 list domain

allison2patrick[.]online

C2 list domain

applicationsdown[.]store

C2 list domain

apsocreto[.]online

C2 list domain

avdeeva[.]info

C2 list domain

betfury-platform[.]net

C2 list domain

bilpoinsaat[.]net

C2 list domain

bocc[.]live

C2 list domain

bookinbournemouth[.]co[.]uk

C2 list domain

botanica-online[.]ru

C2 list domain

byfuture[.]biz

C2 list domain

canlicerrahi[.]xyz

C2 list domain

ceu84g[.]com

C2 list domain

chiyiqian[.]net

C2 list domain

christmatoy[.]com

C2 list domain

cinemamaxz[.]com

C2 list domain

coffeelectro[.]online

C2 list domain

dedmorozvidos[.]store

C2 list domain

difozaa[.]life

C2 list domain

dugebitv4[.]xyz

C2 list domain

eatgre[.]wiki

C2 list domain

expertponto[.]com

C2 list domain

farmanow[.]xyz

C2 list domain

flippingfrenzy[.]com

C2 list domain

g2fm[.]co[.]uk

C2 list domain

ginaandhipa[.]com

C2 list domain

graciesvoice[.]info

C2 list domain

guzmanmodels[.]com

C2 list domain

habka[.]online

C2 list domain

hal-skincare[.]com

C2 list domain

hiufouwnwk[.]shop

C2 list domain

hjiqa[.]com

C2 list domain

huifeng-tech[.]com

C2 list domain

identowel[.]com

C2 list domain

inigrey[.]com

C2 list domain

ituyiut[.]wang

C2 list domain

jimtrosper[.]com

C2 list domain

kajainterior[.]com

C2 list domain

loaddown[.]vip

C2 list domain

mgconsultantlogistics[.]com

C2 list domain

myif471ok9ipidk2kkl[.]xyz

C2 list domain

najdlegend1[.]com

C2 list domain

nnhuigou[.]com

C2 list domain

ogei[.]app

C2 list domain

poweroffer[.]net

C2 list domain

realtxt[.]co[.]uk

C2 list domain

seeword[.]site

C2 list domain

solutionsquik[.]net

C2 list domain

themas5erofssuepnse[.]cyou

C2 list domain

uevj[.]win

C2 list domain

vowlashes[.]co[.]uk

C2 list domain

wanknumbers[.]co[.]uk

C2 list domain

wsavxrg[.]shop

C2 list domain

zzaen[.]com

form submtited
Thank you for reading

Was this post useful?

Get the latest Zscaler blog updates in your inbox

By submitting the form, you are agreeing to our privacy policy.