Mar 06, 2026

SORVEPOTEL / Maverick: A Self-Propagating WhatsApp Campaign

Full chain analysis of the SORVEPOTEL campaign — encrypted ZIP lure, VBS dropper, staged Python payload, and WA-JS session hijacking used for automated propagation.

malware-analysiswhatsappself-propagatingpythonvbsbrazil
sample.metadatamedium
family
SORVEPOTEL (Water Saci)

TL;DR

SORVEPOTEL is a malicious campaign initially reported by Trend Micro, and it’s a solid example of how modern campaigns prioritize speed, ambiguity, and operational efficiency over flashy tooling.

In this article, I’m sharing findings from an investigation I personally conducted, grounded in collected evidence. The goal is simple: document the campaign flow, highlight the artifacts and behaviors that were most useful during analysis, and provide a practical lens for defenders who care about campaign tracking and detection engineering.

If your work sits anywhere near threat hunting, incident response, or CTI focused on adversary behavior, this will be a useful read.

Delivery vector: encrypted ZIP via WhatsApp

The entry point was simple. A user received a WhatsApp Web message with a ZIP attachment, paired with a casual line: “Here’s the requested file. Please feel free to ask if you have any questions.” The archive itself was AES encrypted, which immediately limited quick previewing and pushed analysis into an extraction step first.

WhatsApp lure message with encrypted ZIP attachment

Digging into the maldoc, I quickly spotted an embedded file payload with “.vbs” termination. That became my next target. Once I cracked it open, it was clear the script was almost entirely encoded, but it wasn’t random. There was a specific array in there that immediately looked interesting, like it was doing the heavy lifting.

Encoded VBS payload, obfuscation array highlighted

The embedded file with encoded instructions

From there, it was all about finding the pattern, once I had that, the obfuscation started to fall apart and the code became readable.

Decoded VBS instructions

Static analysis: the VBS dropper

The decoded instructions made multiple calls into PowerShell. Early on, the script sets up its execution helpers by creating common COM objects used for process execution and file system access:

Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")

The decoded instructions made multiple PowerShell calls. The script starts by instantiating WScript.Shell for command execution and Scripting.FileSystemObject for file operations, a common setup in droppers.

The script checks the OS language early on. If it’s not set to Portuguese, execution is terminated and a deliberately misleading error message is displayed.

Language check gating execution

Next, it ensures a working directory exists under C:\TEMP. If the folder isn’t there, it creates it and uses it as a staging area for the files required in later phases. The resulting structure is similar to the following:

C:\temp
\- msiexec.exe
\- python.zip
    python312._pth
    python312.zip
    Lib
    Lib\site-packages
    ...
    python.exe
    intalskji.msi
    ...
\- driver.zip > chromedriver.exe
\- get-pip.py
\- harrypotuer.py

To execute successfully, the script needs a couple of libraries. It installs them through a series of command line pip install calls:

pip install setuptools wheel pywin32
pip install requests Pillow numpy opencv-python
pip install pyautogui keyboard mouse pygetwindow
pip install pytesseract selenium packaging webdriver-manager

That library list (pyautogui, keyboard, mouse, selenium, webdriver-manager) is a strong early indicator of browser automation and simulated human interaction — consistent with what shows up later in the chain.

MSI download and install

The script drops and executes intalskji.msi, then deletes it as part of its own cleanup logic. To make infrastructure blocking harder, it also randomizes the MSI download URL by choosing from a small list of hardcoded endpoints. Each run picks one URL at random, then proceeds with the download and execution flow.

After selecting a random MSI URL, the VBS tries up to three times to download intalskji.msi via PowerShell Invoke-WebRequest.

It then checks that the file exists and isn’t too small before installing it silently with msiexec /qn /norestart. If the install fails, it retries using an elevated RunAs fallback. Once installation finishes, it deletes the MSI to reduce artifacts.

Here’s an infographic showing the execution flow.

Execution flow from MSI download through install

Second stage: Expecto Payloadum! harrypotuer.py

harrypotuer.py encoded on disk

At the tail end of execution, there’s a request to the C2 infrastructure to pull down a Python script named harrypotuer.py (spelled exactly like that in the wild). The file was encoded as well, and it wasn’t subtle about it.

harrypotuer.py encoded on disk

The file itself isn’t readable Python logic, it’s a thin wrapper around an obfuscated payload: long base64 blobs, an import of base64, then an immediate pivot into runtime unpacking. The core behavior is simple: decode the blob, decompress it with zlib, decode it back into text, and then execute the resulting code with exec().

In other words, it’s a loader whose job is to reconstruct the actual Python code in memory and run it on the fly. This kind of base64 plus compression layering is a common tradecraft choice because it blocks quick triage, slows down static analysis, and helps the payload slip past basic content based detections that look for obvious strings or imports.

Opening the file revealed a telling header comment: “WhatsApp Automation Script – Version 8”.

Decoded header comment identifying the script version

The following section shows how the malware is configured to talk to its remote server.

C2 server configuration in the decoded script

Configuration and default phishing layout

In this stage, the malware uses a ConfigApp dataclass as its control panel. It hardcodes the backend URL it talks to, sets automation pacing like delays and batch size, chooses headless vs visible browser mode, and leaves browser selection on auto for Chrome, Edge, or Firefox.

It also embeds prewritten WhatsApp message templates with placeholders to personalize greetings and make the outreach look legit. A simple Contato dataclass models each target contact, and there’s an optional webhook field, likely meant for basic run notifications.

The figure below illustrates the default phishing message layout, including the standard lure text delivered to the victim and the malicious artifacts generated downstream from the referenced URL.

Default phishing message layout

Malicious artifacts generated from the referenced URL

The script supported browsers for the malicious activity are Chrome, Edge, and Firefox.

Supported browsers: Chrome, Edge, Firefox

The script’s main class, named WhatsAppAutomation, collects system information from the affected device and also checks the status of the remote server.

The data it gathers includes the session, device name, type, message, details, and timestamp.

System information collected by WhatsAppAutomation

WA-JS injection in WhatsApp Web

Next, the Python stage spins up a WhatsApp Web session and injects WA-JS into the live page.

WA-JS here isn’t just “some random JS”. It’s typically the WPPConnect WA-JS library, built to expose internal WhatsApp Web functions through a helper object, which makes automation and data extraction way easier than trying to click the UI like a human. Once injected, the operator can programmatically read chats, enumerate contacts, pull metadata, and trigger actions in the session, basically turning the browser into an API surface.

This “initialize WhatsApp Web + inject WA-JS” pattern shows up in real world WhatsApp focused campaigns and is a strong signal that the goal is session hijacking and automated propagation, not just a one off payload execution.

WA-JS injection into the live WhatsApp Web session

Continuing the script analysis, another function is designed to check whether the user is already authenticated in WhatsApp. If the session is valid, it kicks off contact harvesting through obter_contatos, which implements two collection paths, a primary “standard” method and an alternative fallback.

Contact harvesting logic with primary and fallback paths

One thing that stood out in this case was the way the script handled errors. Parts of the exception messages and recovery logic read like they were drafted with AI assistance, not in the malware writes itself sense, but in the very practical way threat actors are now using LLMs to speed up development. You can see it in the consistency of the wording, the structured troubleshooting paths, and the overall effort to make the code more resilient and easier to iterate on. Here some examples:

print("✅")
...
print(f"❌ ({erro_msg[:40]})" if len(erro_msg) > 40 else f"❌ ({erro_msg})")
...
self.send_log(TipoLog.SUCESSO, "ENVIO CONCLUÍDO", f"✅ {total_enviados} | ❌ {total_erros}")

This matters because instead of spending days debugging, an operator can use AI to generate clearer error handling, add fallbacks, and ship updates faster. The result is campaigns that break less often in real environments, are easier to maintain, and can be adapted quickly when defenders start blocking infrastructure or disrupting execution paths.

Propagation to the victim’s contacts

In the propagation stage, the script uses the victim’s WhatsApp Web session to message contacts in three steps: greeting, file attachment, then a final line. It adds short delays to look human, skips some contact types, and uses error handling so a failure on one contact doesn’t stop the whole run. The result is automated spreading from a trusted account to the victim’s network.

Automated propagation logic sending to harvested contacts

Conclusion

SORVEPOTEL is a good reminder that modern campaigns don’t need exotic exploits to be effective. In this case, a familiar delivery channel, a layered execution chain, and a very practical WhatsApp Web automation stage were enough to turn one compromised user into a distribution point.

MITRE ATT&CK mapping

TacticTechniqueID
Initial AccessPhishing: Spearphishing via ServiceT1566.003
ExecutionUser Execution: Malicious FileT1204.002
ExecutionCommand and Scripting Interpreter: Visual BasicT1059.005
ExecutionCommand and Scripting Interpreter: PowerShellT1059.001
ExecutionCommand and Scripting Interpreter: PythonT1059.006
Defense EvasionObfuscated Files or InformationT1027
Defense EvasionSystem Language Discovery (evasion gate)T1614.001
DiscoverySystem Information DiscoveryT1082
CollectionAutomated Collection (contact harvesting)T1119
Command and ControlApplication Layer Protocol: Web ProtocolsT1071.001

Indicators of compromise

TypeValue
URLhxxps://principal-page2[.]com/macforte/installer[.]msi
URLhxxps://principal-page2[.]com/macforte/installer2[.]msi
URLhxxps://principal-page2[.]com/macforte/harrypotuer[.]py
URLhxxps://principal-page2[.]com/macforte/gera1[.]php
URLhxxps://principal-page2[.]com/api/api[.]php

References