IBM X-Force Research proactively researches financial cybercrime threats on an ongoing basis. In recent activity, X-Force studied some changes made to the IcedID banking Trojan that help the malware act more stealthily on infected devices.
Banking Trojans and the organized crime gangs that operate them have risen in the past decade to become one of the most prominent online threats affecting the financial sector and online service providers at large. These highly evolving malicious programs are often modular and sophisticated and, in most cases, supported by an in-house developer that keeps updating code to evade security controls.
It’s not always easy to keep up with threat evolution in-house. These details about the updated injection method can help security professionals manage risk to their organization and detect updated versions of IcedID that use this injection tactic.
No New Threads to See Here
Before we delve into the reversing of IcedID’s modified injection, let’s summarize it in a few words.
Prior to the Change
Prior to the recent modifications, IcedID would write its shellcode to targeted operating system (OS) processes via ZwWriteVirtualMemory. IcedID used this tactic in cases where it would inject code into either OS processes or when hooking browser processes.
After the Change
IcedID separated the tactics for OS process injection and browser processes. When the malware injects into an OS process, it creates a process in suspend state. It then hooks the RtlExitUserProcess, which is very often called to terminate userland processes. IcedID’s developer counts on legitimate processes to call on this now-infected process, which will make it jump to the shellcode and run it via a seemingly legitimate flow of events that would not get flagged by most controls.
When IcedID injects to a browser process, it hooks the NtWaitForSingleObject function, again by using ZwWriteVirtualMemory.
A Closer Look Under the Hood
To demonstrate how the threat works, let’s begin when the IcedID payload launches its execution routine on a target device. The malware starts by creating a svchost.exe process instance in suspend state and injects its shellcode into that process. After the injection, svchost.exe resumes and the payload terminates itself.
The newly malicious svchost.exe process writes the payload to the %ProgramData% folder with a globally unique identifier (GUID)-like folder naming convention (the GUID is a 128-bit integer number used to identify resources; the term GUID is generally used by developers working with Microsoft technologies). It uses [a-z] [A-Z]{0-9} and the payload, and a name convention of [a-z] {8-9}.exe. The value and file name are generated differently on each operating system running IcedID. However, the name will be the same upon subsequent executions on the same host.
This process also creates a scheduled task so that it will run every time the system reboots to allow IcedID to maintain persistence on the infected device.
The malicious svchost.exe creates three additional svchost.exe subprocesses and injects its shellcode into each one of them. We’ll explain the injection process in further detail in the following section.
Figure 1: Three svchost.exe instances injected with IcedID shellcode.
At this point, with four svchost.exe processes in total, these instances will remain active until the device is shut down at some point. After a reboot, these processes will be tasked with monitoring the OS for the launch of a browser application. The malware will then attack the browser’s process as well and inject it with shellcode.
Two Injection Methods
IcedID injects its shellcode into browser processes with the goal of eventually hooking the process and allowing the malware to begin intercepting and manipulating the victim’s online activity.
The malware uses API hooking to inject and execute its shellcode via different legitimate threads running on the victim’s device. Let’s look at that tactic more closely:
- IcedID allocates memory in a target process using the NtAllocateVirtualMemory function.
- The malware writes the shellcode into the target process without suspending that process. To do that, IcedID uses the ZwWriteVirtualMemory or NtWriteVirtualMemory functions.
- Next, it hooks a frequently called API that’s commonly used by the target process.
There are two different API hooking scenarios we observed IcedID using:
- Injecting into a web-browser via svchost.exe; and
- Injecting into svchost.exe via its parent process.
The hooked API is called by the targeted process and the shellcode is thus executed.
To wipe its traces, the shellcode fixes the patched library’s function that was used to execute it so that tools that detect hooks and rootkits won’t show any trails of that patch. This injection technique can evade security mechanisms that would otherwise detect suspicious API calls, such as the createRemoteThread (CRT), asynchronous procedure calls (APC) and other common malware tactics.
Remotely setting up a hook in a target process without suspending the target process first can cause unexpected behavior. This can easily result in a crash since two threads can execute the code of the API at the same time.
Deeper Into the Browser Process Injection
The first injection tactic is from a svchost.exe process to a web browser. To begin, the malware needs a detection mechanism to identify that the infected user has launched a browser application. To do that, IcedID takes a snapshot of all the running processes and scans them for browser processes.
Figure 2: IcedID scanning for running browser processes.
The scan creates an infinite loop that calls on IcedID’s function main_func. One of that function’s parameters is a pointer to another malware function: check_target_process. Viewing the details of check_target_process reveals that it gets the process ID (PID) of the svchost.exe it runs inside. Next, it calls NtQuerySystemInformation to fetch a list of all processes currently running on the device.
Figure 3: IcedID fetching process list to scan for open browser applications.
This scheme keeps running in a loop over all the operating system’s processes to allow the malware to detect browser activity. When it encounters a potential browser process, IcedID checks it via the check_target_process.
This last function uses two parameters to check whether a process is of interest or not:
- The target process’ ID; and
- The target process’ name.
The function encodes the process’ name and compares it with hardcoded strings the developer created, representing the names of browsers IcedID can inject into.
Figure 4: IcedID checking for browser processes to inject its shellcode into.
To compare the process’ name to the preconfigured, hardcoded list, the malware calls the resolveProcessName function. That function receives the target process’ name as a parameter, decodes it and creates a hash of the result. That hash is what’s being compared to a list of precomputed hashes that translate into iexplore.exe, firefox.exe and chrome.exe.
The hash codes for the targeted web browsers are:
- Firefox.exe — 1EACD83D;
- Iexplore.exe — 0D31A30C7; and
- Chrome.exe — 0FA7442ED.
Figure 5: IcedID checking for popular browser processes to inject shellcode into.
If there’s no match, the function returns to its scanning routine, looking for other browser processes.
The next step for the malware is to call decode_target_process_event_name. This function receives the targeted process’ PID as a parameter and generates an event name for the web browser process that’s about to be injected with IcedID’s shellcode.
This part of the injection mechanism is in place to compute the event name that the shellcode will create after the injection process so that it indicates to the malware that a specific web browser’s process has already been injected.
Now, after a match has been found and the validation of the target process is complete, the actual injection process begins by calling the function inject_target_process. Here, again, the parameter is the target process’ PID.
Figure 6: IcedID validates and begins injection of shellcode into browser process.
The inject_target_process function receives a handle to the target process using the OpenProcess API. It then checks whether the process is either 32-bit or 64-bit to prepare the correct code version for injection.
Next up, the function that’s responsible for the injection starts by allocating memory in the target process. We can see the memory allocation call and then writing of the shellcode into the target process using three Windows API functions:
- NtAllocateVirtualMemory;
- ZwProtectVirtualMemory; and
- ZwWriteVirtualMemory.
Figure 7: IcedID injects shellcode into targeted browser process.
After it writes the shellcode into the process, IcedID calls to the next function for patching, depending on which architecture applies:
- patch_NtWaitForSingleObject_32bit; or
- patch_NtWaitForSingleObject_64bit
To patch the NtWaitForSingleObject function and make it jump to the shellcode the next time it is called, IcedID begins by modifying the page protection status of that function using ZwProtectVirtualMemory. Then, the malware applies the hook using ZwWriteVirtualMemory, and ends with switching the protection flag back to its normal permission state. From this point on, subsequent calls to the NtWaitForSingleObject function will jump to the malware’s injected shellcode and execute it.
The right-hand part of the image below shows the malware hooking the NtWaitForSingleObject function on the target process, in this case firefox.exe. The left part of the image shows the targeted process with the newly hooked function.
Figure 8: Before and after IcedID’s browser process hooking.
The hooking process does not stop here; it is a perpetual loop that continues for as long as the device is infected with IcedID. If the user opts to launch a different browser, the malware will hook it as well and enable itself to intercept and interfere with the victim’s online banking activities on any of the three browsers it can hook.
A Closer Look at IcedID’s Shellcode
Let’s take a closer look at the shellcode that IcedID injects into browser processes. At the starting point, the newly hooked browser process calls on the NtWaitForSingleObject function. The injected shellcode runs and fixes the hook to make sure the shellcode leading to the main function will only be called once and not loop back repeatedly, especially since NtWaitForSingleObject is a common API and will be called many times. This also wipes the traces of the hook, which can make it harder for researchers to find.
The image below shows the starting address of the shellcode that is executed when the call for NtWaitForSingleObject is launched by a legitimate thread from the hooked process.
Figure 9: Starting address of the shellcode executed when the call for NtWaitForSingleObject is launched.
The function fix_NtWaitForSingleObject will uninstall the hook from NtWaitForSingleObject in the browser process, reinstating the original code that was there before the malware hooked it. Next, the shellcode will install the hooks inside the web browser’s process and then call NtWaitForSingleObject to continue executing the browser’s process.
Figure 10: IcedID restores the NtWaitForSingleObject function code to its original content.
Examining the shellcode in WinDbg, a commonly used Windows debugger, we can see that NtWaitForSingleObject has indeed been restored to its original code.
One of the browser functions hooked by IcedID is Ws2_32:connect. This function redirects the victim’s browsing traffic to the now-malicious svchost.exe process, allowing the malware to identify data the attacker wishes to receive, such as credentials, payment card details, etc. The data is exfiltrated to the attacker’s command-and-control (C&C) sever.
We can see that the malware function hook_connect gets the same three parameters as the connect function. First, it checks for the address family, browser type and port number and uses the information to determine whether to call the original connect function and exit or continue with the hook.
In the next step, the malware calls the original connect function with a new sockaddr object and new parameters: 127.0.0.1 as the IP address, AF_INET as the family address and a new calculated port number. The traffic is ultimately redirected to the malicious svchost.exe process.
Figure 11: Reversing the hook installed on the connect function in the web browser.
This shellcode is what enables IcedID to gain some control and insight into what the victim is doing online and allows the attacker to interfere with that activity.
Deeper Into the OS Process Injection
The second injection method IcedID uses is designed for injecting into processes of the operating system, not the browser.
This second tactic relies on the familiar method of creating a new process with the CREATE_SUSPEND flag and then hooking the RtlExitUserProcess API. Once the new process starts executing, it will fix the function back to its original state.
This injection method is an interaction between the different svchost.exe processes and the malware’s payload.
Figure 12: Before and after IcedID’s hooks on targeted OS processes.
The left-hand part of the image above shows the svchost.exe process where a hook was installed on the RtlExitUserProcess API, making it jump to the shellcode the next time it is called by a legitimate thread from the svchost.exe process.
We can also see the start of the shellcode in the Interactive Disassembler (IDA), and we can see that the first function being called fixes the hook that was installed on the RtlExitUserProcess API.
Figure 13: Starting address for the shellcode that’s executed upon a call to RtlExitUserProcess.
Examining the code of the RtlExitUserProcess API after the removal of the malware’s hook, we can see that the code was restored to its original state:
Figure 14: RtlExitUserProcess code restored to original content after uninstalling the hook.
This sums up IcedID’s split injection tactics. It appears that the malware’s operators are getting advice from other coders, likely those working on the TrickBot project. These modifications can make the malware’s activity stealthier, yet effective.
IcedID Keeps It Moving
IcedID emerged in 2017 as a modular banking Trojan with advanced capabilities to automate fraudulent transactions and control user devices to take over their bank accounts. Since its initial analysis, it has been evolving gradually over time and showing explicit collaboration with the TrickBot Trojan by ways of common distribution and feature similarity.
In August 2018, our researchers noted that IcedID had been upgraded to behave in a similar way to the TrickBot Trojan in terms of its deployment. The binary file had been modified to become smaller and no longer featured embedded modules. The malware’s plugins were being fetched and loaded on demand from a remote server after the Trojan was installed on infected devices. These changes made IcedID stealthier, modular and also more similar to TrickBot.
In addition to its increased stealth level, IcedID started encrypting its binary file by obfuscating file names associated with its deployment on infected devices. Another TrickBot-inspired modification saw IcedID add event objects, which are a means to coordinate multiple threads of execution in Windows-based operating systems. IcedID began using named events to synchronize the execution between its core binary and the modular plugins it could fetch from its control server.
Although malware authors do sometimes copy from one another, these modifications were not coincidental. Even if we only looked at the fact that TrickBot and IcedID fetch one another into infected devices, that would be indication enough that these Trojans are operated by teams that work together.
X-Force data from 2018 placed IcedID in the top five most active banking Trojans on a global scale. The malware’s operators have links to other key cybergangs in the threat arena, and they have been using IcedID to actively target the customers of major banks, payment card providers, e-commerce companies and cryptocurrency platforms. X-Force researchers expect this malware to continue targeting banks and payment platforms as we move into the second quarter of 2019.
Figure 15: Top most active banking Trojan families in 2019 (source: IBM Trusteer).
Malware Researcher – Trusteer IBM
Principal Consultant, X-Force Cyber Crisis Management, IBM