We continue tracking the activity of Toy Ghouls (also known as Bearlyfy, Laboo.boo, and Feral Wolf), a financially motivated group that has been targeting Russian organizations since 2025. The attackers initially relied exclusively on tools pulled from public GitHub repositories along with leaked Babuk and LockBit ransomware builders, later shifting to their own custom ransomware, GenieLocker. In early July 2026, we observed the group using a custom backdoor for the first time.
We identified two versions of this backdoor: one uses the HiveMQ MQTT broker as its C2 server, while the other relies on the Element messenger. Both versions include “bird” in their names:
mqtt-bird-agent 0.1.0 (HiveMQ version)
matrix-bird-agent 0.1.0 (Element version)
This post examines how the backdoor is delivered to target systems, how it establishes persistence, and how it communicates with its C2 server.
Technical details
Delivery
In this campaign, the attackers use Windows Remote Management (WinRM) to deliver the backdoors and their configuration files to compromised systems. The group relies on open-source tools such as Evil-WinRM and WinRM-fs to do this.
Installation
The backdoor can both run within an interactive command-line session and establish persistence as a Windows service, using the --install or install option, depending on the backdoor version. The --service (or service) option is not available by default and is instead used as an argument for the installed Windows service.
Other launch options are listed in the backdoor’s help output:
C:\cplsupport.exe -h
Bird Agent - MQTT server monitor
Usage: cplsupport.exe [OPTIONS]
Options:
-c, --config <CONFIG> Path to config.toml config file
--install Install as a system service
--uninstall Uninstall the system service
--seal Encrypt sensitive config fields in-place using a machine-bound key
-h, --help Print help
-V, --version Print version
HiveMQ version backdoor help output
In the Element version, the backdoor help output looks as follows:
C:\wtass.exe -h
Matrix monitoring agent
Usage: wtass.exe [OPTIONS] [COMMAND]
Commands:
install Register this agent with the Matrix homeserver and panel
uninstall Remove this agent's service and credentials
service Run as a Windows service (internal)
help Print this message or the help of the given subcommand(s)
Options:
-c, --config <CONFIG>
-h, --help Print help
-V, --version Print version
Element version backdoor help output
By default, the backdoor looks for a config.toml configuration file in the directory where the executable was launched, then falls back to %PROGRAMDATA%\SynapseAgent\config.toml (Element version) or %PROGRAMDATA%\cplsupport\config.toml (HiveMQ version). If no configuration file is found in either location, the full path can be specified using the -c (--config) option.
The backdoor accepts both unencrypted configuration files and files with partially encrypted sections. In the first case, once the backdoor is launched, it reads the file and partially encrypts it using the seal() function (the --seal option in the HiveMQ version), applying the ChaCha20-Poly1305 algorithm with a key derived from the value of the HKLM\Software\Microsoft\Cryptography\MachineGuid registry key. This means that after the backdoor’s first run, the configuration file becomes bound to that specific machine. On subsequent runs, the configuration is decrypted automatically. If the input configuration was already partially encrypted, it is likewise decrypted automatically.
If the configuration cannot be decrypted, the backdoor stops running.
Encrypted configuration files look as follows:
Encrypted backdoor configuration file, HiveMQ version
The encrypted portion of the HiveMQ version’s configuration contains the following parameters:
agent_privkey: the agent’s private key
channel_id: the channel identifier used to communicate with the broker
server_pubkey: the server’s public key
Decrypted blob field in the HiveMQ version’s configuration
In the Element version, the configuration file is deleted immediately after the first run, and the relevant parameters are instead written to the HKLM\Software\synapse\Config\SealedConfig registry key. On subsequent runs, the backdoor checks the registry for its configuration first.
Decrypted Element version configuration file, retrieved from the registry
The Element version’s configuration specifies the address of an Element server controlled by the attackers, a room identifier, and an access_token used to access that room. If this parameter is left empty, the backdoor prompts for the password interactively during installation. After successfully creating a session, the backdoor saves the received token to the blob field.
Communication
At startup, both backdoor versions send a GET request to http://ip-api.com/json to determine the system’s public IP address and country of origin.
The first version uses the public HiveMQ MQTT broker (broker.hivemq.com) as its C2 server. The free tier of this broker supports up to 100 concurrent connections and up to 10 GB of traffic per month. The attackers set up their own cluster and used it both to collect telemetry from compromised systems and to send commands to the backdoor.
Once a connection is established, the system’s status is sent via a POST request to
broker.hivemq.com:8883/[cluster_id]/status. The message format is:
{"online":bool,"hostname":"hostname.domain","timestamp":unix_timestamp,"location":{"json"}}.
At intervals defined in the configuration file, system information, such as CPU load and available memory, is sent via a POST request to
broker.hivemq.com:8883/[cluster_id]/metrics3. The message format is:
{cpu_percent":float,"mem_used_bytes":int,"mem_total_bytes":int,"disk_used_bytes":int,"disk_total_bytes":int,"load_1m":float,"load_5m":float,"load_15m":float,"uptime_secs":int,"hostname":"hostname.domain","timestamp":unix_timestamp}.
The backdoor sends GET requests to
broker.hivemq.com:8883/[cluster_id]/cmd/req to retrieve commands from the C2 server. The server responds in the format:
{"cmd_id":int,"command":"str","timeout_secs":int}.
Commands are executed via PowerShell.exe in hidden mode, using the -NonInteractive -NoProfile -Command parameters.
Command execution results are sent to the command server at
broker.hivemq.com:8883/[cluster_id]/cmd/res in the
{"stdout":"str","stderr":"str","exit_code":int,"duration_ms":int} format.
For the second backdoor version, the attackers set up their own Element server running on the Matrix protocol, meet.element[.]tw, as the C2 server. On this server, they created a room used to receive messages containing device information and to send commands for execution on the compromised system. The communication flow is as follows:
Once a connection is successfully established, the backdoor sends an m.bird.status message containing the system’s status. This message format is identical to that used in the HiveMQ version.
At intervals defined in the configuration file, information about the compromised system is sent as an m.bird.metrics message. Field names are slightly different from those in the first version:
{cpu_percent_x100":float,"mem_used_bytes":int,"mem_total_bytes":int,"disk_used_bytes":int,"disk_total_bytes":int,"load_1m_x100":float,"load_5m_x100":float,"load_15m_x100":float,"uptime_secs":int,"hostname":"hostname.domain","timestamp":unix_timestamp}.
This version of the backdoor supports two types of commands, distinguished by the start of the received message.
To set a new interval for sending metrics, the attackers send a message beginning with config:set_interval (accepting values from 5 to 3600 seconds). The new value is saved to the HKLM\Software\SynapseAgent\metrics_interval registry key.
Messages containing commands to execute begin with the string cmd:. Based on data extracted from Element’s SQLite databases on the compromised system, we were able to identify the account name the attackers used to send commands: panel-bot.
Received commands are executed via the Windows command line interface.
Command output is sent as an m.bird.cmd_response message. This message format mirrors the one used in the HiveMQ version.
Takeaways
We have been tracking Toy Ghouls’ activity for quite some time. We previously found that the group had expanded its arsenal with a custom ransomware strain, GenieLocker, and we have now discovered that it has also developed a backdoor capable of giving it full control over an infected device. The new tools use unconventional channels to communicate with their C2 server: the HiveMQ MQTT broker and the Matrix-based Element messenger. This shift away from publicly available open-source projects toward custom-built tools suggests that Toy Ghouls is working to make its attacks more sophisticated and to evade detection for longer.
While monitoring Android threats in June 2026, we discovered a new piece of Android malware. What struck us as unusual was that it installed like an ordinary user app yet made no attempt to disguise itself as legitimate software: it had no user interface at all. This led us to suspect the app might be reaching users’ devices without their knowledge. Further investigation confirmed that hypothesis and allowed us to reconstruct the entire infection chain.
Key findings:
We identified new Android malware: a multi-stage downloader whose ultimate purpose is ad fraud and creation of a proxy botnet.
The malware spread through the built-in updaters of Android-based automotive head unit firmware. This is the first documented case of malware found on a car head unit with an infection chain specific to that type of device.
We attribute this activity, with high confidence, to the MoYu Group, an actor linked to the BADBOX botnet.
Kaspersky solutions detect the threats described below under the following detection names:
HEUR:Trojan-Dropper.AndroidOS.Agent.vu
HEUR:Trojan-Downloader.AndroidOS.Agent.ov
HEUR:Trojan-Proxy.AndroidOS.Zhima.*
HEUR:Trojan.AndroidOS.Vo1d.*
Head unit firmware overview
A head unit is a system that combines multimedia functions with partial control over certain vehicle functions. Head units may come as part of a car’s factory equipment or as an aftermarket upgrade. The main attack vectors for these systems are compromise via physical access and vulnerabilities in the head unit’s OS or components, both of which we’ve covered previously.
In some cases, head units run on Android, primarily because it’s convenient for manufacturers: Android’s source code already accounts for use cases within automotive head units. Android also allows manufacturers to add their own system applications during the build process, which they can use for a range of purposes: customizing the UI, adding system components tailored to the vendor’s needs, and more.
Most apps developed for Android devices can also run on an Android-based head unit, and that is true for malware as well. That said, it’s hard to imagine certain categories of smartphone-targeted malware being used to attack a head unit. Banking Trojans are a good example: since mobile banking is used almost exclusively on smartphones, infecting a head unit with a banking Trojan would be a waste of the attacker’s resources.
It’s worth noting that head units often include SIM card slots and can connect to the internet, enabling features like navigation and software updates. Since a head unit typically holds nothing of value to an attacker, one of the more likely attack scenarios using “classic” Android malware is infecting the device to recruit it into a botnet – similar to attacks on IoT devices.
During our research, we found exactly that kind of malware. The design of firmware for DoFun head units enabled attackers to distribute malware. We notified the vendor about the distribution scheme, and they subsequently reported fixing the security issues.
Below is the entire infection chain:
Head unit infection scheme
Let’s look at exactly how these head units became infected.
The TWCore app
TWCore is a legitimate system application responsible for collecting analytics data and updating the head unit software. Let’s take a closer look at how the update function works.
The process is fairly simple. An MQTT message broker hosted on the subdomain cardoor[.]cn sends a message containing information about the APK files that need to be downloaded and installed on the head unit. Notably, the object describing this message includes an installNotExists field, a Boolean flag that can be set to true or false. This flag allows TWCore to install apps that weren’t originally present on the device.
TWCore only checks whether an app is already installed on the device when installNotExists = false
The APK file is downloaded to <TWCore external cache dir>/push/apk/ for installation.
The path TWCore uses to download APK files
Our telemetry revealed previously unknown malware at these file paths. On top of that, our data indicates that in every observed case, the malware was installed by an app with the package name com.tw.core, which matches the TWCore package name.
Next, we’ll break down the malware installed by TWCore: the JarService dropper.
Stage 1: the JarService dropper
As mentioned earlier, JarService is a small dropper app with no UI of any kind. It decrypts data stored as encrypted blocks within the Trojan’s code. Each block is XOR-encrypted with a single-byte key that shifts linearly from block to block. The decrypted data contains serialized information about the payload version and entry point, along with the malware’s own code for further loading.
Decrypting and deserializing information about the stage 2 payload
In the version of JarService we analyzed, the entry point for the next-stage payload was the wa method of the com.c.j.qbh class.
Stage 2: the loader
This stage’s payload is a malicious loader. Its code contains encrypted strings that are later used as class names to execute the stage 3 payload using the reflection mechanism. The loader sends implant information to one of the attackers’ servers via a POST request. Example of a request to the C2 server:
The Trojan uses the link in the dexUrl field of the data object to download serialized data for loading the next stage. This data begins with a single-byte integer, a key used to decrypt the strings in the loader’s code. Immediately following this number is a four-byte floating-point value used to XOR-decrypt the stage 3 payload, which itself is located after these keys.
Decrypting the stage 3 payload
In the decrypted payload, the entry point is the init method of the com.ast.sdk.BillingMain class, shown in the screenshot below.
Entry point of the stage 3 payload
While analyzing this stage, we noticed that the download link for the next-stage payload includes a version number. We decided to try other version numbers to retrieve different payload versions, and ultimately obtained seven distinct variants, which we list under “Indicators of Compromise” at the end of this report. The earliest version, numbered 3.57, uses a different decoding algorithm than the one described above. This may indicate that an earlier version of the infection chain used a different loader between JarService and the stage 3 payload.
Stage 3: clicker / reverse proxy loader
In this stage, the malware sends a POST request to /cpc/api/task every 90 minutes by default, containing information about the infected device (display resolution, device model, the SSID of the connected Wi-Fi network, MAC address, and so on) along with the Trojan’s configuration version. If the configuration is outdated, the C2 server returns an updated configuration containing new C2 addresses and new paths for sending HTTP requests. An example of a response is shown below. Note that at the time of our research, the most up-to-date configuration version was 3.82.
If the configuration version doesn’t need updating, the C2 server instead returns integer command identifiers, which the attackers refer to as productId. The Trojan maps each identifier to command information, which it stores as a serialized JSON object using the SharedPreferences API. Each identifier also has its own version, expressed as a UNIX timestamp. If the C2 response includes an unknown productId or one whose version is outdated, the malware sends a GET request to the attackers’ server at /cpc/api/xml to retrieve the command contents for all such identifiers. The C2 server responds with command information for each unknown identifier. An example of a response is shown below.
The command information includes a tagName field, which is the command name. The code maps each name to the corresponding class responsible for executing it.
List of executable commands
At the time of our research, the attackers had implemented nine commands. The table below lists command names, brief descriptions, and arguments. The functionality of these commands suggests that the malware can be used to display ads, commit ad fraud (serving as a clicker), and download additional malicious code.
Command name
Description
Arguments
return
Return a value from SharedPreferences.
key: the key whose value should be returned
copy
Set the contents of the clipboard.
text: the key whose value from SharedPreferences is returned as the clipboard contents url: a link for downloading gzip-compressed data (optional); this data is then concatenated with the value of the text key, with (5 spaces) used as a separator
http
Make a POST/GET HTTP request to a specified resource and, if instructed, save the response in SharedPreferences under a specified key.
url: the resource address method: the HTTP method name (optional) startLabel: a marker for the start of the data to save from the resource (optional) endLabel: a marker for the end of the data to save from the resource (optional) valueLabel: the key under which to save the value (optional) header: a dictionary of headers for the HTTP request (optional) content: the content of the POST request (optional)
web
Open a link in the WebView and execute arbitrary JavaScript code within it.
url: the link to open in the WebView js: base64-encoded JavaScript code to execute in the WebView; used when the url parameter is empty or absent corejs: JavaScript code to execute when the resource loads in the WebView (optional) param: a string dictionary of parameters for launching the WebView client: if this key is present, WebViewClient is used to handle redirects manually time: task timeout
loadlib
Not fully implemented at the time of publishing this report.
–
loadlib2
Download and execute arbitrary code.
url: the address to download the payload from name: the name of the module being downloaded md5: the MD5 hash of the payload clear: a comma-separated list of payload names to delete (optional) params: an array of parameters to launch the payload with className: the class name of the payload entry point method: the name of the virtual method at the payload entry point cmethod: the name of the static method used to instantiate the entry-point class (optional) thread: a flag; the payload runs in a separate thread if this flag is not set reload: a flag that, when set, restarts already loaded modules
loadlib3
Not fully implemented at the time of publishing this report.
–
deeplink
Open a resource in the browser.
url: a link to the resource
traceroute
Check resource availability via an ICMP ping.
host: comma-separated list of resources to check
However, attackers use only a relatively small subset of these commands in real-world attacks. As shown in the example C2 response above, at the time of publishing this report the attackers were using the loadlib2 and http commands. The payload downloaded via the loadlib2 command is a reverse proxy module named “zhima”, which researchers from the Nokia Deepfield Emergency Response Team independently discovered in TV set-top boxes around the same time as we did and also described in their report. This confirms that the attackers’ ultimate goal is building a proxy botnet.
While investigating this stage of the attack chain, we noticed that the zhima download link also included a version number. As with the previous stage, we tried other possible version numbers and found eight variants of the zhima module, the earliest of which was version 57. The complete list of identified zhima modules is provided under “Indicators of Compromise” below.
Attribution
While analyzing the complete infection chain, we noticed that the stage 2 loader created a thread with the meaningful name mosdk-host-loader. We decided to investigate what mosdk referred to in that name. This led us to a malicious app installed on various TV set-top boxes with the package name com.abc.nexus (3AD4BF5A86D26FFBF09CAE42AF330A98). It consists of several components (including a dropper similar to JarService), each used by the attackers to covertly monetize the device’s computing power. Each malicious component in the app corresponds to its own service, and the service containing the launch code for the JarService-like dropper is named AdmoyuService. In light of this and the name of the malicious thread found in the payload code, we concluded that moyu in the service name referred to MoYu Group, one of the actors linked to the BADBOX malware platform, which had been described by researchers at HUMAN. This assessment is further supported by extensive overlap between the malware’s network infrastructure and that of MoYu Group, which was independently identified by researchers from the Nokia Deepfield Emergency Response Team around the same time as our own research. Based on these similar naming patterns and prominent infrastructure overlap between the activity of MoYu Group and the attacks described in this report, we attribute it to the same actor with high confidence.
While investigating the malware downloaded by TWCore, we noticed that the domain admin.uipoxy[.]com resolved to the IP address 128.14.210[.]58, one of the C2 servers for the zhima reverse proxy module. It appears that the URL hxxp://admin.uipoxy[.]com/proxy/u/login hosts the zhima admin panel. Interestingly, this panel allows anyone to register as long as they have a valid invite code.
The malware operator registration page
During registration, users are prompted to review the terms of use and privacy policy. Both documents are hosted on links under the pxyedge[.]com domain, which belongs to PXYEDGE, a vendor specializing in the sale of residential proxies.
We found several similarities in the authentication APIs across all of these sites:
The sign-in page was hosted on an admin.* subdomain.
The sign-in page was located at /proxy/u/login.
The signup page was located at /proxy/register?channelKey=<invitation code>.
Based on this, we believe these services are connected to MoYu Group.
Conclusion
Despite efforts by cybersecurity professionals and law enforcement to shut down the BADBOX botnet, individual actors linked to it continue their malicious activity, infecting devices worldwide. Delivery methods for this kind of malware vary widely, from downloads via pre-installed backdoors to infected builds of IPTV apps. The case examined here demonstrates an even more sophisticated delivery method: distribution through the legitimate update functionality of a system application. Attackers are also actively expanding into new platforms. This malware is the first known malicious app targeting head units, which means these platforms now require protection against malware as well.