Web injections, a favored technique employed by various banking trojans, have been a persistent threat in the realm of cyberattacks. These malicious injections enable cyber criminals to manipulate data exchanges between users and web browsers, potentially compromising sensitive information.
Since the beginning of 2023, we have seen over 50,000 infected user sessions where these injections were used by attackers, indicating the scale of threat activity, across more than 40 banks that were affected by this malware campaign across North America, South America, Europe and Japan.
In this blog post, we will delve into an analysis of the web injection utilized in the recent campaign, its evasive techniques, code flow, targets and the methods employed to achieve them.
A dangerous new campaign
Our analysis indicates that in this new campaign, threat actors’ intention with the web injection module is likely to compromise popular banking applications and, once the malware is installed, intercept the users’ credentials in order to then access and likely monetize their banking information.
Our data shows that threat actors purchased malicious domains in December 2022 and began executing their campaigns shortly after. Since early 2023, we’ve seen multiple sessions communicating with those domains, which remain active as of this blog’s publication.
Upon examining the injection, we discovered that the JS script is targeting a specific page structure common across multiple banks. When the requested resource contains a certain keyword and a login button with a specific ID is present, new malicious content is injected.
Credential theft is executed by adding event listeners to this button, with an option to steal a one-time password (OTP) token with it.
This web injection doesn’t target banks with different login pages, but it does send data about the infected machine to the server and can easily be modified to target other banks.
In the past, we observed malware that directly injected the code into the compromised web page. However, in this campaign, the malicious script is an external resource hosted on the attacker’s server. It is retrieved by injecting a script tag into the head element of the page’s HTML document, with the src attribute set to the malicious domain.
During our investigation, we observed that the malware initiates data exfiltration upon the initial retrieval of the script. It appends information, such as the bot ID and different configuration flags, as query parameters. The computer’s name is usually used as the bot ID, which is information that isn’t available through the browser. It indicates that the infection has already occurred at the operating system level by other malware components, before injecting content into the browser session.
Figure 1: The initial obfuscated GET request fetching the script
The retrieved script is intentionally obfuscated and returned as a single line of code, which includes both the encoded script string and a small decoding script.
To conceal its malicious content, a large string is added at the beginning and end of the decoder code. The encoded string is then passed to a function builder within an anonymous function and promptly executed, which also initiates the execution of the malicious script.
Figure 2: Encoded string passed to de-obfuscation function, followed by removal of artifacts used for decoding the script. Two long strings were added to the beginning and end of the string to make it harder to find the code manually.
Scroll to view full table
Figure 3: Searching for a security product’s keyword and doing nothing if it’s found
All of these actions are performed to help conceal the presence of the malware.
Dynamic web injection
The script’s behavior is highly dynamic, continuously querying both the command and control (C2) server and the current page structure and adjusting its flow based on the information obtained.
The structure is similar to a client-server architecture, where the script maintains a continuous flow of updates to the server while requesting further instructions.
To keep a record of its actions, the script sends a request to the server, logging pertinent information, such as the originating function, success or failure status and updates on various flags indicating the current state.
Figure 4: Every a.V function call sends an update to the server about what function it was sent from and the current state of different flags
Figure 5: An example of multiple traffic logs, sent within a few seconds of the script running
The script relies on receiving a specific response from the server, which determines the type of injection it should execute, if any. This type of communication greatly enhances the resilience of the web injection.
For instance, it enables the injection to patiently wait for a particular element to load, provide the server with updates regarding the presence of the injected OTP field, retry specific steps (such as injecting an SMS submission overlay) or redirect to the login page before displaying an alert indicating that the bank is temporarily unavailable.
The server keeps identifying the device by the bot ID, so even if the client tries to refresh or load the page again, the injection can continue from its previously executed step.
If the server does not respond, the injection process will not proceed. Hence, for this injection to be effective, the server must remain online.
The script is executed within an anonymous function, creating an object that encompasses various fields and helper functions for its usage. Within the object, the injection holds the initial configuration with fields such as bot ID, phone number and password. These fields are initially empty but are populated with relevant values as the run progresses.
Additionally, the object includes details such as the C2 server’s domain and requests path, default values for query parameters and default settings for various flags such as “send SMS” and “send token.” These default values can be modified later based on the server’s response, allowing for dynamic adjustments during runtime.
Following the initial configuration, the script sends a request to the server providing initial details, and assigns a callback to handle the response, allowing the execution to proceed.
Subsequently, the script proceeds to remove itself from the DOM tree, enhancing its ability to conceal its actions. From that stage onward, all subsequent script actions are asynchronous, saved inside event handlers and dependent on the responses received from the server.
The steps the script should perform are mostly based on an “mlink” flag received from the server on the initial request. The next step of the injection is to check for the specific login button of the targeted bank. The results of the element query are sent, and the “mlink” state changes accordingly.
Following that, a new function runs asynchronously on an interval, looking for the login button and assigning a malicious event listener if found. The listener waits for a click event, collects the login credentials and handles it based on the current configuration.
For example, if the “collect token” flag is on, but the script can’t find the two-factor authentication (2FA) token input field, it just stops the current run and does nothing. If the token is found or wasn’t looked for in the first place, the script sends all the gathered information to the server.
After that, it can inject a “loading” bar to the page (opengif function), cancel the original login action or allow the client to continue with the actions by removing the handler and “clicking” it again on behalf of the user (by dispatching another “click” event).
Figure 6: The event listener prevents the default action of the login button or deletes itself and dispatches another click event based on the outcome of function G
Figure 7: This section of function G reads credentials and tries to read the injected token field value, depending on the current state of the page and flags
Potential operational states
Returning to the “synchronous” part of the callback, let’s examine some potential operational states and the corresponding actions taken.
When the “mlink” value is 2, the script injects a div that prompts the user to choose a phone number for 2FA. Once the user selects a phone number, a login attempt can be executed using the stolen credentials, and a valid token is sent to the victim from the bank.
Figure 8: Prompting a phone number for two-factor authentication
The following state is when “mlink” is equal to three, where the input field for the OTP token is injected. In this manner, DanaBot deceives the victim into providing the token, effectively bypassing the 2FA protection mechanism.
Figure 9: Prompting for the received token
When the “mlink” value is four, the script introduces an error message on the login page, indicating that online banking services will be unavailable for a duration of 12 hours. This tactic aims to discourage the victim from attempting to access their account, providing the threat actor with an opportunity to perform uninterrupted actions.
Figure 10: An error message that banking services are unavailable for 12 hours, giving the threat actor ample time to work
When the “mlink” value is 5, the script injects a page loading overlay that mimics the appearance of the original website’s loading animation. A timeout is set before transitioning to a different state, effectively “completing” the page load process.
Figure 11: An injected loading screen, an exact duplicate of the original loading screen
When the value of “mlink” is six, a “clean up” flow is initiated, removing any injected content from the page. This value serves as the default assignment for the flag in case no specific instruction is received from the server.
2FA choose phone number prompt
2FA insert token prompt
Online banking unavailable error
Page loading overlay
Scroll to view full table
In total, there are nine distinct potential values for the “mlink” variable, each corresponding to different states and behaviors. Additionally, multiple flags activate various actions and result in different data being sent back to the server. Combining these “mlink” values and flags allows for a diverse range of actions and data exchanges between the script and the server.
IBM has observed widespread activity from this malware campaign affecting banking applications of numerous financial institutions across North America, South America, Europe and Japan. This sophisticated threat showcases advanced capabilities, particularly in executing man-in-the-browser attacks with its dynamic communication, web injection methods and the ability to adapt based on server instructions and current page state. The malware represents a significant danger to the security of financial institutions and their customers.
Users should practice vigilance when using banking apps. This includes contacting their bank to report potentially suspicious activity on their accounts, not downloading software from unknown sources and following best practices for password hygiene and email security hygiene.
Individuals and organizations must also remain vigilant, implement robust security measures and stay informed about emerging malware to effectively counteract these threats.
IBM Security Trusteer helps you to detect fraud, authenticate users and establish identity trust across the omnichannel customer journey. More than 500 leading organizations rely on Trusteer to help secure their customers’ digital journeys and support business growth.