Contributors: Oleg Boyarchuk (T-Rex), Jason Zhang (NSBU TAU), Giovanni Vigna (NSBU TAU)

Dridex is a banking Trojan. After almost a decade since it was first discovered, the threat is still active. According to report published by Check Point [1]Dridex was one of the most prevalent malware in 2020. The recent Dridex campaign detected by VMware demonstrates that this ongoing threat constantly evolves with new tactics, techniques, and procedures (TTPs), which exhibit great differences with respect to the variants we’ve collected from campaigns since April 2020 (as discussed in the section Comparison with old Dridex samples). 

In this blog post, we first examine the recent Dridex attack by looking into some of VMware’s NSX Advanced Threat Prevention telemetry, which showcases the magnitude of the campaign. We then present the analysis for the most distinctive aspects of the attack, from the techniques leveraged by the XLSM downloader to the main functionality of the DLL payloads. Finally, we provide a comparison to some other Dridex variants seen in the past, which leads to the conclusion that the Dridex variant from the January 2021 campaign is very different from previous variants. 

The Dridex Campaign 

The chart below shows the detection timeline of the campaign that affected some of our customers in the APAC region, mostly universities. As we can see, the campaign started on January 11, and peaked on January 21 before fading away.  

Dridex Detection Timeline in APAC Region

Detection timeline of the campaign that affected some of our customers in the APAC region.

To entice potential victims to activate a malicious payloadattackers often use social engineering techniques in spam emails. Typical examples include using logistics invoices and online order confirmations as lures. In the recent Dridex campaign, the malspam appears to come from the customer service department of a logistic company, with subject lines saying that new invoices are available to be viewed. The screenshot below shows the email sender names, subject lines, and attachment filenames of some typical emails from the campaign we detected.  

Email Header and Attachment Examples from Dridex Campaign

Examples of typical email header and attachment names from the Dridex campaign.

The email attachments are weaponized XLSM spreadsheet documents, which serve as the Dridex downloaders in the campaign, as discussed in the following section.  

XLSM Downloader 

To better understand the attack, we analyzed one of the XLSM samples from the campaign, as shown in the table below: 

MD5  a47b6adc87b8000c91a706d3c5ed540f 
SHA1  5337c9cd3d3813178bd5e7d1e1334ab973bdbb3f 
SHA256  fa8ed75cfc69a06cf1e809531f7371b5c75fd480339ae65568785b76387ceaa0 
File name  1 Total New Invoices-Thursday January 21 2021.xlsm 
Size  30953 bytes 
Type  application/msoffice-xlsm 

The properties of a typical XLSM sample from the campaign.  

Like typical weaponized Office documents, this document uses social engineering to ask for enabling macro execution upon opening the file: 

XLSM Sample from Dridex Campaign

The opening page of the XLSM sample.

The .  

Auto Open Function in the Embedded VBA Macro Execution

Protected VBA macro.

The VBA macro is locked (as shown above). Protecting VBA macro is not necessarily malicious, but this feature is seldom used in benign documents. For this reason, NSX catches this suspicious behavior as follows: 

NSX Detects VBA Project is not Viewable

NSX detection: VBA Project is not viewable.

Exploring the embedded macro reveals that it leverages URLDownloadToFileA to download a DLL payload. Calling URLDownloadToFileA takes into account both 32-bit and 64-bit operating systems: 

Leveraging the API function URLDownloadToFileA to download a DLL payload.

NSX detects the network traffic attempting to download the payload: 

NSX detects the network traffic attempting to download the payload

NSX detection: attempting to download an executable file.

The payload is saved to the system’s %TEMP% directory upon successful download, which NSX detects as suspicious behavior: 

NSX detects as suspicious behavior

NSX detection: dropping an executable file.

The downloaded DLL is then loaded with regsvr32.exewhich is detected by NSX as malicious behavior: 

NSX Detects Malicious Behavior

NSX detection: document is executing regsvr32.exe.

DLL Payload 

The downloaded DLL (swwwbudo.dll) has the following basic properties: 

MD5  002c56165a0e78369d0e1023ce044bf0 
SHA1  78ebfe4167a851bc75d9702aa1aea5efe1514b0c 
SHA256  a5ffce2a8d98ddc0ccc20744e88443eac323caf1cd8a218b8ccd50bc5ab8f1ac 
File name  swwwbudo.dll 
Size  856064 bytes 
Type  application/x-pe-dll-32bit-i386 

The properties of the downloaded DLL (swwwbudo.dll). 

The code inside the DLL is obfuscated with multiple arithmetic operations using arbitrary integers, which makes static analysis challenging. The obfuscated code is shown below: 

Code obfuscation with multiple arithmetic operations using arbitrary integers.

The payload performs several steps: 

  1. The shellcode gets is extracted into the .data section; 
  2. The shellcode decrypts itself; 
  3. The decrypted shellcode runs a copy of itself; 
  4. The decrypted shellcode runs an embedded core DLL; 
  5. The embedded core DLL communicates with the C&C external host. 

More details are discussed below. 

Shellcode: first stage 

The firststage shellcode is stored in encrypted form in the .rdata section of the DLL. There are two functions responsible for the shellcode decryption: 

  1. The first one writes the shellcode into the .data section; 
  1. The second one decrypts it. The decryption process gets called repeatedly until the code is successfully decrypted. 

The decryption takes ~30 secondsgenerating an unusually high CPU load. The decrypted shellcode contains another encrypted layer. There is a short code at the beginning of the shellcode that loops through the bytes and decrypts the XOR-encrypted code: 

Decryption loop to decrypt the inner encrypted code.

After the decryption, the shellcode finds the address of VirtualAlloc. It does so via module list traversal taken from the PEB: 

  1. Get pointer to _PEB: 
  2. Get pointer to _PEB_LDR_DATA from _PEB.Ldr: 
  3. Get pointer to _LDR_DATA_TABLE_ENTRY.InLoadOrderLinks from _PEB_LDR_DATA.InLoadOrderModuleList. This is the first structure that belongs to ntdll.dll: 
  4. Enumerate _LDR_DATA_TABLE_ENTRY structures to find the one that belongs to kernel32.dll. 

This is a known technique widely used by malwareNSX detects this technique as well: 

NSX detection: presence of position independent code (shellcode).

Instead of comparing strings of function names characterbycharacter, the DLL implements string hashing algorithm, as shown below:  

The string hashing code.

This is a known API hashing algorithm used in packers, which was also seen in the Ursnif downloaders. This implies that the API hashing algorithm has been reused by both Ursnif and Dridex (code reuse by different malware writers is common.) NSX identifies this specific technique:  

NSX detects use of a known API hashing algorithm

NSX detection: makes use of a known API hashing algorithm.

After successfully finding the address of VirtualAlloc, the shellcode then runs the second stage: 

  1. Uses VirtualAlloc to allocate 0x3000 bytes of RWE memory; 
  2. Uses the REP MOVSB to copy itself into the allocated chunk of memory; 
  3. Calls JMP EAX to transfer execution to the copy of the code. 

It’s worth pausing on the obfuscated offset used to access the shellcode’s memory. Instead of having zero as an offset, like a benign code would normally have, an extremely large offset 0x658871 was subtracted from the EBX register before retrieving the addresses of the functions stored in shellcode’s memory.

A large offset is used to access shellcode’s memory.

The shellcode remembers that it has already been decrypted (a decryption flag has been set to True after the decryption in the first stage discussed above) before jumping into the second copy. Therefore, this time the code skips the decryption routine  

The shellcode has an embedded DLL, which is termed core DLL herein. With VirtualAlloc the shellcode allocates memory for the core DLL. VirtualProtect helps assign the correct memory access protection to each allocated memory region. LoadLibraryExA and GetProcAddress help find the functions to be imported. 

Core DLL 

The core DLL we dumped from memory has the following checksum (SHA1): 

 254b7ec984c3cdd479584c670822b9f65a31ce80 

The DLL might contain some trash bytes introduced by the memory dump process, which means the hash of the DLL shown above could be different from the one for the actual core DLL. 

The DLL imports only Sleep and OutputDebugStringA. The addresses of other functions are obtained dynamically via two functions: 

  1. Get module handle by hash; 
  2. Get function address by hash. 

These functions implement a standard function retrieval mechanism via the FS register, similar to the hash-based technique that was described previously. Whenever the DLL needs to call another function, it retrieves an address dynamically and then makes a corresponding API call without storing pointers anywhere.  

Calling API functions via the INT3-RET instruction pair.

In the example shown in the screenshot abovethe code in the DLL performs a call to RegEnumKeyA (a Windows API function for enumerating the subkeys of an open registry keyvia the INT3-RET instruction pair. More specifically, the function call process can be summarized as follows: 

  1. Retrieve the address of RegEnumKeyA and leave it in EAX; 
  2. Push the function parameters to stack; 
  3. Call INT 3 with RET instruction going afterwards; 
  4. Execute RET with return address pointing to RegEnumKeyA; 
  5. Transfer execution to RegEnumKeyA; return address points to instruction after RET (see the next screenshot). 

Return address points to instruction “test eax, eax” after RET instruction.

The trick here is that, prior to the function call, the DLL registers a VEH handler. The handler is called when the CPU raises an exception for INT 3. This is the prototype of a VEH handler:

The prototype of a VEH handler.

A handler is registered by calling AddVectoredExceptionHandler. The process performs the following steps if the exception handler gets called with STATUS_BREAKPOINT (0x80000003):

The multi-step process after the exception handler is called with STATUS_BREAKPOINT.

If one is not familiar with the technique, it can be confusing to see a lot of __debugbreak() across the code:

_debugbreak() is called in the code. _debugbreak() is an intrinsic command of the Microsoft compiler which directly translates to the INT 3 assembly instruction. Whenever an INT 3 instruction is called, it raises an exception.

In addition to the challenging static analysis described abovethe debugger pops up every time INT 3 executes. Suppressing the STATUS_BREAKPOINT exception can be done in debugger settings, and it doesn’t affect the debugging process: 

Debugger works well by ignoring the INT 3 exceptions

Debugger works well by ignoring the INT 3 exceptions.

Connection to C&C 

The DLL has four IP:port pairs of C&C servers, through which it iterates: 

C&C server and port pairs.

The DLL feeds IP address and port to InternetConnectW during connection attempt. After that, HttpOpenRequestW is called to initiate a POST request to the root folder of a web server. It provides the following flags to HttpOpenRequestW: 

  • INTERNET_FLAG_SECURE – use PCT/SSL if applicable (HTTP); 
  • INTERNET_FLAG_IGNORE_CERT_CN_INVALID – bad common name in X509 certificate; 
  • INTERNET_FLAG_IGNORE_CERT_DATE_INVALID – expired X509 certificate; 
  • INTERNET_FLAG_NO_AUTO_REDIRECT – don’t handle redirections automatically; 
  • INTERNET_FLAG_RELOAD – retrieve the original item; 
  • INTERNET_FLAG_NO_UI – no cookie popup. 

 Combining the flags of INTERNET_FLAG_SECURE and INTERNET_FLAG_IGNORE_CERT_XXX makes the connection secure and forces wininet.dll to ignore problems with server-side SSL certificates. 

As soon as the connection is established, the DLL calls HttpSendRequestW to transfer ~5 KB of encrypted data about the target environment:

Data collected from the victim’s machine is encrypted prior to exfiltration.

NSX detects the network traffic to the Dridex C&C server: 

NSX detects the network traffic to the Dridex C&C server

NSX detection: network traffic to a Dridex C&C server. 

Comparison with old Dridex samples 

To find out how the TTPs used by the samples from the recent campaign differ from Dridex samples seen in the past, we performed a comparison study.  

The table below lists some typical samples collected from previous Dridex campaigns: 

Old samples  SHA1 
XLS (2020.4)  c2c873baf147aa74843382a1e2dae33659bd49d5 
DLL (2020.4)  1c3bd35dd430c10a4dd2e188ebad12cc85b6fa63 

0554438b88e9463f6c8f040ba2370359ed42d80c (64-bit) 

DLL (2020.11)  1c6dfce105f9af04358901c46a50a8f419b621fb 
DLL (2020.12)  d240ad10acf8f15e8ac29fa8bee58796900bc55a 

Dridex samples from past campaigns.  

Based on our analysis, we make the following observations: 

  1. Samples from 2020.4 and 2020.11 are very different from the samples from the new wave, except for the 64-bit DLL (0554438b88e9463f6c8f040ba2370359ed42d80c)the VEH implementation in this older sample looks very similar to the approach described above. 
  2. The code obfuscation method of the DLL from 2020.12 is very similar to swwwbudo.dll from the new wave. 

detailed comparative analysis can be found in the Appendix. 

Conclusion 

In this report, we analyzed some samples from a recent wave of Dridex infections. As usual, the infection process starts with a spam campaign using social engineering emails with malicious attachments (XLSM in this case)The attack comprises two main stagesThe first stage is to leverage malicious VBA macros embedded in the XLSM file to download the initial Dridex payloadIn the second stage, the payload runs an embedded DLL from memory, which then exfiltrates data collected from the infected system. Our analysis demonstrates that the malware writer used various TTPs in the attack, including downloading a DLL payload via URLDownloadToFileA, using an API hashing algorithm to locate function names, and registering a VEH handler to complicate analysis. 

In addition, we also compared the new samples from the recent wave to some previous samples collected since April 2020. Our comparison analysis shows that tricks such as using the VEH handler and code obfuscation used in the new samples were also seen in some past samplesHowever, to a large extentthe new samples are very different from the old Dridex variants. 

VMware’s NSX Advanced Threat Prevention offering for the NSX Service-defined Firewall delivers the broadest set of threat detection capabilities that span network IDS/IPS and behavior-based network traffic analysis. This also includes VMware NSX Advanced Threat Analyzer™, a sandbox offering based on a full-system emulation technology that has visibility into every malware  action. VMware NSX is purpose-built to protect data center traffic with the industry’s highest fidelity insights into advanced threats. 

Bibliography

[1] Check Point, “March 2020’s Most Wanted Malware: Dridex Banking Trojan Ranks On Top Malware List For First Time,” March 2020. [Online]. Available: https://blog.checkpoint.com/2020/04/09/march-2020s-most-wanted-malware-dridex-banking-trojan-ranks-on-top-malware-list-for-first-time/.

Appendix: Comparison with old Dridex samples

XLS (2020.4) – sha1: c2c873baf147aa74843382a1e2dae33659bd49d5

This file also leverages social engineering techniques to display content related to a logistic invoice, but it looks completely different from the new XLSM sample.

The opening page of the XLS sample.

The embedded VBA macro code in the sample looks plain, and it doesn’t run regsvr32.dll:

Embedded VBA macro code in the XLS sample.

This implies that this sample is different from the new XLSM sample.

DLL (2020.4) – sha1: 1c3bd35dd430c10a4dd2e188ebad12cc85b6fa63

This DLL doesn’t have DllRegisterServer in the exports. DllEntry is not obfuscated, and it uses normal API calls such as GetWindowsDirectoryA, CreateSemaphoreA and GetCurrentDirectoryA.

The sample doesn’t look similar to the new swwwbudo.dll.

DLL (2020.4) – sha1: 0554438b88e9463f6c8f040ba2370359ed42d80c

This is a 64-bit DLL, unlike the new one, which is 32-bit. The code inside the DLL is not obfuscated. The DLL doesn’t have DllRegisterServer in exports. It imports 4 functions: AddVectoredExceptionHandler, GetComputerNameW, ExitProcess, GetExitCodeProcess. It is worth noting that the VEH handler used in the DLL is very similar to the one used by the new sample described above:

The VEH handler used in the DLL.

However, the DLL doesn’t implement the INT 3 technique described above. Though the sample doesn’t look similar to the new swwwbudo.dll, the code for VEH handler used to manipulate registers is identical to the code found in the new swwwbudo.dll, which led us to believe that both samples were coming from the same author.

DLL (2020.11) – sha1: 1c6dfce105f9af04358901c46a50a8f419b621fb

The DLL doesn’t have DllRegisterServer in exports. There are following imports:

List of imports used by the DLL.

The main function looks completely different from the new swwwbudo.dll:

The main function in the DLL.

The inner code also looks very different:

The inner code in the DLL.

The code difference shows that this sample is not similar to the new swwwbudo.dll.

DLL (2020.12) – sha1: d240ad10acf8f15e8ac29fa8bee58796900bc55a

There are two DllRegisterServer and DllUnregisterServer functions in the export table, similar to the new swwwbudo.dll. The code obfuscation also looks similar:

Obfuscated code in the DLL, which is similar to the new swwwbudo.dll.

There is a third function in the export table with a random name. The way strings are passed to function calls in this function is similar to the method implemented in the new swwwbudo.dll. This implies that the sample is very similar to the new swwwbudo.dll.