A Ukrainian man who carried out ransomware attacks for both the Nefilim and Hive gangs, including attacks on law firms and a healthcare provider during the COVID-19 pandemic, has been sentenced to 70 months in a US prison.
Read more in my article on the Hot for Security blog.
Sead Fadilpašić reports: Japanese internet giant GMO Group has told its customers to scan their computers for viruses, back up all of their data, disconnect the devices from the internet and then shut them down. The announcement, which literally told customers, “unplug any cords connected to your PC,” was made in response to a cyberattack…
Cron and its spares. Clear root’s crontab in one go, so the restore and watchdog lines are removed. Then kill any running zen2, devnull2, or serv processes; killed earlier, the watchdog restarts them within a minute. Remove the k.php beacons (/var/lib/asterisk/bin/zen2, devnull2, zen222, devnull312, devnull212, /tmp/.cache/update, /dev/shm/.systemd/kernel). Unlock (chattr -i, -a) and delete the eight crontab backups: /usr/lib/.systemd/.cache, /usr/share/.fonts/.config, /var/lib/.dbus/.data, /var/tmp/.X11/.Xauthority, /opt/.local/.bashrc, /var/cache/.fontconfig/.cfg, /etc/.ssl/.cert, and /root/.config/.pref.
Login and boot triggers. Remove the base64 stager from /root/.bash_profile, /root/.bashrc, and /etc/rc.local.
Accounts and SSH. Remove every extra UID-0 account and any account whose /etc/shadow hash begins with $1$nRz1Cbtk$ (legitimate service accounts with that hash need new passwords, not deletion). Restore sshd_config, where root login was re-enabled.
Web application. Remove the 12 webshell copies, the .htaccess, the freepbxusers row in ampusers, and /var/www/html/admin/modules/freepbx_ha/license.php.
Credentials. Only now rotate the secrets in the 17 files and across every account on the host.
Conclusion
The hard part of the campaign used to be getting the implant off the box, and that has not changed. What has changed is that the box is no longer the whole problem: an organization that rebuilds but does not rotate has removed the operator’s calling platform and handed over its carrier account.
The code ties this generation to the actor in our May report through the takeover script, backdoor hash, and staging host, and a gate rotated in step across JOMANGY and ZenharR.
The operator is now rotating exactly the strings defenders were given, so detections built on provenance and behavior will outlast this build, and detections built on its hashes will not. CRIL continues to monitor INJ3CTOR3’s infrastructure.
Indicators of Compromise
Hashes and strings rotate between builds; use them to confirm a finding, not as your only detection.
Network: outbound HTTP POSTs from a PBX to any /hima_data/ path or to either staging host. A PBX seldom needs outbound web access beyond vendor updates, so egress allow-listing closes this channel.
Carrier: alert on trunk registrations from outside your own IP ranges, and on premium or international destinations, off-hours calling, and unusual concurrency. Set spend caps and destination blocks where your carrier offers them.
For Law Enforcement Agencies and CERTs
By design, victims’ credentials were collected on the two staging hosts, which stopped responding in late September 2026. Going offline does not end exposure: hosting providers may still retain server data, and infected PBXs keep trying to upload their credentials every minute to addresses that could be reassigned to someone else.
Both are reasons for law enforcement and national CERTs, which have the legal standing that researchers lack, to work with the providers on preserving that data and sinkholing the addresses, which would also identify victims for notification. We saw the uploads in code; we did not capture one in transit and hold no victim list.
Two Staging Hosts, Split by Encoding
An infected host now talks to both staging hosts, and which one depends on whether the command was written in plain text or base64. The /x takeover script is byte-identical on both.
Reference
Encoding
Points to
k.php line 2 (re-runs /x), eight cron schedules in /x, wr.php’s callback
Plain text
160[.]119[.]69[.]4
Camouflaged cron payloads, login-triggered stager
Base64
45[.]95[.]147[.]178
Credential upload
Base64
Both
In April, all of these pointed to 45[.]95[.]147[.]178. We assess that the operator replaced the address in plain text and never re-encoded the base64 strings; the credential upload, which names both hosts, appears deliberate. This host is distinct from the scanner 160[.]119[.]76[.]250, which is named in our May report.
Defend: block both hosts in both directions, and decode every base64-encoded string in a crontab or shell profile before deciding which hosts a host talks to. A plain-text review would miss every channel that still reaches 45[.]95[.]147[.]178.
The Webshell: Same Program, New Identifiers
Decoded, the September JOMANGY panel is identical to April’s except for the values defenders were told to look for.
Per-build watermark. Our May report advised hunting for trace_e1ebf9066a951be519a24140711839ea, present in every April copy. The September builds carry trace_bd42a8a0d23ae16c85dede7d2abaec05 (45[.]95[.]147[.]178) and trace_7544725ebaaadac217dfaa7ca8e8fe7e (160[.]119[.]69[.]4), so that hunt now misses current infections.
One gate for two generations. JOMANGY and ZenharR share a single hardcoded MD5 gate hash, rotated together from a2f6863413c4192a4abfa360577b9961 to 8b3a1d970fda0c17a0b785a990fb7b6a. Our May report listed the April value under ZenharR only.
A forged administrator session. Behind the gate, the FreePBX button builds an admin session using FreePBX’s own ampuser class and setAdmin(); in our lab, it opened the full console without any password. Therefore, changing the FreePBX admin password affects neither the gate nor this session. (See Figure 3 below)
Figure 3: The panel’s Freepbx and Elastix buttons forge an admin session without a password
Because each of these identifiers changes between builds, the detection approach later in this report focuses on who put a file on disk rather than on what the file contains.
The .htaccess Door and the Access-Log Gap
k.php writes a seven-line .htaccess to /var/www/html/admin/views/. It does not hide the webshell; it unblocks it, and it lets requests reach a copy without naming it.
Simulating an operator and a victim in an isolated environment, we confirmed that a request to a URL containing an encoded space returned a working webshell for the attacker. The access log records only the requested path, never the file that answered, so searching logs for the webshell byits filename can return nothing on a host that is serving it.
Defend: check every .htaccess across the web root for who put it there; one that no package, FreePBX module, or member of your team accounts for is suspect, whatever it contains. Because the takeover script wipes logs, treat log hunting as secondary: requests with %20, %09, or %0A directly after a slash that return 200 are worth a look where logs survive.
What the FreePBX Console Won’t Show You
FreePBX lists the trunks it created from its own database; Asterisk reads whatever is in its configuration files. In our lab, a trunk we wrote directly into /etc/asterisk/pjsip_custom.conf carried a call while the FreePBX Trunks page reported no records (See Figure 4 below). We did not see the campaign plant one, but its root-equivalent accounts and stolen trunk secrets give the operator everything needed to do so.
Figure 4: Our lab trunk, known to Asterisk and carrying calls, invisible in the FreePBX console
Defend: audit from the Asterisk CLI (pjsip show endpoints, pjsip show registrations, sip show peers, sip show registry) and compare with what the console lists. Review the *_custom.conf files for trunks or contexts that nobody on your team added.
What Keeps It There
Before detecting or cleaning, it helps to see the whole apparatus. Everything in Figure 5 is read from /x and k.php: ways the implant stays on the box, and doors allowing the operator to come back, through a system account or a single web request. Each piece can rebuild/repair all others under a minute, which is why removing any one of them on its own fails. (See Figure 5 below)
Figure 5: Persistence channels that rebuild each other, and the backdoors they stay open
Detecting It Without IOCs
The operator rotates hashes, watermarks, gate values, and hosts between builds, so asking whether a file is known-bad quickly stops working. The better question is who put the file there: compare what is on disk with what the package manager or a clean install says it should be.
On the host: triage with beagle.sh
PBX admins can run beagle.sh, a report-only script that makes no changes to the host, to check a PBX server for signs of compromise. (See Figure 6 below)
Check
What it flags
Packed shells
Several identical, same-size PHP files across unrelated directories
Locked files
chattr +i or +a under the web root or in hidden directories
Unowned .htaccess
An .htaccess no installed package accounts for
Self-rebuilding cron
A cron line that downloads a file and hands it to a shell
Accounts
An extra UID-0 account, or a full-permission FreePBX admin, nobody created
Blocklist
Addresses dropped in and out by iptables outside fail2ban’s chains
Figure 6: beagle.sh (Source: 0x1da5 GitHub)
The script separated one infected server from one clean server of the same FreePBX version, a sample of one, so expect some legitimate findings. However, it cannot tell you whether the credentials have already been left: that happens during the first cron run, within a minute of infection, and long before anyone scans. The upload leaves no file behind, and the takeover script wipes what might have recorded it: the web server, Asterisk, cron and audit logs, plus shell and MySQL histories, which makes forensic timelines hard to reconstruct.
With telemetry
The most distinctive actions leave little on disk; in our detonation, backdoor accounts were created and deleted 27 seconds apart. These eleven behaviors are detection strategies to build into your own tooling:
Layer
Behavior
What it catches
Host
crond spawns a shell that downloads a file and runs it
The takeover script on its single run, before persistence and before credentials leave
Host
The web server account spawns a shell
The webshell’s command box in use
Host
A cron child rewrites the crontab every minute without reading it first
Beacon reinstallation
Host
chattr +i or touch -r on a file in the web root
The lock and the borrowed timestamp
Host
Twelve PHP files were written to the web root in the same second
The webshell copies, as they are written; fan-out
Network
The PBX contacts staging host roughly every 60 seconds
The beacon’s rhythm, whatever the address
Network
Many configuration files read, then outbound POST requests
Credentials leaving
Web
A request with leading encoded whitespace returns 200 for a path not on disk
The .htaccess door
Decoy
Any read of a decoy config file that the implant reads, but nothing legitimate does
Credential collection, with no false positives by design
Phone
Channels originate over AMI or the CLI outside your dial plan
A call asking for nothing legitimate
Phone
CDRs with premium destinations, off-hours calling and unusual concurrency
The fraud itself
The last two catch the fraud rather than the machinery, whatever the files are called, and whether you ever find the shell. The host rows need process telemetry that PBX appliances rarely have, which is part of why so many stay infected.
Why Cleanup Fails, and the Order That Works
First move
Why it fails on its own
Delete the webshell
Cron re-runs k.php within minutes and rewrites all 12 copies, the .htaccess, and the immutable flags.
Patch FreePBX
Sangoma’s advisory for CVE-2025-57819 states the fix prevents new exploitation but does not clean a compromised system.
Change the admin password
The gate and the forged session never use it, and the backdoor accounts bypass the FreePBX login.
Shadowserver data showed more than 900 compromised FreePBX instances in February 2026; over 700 of them were still compromised in May. Rebuilding from a clean image is the most reliable fix; if you clean in place, work from the system layer up, because it replants everything above it.
Preserve first
The takeover script truncates logs, then deletes them, and clears shell and database history, so evidence shrinks with every run:
Cut the host off from the internet, or at least block all outbound traffic except to your SIP carrier. Blocking only the two known staging hosts is not enough if the operator has moved. With no route out, the minute-by-minute downloads fail, and nothing more is uploaded. One local loop survives: a watchdog cron line re-runs the cached copy of k.php at /var/lib/asterisk/bin/zen2 every minute, rebuilding the webshells offline, which is why cron comes first in the cleanup below.
Capture all crontabs and the eight backups listed below, /etc/passwd, /etc/shadow, shell profiles, and the web root with lsattr output and change times.
Export the FreePBX ampusers table, the call records database, and the Asterisk full log, and ask your carrier to preserve CDRs and registration logs.
Then clean, in this order
Cron and its spares. Clear root’s crontab in one go, so the restore and watchdog lines are removed. Then kill any running zen2, devnull2, or serv processes; killed earlier, the watchdog restarts them within a minute. Remove the k.php beacons (/var/lib/asterisk/bin/zen2, devnull2, zen222, devnull312, devnull212, /tmp/.cache/update, /dev/shm/.systemd/kernel). Unlock (chattr -i, -a) and delete the eight crontab backups: /usr/lib/.systemd/.cache, /usr/share/.fonts/.config, /var/lib/.dbus/.data, /var/tmp/.X11/.Xauthority, /opt/.local/.bashrc, /var/cache/.fontconfig/.cfg, /etc/.ssl/.cert, and /root/.config/.pref.
Login and boot triggers. Remove the base64 stager from /root/.bash_profile, /root/.bashrc, and /etc/rc.local.
Accounts and SSH. Remove every extra UID-0 account and any account whose /etc/shadow hash begins with $1$nRz1Cbtk$ (legitimate service accounts with that hash need new passwords, not deletion). Restore sshd_config, where root login was re-enabled.
Web application. Remove the 12 webshell copies, the .htaccess, the freepbxusers row in ampusers, and /var/www/html/admin/modules/freepbx_ha/license.php.
Credentials. Only now rotate the secrets in the 17 files and across every account on the host.
Conclusion
The hard part of the campaign used to be getting the implant off the box, and that has not changed. What has changed is that the box is no longer the whole problem: an organization that rebuilds but does not rotate has removed the operator’s calling platform and handed over its carrier account.
The code ties this generation to the actor in our May report through the takeover script, backdoor hash, and staging host, and a gate rotated in step across JOMANGY and ZenharR.
The operator is now rotating exactly the strings defenders were given, so detections built on provenance and behavior will outlast this build, and detections built on its hashes will not. CRIL continues to monitor INJ3CTOR3’s infrastructure.
Indicators of Compromise
Hashes and strings rotate between builds; use them to confirm a finding, not as your only detection.
Credentials leave on every run. k.php uploads 17 configuration files to both staging hosts every minute the host stays infected.
The fraud no longer needs your PBX. A stolen trunk secret lets the operator register and place calls as you from anywhere.
Last report’s identifiers have expired. Each build now carries its own watermark, and the webshell gate hash was rotated.
Two staging hosts. Plain-text commands now point to 160[.]119[.]69[.]4, while base64-encoded ones still use 45[.]95[.]147[.]178.
Clean-looking logs and consoles prove nothing. Requests through an encoded space reach the shell without being named in the access log, and trunks are written directly to /etc/asterisk; they do not appear in the FreePBX console.
The Campaign So Far
INJ3CTOR3 has been abusing Asterisk-based phone systems for toll fraud since at least 2020. Check Point Research named the operation that November, when it exploited CVE-2019-19006 in FreePBX and used a basic webshell to read amportal.conf and sip_additional.conf by hand. Unit 42 reported a related operation against Elastix in 2022, Fortinet documented the EncystPHP webshell in January 2026, and our May report covered JOMANGY.
The playbook has held for six years: scan for exposed PBXs, exploit or brute-force a way in, plant a password-gated webshell, and dial out at the victim’s expense. Figure 1 below shows the chain as it stands in the September build, now including the credential upload back to the staging hosts.
Figure 1: INJ3CTOR3 attack chain, September generation. (Source: 0x1da5 GitHub)
What Changed Since Our Last Report
We compared the April artifacts behind our May report with the 9 September collection from both staging hosts, after decoding every layer.
Unchanged: the decoded webshell panel, the encoding chain (base64 in the shell script, base64 again in PHP, then ROT13), the persistence channels, the backdoor account password hash, and the borrowed timestamp. Behavioral detections built on our May report still apply.
Credential Exfiltration: The Implant Is No Longer the Product
The September k.php ends with two new base64-encoded lines, each holding 17 curl commands that upload one configuration file apiece to /hima_data/index.php, one line per staging host (See Figure 2 below). The paths are typed out by hand and cover FreePBX, Issabel, and Elastix layouts. The collector path echoes hima, a backdoor account the takeover script creates. The name goes back further: Check Point’s 2020 INJ3CTOR3 report shows a Telegram post by a user called Hima sharing a tool that scans and cracks SIP ranges, saving its output as HimaResults.
Figure 2: k.php line 34, decoded into 17 file uploads
Three of the cron beacons fetch and run k.php every minute (the others every two or three minutes), so the upload repeats every minute during the infection. Rotating credentials on a still-infected host hands the new secrets to the operator within minutes.
The database password (AMPDBPASS), the MySQL root password, and any AMI password stored there
Outbound mail
/etc/postfix/sasl_passwd
The SMTP relay account, with your mail provider
This opens a second route to fraud. Our May report covered calls placed through the webshell, which require the shell to be alive and to land in the victim’s own call records. With a stolen trunk secret, the operator can register to the carrier from any machine and call as the victim, leaving nothing on the host, unless the carrier also restricts the trunk to the victim’s IP addresses. A successful disinfection stops the first route; only rotation stops the second.
Detection
Network: outbound HTTP POSTs from a PBX to any /hima_data/ path or to either staging host. A PBX seldom needs outbound web access beyond vendor updates, so egress allow-listing closes this channel.
Carrier: alert on trunk registrations from outside your own IP ranges, and on premium or international destinations, off-hours calling, and unusual concurrency. Set spend caps and destination blocks where your carrier offers them.
For Law Enforcement Agencies and CERTs
By design, victims’ credentials were collected on the two staging hosts, which stopped responding in late September 2026. Going offline does not end exposure: hosting providers may still retain server data, and infected PBXs keep trying to upload their credentials every minute to addresses that could be reassigned to someone else.
Both are reasons for law enforcement and national CERTs, which have the legal standing that researchers lack, to work with the providers on preserving that data and sinkholing the addresses, which would also identify victims for notification. We saw the uploads in code; we did not capture one in transit and hold no victim list.
Two Staging Hosts, Split by Encoding
An infected host now talks to both staging hosts, and which one depends on whether the command was written in plain text or base64. The /x takeover script is byte-identical on both.
Reference
Encoding
Points to
k.php line 2 (re-runs /x), eight cron schedules in /x, wr.php’s callback
Plain text
160[.]119[.]69[.]4
Camouflaged cron payloads, login-triggered stager
Base64
45[.]95[.]147[.]178
Credential upload
Base64
Both
In April, all of these pointed to 45[.]95[.]147[.]178. We assess that the operator replaced the address in plain text and never re-encoded the base64 strings; the credential upload, which names both hosts, appears deliberate. This host is distinct from the scanner 160[.]119[.]76[.]250, which is named in our May report.
Defend: block both hosts in both directions, and decode every base64-encoded string in a crontab or shell profile before deciding which hosts a host talks to. A plain-text review would miss every channel that still reaches 45[.]95[.]147[.]178.
The Webshell: Same Program, New Identifiers
Decoded, the September JOMANGY panel is identical to April’s except for the values defenders were told to look for.
Per-build watermark. Our May report advised hunting for trace_e1ebf9066a951be519a24140711839ea, present in every April copy. The September builds carry trace_bd42a8a0d23ae16c85dede7d2abaec05 (45[.]95[.]147[.]178) and trace_7544725ebaaadac217dfaa7ca8e8fe7e (160[.]119[.]69[.]4), so that hunt now misses current infections.
One gate for two generations. JOMANGY and ZenharR share a single hardcoded MD5 gate hash, rotated together from a2f6863413c4192a4abfa360577b9961 to 8b3a1d970fda0c17a0b785a990fb7b6a. Our May report listed the April value under ZenharR only.
A forged administrator session. Behind the gate, the FreePBX button builds an admin session using FreePBX’s own ampuser class and setAdmin(); in our lab, it opened the full console without any password. Therefore, changing the FreePBX admin password affects neither the gate nor this session. (See Figure 3 below)
Figure 3: The panel’s Freepbx and Elastix buttons forge an admin session without a password
Because each of these identifiers changes between builds, the detection approach later in this report focuses on who put a file on disk rather than on what the file contains.
The .htaccess Door and the Access-Log Gap
k.php writes a seven-line .htaccess to /var/www/html/admin/views/. It does not hide the webshell; it unblocks it, and it lets requests reach a copy without naming it.
Simulating an operator and a victim in an isolated environment, we confirmed that a request to a URL containing an encoded space returned a working webshell for the attacker. The access log records only the requested path, never the file that answered, so searching logs for the webshell byits filename can return nothing on a host that is serving it.
Defend: check every .htaccess across the web root for who put it there; one that no package, FreePBX module, or member of your team accounts for is suspect, whatever it contains. Because the takeover script wipes logs, treat log hunting as secondary: requests with %20, %09, or %0A directly after a slash that return 200 are worth a look where logs survive.
What the FreePBX Console Won’t Show You
FreePBX lists the trunks it created from its own database; Asterisk reads whatever is in its configuration files. In our lab, a trunk we wrote directly into /etc/asterisk/pjsip_custom.conf carried a call while the FreePBX Trunks page reported no records (See Figure 4 below). We did not see the campaign plant one, but its root-equivalent accounts and stolen trunk secrets give the operator everything needed to do so.
Figure 4: Our lab trunk, known to Asterisk and carrying calls, invisible in the FreePBX console
Defend: audit from the Asterisk CLI (pjsip show endpoints, pjsip show registrations, sip show peers, sip show registry) and compare with what the console lists. Review the *_custom.conf files for trunks or contexts that nobody on your team added.
What Keeps It There
Before detecting or cleaning, it helps to see the whole apparatus. Everything in Figure 5 is read from /x and k.php: ways the implant stays on the box, and doors allowing the operator to come back, through a system account or a single web request. Each piece can rebuild/repair all others under a minute, which is why removing any one of them on its own fails. (See Figure 5 below)
Figure 5: Persistence channels that rebuild each other, and the backdoors they stay open
Detecting It Without IOCs
The operator rotates hashes, watermarks, gate values, and hosts between builds, so asking whether a file is known-bad quickly stops working. The better question is who put the file there: compare what is on disk with what the package manager or a clean install says it should be.
On the host: triage with beagle.sh
PBX admins can run beagle.sh, a report-only script that makes no changes to the host, to check a PBX server for signs of compromise. (See Figure 6 below)
Check
What it flags
Packed shells
Several identical, same-size PHP files across unrelated directories
Locked files
chattr +i or +a under the web root or in hidden directories
Unowned .htaccess
An .htaccess no installed package accounts for
Self-rebuilding cron
A cron line that downloads a file and hands it to a shell
Accounts
An extra UID-0 account, or a full-permission FreePBX admin, nobody created
Blocklist
Addresses dropped in and out by iptables outside fail2ban’s chains
Figure 6: beagle.sh (Source: 0x1da5 GitHub)
The script separated one infected server from one clean server of the same FreePBX version, a sample of one, so expect some legitimate findings. However, it cannot tell you whether the credentials have already been left: that happens during the first cron run, within a minute of infection, and long before anyone scans. The upload leaves no file behind, and the takeover script wipes what might have recorded it: the web server, Asterisk, cron and audit logs, plus shell and MySQL histories, which makes forensic timelines hard to reconstruct.
With telemetry
The most distinctive actions leave little on disk; in our detonation, backdoor accounts were created and deleted 27 seconds apart. These eleven behaviors are detection strategies to build into your own tooling:
Layer
Behavior
What it catches
Host
crond spawns a shell that downloads a file and runs it
The takeover script on its single run, before persistence and before credentials leave
Host
The web server account spawns a shell
The webshell’s command box in use
Host
A cron child rewrites the crontab every minute without reading it first
Beacon reinstallation
Host
chattr +i or touch -r on a file in the web root
The lock and the borrowed timestamp
Host
Twelve PHP files were written to the web root in the same second
The webshell copies, as they are written; fan-out
Network
The PBX contacts staging host roughly every 60 seconds
The beacon’s rhythm, whatever the address
Network
Many configuration files read, then outbound POST requests
Credentials leaving
Web
A request with leading encoded whitespace returns 200 for a path not on disk
The .htaccess door
Decoy
Any read of a decoy config file that the implant reads, but nothing legitimate does
Credential collection, with no false positives by design
Phone
Channels originate over AMI or the CLI outside your dial plan
A call asking for nothing legitimate
Phone
CDRs with premium destinations, off-hours calling and unusual concurrency
The fraud itself
The last two catch the fraud rather than the machinery, whatever the files are called, and whether you ever find the shell. The host rows need process telemetry that PBX appliances rarely have, which is part of why so many stay infected.
Why Cleanup Fails, and the Order That Works
First move
Why it fails on its own
Delete the webshell
Cron re-runs k.php within minutes and rewrites all 12 copies, the .htaccess, and the immutable flags.
Patch FreePBX
Sangoma’s advisory for CVE-2025-57819 states the fix prevents new exploitation but does not clean a compromised system.
Change the admin password
The gate and the forged session never use it, and the backdoor accounts bypass the FreePBX login.
Shadowserver data showed more than 900 compromised FreePBX instances in February 2026; over 700 of them were still compromised in May. Rebuilding from a clean image is the most reliable fix; if you clean in place, work from the system layer up, because it replants everything above it.
Preserve first
The takeover script truncates logs, then deletes them, and clears shell and database history, so evidence shrinks with every run:
Cut the host off from the internet, or at least block all outbound traffic except to your SIP carrier. Blocking only the two known staging hosts is not enough if the operator has moved. With no route out, the minute-by-minute downloads fail, and nothing more is uploaded. One local loop survives: a watchdog cron line re-runs the cached copy of k.php at /var/lib/asterisk/bin/zen2 every minute, rebuilding the webshells offline, which is why cron comes first in the cleanup below.
Capture all crontabs and the eight backups listed below, /etc/passwd, /etc/shadow, shell profiles, and the web root with lsattr output and change times.
Export the FreePBX ampusers table, the call records database, and the Asterisk full log, and ask your carrier to preserve CDRs and registration logs.
Then clean, in this order
Cron and its spares. Clear root’s crontab in one go, so the restore and watchdog lines are removed. Then kill any running zen2, devnull2, or serv processes; killed earlier, the watchdog restarts them within a minute. Remove the k.php beacons (/var/lib/asterisk/bin/zen2, devnull2, zen222, devnull312, devnull212, /tmp/.cache/update, /dev/shm/.systemd/kernel). Unlock (chattr -i, -a) and delete the eight crontab backups: /usr/lib/.systemd/.cache, /usr/share/.fonts/.config, /var/lib/.dbus/.data, /var/tmp/.X11/.Xauthority, /opt/.local/.bashrc, /var/cache/.fontconfig/.cfg, /etc/.ssl/.cert, and /root/.config/.pref.
Login and boot triggers. Remove the base64 stager from /root/.bash_profile, /root/.bashrc, and /etc/rc.local.
Accounts and SSH. Remove every extra UID-0 account and any account whose /etc/shadow hash begins with $1$nRz1Cbtk$ (legitimate service accounts with that hash need new passwords, not deletion). Restore sshd_config, where root login was re-enabled.
Web application. Remove the 12 webshell copies, the .htaccess, the freepbxusers row in ampusers, and /var/www/html/admin/modules/freepbx_ha/license.php.
Credentials. Only now rotate the secrets in the 17 files and across every account on the host.
Conclusion
The hard part of the campaign used to be getting the implant off the box, and that has not changed. What has changed is that the box is no longer the whole problem: an organization that rebuilds but does not rotate has removed the operator’s calling platform and handed over its carrier account.
The code ties this generation to the actor in our May report through the takeover script, backdoor hash, and staging host, and a gate rotated in step across JOMANGY and ZenharR.
The operator is now rotating exactly the strings defenders were given, so detections built on provenance and behavior will outlast this build, and detections built on its hashes will not. CRIL continues to monitor INJ3CTOR3’s infrastructure.
Indicators of Compromise
Hashes and strings rotate between builds; use them to confirm a finding, not as your only detection.
In May, Cyble Research & Intelligence Labs (CRIL) documented JOMANGY, a PHP webshell generation deployed by INJ3CTOR3 with self-healing persistence channels and a built-in toll-fraud dialer.
Back then, the fraud ran through the victim’s own PBX, so removing the implant would have stopped it. Artifacts we collected from the operator’s C2 staging servers during mid-September 2026 show that this is no longer true: the installer now sends credentials off the box on every run, and a second staging host serves it.
INJ3CTOR3’s FreePBX implant now uploads the victim’s SIP trunk, AMI, database, and mail-relay credentials to the operator, so disinfecting or rebuilding an infected host no longer ends the exposure. Anyone running FreePBX, Issabel, or Elastix who suspects compromise must rotate the secrets in 17 configuration files, and only after the host is clean.
The infection and persistence code is nearly unchanged; what the operator rotated are the identifiers that were available to defenders since the last time. Each section below pairs a finding with what to do about it.
Key Takeaways
Credentials leave on every run. k.php uploads 17 configuration files to both staging hosts every minute the host stays infected.
The fraud no longer needs your PBX. A stolen trunk secret lets the operator register and place calls as you from anywhere.
Last report’s identifiers have expired. Each build now carries its own watermark, and the webshell gate hash was rotated.
Two staging hosts. Plain-text commands now point to 160[.]119[.]69[.]4, while base64-encoded ones still use 45[.]95[.]147[.]178.
Clean-looking logs and consoles prove nothing. Requests through an encoded space reach the shell without being named in the access log, and trunks are written directly to /etc/asterisk; they do not appear in the FreePBX console.
The Campaign So Far
INJ3CTOR3 has been abusing Asterisk-based phone systems for toll fraud since at least 2020. Check Point Research named the operation that November, when it exploited CVE-2019-19006 in FreePBX and used a basic webshell to read amportal.conf and sip_additional.conf by hand. Unit 42 reported a related operation against Elastix in 2022, Fortinet documented the EncystPHP webshell in January 2026, and our May report covered JOMANGY.
The playbook has held for six years: scan for exposed PBXs, exploit or brute-force a way in, plant a password-gated webshell, and dial out at the victim’s expense. Figure 1 below shows the chain as it stands in the September build, now including the credential upload back to the staging hosts.
Figure 1: INJ3CTOR3 attack chain, September generation. (Source: 0x1da5 GitHub)
What Changed Since Our Last Report
We compared the April artifacts behind our May report with the 9 September collection from both staging hosts, after decoding every layer.
Unchanged: the decoded webshell panel, the encoding chain (base64 in the shell script, base64 again in PHP, then ROT13), the persistence channels, the backdoor account password hash, and the borrowed timestamp. Behavioral detections built on our May report still apply.
Credential Exfiltration: The Implant Is No Longer the Product
The September k.php ends with two new base64-encoded lines, each holding 17 curl commands that upload one configuration file apiece to /hima_data/index.php, one line per staging host (See Figure 2 below). The paths are typed out by hand and cover FreePBX, Issabel, and Elastix layouts. The collector path echoes hima, a backdoor account the takeover script creates. The name goes back further: Check Point’s 2020 INJ3CTOR3 report shows a Telegram post by a user called Hima sharing a tool that scans and cracks SIP ranges, saving its output as HimaResults.
Figure 2: k.php line 34, decoded into 17 file uploads
Three of the cron beacons fetch and run k.php every minute (the others every two or three minutes), so the upload repeats every minute during the infection. Rotating credentials on a still-infected host hands the new secrets to the operator within minutes.
The database password (AMPDBPASS), the MySQL root password, and any AMI password stored there
Outbound mail
/etc/postfix/sasl_passwd
The SMTP relay account, with your mail provider
This opens a second route to fraud. Our May report covered calls placed through the webshell, which require the shell to be alive and to land in the victim’s own call records. With a stolen trunk secret, the operator can register to the carrier from any machine and call as the victim, leaving nothing on the host, unless the carrier also restricts the trunk to the victim’s IP addresses. A successful disinfection stops the first route; only rotation stops the second.
Detection
Network: outbound HTTP POSTs from a PBX to any /hima_data/ path or to either staging host. A PBX seldom needs outbound web access beyond vendor updates, so egress allow-listing closes this channel.
Carrier: alert on trunk registrations from outside your own IP ranges, and on premium or international destinations, off-hours calling, and unusual concurrency. Set spend caps and destination blocks where your carrier offers them.
For Law Enforcement Agencies and CERTs
By design, victims’ credentials were collected on the two staging hosts, which stopped responding in late September 2026. Going offline does not end exposure: hosting providers may still retain server data, and infected PBXs keep trying to upload their credentials every minute to addresses that could be reassigned to someone else.
Both are reasons for law enforcement and national CERTs, which have the legal standing that researchers lack, to work with the providers on preserving that data and sinkholing the addresses, which would also identify victims for notification. We saw the uploads in code; we did not capture one in transit and hold no victim list.
Two Staging Hosts, Split by Encoding
An infected host now talks to both staging hosts, and which one depends on whether the command was written in plain text or base64. The /x takeover script is byte-identical on both.
Reference
Encoding
Points to
k.php line 2 (re-runs /x), eight cron schedules in /x, wr.php’s callback
Plain text
160[.]119[.]69[.]4
Camouflaged cron payloads, login-triggered stager
Base64
45[.]95[.]147[.]178
Credential upload
Base64
Both
In April, all of these pointed to 45[.]95[.]147[.]178. We assess that the operator replaced the address in plain text and never re-encoded the base64 strings; the credential upload, which names both hosts, appears deliberate. This host is distinct from the scanner 160[.]119[.]76[.]250, which is named in our May report.
Defend: block both hosts in both directions, and decode every base64-encoded string in a crontab or shell profile before deciding which hosts a host talks to. A plain-text review would miss every channel that still reaches 45[.]95[.]147[.]178.
The Webshell: Same Program, New Identifiers
Decoded, the September JOMANGY panel is identical to April’s except for the values defenders were told to look for.
Per-build watermark. Our May report advised hunting for trace_e1ebf9066a951be519a24140711839ea, present in every April copy. The September builds carry trace_bd42a8a0d23ae16c85dede7d2abaec05 (45[.]95[.]147[.]178) and trace_7544725ebaaadac217dfaa7ca8e8fe7e (160[.]119[.]69[.]4), so that hunt now misses current infections.
One gate for two generations. JOMANGY and ZenharR share a single hardcoded MD5 gate hash, rotated together from a2f6863413c4192a4abfa360577b9961 to 8b3a1d970fda0c17a0b785a990fb7b6a. Our May report listed the April value under ZenharR only.
A forged administrator session. Behind the gate, the FreePBX button builds an admin session using FreePBX’s own ampuser class and setAdmin(); in our lab, it opened the full console without any password. Therefore, changing the FreePBX admin password affects neither the gate nor this session. (See Figure 3 below)
Figure 3: The panel’s Freepbx and Elastix buttons forge an admin session without a password
Because each of these identifiers changes between builds, the detection approach later in this report focuses on who put a file on disk rather than on what the file contains.
The .htaccess Door and the Access-Log Gap
k.php writes a seven-line .htaccess to /var/www/html/admin/views/. It does not hide the webshell; it unblocks it, and it lets requests reach a copy without naming it.
Simulating an operator and a victim in an isolated environment, we confirmed that a request to a URL containing an encoded space returned a working webshell for the attacker. The access log records only the requested path, never the file that answered, so searching logs for the webshell byits filename can return nothing on a host that is serving it.
Defend: check every .htaccess across the web root for who put it there; one that no package, FreePBX module, or member of your team accounts for is suspect, whatever it contains. Because the takeover script wipes logs, treat log hunting as secondary: requests with %20, %09, or %0A directly after a slash that return 200 are worth a look where logs survive.
What the FreePBX Console Won’t Show You
FreePBX lists the trunks it created from its own database; Asterisk reads whatever is in its configuration files. In our lab, a trunk we wrote directly into /etc/asterisk/pjsip_custom.conf carried a call while the FreePBX Trunks page reported no records (See Figure 4 below). We did not see the campaign plant one, but its root-equivalent accounts and stolen trunk secrets give the operator everything needed to do so.
Figure 4: Our lab trunk, known to Asterisk and carrying calls, invisible in the FreePBX console
Defend: audit from the Asterisk CLI (pjsip show endpoints, pjsip show registrations, sip show peers, sip show registry) and compare with what the console lists. Review the *_custom.conf files for trunks or contexts that nobody on your team added.
What Keeps It There
Before detecting or cleaning, it helps to see the whole apparatus. Everything in Figure 5 is read from /x and k.php: ways the implant stays on the box, and doors allowing the operator to come back, through a system account or a single web request. Each piece can rebuild/repair all others under a minute, which is why removing any one of them on its own fails. (See Figure 5 below)
Figure 5: Persistence channels that rebuild each other, and the backdoors they stay open
Detecting It Without IOCs
The operator rotates hashes, watermarks, gate values, and hosts between builds, so asking whether a file is known-bad quickly stops working. The better question is who put the file there: compare what is on disk with what the package manager or a clean install says it should be.
On the host: triage with beagle.sh
PBX admins can run beagle.sh, a report-only script that makes no changes to the host, to check a PBX server for signs of compromise. (See Figure 6 below)
Check
What it flags
Packed shells
Several identical, same-size PHP files across unrelated directories
Locked files
chattr +i or +a under the web root or in hidden directories
Unowned .htaccess
An .htaccess no installed package accounts for
Self-rebuilding cron
A cron line that downloads a file and hands it to a shell
Accounts
An extra UID-0 account, or a full-permission FreePBX admin, nobody created
Blocklist
Addresses dropped in and out by iptables outside fail2ban’s chains
Figure 6: beagle.sh (Source: 0x1da5 GitHub)
The script separated one infected server from one clean server of the same FreePBX version, a sample of one, so expect some legitimate findings. However, it cannot tell you whether the credentials have already been left: that happens during the first cron run, within a minute of infection, and long before anyone scans. The upload leaves no file behind, and the takeover script wipes what might have recorded it: the web server, Asterisk, cron and audit logs, plus shell and MySQL histories, which makes forensic timelines hard to reconstruct.
With telemetry
The most distinctive actions leave little on disk; in our detonation, backdoor accounts were created and deleted 27 seconds apart. These eleven behaviors are detection strategies to build into your own tooling:
Layer
Behavior
What it catches
Host
crond spawns a shell that downloads a file and runs it
The takeover script on its single run, before persistence and before credentials leave
Host
The web server account spawns a shell
The webshell’s command box in use
Host
A cron child rewrites the crontab every minute without reading it first
Beacon reinstallation
Host
chattr +i or touch -r on a file in the web root
The lock and the borrowed timestamp
Host
Twelve PHP files were written to the web root in the same second
The webshell copies, as they are written; fan-out
Network
The PBX contacts staging host roughly every 60 seconds
The beacon’s rhythm, whatever the address
Network
Many configuration files read, then outbound POST requests
Credentials leaving
Web
A request with leading encoded whitespace returns 200 for a path not on disk
The .htaccess door
Decoy
Any read of a decoy config file that the implant reads, but nothing legitimate does
Credential collection, with no false positives by design
Phone
Channels originate over AMI or the CLI outside your dial plan
A call asking for nothing legitimate
Phone
CDRs with premium destinations, off-hours calling and unusual concurrency
The fraud itself
The last two catch the fraud rather than the machinery, whatever the files are called, and whether you ever find the shell. The host rows need process telemetry that PBX appliances rarely have, which is part of why so many stay infected.
Why Cleanup Fails, and the Order That Works
First move
Why it fails on its own
Delete the webshell
Cron re-runs k.php within minutes and rewrites all 12 copies, the .htaccess, and the immutable flags.
Patch FreePBX
Sangoma’s advisory for CVE-2025-57819 states the fix prevents new exploitation but does not clean a compromised system.
Change the admin password
The gate and the forged session never use it, and the backdoor accounts bypass the FreePBX login.
Shadowserver data showed more than 900 compromised FreePBX instances in February 2026; over 700 of them were still compromised in May. Rebuilding from a clean image is the most reliable fix; if you clean in place, work from the system layer up, because it replants everything above it.
Preserve first
The takeover script truncates logs, then deletes them, and clears shell and database history, so evidence shrinks with every run:
Cut the host off from the internet, or at least block all outbound traffic except to your SIP carrier. Blocking only the two known staging hosts is not enough if the operator has moved. With no route out, the minute-by-minute downloads fail, and nothing more is uploaded. One local loop survives: a watchdog cron line re-runs the cached copy of k.php at /var/lib/asterisk/bin/zen2 every minute, rebuilding the webshells offline, which is why cron comes first in the cleanup below.
Capture all crontabs and the eight backups listed below, /etc/passwd, /etc/shadow, shell profiles, and the web root with lsattr output and change times.
Export the FreePBX ampusers table, the call records database, and the Asterisk full log, and ask your carrier to preserve CDRs and registration logs.
Then clean, in this order
Cron and its spares. Clear root’s crontab in one go, so the restore and watchdog lines are removed. Then kill any running zen2, devnull2, or serv processes; killed earlier, the watchdog restarts them within a minute. Remove the k.php beacons (/var/lib/asterisk/bin/zen2, devnull2, zen222, devnull312, devnull212, /tmp/.cache/update, /dev/shm/.systemd/kernel). Unlock (chattr -i, -a) and delete the eight crontab backups: /usr/lib/.systemd/.cache, /usr/share/.fonts/.config, /var/lib/.dbus/.data, /var/tmp/.X11/.Xauthority, /opt/.local/.bashrc, /var/cache/.fontconfig/.cfg, /etc/.ssl/.cert, and /root/.config/.pref.
Login and boot triggers. Remove the base64 stager from /root/.bash_profile, /root/.bashrc, and /etc/rc.local.
Accounts and SSH. Remove every extra UID-0 account and any account whose /etc/shadow hash begins with $1$nRz1Cbtk$ (legitimate service accounts with that hash need new passwords, not deletion). Restore sshd_config, where root login was re-enabled.
Web application. Remove the 12 webshell copies, the .htaccess, the freepbxusers row in ampusers, and /var/www/html/admin/modules/freepbx_ha/license.php.
Credentials. Only now rotate the secrets in the 17 files and across every account on the host.
Conclusion
The hard part of the campaign used to be getting the implant off the box, and that has not changed. What has changed is that the box is no longer the whole problem: an organization that rebuilds but does not rotate has removed the operator’s calling platform and handed over its carrier account.
The code ties this generation to the actor in our May report through the takeover script, backdoor hash, and staging host, and a gate rotated in step across JOMANGY and ZenharR.
The operator is now rotating exactly the strings defenders were given, so detections built on provenance and behavior will outlast this build, and detections built on its hashes will not. CRIL continues to monitor INJ3CTOR3’s infrastructure.
Indicators of Compromise
Hashes and strings rotate between builds; use them to confirm a finding, not as your only detection.
In May, Cyble Research & Intelligence Labs (CRIL) documented JOMANGY, a PHP webshell generation deployed by INJ3CTOR3 with self-healing persistence channels and a built-in toll-fraud dialer.
Back then, the fraud ran through the victim’s own PBX, so removing the implant would have stopped it. Artifacts we collected from the operator’s C2 staging servers during mid-September 2026 show that this is no longer true: the installer now sends credentials off the box on every run, and a second staging host serves it.
INJ3CTOR3’s FreePBX implant now uploads the victim’s SIP trunk, AMI, database, and mail-relay credentials to the operator, so disinfecting or rebuilding an infected host no longer ends the exposure. Anyone running FreePBX, Issabel, or Elastix who suspects compromise must rotate the secrets in 17 configuration files, and only after the host is clean.
The infection and persistence code is nearly unchanged; what the operator rotated are the identifiers that were available to defenders since the last time. Each section below pairs a finding with what to do about it.
Key Takeaways
Credentials leave on every run. k.php uploads 17 configuration files to both staging hosts every minute the host stays infected.
The fraud no longer needs your PBX. A stolen trunk secret lets the operator register and place calls as you from anywhere.
Last report’s identifiers have expired. Each build now carries its own watermark, and the webshell gate hash was rotated.
Two staging hosts. Plain-text commands now point to 160[.]119[.]69[.]4, while base64-encoded ones still use 45[.]95[.]147[.]178.
Clean-looking logs and consoles prove nothing. Requests through an encoded space reach the shell without being named in the access log, and trunks are written directly to /etc/asterisk; they do not appear in the FreePBX console.
The Campaign So Far
INJ3CTOR3 has been abusing Asterisk-based phone systems for toll fraud since at least 2020. Check Point Research named the operation that November, when it exploited CVE-2019-19006 in FreePBX and used a basic webshell to read amportal.conf and sip_additional.conf by hand. Unit 42 reported a related operation against Elastix in 2022, Fortinet documented the EncystPHP webshell in January 2026, and our May report covered JOMANGY.
The playbook has held for six years: scan for exposed PBXs, exploit or brute-force a way in, plant a password-gated webshell, and dial out at the victim’s expense. Figure 1 below shows the chain as it stands in the September build, now including the credential upload back to the staging hosts.
Figure 1: INJ3CTOR3 attack chain, September generation. (Source: 0x1da5 GitHub)
What Changed Since Our Last Report
We compared the April artifacts behind our May report with the 9 September collection from both staging hosts, after decoding every layer.
Unchanged: the decoded webshell panel, the encoding chain (base64 in the shell script, base64 again in PHP, then ROT13), the persistence channels, the backdoor account password hash, and the borrowed timestamp. Behavioral detections built on our May report still apply.
Credential Exfiltration: The Implant Is No Longer the Product
The September k.php ends with two new base64-encoded lines, each holding 17 curl commands that upload one configuration file apiece to /hima_data/index.php, one line per staging host (See Figure 2 below). The paths are typed out by hand and cover FreePBX, Issabel, and Elastix layouts. The collector path echoes hima, a backdoor account the takeover script creates. The name goes back further: Check Point’s 2020 INJ3CTOR3 report shows a Telegram post by a user called Hima sharing a tool that scans and cracks SIP ranges, saving its output as HimaResults.
Figure 2: k.php line 34, decoded into 17 file uploads
Three of the cron beacons fetch and run k.php every minute (the others every two or three minutes), so the upload repeats every minute during the infection. Rotating credentials on a still-infected host hands the new secrets to the operator within minutes.
The database password (AMPDBPASS), the MySQL root password, and any AMI password stored there
Outbound mail
/etc/postfix/sasl_passwd
The SMTP relay account, with your mail provider
This opens a second route to fraud. Our May report covered calls placed through the webshell, which require the shell to be alive and to land in the victim’s own call records. With a stolen trunk secret, the operator can register to the carrier from any machine and call as the victim, leaving nothing on the host, unless the carrier also restricts the trunk to the victim’s IP addresses. A successful disinfection stops the first route; only rotation stops the second.
Detection
Network: outbound HTTP POSTs from a PBX to any /hima_data/ path or to either staging host. A PBX seldom needs outbound web access beyond vendor updates, so egress allow-listing closes this channel.
Carrier: alert on trunk registrations from outside your own IP ranges, and on premium or international destinations, off-hours calling, and unusual concurrency. Set spend caps and destination blocks where your carrier offers them.
For Law Enforcement Agencies and CERTs
By design, victims’ credentials were collected on the two staging hosts, which stopped responding in late September 2026. Going offline does not end exposure: hosting providers may still retain server data, and infected PBXs keep trying to upload their credentials every minute to addresses that could be reassigned to someone else.
Both are reasons for law enforcement and national CERTs, which have the legal standing that researchers lack, to work with the providers on preserving that data and sinkholing the addresses, which would also identify victims for notification. We saw the uploads in code; we did not capture one in transit and hold no victim list.
Two Staging Hosts, Split by Encoding
An infected host now talks to both staging hosts, and which one depends on whether the command was written in plain text or base64. The /x takeover script is byte-identical on both.
Reference
Encoding
Points to
k.php line 2 (re-runs /x), eight cron schedules in /x, wr.php’s callback
Plain text
160[.]119[.]69[.]4
Camouflaged cron payloads, login-triggered stager
Base64
45[.]95[.]147[.]178
Credential upload
Base64
Both
In April, all of these pointed to 45[.]95[.]147[.]178. We assess that the operator replaced the address in plain text and never re-encoded the base64 strings; the credential upload, which names both hosts, appears deliberate. This host is distinct from the scanner 160[.]119[.]76[.]250, which is named in our May report.
Defend: block both hosts in both directions, and decode every base64-encoded string in a crontab or shell profile before deciding which hosts a host talks to. A plain-text review would miss every channel that still reaches 45[.]95[.]147[.]178.
The Webshell: Same Program, New Identifiers
Decoded, the September JOMANGY panel is identical to April’s except for the values defenders were told to look for.
Per-build watermark. Our May report advised hunting for trace_e1ebf9066a951be519a24140711839ea, present in every April copy. The September builds carry trace_bd42a8a0d23ae16c85dede7d2abaec05 (45[.]95[.]147[.]178) and trace_7544725ebaaadac217dfaa7ca8e8fe7e (160[.]119[.]69[.]4), so that hunt now misses current infections.
One gate for two generations. JOMANGY and ZenharR share a single hardcoded MD5 gate hash, rotated together from a2f6863413c4192a4abfa360577b9961 to 8b3a1d970fda0c17a0b785a990fb7b6a. Our May report listed the April value under ZenharR only.
A forged administrator session. Behind the gate, the FreePBX button builds an admin session using FreePBX’s own ampuser class and setAdmin(); in our lab, it opened the full console without any password. Therefore, changing the FreePBX admin password affects neither the gate nor this session. (See Figure 3 below)
Figure 3: The panel’s Freepbx and Elastix buttons forge an admin session without a password
Because each of these identifiers changes between builds, the detection approach later in this report focuses on who put a file on disk rather than on what the file contains.
The .htaccess Door and the Access-Log Gap
k.php writes a seven-line .htaccess to /var/www/html/admin/views/. It does not hide the webshell; it unblocks it, and it lets requests reach a copy without naming it.
Simulating an operator and a victim in an isolated environment, we confirmed that a request to a URL containing an encoded space returned a working webshell for the attacker. The access log records only the requested path, never the file that answered, so searching logs for the webshell byits filename can return nothing on a host that is serving it.
Defend: check every .htaccess across the web root for who put it there; one that no package, FreePBX module, or member of your team accounts for is suspect, whatever it contains. Because the takeover script wipes logs, treat log hunting as secondary: requests with %20, %09, or %0A directly after a slash that return 200 are worth a look where logs survive.
What the FreePBX Console Won’t Show You
FreePBX lists the trunks it created from its own database; Asterisk reads whatever is in its configuration files. In our lab, a trunk we wrote directly into /etc/asterisk/pjsip_custom.conf carried a call while the FreePBX Trunks page reported no records (See Figure 4 below). We did not see the campaign plant one, but its root-equivalent accounts and stolen trunk secrets give the operator everything needed to do so.
Figure 4: Our lab trunk, known to Asterisk and carrying calls, invisible in the FreePBX console
Defend: audit from the Asterisk CLI (pjsip show endpoints, pjsip show registrations, sip show peers, sip show registry) and compare with what the console lists. Review the *_custom.conf files for trunks or contexts that nobody on your team added.
What Keeps It There
Before detecting or cleaning, it helps to see the whole apparatus. Everything in Figure 5 is read from /x and k.php: ways the implant stays on the box, and doors allowing the operator to come back, through a system account or a single web request. Each piece can rebuild/repair all others under a minute, which is why removing any one of them on its own fails. (See Figure 5 below)
Figure 5: Persistence channels that rebuild each other, and the backdoors they stay open
Detecting It Without IOCs
The operator rotates hashes, watermarks, gate values, and hosts between builds, so asking whether a file is known-bad quickly stops working. The better question is who put the file there: compare what is on disk with what the package manager or a clean install says it should be.
On the host: triage with beagle.sh
PBX admins can run beagle.sh, a report-only script that makes no changes to the host, to check a PBX server for signs of compromise. (See Figure 6 below)
Check
What it flags
Packed shells
Several identical, same-size PHP files across unrelated directories
Locked files
chattr +i or +a under the web root or in hidden directories
Unowned .htaccess
An .htaccess no installed package accounts for
Self-rebuilding cron
A cron line that downloads a file and hands it to a shell
Accounts
An extra UID-0 account, or a full-permission FreePBX admin, nobody created
Blocklist
Addresses dropped in and out by iptables outside fail2ban’s chains
Figure 6: beagle.sh (Source: 0x1da5 GitHub)
The script separated one infected server from one clean server of the same FreePBX version, a sample of one, so expect some legitimate findings. However, it cannot tell you whether the credentials have already been left: that happens during the first cron run, within a minute of infection, and long before anyone scans. The upload leaves no file behind, and the takeover script wipes what might have recorded it: the web server, Asterisk, cron and audit logs, plus shell and MySQL histories, which makes forensic timelines hard to reconstruct.
With telemetry
The most distinctive actions leave little on disk; in our detonation, backdoor accounts were created and deleted 27 seconds apart. These eleven behaviors are detection strategies to build into your own tooling:
Layer
Behavior
What it catches
Host
crond spawns a shell that downloads a file and runs it
The takeover script on its single run, before persistence and before credentials leave
Host
The web server account spawns a shell
The webshell’s command box in use
Host
A cron child rewrites the crontab every minute without reading it first
Beacon reinstallation
Host
chattr +i or touch -r on a file in the web root
The lock and the borrowed timestamp
Host
Twelve PHP files were written to the web root in the same second
The webshell copies, as they are written; fan-out
Network
The PBX contacts staging host roughly every 60 seconds
The beacon’s rhythm, whatever the address
Network
Many configuration files read, then outbound POST requests
Credentials leaving
Web
A request with leading encoded whitespace returns 200 for a path not on disk
The .htaccess door
Decoy
Any read of a decoy config file that the implant reads, but nothing legitimate does
Credential collection, with no false positives by design
Phone
Channels originate over AMI or the CLI outside your dial plan
A call asking for nothing legitimate
Phone
CDRs with premium destinations, off-hours calling and unusual concurrency
The fraud itself
The last two catch the fraud rather than the machinery, whatever the files are called, and whether you ever find the shell. The host rows need process telemetry that PBX appliances rarely have, which is part of why so many stay infected.
Why Cleanup Fails, and the Order That Works
First move
Why it fails on its own
Delete the webshell
Cron re-runs k.php within minutes and rewrites all 12 copies, the .htaccess, and the immutable flags.
Patch FreePBX
Sangoma’s advisory for CVE-2025-57819 states the fix prevents new exploitation but does not clean a compromised system.
Change the admin password
The gate and the forged session never use it, and the backdoor accounts bypass the FreePBX login.
Shadowserver data showed more than 900 compromised FreePBX instances in February 2026; over 700 of them were still compromised in May. Rebuilding from a clean image is the most reliable fix; if you clean in place, work from the system layer up, because it replants everything above it.
Preserve first
The takeover script truncates logs, then deletes them, and clears shell and database history, so evidence shrinks with every run:
Cut the host off from the internet, or at least block all outbound traffic except to your SIP carrier. Blocking only the two known staging hosts is not enough if the operator has moved. With no route out, the minute-by-minute downloads fail, and nothing more is uploaded. One local loop survives: a watchdog cron line re-runs the cached copy of k.php at /var/lib/asterisk/bin/zen2 every minute, rebuilding the webshells offline, which is why cron comes first in the cleanup below.
Capture all crontabs and the eight backups listed below, /etc/passwd, /etc/shadow, shell profiles, and the web root with lsattr output and change times.
Export the FreePBX ampusers table, the call records database, and the Asterisk full log, and ask your carrier to preserve CDRs and registration logs.
Then clean, in this order
Cron and its spares. Clear root’s crontab in one go, so the restore and watchdog lines are removed. Then kill any running zen2, devnull2, or serv processes; killed earlier, the watchdog restarts them within a minute. Remove the k.php beacons (/var/lib/asterisk/bin/zen2, devnull2, zen222, devnull312, devnull212, /tmp/.cache/update, /dev/shm/.systemd/kernel). Unlock (chattr -i, -a) and delete the eight crontab backups: /usr/lib/.systemd/.cache, /usr/share/.fonts/.config, /var/lib/.dbus/.data, /var/tmp/.X11/.Xauthority, /opt/.local/.bashrc, /var/cache/.fontconfig/.cfg, /etc/.ssl/.cert, and /root/.config/.pref.
Login and boot triggers. Remove the base64 stager from /root/.bash_profile, /root/.bashrc, and /etc/rc.local.
Accounts and SSH. Remove every extra UID-0 account and any account whose /etc/shadow hash begins with $1$nRz1Cbtk$ (legitimate service accounts with that hash need new passwords, not deletion). Restore sshd_config, where root login was re-enabled.
Web application. Remove the 12 webshell copies, the .htaccess, the freepbxusers row in ampusers, and /var/www/html/admin/modules/freepbx_ha/license.php.
Credentials. Only now rotate the secrets in the 17 files and across every account on the host.
Conclusion
The hard part of the campaign used to be getting the implant off the box, and that has not changed. What has changed is that the box is no longer the whole problem: an organization that rebuilds but does not rotate has removed the operator’s calling platform and handed over its carrier account.
The code ties this generation to the actor in our May report through the takeover script, backdoor hash, and staging host, and a gate rotated in step across JOMANGY and ZenharR.
The operator is now rotating exactly the strings defenders were given, so detections built on provenance and behavior will outlast this build, and detections built on its hashes will not. CRIL continues to monitor INJ3CTOR3’s infrastructure.
Indicators of Compromise
Hashes and strings rotate between builds; use them to confirm a finding, not as your only detection.
Malware Service Report Loader Defense Evasion Anti-Analysis Unverified
Spectator Loader Advertised as FUD Resident Loader With Anti-Analysis and Evasion Features
ServiceSpectator Loader
AV products referenced16
Delivery optionsEXE / MSI / sideload
ContactTox
Severity HIGH
Overview
An actor using the handle “Madrex” is advertising a malware service called Spectator Loader. The Russian-language post describes it as a private resident loader built for long-term persistence, resistance to blocking and evasion of modern security products.
The advertisement claims the service can be delivered through sideloading or installer formats, uses custom obfuscation, anti-sandbox checks and low-level system-call techniques, and is designed to reduce static and behavioral detection. It also advertises remote-management capabilities such as system-information collection, in-memory payload execution, file transfer, process control, registry operations and filesystem management. These are actor claims from the advertisement and have not been independently validated.
Delivery methods advertisedSideloading, EXE, MSI and custom formats
Security products referenced16 consumer AV products
Contact methodTox
Observed date
What the post advertises
Private resident loader
Long-term persistence focus
FUD / low-detection positioning
Sideloading and installer delivery options
Custom metamorphic obfuscation
Anti-sandbox and anti-analysis checks
Claims resistance to static signatures
Claims reduced behavioral detection
Low-level syscall-based execution claims
Traffic imitation / trusted relay claims
System-information collection
In-memory payload execution
File download capability
Execution of multiple file/script formats
Registry manipulation
Process creation and termination
Filesystem management
Operator support advertised
The post also claims its protection stack can detect sandboxes, reverse-engineering tools, hypervisors and virtual machines, and that builds are altered to reduce similarity between samples. These statements are summarized as service claims and are not reproduced as implementation guidance.
The supplied captures show the Spectator Loader advertisement, including FUD claims, antivirus and anti-analysis positioning, obfuscation and sandbox-evasion claims, network-resilience claims, remote-management features and a Tox contact identifier.
IOCs & identifiers
Identifiers directly visible in the supplied material. These support correlation and do not independently verify the advertised capabilities.
No malware hash, Session ID, Telegram handle, attacker-controlled domain or attacker-controlled IP address is visible in the supplied screenshots.
Mapped techniques
Claimed identifies behavior explicitly advertised by the actor. Inferred identifies an analytical mapping supported by the supplied material. Neither label means the capability has been independently validated.
Defense Evasion T1027 Obfuscated Files or Information Claimed The advertisement explicitly promotes custom obfuscation and build-to-build variation intended to reduce static detection.
Defense Evasion T1497 Virtualization/Sandbox Evasion Claimed The post claims the loader can detect sandboxes, virtual machines, hypervisors and reverse-engineering environments.
Command and Control T1105 Ingress Tool Transfer Inferred The advertised management feature set includes downloading files to the compromised host.
Potential impact
If the advertised capabilities are genuine, Spectator Loader could provide operators with a stealth-focused foothold that supports payload delivery, in-memory execution, file transfer, process control, registry modification and filesystem access. Its anti-analysis and evasion claims could also increase the time required for defenders to identify and investigate malicious activity.
Status Unverified
Dark Web Informer has not independently verified the loader’s detection-evasion claims, supported delivery methods, anti-sandbox behavior, network resilience or remote-management functionality. The supplied screenshots document the advertisement and visible contact information only.
Want everything on this threat? Paid subscribers get the full unredacted claim details and more. After subscribing, check out the threat feed and search there for this alert.
Back in October 2022, Kaspersky GReAT was looking into a peculiar advertisement on a hacking forum. A cybercriminal had been advertising a UEFI bootkit, dubbed BlackLotus, that could bypass Secure Boot, for $5,000. Given the low price for such powerful capabilities, the cybersecurity community initially took this advertisement with a grain of salt. As no BlackLotus samples were found in the wild, no one could at the time verify the claims made in the forum post.
BlackLotus advertisement
However, about half a year later, in March 2023, ESET confirmed that the BlackLotus advertisement was genuine. The researchers tracked down samples of this bootkit and confirmed that it can indeed bypass Secure Boot through a vulnerability called Baton Drop (CVE-2022-21894). As this vulnerability was still exploitable in 2023, Microsoft had to release multiple security updates to address it.
Since the release of these updates, the BlackLotus bootkit has gone quiet — no publicly known attacks have used it. Yet in early 2026, we discovered a malware sample similar to BlackLotus that was used in an APT campaign in Central Asia. In this report, we share our findings, including new payloads and possible attribution of this campaign.
The return of the infamous bootkit
In early 2026 we identified a complex APT attack targeting diplomatic agencies in Central Asian countries — namely Tajikistan, Uzbekistan, and Turkmenistan. This attack involved a UEFI bootkit that we dubbed BlackX. Having examined it, we found it to be similar to BlackLotus.
Attackers have been deploying BlackX after compromising the victim machine networks and obtaining administrator privileges. To install the bootkit, we observed them dropping the following files:
inst.exe, an installer executable
PVMDIJGKAX2, an RC4-encrypted extensionless file containing files and strings required for the installation
The installer was launched with the command line C:\ProgramData\Intel\inst.exe PVMDIJGKAX2 Q46F6MDLI3RX2KV1. The last argument here is the RC4 key used for decryption.
Curiously, the file inst.exe does not contain any malicious strings or embedded blobs. All the files needed to install the malicious payload are inside PVMDIJGKAX2, which makes it harder for incident responders to retrieve the bootkit components.
The installer package drops a set of files to both system and UEFI partitions, designed to exploit the Baton Drop vulnerability. We noted that the implementation of the exploit is remarkably similar to the one used in the BlackLotus malware. As with BlackLotus, the goal of the exploit is to install custom Machine Owner Keys (MOK), which allows loading arbitrary EFI files on the target machines to manipulate Secure Boot.
While the vulnerability exploitation chain was implemented very similarly to BlackLotus, we spotted differences between the exploits in BlackLotus and BlackX. That said, the source code of the BlackLotus bootkit has never been published online. This could mean that the attackers behind BlackX either reverse-engineered the BlackLotus bootkit or purchased its source code from cybercriminals. Either way, this is an interesting observation, as we rarely see APT actors borrow ideas or code from cybercrime actors.
Payload launched by the bootkit
The UEFI payload delivered during the Baton Drop exploitation has a classic bootkit goal — to inject a malicious driver into the Windows kernel. This driver differs from the one deployed by BlackLotus and is signed with a certificate belonging to an organization named 温江区明宇网络技术服务工作室 (English: Wenjiang District Mingyu Network Technology Service Studio). At the time of writing, this certificate (serial number: 0bcdf5e0da32d68900b8b7638459d072) has been revoked by its issuer.
The driver aims to tamper with security solutions installed on the machine. For example, it disables process, thread, and registry callbacks related to security solutions. It also tampers with ETW providers so that security solutions cannot access their output. Curiously, some of the tampering procedures are designed to run only if Kaspersky or Avast solutions are installed on the infected machine. However, during dynamic analysis of this driver, we found that these procedures are useless — they fail to interfere with Kaspersky products in any way.
The driver also protects the bootkit’s files from tampering. It intercepts kernel requests to open these files and returns a handle to calc.exe instead of a malicious file handle.
User-mode second-stage payloads
Apart from tampering with security solutions, the driver also deploys a user-mode payload. We observed two such payloads, used for cyberespionage, in the wild. One of them is a RAT that can load further-stage .NET payloads. This RAT uses cloud storage for C2 server communications — we observed its versions using Google Drive, OneDrive, and Yandex.Disk. Having connected to a cloud storage service, this RAT iterates over files available there and interprets them depending on the file suffix. Below is a list of supported suffixes:
File suffix
Description
Type
0X01
A .NET file. The task is to parse it and run the method TEST.Class1.RunCMD(argument_array) from the module
Input
0X02
Result of the call TEST.Class1.RunCMD(argument_array)
Output
0X03
Task to upload a file to the drive
Input
0X04
Uploaded file
Output
0X05
Task to download a file to an infected machine
Input
0X06
File download status
Output
SSSR
Contains initial system info
Output
We also observed another payload delivered through the bootkit, which we dubbed OxiDLLRunner. It is a malicious DLL obfuscated with OLLVM. It is loaded by the legitimate file googleupdate.exe (original filename: jcef_helper.exe, MD5: AF8A983B9ADA07EAE1259BF2953ABD40), which is also delivered by the bootkit. The DLL decrypts the contents of the file v8_context_snapshot.bin, which resides in the same folder, and then jumps to the beginning of the decrypted content. The decrypted data starts with shellcode followed by a PE file, which is a Rust payload designed to communicate with the www[.]archivetj[.]com C2 server. During communication, this payload is supposed to receive DLL files exporting a function named create_plugin, which are then executed in separate threads.
Attribution
Based on the following facts, we have attributed this attack to the APT41 threat actor with medium confidence:
In June 2026, ESET published a blog post on attacks by the FishMonger threat actor, which the community assumes to be related to APT41. This post describes a kernel implant with a PDB path starting with C:\Users\xdd\Desktop. While describing the campaign, the post also suggests the possible use of an unspecified UEFI bootkit component, which might exploit CVE‑2023‑24932. We observed the identifier “xdd” used multiple times in the BlackX bootkit payloads, so we assess that BlackX is the UEFI bootkit referenced in the ESET blog post;
The payloads deployed by BlackX are loaded using a legitimate executable abused via DLL sideloading, and an encrypted file — this pattern is common among Chinese-speaking threat actors;
The OxiDLLRunner file has been obfuscated with OLLVM, an obfuscator commonly used by Chinese-speaking actors.
Why did the target organizations get infected?
During our initial analysis of BlackX, we thought that attackers succeeded in deploying it due to the negligence of the target organizations. Baton Drop, the vulnerability used to deploy BlackX, was disclosed back in 2023 and was patched, so we first thought that the infected machines lacked the relevant updates. However, our investigation proved this wrong — the targeted machines were fully patched.
Instead, we found out that the problem was with the patch itself. As it turned out, the patch for the Baton Drop vulnerability is designed to revoke the “Windows Production PCA 2011” certificate used to sign vulnerable UEFI components. However, this revocation does not happen automatically upon patch installation — the user has to trigger it manually. This is done with the following steps:
A registry flag is set inside HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Secureboot;
A scheduled task named Secure-Boot-Update is created.
This patch is designed to be applied manually — for instance, applying it automatically could break dual-boot systems relying on the “Windows Production PCA 2011” certificate to load. At the same time, this patch deployment practice creates a problem — as it is extremely unusual for a system administrator to take extra steps after installing an update. Usually, when applying patches, system administrators take no further action unless an installation failure occurs. These instructions for activating the patch are buried deep on the Microsoft website, and we assess that they are quite hard for an average IT administrator to notice.
Curiously, Microsoft’s plan for deploying the Baton Drop patches mentions an “Enforcement Phase” that involves revoking the “Windows Production PCA 2011” certificate automatically, without user interaction. At the time of publishing this post, this stage has not been implemented. On October 19, 2026, this certificate expired, but this does not prevent the vulnerable binaries from loading unless the certificate is blocklisted.
It is likely that the threat actor behind this campaign took into account that few organizations realized the importance of manually activating the Baton Drop patch. As such, we believe they decided to invest time in understanding how BlackLotus works and creating a copycat of this bootkit.
Conclusion
Patch management is important for every organization, as updates from vendors help protect software and hardware against known vulnerabilities. However, sometimes installed patches fail to protect against the flaws they address. The BlackLotus story we described is not the only such example — for instance, in 2025 Microsoft released an update against the ToolShell vulnerability that also had to be applied manually.
However, modern security solutions are designed to account for such cases when installed patches fail to protect against security flaws for one reason or another. For instance, the Exploit Prevention component available in Kaspersky products prevents exploits from running if the patch is not applied, or if it is a zero-day vulnerability with no available patch. It is thus crucial to equip all machines in an organization with security solutions and constantly monitor the alerts they raise.
Imagine buying a brand new hardware wallet for your cryptocurrency with the shrink-wrap still on… what could possibly be hiding inside? We break out a Geiger counter and a cat to find out.
Meanwhile, malicious ads are targeting journalists with zero-click attacks.
Plus, Origin’s Spencer Thompson reveals what AI agents are really getting up to on your employees’ laptops when nobody’s looking.
All this and more in episode 489 of the “Smashing Security” podcast with cybersecurity expert and keynote speaker Graham Cluley, and special guest Lianne Potter.
A 28-year-old Russian man has been arrested by German police, suspected of being a key member of the Qilin ransomware gang. Detectives have linked the man to the attack against blood-testing firm Synnovis, that disrupted London hospitals, and resulted in the death of a patient.
Read more in my article on the Hot for Security blog.
Krybit is a fast-growing ransomware gang that was hacked by a rival, hit back, and is still extorting victims. Find out how it works, who it has targeted, and what organisations can do to protect themselves.