Felipe Duarte DominguesSeptember 24, 2020
Breaking Dridex and Creating a Vaccine
Insights from Appgate Labs
The technical details of Dridex were already covered in our blog post—Reverse Engineering Dridex and Automating IOC Extraction.
In this post we will focus on the mechanisms involving the Dridex API resolution method, and how we used that to create a vaccine for Dridex Loader.
In addition, at the end of the post we are providing a link to the published vaccine source code and a compiled version ready to be deployed, as we believe that can be used to detect computers infected with Dridex and enable organizations to prevent themselves against that threat.
1. PE Import Table
To understand how Dridex works we will first briefly describe how regular PE files manage their imports.
According to Microsoft PE Format specification³ each binary is built with the Import Directory Table. Along with other fields, each entry contains a pointer to the DLL name, and a pointer to an Import Lookup Table and an Import Address Table.
The Import Lookup Table is basically a list with pointers to the DLL exported function name (or the function ordinal number), and the Import Address Table follows the same order as the Import Lookup Table, containing pointers to the function Virtual Addresses.
For instance, let’s consider a PE file that, among other things, is compiled to show a dialog box, using a call to MessageBoxA from Windows user32.dll. In the binary Import Directory Table we would find the following structure after the program is loaded into the memory:
For the program to make a call to MessageBoxA it gets the pointer to the function by searching for user32.dll in the Import Directory Table, then looking for the function name in Import Lookup Table.
Lots of tools like the amazing Detect-It-Easy can be used to easily see the import table for any PE File.
As one can imagine, the import table of the malware is telling about its capabilities and is consistent among samples of the same family. In fact, there is a method to classify executables based on its import table, by creating a hash known as “imphash” and comparing it with other suspicious PE Files.
However, the Dridex payload doesn’t have an import table like other regular PE files, instead it dynamically resolves the imports using a custom technique, and before we dig into it, we need to touch on the Process Environment Block (PEB).
2. Process Environment Block (PEB)
The Process Environment Block is a data structure present in the Windows NT operating system family and is heavily used by the operating system. According to the Windows internals documentation, a lot of the fields in the PEB are for the operating system use only and are not documented at all.
In the offset 0x60 we have a pointer to PEB loader (or Ldr), which holds a double linked list structure where each entry on the list holds another structure that can be used to detect loaded DLLs in the process.
PEB Ldr structure
Amongst those fields, we can highlight the “FullDllName” string and the DLL “BaseAddress”. Those parameters guarantee that one can find a DLL loaded in the process by its name.
Whenever a process is started, all the DLLs in the import table are added to the PEB. Dynamically loaded DLLs (like using “LoadLibrary” API call) are also loaded in the process PEB.
3. Dridex API Loader
As mentioned in our previous article, Dridex API calls are dynamically resolved by the malware using a custom technique to avoid detection by APIs and to make reverse engineering more difficult.
To resolve API calls, the malware uses a function that receives two custom hashes (4 bytes). The first one is a hash for the DLL name and the second one is a hash for the API name. Using the IDA script we created, it’s possible to see which DLLs and APIs are being used throughout the code.
IDA Script for API Resolution.
Once the “resolver” function is called, the API hashes are checked against a linked list data structure the malware creates to store the DLL address by hash. Then, if the API is not already in the structure, the malware first checks if the DLL is present in memory (i.e. already loaded by the process), by checking the PEB for the DLL name, and generating the hash for each entry and comparing with the desired hash.
If the DLL was not already loaded by the process, the malware needs to load it from disk, and that is exactly where we can break Dridex.
First, the malware reads the “SysWOW64” directory (or “System32” if the system architecture is not x64). Then, for each DLL in the folder, a custom hash is created so the malware can compare it with the desired hash.
When found, the malware loads the DLL using the undocumented NTAPI LdrLoadDLL
Dridex LdrLoadDLL snippet
Summarizing, the complete flow for API resolution Is the following.
Workflow of the API Resolver Function of Dridex.
4. Breaking Dridex (Vaccine)
Our vaccine attacks the last branch in the flow, when the DLL is neither cached or available in the PEB, so the malware needs to load it from the SysWOW64 directory. In brief, we generated a CRC32 collision for one of the DLLs that Dridex needs to load from disk, that way, the malware loads our DLL instead of loading the one that it really needs.
Not all DLLs are loaded from disk, as some critical DLLs like kernel32.dll are pre-loaded in the process. In Dridex’s case, even though the final payload’s import table is non-existent, the payload is injected in the loader process and has the following import table.
Packed Loader Import Table
The following DLLs are present in the import table and will be available in memory when the malware starts its execution:
That means every additional DLL the malware requires it will have to be loaded from disk. By attaching a breakpoint at the LdrLoadDLL call, we were able to map the first DLL that’s effectively loaded from the disk.
Dridex loading Shell32.dll
With this in mind, we can brute force a CRC32 collision with the string “SHELL32.DLL” (as the DLL name is used in uppercase in the malware algorithm). The requirements for our collision are that the string needs to be found first by the “FindFirstFileA” API than our target DLL. After some time, we quickly found four different candidates for our vaccine.
As the XOR value is applied to the value after being encoded, we can safely conclude this collision will work regardless of the hard-coded key, so the vaccine should apply to any Dridex sample.
Now that we have a DLL name that will be loaded by the malware, we need to create the DLL itself. We want the malware to exit when calling our functions, so we added a message box that alerts when the vaccine is loaded, exiting the process and preventing Dridex to be executed correctly.
Snippet of Vaccine’s Source Code
As we are expecting the malware to call our stub, our DLL export table needs to be the same as SHELL32.DLL export table, so we are adding all the API names in our exports.def file.
Fake Exports from “shell32.dll” Pointing to our Custom Function.
Now we just need to add our compiled DLL to SysWOW64 directory so Dridex can load it instead of “shell32.dll”.
Debugging the malware, we can see it loading our vaccine instead of the shell32.dll
Dridex loading the Vaccine DLL.
And Voilà! The DLL makes the Dridex loader exit when trying to evoke CommandLineToArgv function from Shell32.dll, preventing the malware to be correctly executed.
This video is just a POC, please keep in mind Dridex does incorporate sandbox detection techniques and therefore it can sleep for an extended period of time before starting the main code (and therefore triggering the vaccine a little later).
The vaccine was tested in some samples from 2019/2020 campaigns, but keep in mind that depending on how the loader was compiled and how it's executed, the DLL that needs to be simulated might not be "shell32.dll". However, the same methodology can be used to compile a new vaccine for other DLLs that the loader is fetching from disk.
In this post we show how Dridex loads APIs from memory and from disk, and the methodology used to break the malware and develop a vaccine.
This DLL can be used by companies to stop Dridex infections and to prevent systems from being infected, and following our methodology, every other DLL that’s loaded from the disk can be used to vaccinate and stop Dridex from compromising the system.
Advanced users can extend our vaccine source code to deploy more actions, like adding memory dump capabilities or taking more severe actions to block Dridex infections, we hope that this can help organizations and the cyber security community altogether.
Packed Dridex Loader
Unpacked Dridex Loader
Vaccine Source Code
Dridex Analysis Scripts
Vaccine POC Files
Warning! Real malware in the zip file for testing purposes, use carefully (password: infected)