Introduction
Chaes is a banking trojan that operates solely in Brazil
and was first reported in November 2020
by Cybereason. In Q4 2021
, Avast observed an increase in Chaes’ activities, with infection attempts detected from more than 66,605
of our Brazilian
customers. In our investigation, we found the malware is distributed through many compromised websites, including highly credible sites. Overall, Avast has found Chaes’ artifacts in 800+
websites. More than 700
of them contain Brazilian TLDs. All compromised websites are WordPress
sites, which leads us to speculate that the attack vector could be exploitation of vulnerabilities in WordPress CMS
. However, we are unable to perform forensics to confirm this theory. We immediately shared our findings with the Brazilian CERT
(BR Cert) with the hope of preventing Chaes from spreading. By the time of this publication, Chaes’ artifacts still remain on some of the websites we observed.
Chaes is characterized by the multiple-stage delivery that utilizes scripting frameworks such as JScript
, Python
, and NodeJS
, binaries written in Delphi
, and malicious Google Chrome extensions
. The ultimate goal of Chaes is to steal credentials stored in Chrome and intercept logins of popular banking websites in Brazil
.
In this posting, we present the results of our analysis of the Chaes samples we found in Q4 2021
. Future updates on the latest campaign will be shared via Twitter or a later post.
Infection Scheme
When someone reaches a website compromised by Chaes, they are presented with the below pop-up asking users to install the Java Runtime
application:
If the user follows the instructions, they will download a malicious installer that poses as a legitimate Java Installer
. As shown below, the fake installer closely imitates the legitimate Brazilian Portuguese Java installer
in terms of appearance and behavior.
Once the installation begins, the user’s system is compromised. After a few minutes, all web credentials, history, user profiles stored by Chrome will be sent to attackers. Users may experience Google Chrome getting closed and restarted automatically. This indicates any future interactions with the following Brazilian banking websites will be monitored and intercepted:
mercadobitcoin.com.br
mercadopago.com.[ar|br]
mercadolivre.com.br
lojaintegrada.com.br
Technical Analysis
Infected websites
Upon inspecting the HTML code of the compromised websites, we found the malicious script inserted as shown below:
In this case, the V=28
likely represents the version number. We also found a URL with other versions as well:
https://is[.]gd/EnjN1x?V=31
https://is[.]gd/oYk9ielu?D=30
https://is[.]gd/Lg5g13?V=29
https://is[.]gd/WRxGba?V=27
https://is[.]gd/3d5eWS?V=26
The script creates an HTML element that stays on top of the page with “Java Runtime Download” lure. This element references an image from a suspicious URL
https://sys-dmt[.]net/index.php?D\
https://dmt-sys[.]net/
and an on-click download of a Microsoft Installer
from
https://bkwot3kuf[.]com/wL38HvYBiOl/index.php?get
orhttps://f84f305c[.]com/aL39HvYB4/index.php?get
orhttps://dragaobrasileiro[.]com.br/wp-content/themes/getCorsFile.php
Microsoft Installer
The flowchart below shows the infection chain following the malicious installer execution.
The installer contains 3 malicious JS files install.js
, sched.js
, and sucesso.js
renamed to Binary._
as shown above. Each of them handles a different task, but all are capable of reporting the progress to the specified CnC
:
install.js
The purpose of this script is to download and execute a setup script called runScript
that will prepare the proper Python
environment for the next stage loader. After making an HTTP request to a hardcoded domain, the obfuscated runScript
is downloaded and then executed to perform the following tasks:
- Check for Internet connection (using
google.com
) - Create
%APPDATA%\\<pseudo-random folder name>\\extensions
folder - Download password-protected archives such as
python32.rar/python64.rar
andunrar.exe
to that extensions folder - Write the path of the newly created extensions folder to
HKEY_CURRENT_USER\\Software\\Python\\Config\\Path
- Performs some basic system profiling
- Execute
unrar.exe
command with the password specified as an argument to unpackpython32.rar/python64.rar
- Connect to
C2
and download 32bit and 64bit__init__.py
scripts along with 2 encrypted payloads. Each payload has a pseudo-random name.
sched.js
The purpose of this script is to set up persistence and guarantee the execution of __init__.py
downloaded by runScript
from the previous step. Sched.js
accomplishes this by creating a Scheduled Task
as its primary means and creating a Startup link
as its backup means. Ultimately, they are both able to maintain the after-reboot execution of the following command:
...\\<python32|python64>\\pythonw.exe __init__.py /m
sucesso.js
This script reports to CnC
that the initial installation on the victim’s computer has succeeded and is ready for the next stage
Python Loader Chain
The Scheduled Task
created by sched.js
eventually starts __init__.py
which initiates the Python
in-memory loading chain. The loading chain involves many layers of Python scripts, JS scripts, shellcode, Delphi DLLs, and .NET PE which we will break down in this section. Impressively, the final payload is executed within __init__.py
process (PID 2416
and 4160
) as shown below:
__init__.py
The __init__.py
xor decrypts and decompresses the pseudo-random filename downloaded by runScript.js
into another Python script. The new Python script contains 2 embedded payloads: image
and shellcode
in encrypted form. Image
represents the Chaes loader module called chaes_vy.dll
while shellcode
is an in-memory PE loader. We found this particular loader shellcode reappearing many times in the later stages of Chaes. Running the shellcode using CreateThread
API with proper parameters pointing to chaes_vy.dll
, the Python script eventually loads chaes_vy.dll
into memory:
Chaes_vy.dll
Chaes_vy is a Delphi
module that loads an embedded .NET executable
that in turn runs 2 JavaScripts
: script.js
and engine.js
. These two scripts hold the core functionalities of the Chaes_vy module.
script.js
This script acts as the interface between .NET framework and JScript framework, providing the necessary utilities to execute any scripts and modules that engine.js
downloads in the future. By default, script.js
will try to retrieve the paths to payloads specified in the argument of __init__.py
. If nothing is found it will execute engine.js
engine.js
This script employs 2 methods of retrieving a JS payload: getBSCode()
and getDWCode()
both are called every 6 minutes.
GetBSCode
is the primary method, also the only one we are able to observe serving payload. The encrypted payload is hidden as commented-out code inside the HTML page of a Blogspot which is shown below. Without being infected by Chaes, this site is completely harmless.
On the other hand, when executed, engine.js
will parse the string starting from <div id=\”rss-feed\”> which is the marker of the encrypted payload:
After decrypting this text using AES
with a hardcoded key, instructions.js
is retrieved and executed. This script is in charge of downloading and executing Chaes’ malicious Chrome “extensions”. More info about instructions.js
is provided in the next section.
getDWCode
is the secondary method of retrieving instruction.js
. It employs a DGA
approach to find an active domain weekly when getBSCode
fails. Since we are not able to observe this method being used successfully in the wild, we have no analysis to present here. However, you can check out the full algorithm posted on our Github.
Instructions.js
Instructions.js
is the last stage of the delivery chain. Nothing up to this point has gained the attacker any true benefits. It is the job of instructions.js
to download and install all the malicious extensions that will take advantage of the Chrome browser and harm the infected users. The address of all payloads are hardcoded as shown below:
The extensions are separated into password-protected archives vs encrypted binaries. The non-compressed payloads are PE files that can be run independently while compressed ones add necessary NodeJS packages for the extension to run. Below is the example of chremows63_64
archive contents:
All the binaries with dll_filename
argument such as chromeows2.bin
are encrypted, including the ones inside the RAR archive. The decryption algorithm is located inside script.js
as we mentioned in the previous section. To decrypt and run each binary, Chaes needs to call __init__.py
with the file path specified as an argument.
The extension installation can be simplified into the following steps:
- An HTTP Request (
aws/isChremoReset.php
) is sent to check if Google Chrome from a particularuid
has been hooked. If not, Chrome and NodeJS will be closed. More information aboutuid
in the “Online” section below. - The download request is constructed based on 3 scenarios: 32bit, 64bit, and no Google Chrome found. Each scenario will contain suitable versions of the extensions and their download links.
- The extension is downloaded. The compressed payload will be unpacked properly.
- A
hosts
file is created for the newly downloaded module. Inside the file is the CnC randomly picked from the following pool:
Each extension will use the address specified in hosts
for CnC communication
- Launch each extension through
python.exe __init__.py
with proper arguments as shown below
Extensions
Online
online.dll
is a short-lived Delphi
module that is executed by instruction.js
before other modules are deployed. Its main purpose is to fingerprint the victim by generating a uid
which is a concatenation of drive C: VolumeSerialNumber
, UserName
, and Computername
. The uid
is written to a register key SOFTWARE\\Python\\Config\uid
before being included inside the beaconing message.
This registry key is also where instruction.js
previously gets the uid
asking CnC if the victim’s Chrome has been hooked. The first time instruction.js
gets launched this registry has not been created yet, therefore the Chrome process is always killed.
Online.dll
retrieves the CnC server specified in the hosts
file and performs the beaconing request /aws/newClient.php
, sending the victim’s uid
and basic system information.
Mtps4 (MultiTela Pascal)
Module mtps4
is a backdoor written in Delphi
. Its main purpose is to connect to CnC
and wait for a responding PascalScript
to execute. Similar to the previous module, CnC is retrieved from the hosts
file. Mtps4
sends a POST
request to the server with a hardcoded User-Agent
containing uid
and command type. It currently supports 2 commands: start
and reset
. If the reset
command is responded with ‘(* SCRIPT OK *)
’, it will terminate the process.
Start
command is a bit more interesting.
As a reply to this command, it expects to receive a PascalScript
code containing a comment ‘(* SCRIPT OK *
)’.
mtps4
is compiled with https://github.com/remobjects/pascalscript to support PascalScript. Before running, the script they create a window that copies the screen and covers the entire desktop to avoid raising suspicion when performing malicious tasks on the system.
Unfortunately during the investigation, we couldn’t get hold of the actual script from the CnC.
Chrolog (ChromeLog)
Chrolog
is a Google Chrome Password Stealer written in Delphi. Although it is listed as an extension, Chrolog
is an independent tool that extracts user personal data out of the Chrome database and exfiltrates them through HTTP. The CnC server is also retrieved from the hosts
file previously created by instruction.js
.
HTTP Request | Data Exfiltration |
/aws/newUpload.php | Cookies, Web Data, and Login Data (encrypted) |
/aws/newMasterKey.php | Chrome Master Key used to decrypt Login Data |
/aws/newProfileImage.php | Profile Image URL collected from last_downloaded_gaia_picture_url_with_size attribute inside Local State |
/aws/newPersonalData.php | Username, fullname, gaia_name |
/aws/bulkNewLogin.php | All Login Data is decrypted and added to local.sql database. Then the corresponding section of the database is exfiltrated |
/aws/bulkNewUrl.php | History is added to local.sql database. Then the corresponding section of the database is exfiltrated |
/aws/bulkNewAdditionalData.php | Web Data is written to local.sql database. Then the corresponding section of the database is exfiltrated |
/aws/bulkNewProcess.php | All running processes are collected and written to local.sql database. Then the corresponding section of the database is exfiltrated |
Chronodx (Chrome Noder)
The chronodx
extension package can be separated into 2 parts: a loader called Chronod2.dll
and a JavaScript banking trojan called index_chronodx2.js
. First, Chronod2.dll
performs an HTTP request /dsa/chronod/index_chronodx2.js to retrieve index_chronodx2.js
. If successful, Chronod2.dll
will run silently in the background until it detects the Chrome browser opened by the user. When that happens, it will close the browser and reopen its own instance of Chrome along with index_chronodx2.js
being run from the node.exe
process.
Index_chronodx2.js
utilizes puppeteer-core, a NodeJS framework that provides APIs to control Chrome browser, for malicious purposes. Index_chronodx2.js
implements many features to intercept popular banking websites in Brazil including
bancobrasil.com.br/aapf
bancodobrasil.com.br/aapf
bb.com.br/aapf
mercadopago.com/…/card_tokens/
mercadopago.com/enter-pass/
mercadolivre.com/enter-pass/
lojaintegrada.com.br/public/login/
mercadobitcoin.com.br
Upon visiting any of the above websites, index_chronodx2.js will start collecting the victim’s banking info and send it to the attacker through a set of HTTP commands. The CnC server is stored in the hosts
file, but when it is not found in the system, a hardcoded backup CnC will be used instead:
C2 Command | Meaning |
/aws/newQRMPClient.php | Supposedly sending user info to the attacker when a QR code scan is found on banking websites listed above, but this feature is currently commented out |
/aws/newContaBBPF.php | Sending user banking info when Bancodobrasil banking sites are intercepted |
/aws/newContaCef.php | Sending user banking info when Caixa banking sites are intercepted |
/aws/newCaixaAcesso.php | Telling the attacker that a victim has accessed Caixa banking page |
aws/newMercadoCartao.php | Sending user banking info when Mercado banking sites are intercepted |
/aws/newExtraLogin.php | Send user credentials when logging in to one of the listed pages |
Chremows (Chrome WebSocket)
Chremows
is another extension that uses NodeJS and puppeteer-core, and is similar to the functionality of node.js mentioned in the Cybereason 2020 report. Chremows
targets two platforms Mercado Livre (mercadolivre.com.br) and Mercado Pago (mercadopago.com.br) both belong to an online marketplace company called Mercado Libre, Inc.
Sharing the same structure of chronodx module, chremows
contains a loader, CreateChrome64.dll
that downloads a JavaScript-based banking trojan called index.js
. CreateChrome64.dll
will automatically update index.js
when a newer version is found. Unlike chronodx, chremows
executes index.js
immediately after download and doesn’t require Google Chrome to be opened. In a separate thread, CreateChrome64.dll
loads an embedded module ModHooksCreateWindow64.dll that Cybereason has analyzed in their 2020 report. Overall, this module help increase the capabilities that chremows
has on Google Chrome, allowing the attacker to perform “active” tasks such as sending keypresses/mouse clicks to Chrome, or opening designated pages. Finally, CreateChrome64.dll
copies Chrome’s Local State file to the same location of index.js
with the name local.json. Index.js
uses local.json to help the attacker identify the victim.
Index.js
employs two methods of communicating with the attacker: through WebSocket and through HTTP. Each method has its own set of C2 servers as shown in the above picture. WebSocket is used to receive commands and send client-related messages. On the other hand, HTTP is for exfiltrating financial data such as banking credentials and account information to the attacker.
List of known Index.js
WebSocket commands
Command from C2 | Functionality |
welcome:: | Send uid and information extract from local.json to the attacker |
control:: | The attacker establishes control over Google Chrome |
uncontrol:: | The attacker removes control over Google Chrome |
ping:: | Check if the connection to the client is OK |
command:: | Send command such as keystroke, mouseclick |
openbrowser:: | Open Chrome window with minimal size to stay hidden |
If the user stays connected to the WebSocket C2 server, every six minutes it automatically goes to the targeted Mercado Pago and Mercado Livre pages and performs malicious tasks. During this routine, the attacker loses direct control of the browser. The target pages are banking, credit, and merchant pages that require users’ login. If the user has not logged out of these pages, the attacker will start to collect data and exfiltrate them through the following HTTP requests:
/aws/newMercadoCredito.php
/aws/newMercadoPago.php
If the user is not logged in to those pages but has the password saved in Chrome, after the routine ends, the attackers will get back their direct control of Chrome and log in manually.
Summary
Chaes exploits many websites containing CMS WordPress to serve malicious installers. Among them, there are a few notable websites for which we tried our best to notify BR Cert. The malicious installer communicates with remote servers to download the Python framework and malicious Python scripts which initiate the next stage of the infection chain. In the final stage, malicious Google Chrome extensions are downloaded to disk and loaded within the Python process. The Google Chrome extensions are able to steal users’ credentials stored in Chrome and collect users’ banking information from popular banking websites.
IOCs
The full list of IoCs is available here
Network
HTML Scripts
is[.]gd/EnjN1x?V=31
is[.]gd/oYk9ielu?D=30
is[.]gd/Lg5g13?V=29
tiny[.]one/96czm3nk?v=28
is[.]gd/WRxGba?V=27
is[.]gd/3d5eWS?V=26
MSI Download URLs
dragaobrasileiro[.]com.br/wp-content/themes/getcorsfile.php?
chopeecia[.]com.br/D4d0EMeUm7/index.php?install
bodnershapiro[.]com/blog/wp-content/themes/twentyten/p.php?
dmt-sys[.]net/index.php?
up-dmt[.]net/index.php?
sys-dmt[.]net/index.php?
x-demeter[.]com/index.php?
walmirlima[.]com.br/wp-content/themes/epico/proxy.php?
atlas[.]med.br/wp-content/themes/twentysixteen/proxy.php?
apoiodesign[.]com/language/overrides/p.php?
CnC Servers
200[.]234.195.91
f84f305c[.]com
bkwot3kuf[.]com
comercialss[.]com
awsvirtual[.]blogspot.com
cliq-no[.]link
108[.]166.219.43
176[.]123.8.149
176[.]123.3.100
198[.]23.153.130
191[.]252.110.241
191[.]252.110.75
SHA256 Hashes
Filename | Hash |
MSI installer | f20d0ffd1164026e1be61d19459e7b17ff420676d4c8083dd41ba5d04b97a08c |
__init__.py | 70135c02a4d772015c2fce185772356502e4deab5689e45b95711fe1b8b534ce |
runScript.js | bd4f39daf16ca4fc602e9d8d9580cbc0bb617daa26c8106bff306d3773ba1b74 |
engine.js | c22b3e788166090c363637df94478176e741d9fa4667cb2a448599f4b7f03c7c |
image | 426327abdafc0769046bd7e359479a25b3c8037de74d75f6f126a80bfb3adf18 |
chremows | fa752817a1b1b56a848c4a1ea06b6ab194b76f2e0b65e7fb5b67946a0af3fb5b |
chrolog | 9dbbff69e4e198aaee2a0881b779314cdd097f63f4baa0081103358a397252a1 |
chronod | ea177d6a5200a39e58cd531e3effb23755604757c3275dfccd9e9b00bfe3e129 |
online | 3fd48530ef017b666f01907bf94ec57a5ebbf2e2e0ba69e2eede2a83aafef984 |
mtps4 | 5da6133106947ac6bdc1061192fae304123aa7f9276a708e83556fc5f0619aab |