A Brief History of SmokeLoader, Part 2

Introduction

In this two-part blog series, we explore the evolution of SmokeLoader, a malware downloader that has been active since 2011. In Part 1, we explored early versions of SmokeLoader, from its initial rudimentary framework to its adoption of a modular architecture and introduction of encryption and obfuscation. 

This blog provides an overview of SmokeLoader’s development from 2015 to 2022, where the malware continued to update its algorithms and improve anti-analysis techniques.

2015-2017: Protocol Renaissance

Versions 2015 and 2017 of SmokeLoader signify major releases in the evolution of the malware. One of the major changes was to the communication with the command-and-control (C2) server. In version 2015, the network protocol was updated with fields that became the basis for all subsequent versions, where each field was separated by a ‘#’ delimiter.

The following table shows these fields, which have remained unchanged in all versions of SmokeLoader.

Field

Description

BOT_MAGIC

Magic bytes (version number).

BOT_ID

Unique victim ID.

BOT_AFFID

Affiliate ID.

BOT_WINVER

Victim’s Windows version.

BOT_WINBIT

Victim’s Windows OS architecture.

BOT_PRIVIL

User privileges.

BOT_CMD

Command ID.

BOT_OPT

Command argument (the meaning depends on the specific command).

BOT_RES

Command result (the meaning depends on the specific command).

BOT_DATA

Additional data sent with the packet.

Table 1: SmokeLoader C2 fields present in all versions from 2015-2022.

Introduction of a binary protocol

Beginning in 2017, all SmokeLoader versions started using a binary protocol with the same fields as version 2015. SmokeLoader version 2017 also updated the network communication to use two different static RC4 keys to encrypt the requests and decrypt the responses. Each RC4 key is 4 bytes and hardcoded in the main module of the malware. This modification provides an additional advantage – the data encrypted by SmokeLoader cannot be decrypted from a network capture unless the encryption keys are known.

The binary protocol has remained largely unchanged since 2017, except for the addition of the computer name field, which was added in version 2020. The figure below shows an example packet using SmokeLoader’s binary request format for versions 2020 through 2022.

Figure 1: Example SmokeLoader 2020-2022 network data (with RC4 encryption removed).

Figure 1: Example SmokeLoader 2020-2022 network data (with RC4 encryption removed).

SmokeLoader versions 2017-2022 implement the following command IDs:

Command

Value

Description

CMD_ONLINE

0x2711

Notifies the server that the infected system is online and awaiting tasking.

CMD_GETTASK

0x2712

Notifies the server that the infected system is ready to execute a personal task, or an update and awaiting more information. May also be used to report the result of an uninstall command.

CMD_TASKRESULT

0x2713

This command is used to confirm that an update or a task was executed correctly and the BOT_RES argument is set to 1.

CMD_STEALERRESULT

0x2714

Exfiltrates data produced by the stealer plugin. The BOT_DATA argument contains the base64 encoded results.

CMD_PROCMON

0x2715

This command is used to notify the server about the presence of a specific process running on the victim’s computer. The BOT_DATA argument contains the name of the process. The server queries the database. If the database contains tasks associated with the given process, the server returns these tasks to the infected system.

CMD_PROCMONRESULT

0x2716

Reports the status of the CMD_PROCMON command and sets BOT_OPT to 1 for success.

CMD_FGRESULT

0x2717

Submits the formgrabber plugin data to the server, where BOT_DATA contains the base64-encoded data. Under the base64 layer, the results are split by the {:!:} substring to get each form grabbing result. Each result is split by comma to get the values: cnamebrowserurluagentcookiesdata, and time.

CMD_PASSSNIFRESULT

0x2718

Submits passwords harvested from the sniffer plugin where BOT_DATA contains the base64-encoded data.

CMD_FSRESULT

0x2719

Sends files from the infected system discovered by the fsearch plugin where the BOT_DATA contains the base64-encoded content of the files.

CMD_DDOSRESULT

0x271A

Confirms the successful execution of a DDoS attack (against the targets configured in the plugins’ rules). If BOT_OPT is 1, the attack was executed successfully. If it is 0, the attack was unsuccessful.

CMD_KEYLOGRESULT

0x271B

Submits the results of the keylogger plugin where BOT_DATA contains the base64-encoded content of the keylogged data.

CMD_HIDDENTV

0x271C

This command is used to request the TeamViewer package from the server.

CMD_HIDDENTVRESULT

0x271D

Once a TeamViewer instance is run, this command sends the ID and password of the TeamViewer session to the server. They are separated by the substring {:!:}.

CMD_MINER or CMD_EMPTY

0x271E

Requests a cryptocurrency miner executable from the server. In newer SmokeLoader versions, this command value is empty (undefined).

*This command was introduced in version 2017 of SmokeLoader and removed in version 2020.

CMD_EGRABBERRESULT

0x271F

This command is used by the bot to submit the email grabber plugin results to the server where BOT_DATA contains the base64-encoded data.

*This command was introduced in version 2017 of SmokeLoader.

Table 2: Message types implemented by SmokeLoader.

The primary message type is the CMD_ONLINE, which serves as a knock or keep-alive. The response from the server may be one of the following values:

Byte

Command

Description

0x69

i

Personal task

0x72

r

Uninstall

0x75

u

Update

Table 3: Commands implemented by SmokeLoader.

For all other scenarios, the server will provide the number of available tasks, as well as (optional) plugins and their respective configurations. The figure below shows an example task, with plugins (and their configurations). 

Figure 2: Example SmokeLoader task with plugins and configuration.

Figure 2: Example SmokeLoader task with plugins and configuration.

Once SmokeLoader receives a response to the CMD_ONLINE request, it will send a sequence of CMD_GETTASK requests for tasks, updates, and confirm the result of previous commands. 

SmokeLoader will request all available tasks from the server, and in response, the server will provide each task individually. These tasks may include either the content of a file to be executed or the location (URL) from which the file can be downloaded.

In May 2024, Operation Endgame with technical assistance provided by Zscaler ThreatLabz, leveraged SmokeLoader’s built-in uninstall (0x72) command to remotely disinfect tens of thousands of infections.

Algorithm changes

One notable characteristic of SmokeLoader is that the algorithms used vary across versions, likely as an attempt to break existing detections. For example, in SmokeLoader version 2014, the bot ID was generated from the computer name and volume information using a custom algorithm that combined CRC32 and XOR. However, version 2015 and newer have since replaced the hash algorithm to generate the bot ID with MD5.

The encryption of C2 servers was also altered in version 2017, which employs an XOR-based algorithm wherein each byte of the unencrypted C2 URL is encrypted into two bytes. The decryption algorithm utilizes these two bytes, along with a hardcoded 1-byte key, to retrieve the original byte.

The Python implementation of the algorithm is shown below:

def smoke2017_c2_xor_decrypt(enc, key):
   out = b''
   i = 0
   while True:
       out += (((enc[i] ^ key[0]) - 
              (enc[i+1] ^ key[0])) & 0xff).to_bytes(1, 'little')
       i += 2
       if i > len(enc) / 2:
           break
   return out

SmokeLoader version 2017 (and newer) also reintroduced string encryption, but replaced the algorithm with RC4.

Also beginning with version 2017, the malware adopted a different hash function to locate the addresses of the required APIs.

The Python code provided below represents the implementation of the algorithm used to hash the API names in version 2017:

def calc_hash_smoke2017(name):
   name = name.encode()
   hash = 0
   for i in range(0, len(name)):
       hash ^= (name[i] << 8)
       temp = ROL(hash, 8)
       temp_bytes = list(struct.pack('<L', temp))
       temp_bytes[0] = 0xff &(temp_bytes[1] ^ temp)
       temp = struct.unpack('<L', bytes(temp_bytes))[0]
       hash = 0xffffffff & (temp * 2)
   return hash

 

2018: The Stager Revolution

The most significant changes introduced in SmokeLoader version 2018, and subsequent versions, revolve around the stager module. These changes enhanced obfuscation techniques and implemented anti-analysis measures.

Starting from version 2018, the stager module incorporates comprehensive anti-analysis features and includes code for injecting the main module, in addition to handling decryption and decompression. Furthermore, the stager module underwent significant improvements to the obfuscation techniques.

It is important to note that the stager module, starting in version 2017, no longer loads the main module within the current process context. Instead, the stager itself injects the main module into the address space of an explorer process. In version 2017, the stager creates a new instance of the explorer.exe process, while versions 2018 and onward inject the main module into the default explorer process. The reason why version 2017 was designed to create a new explorer process is because the SmokeLoader main module code (for that version) is only 32-bit. Therefore, if the victim’s Windows operating system is 64-bit, SmokeLoader can still function by creating and injecting the main module into a 32-bit (WoW64) explorer process.

Code obfuscation

Starting from version 2018, several code obfuscation techniques were implemented in SmokeLoader that persist in the latest versions. These methods are largely targeted at confusing disassemblers and include the following: 

  • Code permutations
  • Opaque predicates (JZ / JNZ)
  • Stack-based obfuscation, which mixes (string) data and code
  • Obfuscated jumps that use the Relative Virtual Address (RVA) of the destination address along with the precalculated image base

Encrypted functions

In previous versions of SmokeLoader, we observed simple encryption layers using 1-byte XOR keys and straightforward non-obfuscated loops in the stagers. However, starting from version 2018, there was a significant improvement to encryption functions. 

In the stager of SmokeLoader version 2018 (and newer), a majority of the code is encrypted using nested XOR layers, enhancing the encryption and obfuscation techniques employed, as shown in the figure below.

Figure 3: SmokeLoader’s stager using nested XOR encryption layers.

Figure 3: SmokeLoader’s stager using nested XOR encryption layers.

Among the final blocks is code responsible for decrypting, decompressing, and injecting the main module. The figure below shows an example of a function that decrypts SmokeLoader’s stager code for versions 2018 through 2022.

Figure 4: Code decryption for SmokeLoader versions 2018-2022.

Figure 4: Code decryption for SmokeLoader versions 2018-2022.

After executing a decrypted block of code, it is immediately re-encrypted to minimize the amount of unencrypted code in memory.

Anti-analysis tricks

Since 2018, a significant portion of the anti-analysis techniques that were previously executed in the main module have been shifted to the stager. As each new version is released, the malware incorporates an expanding array of evasion techniques and expands its target range to include a wider variety of security products.

Furthermore, the new and more intricate stager necessitates the loading and utilization of multiple APIs. To locate the required API addresses, it employs a hash-based search mechanism, utilizing the DJB2 algorithm.

Starting from version 2018, the stager incorporates checks for keyboard layouts. If it detects Ukrainian or Russian languages, the infection process is halted. Additionally, the stager examines the OsMajorVersion field within the PEB structure and ceases execution for operating system versions below 6 (Windows Vista/Server 2008).

PROPagate injection

In SmokeLoader 2018, the stager employs the PROPagate code injection method to inject the main module within the context of the explorer process. This injection process utilizes native Windows APIs such as NtOpenProcess, NtDuplicateObject, NtCreateSection, and NtMapViewOfSection. Additionally, the SetPropA and SetNotifyMessageA APIs are utilized to execute the PROPagate attack.

It is worth mentioning that starting from version 2018, the stager carries both x86 and x64 variants of the main module. Depending on the current platform, it will inject the appropriate variant into the explorer process.

Algorithm changes

In the 2018 version of SmokeLoader, significant changes were made to the algorithms used for decrypting and decompressing the main module. For decryption, a straightforward XOR is employed with a 4-byte key, while decompression is accomplished using the RtlDecompressBuffer function provided by Windows, utilizing the LZNT1 algorithm.

Furthermore, the encryption method for the C2s was altered in the 2018 version. It now utilizes an XOR-based algorithm with a 4-byte key.

The following code shows a Python implementation to decrypt the C2 URLs for SmokeLoader version 2018:

def smoke2018_c2_xor_decrypt(enc, key):
   out = ''
   for i in range(len(enc) - 4):
       xor_key = struct.unpack('<L', key)[0]
       v = struct.unpack('<L', enc[i:i + 4])[0]
       for j in range(4):
           v = v ^ xor_key
           xor_key >>= 8
       tmp = (chr(~v & 0xff))
       out += tmp
   return out.encode()

The algorithm for hashing the API names to locate their addresses was also modified, as shown below.

def calc_hash_smoke2018_2022(name):
   name = name.encode()
   hash = 0
   for i in range(0, len(name)):
       hash = 0xffffffff&((name[i] & 0xDF) + 
              ROL(hash ^ (name[i] & 0xDF), 8))
   return hash

Version 2018 was also the first to use scheduled tasks for persistence. Every SmokeLoader version since then has continued to utilize scheduled tasks with only the task name changing between versions.

2019-2022: Contemporary History

In the most recent versions of SmokeLoader, there haven’t been significant changes in the structure and functionality of the malware itself. However, there have been modifications to the algorithms used and the anti-analysis techniques employed.

NTDLL hook evasion

SmokeLoader’s stager heavily relies on Windows native APIs to carry out its tasks. Instead of utilizing the default ntdll library that is loaded when the process is created, SmokeLoader loads its own copy of ntdll into memory. This approach ensures that if breakpoints are set on the exported functions of the library or if a monitoring tool attempts to perform hooks, they will be unable to trace SmokeLoader’s behavior as it operates with its own separate copy of ntdll.

In version 2020 and 2022, SmokeLoader creates the copy of ntdll in memory using functions like CreateFileMappingW and MapViewOfFile. In version 2019, the malware created a copy of the library on disk within the %TEMP% folder and loaded it from there using LdrLoadDll.

Detection of security products

SmokeLoader has continuously improved its ability to detect security products on a system in order to avoid infection if any are present. This detection capability has significantly increased in the latest versions, with notable enhancements in 2020 and 2022. The table below highlights these improvements.

Process/registry value

2012

2014

2015

2017

2018

2019

2020

2022

qemu

 

x

 

x

x

x

x

x

QEMU

       

x

     

virtio

         

x

x

x

VMWARE

       

x

     

vmware

 

x

 

x

x

x

x

x

VIRTUAL

       

x

     

virtual

 

x

 

x

       

vbox

         

x

x

x

XEN

       

x

     

xen

 

x

 

x

x

x

x

x

AFEA.vmt

             

x

qemu-ga.exe

           

x

x

qga.exe

           

x

x

windanr.exe

           

x

x

vboxservice.exe

           

x

x

vboxtray.exe

           

x

x

vmtoolsd.exe

           

x

x

prl_tools.exe

           

x

x

REGISTRYMACHINESystemCurrentControlSetEnumIDE

         

x

x

x

REGISTRYMACHINESystemCurrentControlSetEnumSCSI

         

x

x

x

vmci.s

           

x

x

vmusbm

           

x

x

vmmous

           

x

x

vm3dmp

           

x

x

vmrawd

           

x

x

vmmemc

           

x

x

vboxgu

           

x

x

vboxsf

           

x

x

vboxmo

           

x

x

vboxvi

           

x

x

vboxdi

           

x

x

vioser

           

x

x

sbiedll

 

x

 

x

x

x

x

x

aswhook

           

x

x

snxhk

           

x

x

dbghelp

 

x

 

x

       

sample

 

x

           

ffffcce24

     

x

       

Table 4: The year SmokeLoader introduced detection for a particular security product.

Furthermore, in the latest versions of SmokeLoader, the malware relies on Windows native APIs, specifically NtQuerySystemInformation and NtQueryInformationProcess, to query the kernel flags SystemCodeIntegrityInformation and ProcessDebugPort.

Algorithm changes

In versions 2019 and 2020 of SmokeLoader, a different XOR-based algorithm is implemented to decrypt the list of C2 servers. The decryption key used is derived from the CRC32b value of the encrypted C2 address. 

The following code provides a Python implementation of the decryption algorithm:

def smoke2019_2020_decrypt(enc, key):
   dec = b''
   for c in enc:
       for i in key: c = c ^ i
       dec += (c ^ 0xE4).to_bytes(1, 'little')
   return dec

In version 2022, SmokeLoader implemented an RC4 algorithm to protect the C2 URLs. Instead, a DWORD within the structure of the encrypted C2 contains the RC4 key, while another DWORD holds the CRC32b value of the URL. This can be defined as the following C structure:

struct EncryptedC2 {
    unsigned char C2_length;
    unsigned int RC4_key;
    unsigned char C2_encrypted_data[C2_LENGTH];
    unsigned int C2_CRC32b;
}

In versions 2020 and 2022, SmokeLoader introduced the LZSA algorithm for decompressing the main module. This replaced the previous algorithm, LZNT1. Additionally, the PROPagate method used for injecting the main module into the explorer process was no longer utilized in these versions. Instead, SmokeLoader creates a shared section as it did in previous versions and creates a remote thread by invoking the native API RtlCreateUserThread.

Conclusion

SmokeLoader is a sophisticated and intricate modular malware downloader that has been prevalent for many years. The developers of this malware family have consistently enhanced its capabilities by introducing new features and employing obfuscation techniques to impede analysis efforts. While the most recent known version of SmokeLoader dates back to 2022, it has been extensively deployed in numerous campaigns throughout recent years. However, it is important to note that the absence of newer versions does not rule out the possibility of future iterations emerging in the near future. Following Operation Endgame, there has been an overall decline in SmokeLoader activity. However, this lull will likely be short-lived as various threat actors who have leveraged SmokeLoader regroup.

Zscaler Coverage

Zscaler sandbox report

In addition to sandbox detections, Zscaler’s multilayered cloud security platform detects indicators related to SmokeLoader at various levels with the following threat names:

Indicators Of Compromise (IOCs)

Version

Sample SHA256

2012

857fc7aafbbf0d4c850c1b1585a72420600bdabe269f343c0c817614aa6c94cd

2014

e92d1c2c1e145c1d6c42dd402e75f46e5edfb2bab5539c4d103d345b5ac965a3

2016

18aa1b79bbeee6a731b897377233d54b1b2464eeb9a25dafc0debfc43af8c04f

2017

32ba1f3b96cf77a08c041d4983d6afa7db8e1948d27d6a8dd55b7bb95e493189

2018

b6ec96043dba7722cac4ed24b6979fc71a758bdf18ca44353c19194c172bf621

2018

5727c2cd54b8408ca0f8e943cad61027a2c3d51da64f2f1224a6b9acc4820f8e

2019

fc20b03299b8ae91e72e104ee4f18e40125b2b061f1509d1c5b3f9fac3104934

2019

d5efd66f54dce6b51870e40a458fa30de366a2982ab2f83dddff5cb3349f654d

2020

070a94ee0cd9ac1b1ed467353f5731e09cab136315447c04f53bc52d4fe3f8cc

2020

7377efde4e4e86650ab8495f57ab4a76d4f8efe31e2962305b8c42a6cee70454

2022

c78bc4fb8955940b3ac9b52cb16744a61f8bdaf673fd64fc106465241c56cc6c

Source: Original Post