TCLBANKER: Brazilian Banking Trojan Spreading via WhatsApp and Outlook

📡 Elastic Security Labs · 0

TCLBANKER: Brazilian Banking Trojan Spreading via WhatsApp and Outlook

Elastic Security Labs identified a new Brazilian banking trojan that we are tracking as TCLBANKER, a malware family we assess is a major update of the MAVERICK/SORVEPOTEL family. The campaign, tracked as REF3076, features a loader with robust anti-analysis capabilities that deploys two embedded .NET Reactor-protected modules: a full-featured banking trojan and a worm module for self-propagation.

The banking trojan monitors the victim's browser address bar via UI Automation, targeting 59 Brazilian banking, fintech, and cryptocurrency domains. Beyond the usual remote access commands, its most notable capability is a WPF-based full-screen overlay framework designed for operator-driven social engineering.

A second module handles distribution through spam agents, of which we recovered two variants: a WhatsApp worm that hijacks authenticated browser sessions to message the victim's contacts, and an Outlook email bot that sends phishing emails through the victim's own accounts via COM automation.

Through this report, we provide a detailed technical breakdown of each stage.

Key takeaways

  • TCLBANKER uses environment-gated payload decryption; incorrect environments, such as sandboxes, silently fail to decrypt the payload
  • A comprehensive watchdog subsystem continuously monitors for analysis tools, debuggers, instrumentation frameworks, and integrity violations throughout execution
  • The banking trojan targets 59 Brazilian banking, fintech, and cryptocurrency domains, activating a WebSocket C2 session when a victim navigates to a monitored site
  • A WPF-based full-screen overlay framework enables operator-driven social engineering, including credential harvesting, vishing wait screens, and fake Windows Update stalls, while hiding overlays from screen capture tools
  • Worm modules propagate the malware: a WhatsApp bot and an Outlook email bot
  • All C2 and distribution infrastructure is hosted on Cloudflare Workers under a single account, with developer artifacts (debug logging paths, test process names) and an incomplete phishing page, suggesting the campaign was identified in an early operational stage

Delivery

TCLBANKER is a Brazilian banking trojan that contains a dynamic infection chain with a heavy anti-analysis loading component that can deploy two embedded payloads (worm, banker). The observed infection chain bundles a malicious MSI installer inside a ZIP file. These MSI installer packages are abusing a signed Logitech program called Logi AI Prompt Builder.

TCLBANKER abuses DLL sideloading against LogiAiPromptBuilder.exe

, a legitimate Logitech application built on the Flutter framework. The malicious DLL screen_retriever_plugin.dll

masquerades as a legitimate Flutter plugin of the same name and is loaded automatically when the host application starts.

After the MSI installation, the malicious DLL is immediately loaded and starts at the DllMain entry point.

Loader

The loader component for TCLBANKER is packed with features, including anti-debugging features, anti-analysis checks, string encryption, system language checks, ETW patching, and a watchdog capability. While it has many features, it lacks depth and has references to older malware analysis tooling. It’s not entirely clear whether the developer used LLM-assisted workflows, but our team wouldn’t be surprised if that were the case.

At the beginning of the execution, TCLBANKER aligns the corresponding .NET assembly payloads based on whether the string (--renderer=sw

) is used in the command-line. Within its main loader function, it first performs allow-list/blocklist operations based on how the DLL was loaded. The malicious DLL will only execute if the host process comes from the following two processes:

logiaipromptbuilder.exe

tclloader.exe

(Possible reference to developer string during testing)

If the DLL was loaded by the following processes, it will refuse to run. These processes are traditionally used by analysts to load and debug DLLs.

rundll32.exe

regsvr32.exe

dllhost.exe

svchost.exe

Next, TCLBBANKER removes any user-mode hooking by replacing ntdll.dll

from disk. For more evasion, the malware generates the following syscall trampolines used later:

NtQueryInformationProcess

NtSetInformationThread

NtSetInformationProcess

NtTerminateProcess

NtAllocateVirtualMemory

NtProtectVirtualMemory

After installing syscall stubs, the malware patches EtwEventWrite

in ntdll.dll

with the classic xor eax, eax; ret

to disable user-mode ETW telemetry.

TCLBANKER performs an initial sandbox check by capturing a start tick using GetTickCount64()

, sleeping for 500 ms, and measuring the elapsed time. If fewer than 450 ms have actually passed, the malware bails — this detects sandboxes or emulation frameworks that hook Sleep to return immediately..

One of the more interesting features of TCLBANKER is an enumeration function that generates three fingerprints based on the following criteria:

  • Anti-debugging checks
  • System disk information and memory checks
  • Language checks

The developer uses magic constants assigned to “clean” paths for each category, then performs an XOR against each one to generate the environment hash. This environment hash value is significant because it affects downstream decryption of the embedded payload.

For example, if a debugger is present, it will produce an incorrect hash, so when the malware attempts to derive the decryption keys from the hash, the payload will not decrypt correctly, and TCLBANKER will stop executing.

Anti-debugging checks

TCLBANKER implements six different anti-debugging checks:

  • Identify the debugger through the

Peb->BeingDebugged

flag - Checks heap-tail/heap-free/check-heap flags set when a process is launched under a debugger

  • Leverages

NtQueryInformationProcess()

usingProcessDebugPort

  • Uses

NtQueryInformationProcess()

usingProcessDebugObjectHandle

  • Hardware breakpoint detection via the debug registers (

DR0-DR3

) - Measures the elapsed time using

QueryPerformanceCounter()

deltas andRDTSC

cycle counts

System information checks

TCLBANKER has the following five different checks based on virtualization, system, and user information:

  • Checks for virtualization software using vendor signature

HypervisorVendor signature VMwareVMwareVMware VirtualBoxVBoxVBoxVBox KVMKVMKVMKVM XenXenVMMXenVMM Parallelsprl hyperv QEMU/TCGTCGTCGTCGTCG

  • Verify the root system drive (

C:\\

) viaGetDiskFreeSpaceExW()

has at least 64 GB - Calls

GlobalMemoryStatusEx()

to verify the system has more than 2 GB of RAM - Checks for 2 or CPU processors via

GetSystemInfo()

  • Checks for generic sandbox/malware usernames

sandbox

,malware

,virus

,sample

,john doe

,currentuser

Language checks

For the last environment fingerprint check, TCLBANKER retrieves geographical information of the infected machine using GetUserGeoID()

, targeting Brazilian users based on the geographical ID (0x20

). A second locale check via GetUserDefaultLCID()

also ensures the user's default language is Brazilian Portuguese (pt-BR, LANGID 0x0416).

After these sets of checks, TCLBANKER will either bail out of execution if anything is detected or, if not, produce another anti-debugging check by patching DbgUiRemoteBreakin()

. The malware patches its first byte to a ret

instruction so that any attempt to remotely break into the process does nothing — the injected thread immediately returns, and the target keeps running, unsuspended.

After this, the malware derives an AES-256 CBC key and IV by using hard-coded constants from the .rdata

section along with the environmental hash calculated earlier. TCLBanker uses BCryptDecrypt()

to decrypt the embedded payload, then decompresses via RtlDecompressBuffer

using the LZNT1 compression algorithm.

Once the respective payload is decrypted, TCLBANKER initializes COM via CoInitializeEx()

and uses the CLR hosting APIs to load the .NET runtime in-process. Before launching the payload entry point, TCLBanker creates two new threads: one serves as the watchdog, and the other monitors the watchdog thread as a heartbeat check.

Watchdog

TCLBANKER has a comprehensive watchdog feature that targets various analysis tools, including disassemblers, debuggers, instrumentation products, anti-virus products, and sandbox products. This section will outline the various techniques used by this feature:

  • Debugger check via

PEB→BeingDebugged

  • Watches for hardware breakpoints

DR0

/DR1

/DR2

/DR3

  • Checks Windows functions (

BCryptDecrypt()

,BCryptOpenAlgorithmProvider()

) for in-line hooks by scanning the first 12 bytes of each function - Monitors for instrumentation tools and related strings (

frida

,cydia

,user-path injection

,hook framework

) - Reviews all kernel named pipes searching for

frida

orlinjector

  • Performs process enumeration via

CreateToolhelp32Snapshot()

targeting the following process names:frida

,de4dot

,dnspy

,megadumper

,extremedumper

,processhacker

,x64dbg

,x32dbg

,pe-sieve

,scylla

,Ilspy

,dotpeek

,netreactorslayer

,cheatengine

  • Employs Windows title detection via

GetWindowTextW()

with these titles:x64dbg

,x32dbg

,ida -

,ida pro

,ghidra

,dnspy

,megadumper

,extremedumper

,processhacker

,ollydbg

,windbg)_

,pe_sieve

,scylla

  • Identifies analyst tooling based on the following window class names via

FindWindowW()

:IDATopLevelWindow

,idaabortwndclass

,TIdaWindow

,x64dbg

,x32dbg

,OLLYDBG

,WinDbgFrameClass

,ProcessHacker

,SystemInformer

,CheatEngine

,HxdClass

  • Checks for the following loaded modules

dbeng.dll

,dbgcore.dll

,SbieDll.dll

,snxhk.dll

,cmdvrt32.dll

,cmdvrt64.dll

,cuckoomon.dll

,pstorec.dll

,vmcheck.dll

,wpespy.dll

  • Targets the following mutexes and events:

Ida_trusted_idbs

,IDA_COMM_PIPE_

,Local\\x64dbg

,Local\\x32dbg

,Frida

,YOURAPPNAMEHERE

  • Performs

CRC32

integrity check on the.text

section to prevent any tampering

Banking Trojan Module

Tcl.Agent

is a banking trojan, the main component of the chain. It is .NET Reactor-protected, and although we failed to deobfuscate it using available open-source tooling such as de4dot and NETReactorSlayer, we managed to statically deobfuscate this stage up to a satisfiable state using a custom deobfuscation pipeline to tackle .NET Reactor’s string encryption, control flow flattening, IL mutation, delegate proxies, and encrypted method bodies (Necrobit). Although it is a new malware, much of the code structure still follows ESET’s LATAM banking trojan implementation blueprint, published in 2020.

At start, the malware performs geofencing, requiring >= 2 of the following indicators to match Brazil; otherwise, it exits immediately if not on a Brazilian machine:

CheckImplementation Region Codenew RegionInfo(CultureInfo.CurrentCulture.LCID).TwoLetterISORegionName == "BR" TimezoneTimeZoneInfo.Local.BaseUtcOffset.TotalHours: if >= -5.0, check if == -2.0 LCIDCultureInfo.CurrentCulture.LCID == 1046 (Portuguese-Brazil) KeyboardGetKeyboardLayoutList() - check each layout: (ToInt32() & 0xFFFF) == 1046

Installation and Persistence

On the first run, the malware copies the entire application directory into %LocalAppData%\LogiAI

. It computes a SHA-256 hash over all .dll

and .exe

files in the source directory and writes it to a .version

marker file. On subsequent runs, it compares hashes to skip redundant copies. After copying, it launches the new instance from the install path and exits.

It creates a scheduled task named RuntimeOptimizeService

using COM interop with the Task Scheduler (CLSID 0F87369F-A4E5-4CFC-BD3E-73E6154572DD

). The task is configured as hidden, enabled, with no execution time limit, allowed on battery, start-when-available, and fires on a logon trigger (type 9

) scoped to the current user. It registers with TASK_CREATE_OR_UPDATE

and TASK_LOGON_SERVICE_ACCOUNT

.

After persistence is established, the agent sends a first-run POST beacon to https://campanha1-api.ef971a42.workers[.]dev/api/installs

with the agentId (MachineName-UserName

), MachineName, UserName (redundant), and the OS version. The request is authenticated with a hardcoded campaign authentication token 0d21613a-2609-45fc-83ff-d0feaa0c891f

. The newer variant adds debug logging around this call (C:\temp\tcl-debug.txt

), a developer artifact that inadvertently exposes agent presence on disk.

Self-Update

The agent implements a hash-based self-update gate that runs early in the startup pipeline. It reads a local version hash from flutter_engine.cfg

in its install directory (migrating from a legacy version.hash

filename if present), then fetches the current hash from the file server endpoint documents.ef971a42.workers[.]dev/api/version

using a truncated User-Agent string (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36

.

The response is parsed for the "hash" key. If the remote hash matches the local hash, execution continues normally. On first install, the remote hash is written to disk, and execution proceeds without updating.

When a hash mismatch is detected, the agent downloads the update payload from documents.ef971a42.workers[.]dev/api/update

as an MSI to %TEMP%\update_{8hexchars}.msi

, authenticated with Bearer token b7ba9e80-0d04-4d9e-b217-c8b3cce335a2

.

The download is validated against a 100KB minimum size as a sanity check. The agent then writes a self-deleting batch script to %TEMP%

that polls the tasklist until the current process exits, executes msiexec /i /qn REINSTALLMODE=amus

for silent installation, and deletes itself. The batch file is launched via a hidden cmd.exe

process before the agent terminates, handing off execution to the updated payload.

Browser URL Monitor and C2 Session Initialization

Every second, the malware agent calls a browser URL monitor function that reads the foreground browser's address bar via UI Automation. It calls GetForegroundWindow

, resolves the owning process, checks the process name against Chrome, Firefox, Microsoft Edge, Brave, Opera, and Vivaldi, then uses AutomationElement.FromHandle -> FindFirst(Descendants, ControlType.Edit) -> ValuePattern.Current.Value

to extract the URL, similar to this Stack Overflow implementation.

The extracted URL is matched against a fixed list of targeted banks embedded in the binary, encoded via XOR with a 16-byte key and base64 encoding.

This GitHub Gist contains a list of 59 targeted domains, including Brazilian banking, fintech platforms, and cryptocurrency exchanges, grouped by the target IDs appended to each decrypted domain.

When a match hits, the domain target ID is passed to the next state, which initializes the official C2 communication by establishing a WebSocket connection to wss://mxtestacionamentos[.]com/ws

. The OnConnect

handler fires, sending a registration packet containing the agent ID (a random GUID at runtime), MachineName, UserName, machine info, timestamp, domain target ID (so the C2 knows which website the victim opened), and a signature.

To produce a handshake signature, HMAC-SHA256 is used to sign the victim identifier (agent ID, MachineName, UserName, OSVersion, timestamp) using the campaign GUID 70e4f943-e323-4484-97d7-35401bf6812c

as the key.

The server then responds with a registration acknowledgment, officially starting the session, and enters the command dispatch loop. At session start, a Task Manager killer is fired every 500ms to prevent the victim from inspecting or terminating the agent's process.

C2 Command Table

A capability summarization is described through the opcode table below:

OpcodePurpose 2Registration ACK, start the Task Manager killer 4Graceful WebSocket disconnect 5Suicide: Kill all sibling processes and exit 6Forced reboot (shutdown.exe /r /t 0 /f ) 7Suicide-then-Uninstall: kill all processes with the same host binary name except itself (siblings) → uninstall → exit 16Screenshot 17Start streaming the screen 18Stop streaming the screen 19Set screen capture quality (1-100) 20Enumerate monitors 32Mouse move (X, Y, MonitorIndex) 33Mouse click through overlay: parse {X,Y,Button,MonitorIndex} → translate to absolute desktop coords → find the implant's own overlay window covering that point → punch a 2x2 region hole in the overlay at that pixel → SetCursorPos + SendInput mouse-down/up (which lands on whatever real desktop content is underneath the overlay). 34Mouse scroll (Delta, SendInput ) 35Key tap (KeyCode, SendInput ) 37Key down (KeyCode, SendInput ) 38Key up (KeyCode, SendInput ) 39Start keylogger (WH_KEYBOARD_LL hook) 40Flush keylogger, exfil to C2 41Clipboard hijack (Clipboard.SetText ) 48File system directory listing 65Get running processes information 67Shell command execution (cmd.exe /c ) 80Enumerate all visible windows 81Window manager: Kill process of a window / minimize window / restore window / bring window to foreground / close window / move window to another monitor 83Show stall overlay: either progress-steps or fake Windows Update screen 84Teardown overlay 85Toggle screen capture immunity. Enables/disables WDA_EXCLUDEFROMCAPTURE on all overlay windows to hide them from screen sharing / screenshots. 86Refresh overlay content 87Show cutout overlay: pin external window inside overlay with visible region cutout 96Show credential prompt overlay

Social Engineering UI Framework

A more interesting capability of the banking trojan is a WPF-based full-screen overlay subsystem that orchestrates bank-themed fraud flows during active C2 sessions.

Overlay Lifecycle

The overlay manager spawns one full-screen WPF window per monitor. Windows are configured as borderless, topmost, and hidden from the taskbar (WindowStyle.None

, Topmost = true

, ShowInTaskbar = false

), with a custom Closing

handle that refuses dismissal until an internal flag is flipped by the operator through the overlay teardown command, preventing the windows from being closed.

At startup, the manager captures a PNG screenshot of every display via CopyFromScreen

as the overlay backdrop, creating a “frozen desktop” look. Depending on the currently active overlay, the victim perceives their real desktop environment behind it.

A 500ms

timer continuously reapplies HWND_TOPMOST

via SetWindowPos

to defeat any window attempting to surface above the overlay.

In addition, an anti-capture feature calls SetWindowDisplayAffinity

with WDA_EXCLUDEFROMCAPTURE

, rendering the overlay invisible to any screen-capturing tools, allowing the operator to see through their own overlay through the screenshot and screenstreaming commands.

Input Blocker

On the primary monitor, two hooks are installed: WH_KEYBOARD_LL

and WH_MOUSE_LL

. Both hooks check their respective injected flags (LLKHF_INJECTED

for keyboard, LLMHF_INJECTED

for mouse), allowing input injected via SendInput

by the operator’s remote commands to pass through untouched. The keyboard hook swallows Tab, Escape, Alt+F4, Win keys, PrintScreen, Ctrl, Alt, and all navigation keys; the mouse hook blocks right-click, middle-click, and scroll, but allows left-click and movement, so the victim can still interact with the overlay prompts.

Social Engineering UI Builders

Five interchangeable content renderers plug into the overlay framework:

Credential Prompt: Supports three input modes, selected based on the operator's request parameters.

  • Phone mode applies real-time Brazilian format masking ((##) ####-#### for 10-digit landlines, (##) #####-#### for 11-digit mobiles) with max 11 digits.
  • Virtual keypad mode renders an on-screen numeric keypad (buttons 0–9 plus "limpar"/clear), displaying input as bullet characters to mimic PIN entry.
  • Default mode accepts plain text with a configurable max length.

All modes run input through a quality validator that algorithmically rejects same-digit sequences (000000

) and ascending/descending runs (123456

, 654321

) to prevent victims from entering throwaway values.

The submit action fires the captured values to the C2.

Vishing Wait Screen: Triggers after the victim submits their phone number in the credential prompt. Displays "Estamos entrando em contato" ("We are getting in touch") with a central image “breathing” animation and three dots with staggered opacity animations (300ms

offset per dot) producing a "connecting" visual. The operator or an accomplice can then call the victim's real phone, impersonating bank security staff.

Progress Steps: A fully operator-templated stall screen displaying a list of fake processing steps with a randomized animation. Step durations are randomized to total approximately 15 minutes. A timer advances through each step sequentially, visually marking completed steps, highlighting the current one, and dimming the remaining ones. When all steps are complete, the sequence resets to an earlier position with new randomized timings and continues.

Fake Windows Update: An alternative stall screen mimicking the Windows 10/11 update-restart screen. Renders a solid #0078D7

(Windows accent blue) background with a five-ellipse spinning indicator arranged in a circle. A percentage readout jumps by a random 25–35% at randomly selected 50–81-second intervals to mimic the irregular progress behavior of real Windows Updates. Default subtitle: "Trabalhando em atualizacoes" ("Working on updates").

Cutout Overlay: Cuts a rectangular hole in the full-screen overlay, exposing the underlying application window. The operator specifies hole dimensions via opcode 87, in which the overlay manager builds a themed card with a transparent-border placeholder, computes its screen coordinates post-layout, and cuts a matching region hole using CreateRectRgn + CombineRgn(RGN_DIFF) + SetWindowRgn

. The target window is repositioned underneath the hole. The result is a real application window framed within the overlay, and the victim interacts with the actual application while the surrounding overlay provides deceptive context.

Worm module

The second module invoked by the loader is Tcl.WppBot

, is designed to propagate spam and phishing messages at scale, to distribute TCLBANKER. Two distinct agent types were recovered from two different loaders and analyzed:

  • A WhatsApp worm that hijacks browser sessions
  • An Outlook email bot that abuses Microsoft Outlook through COM interop

Tcl.WppBot

is also .NET Reactor-protected with the same version used to protect Tcl.Agent

, and so we also managed to statically deobfuscate payloads in this stage.

Both agents share the same C2 backend, authentication credentials, and operational infrastructure. The C2 URL and API key are decrypted at startup using XOR decryption with a hardcoded key.

  • C2 URL:

campanha1-api.ef971a42.workers[.]dev

(Cloudflare Workers app) - API key / Bearer token:

0d21613a-2609-45fc-83ff-d0feaa0c891f

The C2 serves a single superset campaign configuration object via the https://campanha1-api.ef971a42.workers[.]dev/api/campaign

endpoint. Each agent variant deserializes the full object but only reads the fields relevant to its channel.

Captured configuration:

message : Ola tudo bem?

Preciso de um orçamento, estarei encaminhado caso tenha os produtos por favor me retorne para

darmos continuidade no atendimento.

https://arquivos-omie[.]com ð

âï¸*IMPORTANTE*: Este orçamento foi otimizado para visualização em Computadores Desktop,

pois o mesmo necessita de visualizador de excel, word ou pdf.

fileUrl : https://documents.ef971a42.workers[.]dev/file

delayMin : 1

delayMax : 3

maxPerSession : 3000

updatedAt : 2026-04-17T15:54:07.003Z

type : gmail

subject : Prezado(a), NFe disponÃvel para impressão

emailMessage : <!DOCTYPE html>

<html lang="pt-BR">

<head>

<meta charset="UTF-8">

<title>Nota Fiscal DisponÃvel</title>

<style>

body {

font-family: Arial, sans-serif;

margin: 20px;

padding: 0;

text-align: center;

background-color: #f4f4f4; /* Cor de fundo mais clara */

color: #333; /* Cor do texto ajustada para ser visÃvel */

}

h1 {

font-size: 24px;

margin-bottom: 20px;

font-weight: normal; /* TÃtulo sem negrito */

}

p {

font-size: 16px;

margin-bottom: 20px;

line-height: 1.6;

color: #333; /* Garantir que o texto esteja visÃvel */

}

.btn {

background-color: #007BFF;

color: #fff;

padding: 10px 20px;

border: none;

border-radius: 5px;

cursor: pointer;

}

.btn:hover {

background-color: #0056b3;

}

</style>

</head>

<body>

<h1>Prezado(a)</h1>

<p>

Sua Nota Fiscal Eletrônica (NFe) está disponÃvel e pronta para ser acessada.

Para facilitar, basta clicar no botão abaixo para abrir o documento.

</p>

<p>

Caso tenha alguma dúvida sobre os detalhes da nota ou precise de alguma alteração, por

favor, entre em contato conosco.

</p>

<a href="https://arquivos-omie[.]com" target="_blank">

<button class="btn">Abrir Nota Fiscal</button>

</a>

<p>

Agradecemos pela confiança e ficamos à disposição para qualquer outra necessidade.

</p>

</body>

</html>

emailDelayMin : 30

emailDelayMax : 90

emailMaxPerSession : 100

The same Cloudflare account ef971a42

also hosts the payload delivery CDN (domain for fileUrl

in the configuration object) at documents.ef971a42.workers[.]dev

. Accessible through the /file

endpoint, it currently serves a zip file containing the TCLBANKER-trojanized LogiAI Prompt Builder MSI. This infrastructure decision allows the operator to rapidly redeploy infrastructure without maintaining dedicated servers.

As of the time of writing, the phishing domain arquivos-omie[.]com

identified in the configuration above, created on 2026-04-15, is not at an operable state (Welcome! This portal is currently undergoing scheduled maintenance. Please try again later.) The campaign could be in its early operational stages or staged for tasking. This domain is also named to impersonate a popular Brazilian Enterprise Resource Planning (ERP) suite.

Agent 1: WhatsApp Bot

The WhatsApp agent silently takes over the victim’s authenticated WhatsApp Web session to send spam messages and distribute TCLBANKER to Brazilian contacts.

Session Hijacking

The malware starts by discovering Chromium-based browsers on the target system, then scanning both the App Paths

registry entries and common installation directories for Chrome, Edge, Brave, Opera, and Vivaldi. It then walks each browser's user profiles (e.g., "Default," "Profile 1," etc.), looking for evidence of an active WhatsApp Web session. A profile is flagged as having an authenticated session if its IndexedDB storage contains the WhatsApp Web LevelDB directory at <profile_dir>/IndexedDB/https_web.whatsapp[.]com_0.indexeddb.leveldb/

.

Then each profile is sent to the profile-cloning and session-hijacking function.

For each qualifying profile, the malware clones it to a temporary directory at %TEMP%\<GUID>\

, copying only the files needed to resume the WhatsApp Web session: IndexedDB

, Local Storage

, Session Storage

, databases

, Web Data

, Login Data

, and Cookies

. It then launches a headless Chromium instance via Selenium WebDriver, with --user-data-dir

pointing at the cloned profile.

The matching chromedriver.exe

is resolved at runtime by a disguised Selenium Manager binary dropped at %TEMP%\msvc-rt14\bin\hostfxr.exe

, which is invoked with --browser chrome --output json

and returns the path of a chromedriver compatible with the victim's installed Chrome version.

Immediately after launch, the malware injects JavaScript to bypass bot-detection frameworks by hiding navigator.webdriver

, populates chrome.runtime

, reconciles the Notification.permission

/ permissions.query

state mismatch, fakes a non-empty navigator.plugins

array, and sets navigator.languages

to ['pt-BR', 'pt', 'en-US', 'en']

to match the target demographic.

Object.defineProperty(navigator, 'webdriver', {

get: () = > undefined

}

);

delete navigator.__proto__.webdriver;

if (window.chrome) {

window.chrome.runtime = window.chrome.runtime || {};

}

const origQuery = window.navigator.permissions.query;

window.navigator.permissions.query = (p) = > (

p.name == = 'notifications' ?

Promise.resolve( {

state: Notification.permission

}

) :

origQuery(p)

);

Object.defineProperty(navigator, 'plugins', {

get: () = > [1, 2, 3, 4, 5], }

);

Object.defineProperty(navigator, 'languages', {

get: () = > ['pt-BR', 'pt', 'en-US', 'en'], }

);

window.navigator.chrome = {

runtime: {}

};

With the cloned profile loaded, the browser navigates to web.whatsapp[.]com

, and the malware waits up to 45 seconds to observe the resulting page state. If the chat interface appears (the cloned IndexedDB was valid and the session resumed without a QR scan), it injects an embedded WA-JS (WPPConnect) library and waits for WPP.contact.list

and WPP.chat.sendTextMessage

to become callable before starting the campaign dispatch loop. If the QR code prompt is shown instead, the engine returns "qr_code"

without attempting injection, then tries the next candidate profile.

Spam Functionality

Once the injection succeeds, the malware retrieves the active campaign from the C2 endpoint https://campanha1-api.ef971a42.workers[.]dev/api/campaign

, which supplies the message body, optional attachment URL, caption, and timing parameters. TCLBANKER is then downloaded from the file server at https://documents.ef971a42.workers[.]dev/file

and reconstructed in the browser context as a File object, without being dropped to disk.

The malware then calls WPP.contact.list

to harvest the victim's address book, filters out groups, broadcasts, and non-Brazilian numbers, and begins dispatching messages through WPP.chat.sendTextMessage

and sendFileMessage

. The malware reports progress to the C2 endpoint /api/progress

after each batch, and polls /api/control

for remote pause or resend commands from the operator.

Agent 2: Outlook Email Bot

The Outlook agent is an email spambot that abuses the victim’s installed Microsoft Outlook application to send phishing emails from the victim’s email address, making them harder to detect as spam than emails sent from attacker-controlled infrastructure.

Outlook Discovery & COM Attachment

If OUTLOOK.EXE

is not already running, it attempts to locate the installation in App Paths

registry entries and known installation directories and launches it in a new process. The malware then attaches to the process via COM interop: Marshal.GetActiveObject("Outlook.Application")

, and validates that it has at least one email account configured.

Contact Harvesting

The malware then drops a PowerShell script, %TEMP%\oc<guid>.ps1,

that harvests contacts via Outlook COM from a separate process. It harvests contacts from two sources: first, it reads the default Contacts folder for all contact entries, extracting email addresses and full names from each contact item. Second, it iterates every store’s root folders to find inbox-like folders, sorts inbox messages by latest, and extracts sender email addresses and names before writing them to a .txt

file in email|name

format.

For each candidate email, additional filtering is done to maximize deliverability.

Spam Functionality

Similar to the WhatsApp bot, the malware retrieves the active campaign from https://campanha1-api.ef971a42.workers[.]dev/api/campaign

, then sends emails through the victim's own Outlook accounts via COM automation. Each email is constructed via outlookApp.CreateItem(0)

(MailItem

) with the recipient in To

, the campaign subject line, and the campaign content emailMessage

, sent using the victim's actual account via SendUsingAccount

.

Between sends, the agent applies a randomized delay and periodically checks the C2 control endpoint /api/control

for pause or resend commands, and reports progress to /api/progress

.

Infrastructure

The REF3076 actors have leveraged the worker[.]dev

Cloudflare Serverless infrastructure for C2 and file hosting. This decision allows them to inherit any trust victims might already have in Cloudflare and to rotate infrastructure quickly as needed.

Pivoting on the body-hash (91fafaa1240676afe5c55d931261e3798797c408

) of the phishing site above (arquivos-omie[.]com

), we were able to identify additional domains that are likely being prepared for weaponization:

DomainFirst SeenInfoASN (Providor) arquivos-omie[.]com2026-04-17Squatting - Brazilian SaaS for SMBsAS 13335 (Cloudflare) documentos-online[.]com2026-04-11GenericAS 13335 (Cloudflare) afonsoferragista[.]com2026-04-22Hardware store - Likely used in a B2B lureAS 13335 (Cloudflare) doccompartilhe[.]com2026-04-15Generic - “Shared a document”AS 13335 (Cloudflare) recebamais[.]com2026-04-20Squatting - Brazilian credit/loan brokerageAS 13335 (Cloudflare)

More Brazilian phishing infrastructure was discovered after a broader pivot in the banner title (Portal

Corporativo

), but it’s unclear whether it was directly related to REF3076 or to other actors in the Latin American banking trojan ecosystem. Notably, one cluster leveraged the Cloudflare pages[.]dev

free static-site hosting product.

The C2 domain mxtestacionamentos[.]com

previously pointed to a Brazilian-hosted IP 191.96.224[.]96

.

Last year, this IP concurrently hosted a REF3076 C2 domain, a REF3076 phishing domain, and a domain previously associated with the Water Saci campaign and SORVEPOTEL/MAVERICK malware by TrendMicro (saogeraldoshiping[.]com

).

Conclusion

TCLBANKER reflects a broader maturation happening across the Brazilian banking trojan ecosystem. Techniques that were once the hallmark of more sophisticated threat actors: environment-gated payload decryption, direct syscall generation, real-time social engineering orchestration over WebSocket, are now being packaged into commodity crimeware. The barrier to entry continues to drop, especially when powerful LLMs are readily accessible for code generation.

The inclusion of self-propagating worm modules marks a notable shift in this space. The campaign inherits the trust and deliverability of legitimate communications by hijacking victims' WhatsApp sessions and Outlook accounts. This is a distribution model that traditional email gateways and reputation-based defenses are ill-equipped to catch. As Latin American banking trojans continue to adopt these self-spreading mechanisms, organizations should expect the volume and reach of these campaigns to scale accordingly.

Developer artifacts throughout the chain, including debug logging paths, test process names, and a phishing site still under construction, suggest REF3076 is in its early operational stages. This is a campaign still being built out, not wound down.

REF3076 through MITRE ATT&CK

Elastic uses the MITRE ATT&CK framework to document common tactics, techniques, and procedures that threats use against enterprise networks.

Tactics

Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.

  • Initial Access
  • Execution
  • Persistence
  • Defense Evasion
  • Credential Access
  • Discovery
  • Collection
  • Command and Control
  • Exfiltration
  • Impact

Techniques

Techniques represent how an adversary achieves a tactical goal by performing an action.

  • Phishing: Spearphishing Attachment
  • System Binary Proxy Execution: Msiexec
  • Hijack Execution Flow: DLL Side-Loading
  • Command and Scripting Interpreter: PowerShell
  • Command and Scripting Interpreter: Windows Command Shell
  • Scheduled Task/Job: Scheduled Task
  • Deobfuscate/Decode Files or Information
  • Obfuscated Files or Information
  • Debugger Evasion
  • Virtualization/Sandbox Evasion: System Checks
  • Virtualization/Sandbox Evasion: Time Based Evasion
  • Impair Defenses: Disable or Modify Tools
  • Native API
  • Process Injection
  • Process Discovery
  • Application Window Discovery
  • System Information Discovery
  • System Location Discovery: System Language Discovery
  • Screen Capture
  • Input Capture: Keylogging
  • Clipboard Data
  • Input Capture: Web Portal Capture
  • Browser Session Hijacking
  • Application Layer Protocol: Web Protocols
  • Web Service
  • Ingress Tool Transfer
  • Email Collection: Local Email Collection
  • System Shutdown/Reboot

Remediating REF3076

Prevention

  • NTDLL Memory Protection Change via Unsigned DLL
  • NTDLL library loaded for a second time
  • Potential NTDLL Memory Unhooking
  • Parallel NTDLL Loaded from Unbacked Memory
  • Suspicious Windows Core Module Change
  • AMSI Bypass via Unbacked Memory
  • Potential AMSI Bypass via SetThreadContext

YARA

Elastic Security has created YARA rules to identify this activity.

Observations

The following observables were discussed in this research.

ObservableTypeNameReference 701d51b7be8b034c860bf97847bd59a87dca8481c4625328813746964995b626SHA-256screen_retriever_plugin.dllTCLBanker loader component 8a174aa70a4396547045aef6c69eb0259bae1706880f4375af71085eeb537059SHA-256screen_retriever_plugin.dllTCLBanker loader component 668f932433a24bbae89d60b24eee4a24808fc741f62c5a3043bb7c9152342f40SHA-256screen_retriever_plugin.dllTCLBanker loader component 63beb7372098c03baab77e0dfc8e5dca5e0a7420f382708a4df79bed2d900394SHA-256XXL_21042026-181516.zipTCLBanker initial ZIP file campanha1-api.ef971a42[.]workers.devdomain-nameTCLBanker C2 mxtestacionamentos[.]comdomain-nameTCLBanker C2 documents.ef971a42.workers[.]devdomain-nameTCLBanker file server arquivos-omie[.]comdomain-nameTCLBanker phishing page (under development) documentos-online[.]comdomain-nameTCLBanker phishing page (under development) afonsoferragista[.]comdomain-nameTCLBanker phishing page (under development) doccompartilhe[.]comdomain-nameTCLBanker phishing page (under development) recebamais[.]comdomain-nameTCLBanker phishing page (under development)


📌 来源: Elastic Security Labs | 📅 0

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)