Ongoing FreePBX Attacks: 900+ Web Shells Still Active
Just caught the latest Shadowserver report regarding Sangoma FreePBX instances, and the numbers are concerning. Over 900 systems are still actively hosting web shells following the command injection vulnerability that surfaced in December 2025. With 401 of those instances in the U.S. alone, it’s safe to say a lot of orgs missed the initial patch window or are struggling with remediation.
If you're managing FreePBX deployments, specifically those exposing the web interface publicly, you need to assume compromise if you weren't patched by early January. The attackers are dropping persistent PHP web shells, often obfuscated, to maintain access even if the initial vulnerability is patched later.
I've been checking environments for the standard IOCs. The web shells are frequently found in writable directories like /var/www/html/admin/assets or injected into legitimate core files. You can hunt for recently modified PHP files that don't match the package signature:
# Find PHP files modified in the last 30 days
find /var/www/html -type f -name "*.php" -mtime -30 -ls
Also, grep your `access_log` for the specific POST patterns associated with the exploit, usually targeting the REST API endpoints:
grep "POST /admin/ajax.php" /var/log/httpd/access_log | awk '{print $1}' | sort | uniq -c | sort -nr
Is anyone else seeing resistance from clients to take these PBX servers offline for a full re-image? It seems like everyone wants to just 'delete the bad file' and keep the system running, which feels like a recipe for reinfection given the level of access these shells provide.
We saw a massive spike in traffic attempting to exploit this on our honeypots in mid-January. The scary part isn't just the initial shell, but the secondary payloads—specifically IRC-based botnet loaders that attempt to pivot to the internal LAN.
For detection, we wrote a quick YARA rule to catch the base64-encoded payloads often seen in the eval() calls within these shells.
yara rule FreePBX_Webshell_Gen { strings: $eval = "eval(base64_decode(" nocase $system = "system($_POST[" nocase condition: 1 of them }
Don't bother cleaning. If you find a shell, the box is toast. Re-image from a known good backup and rotate all credentials.
From an MSP perspective, this has been a nightmare. We have several clients running FreePBX 15 on unsupported distros, meaning a standard yum update won't patch the CVE.
We had to isolate the management interfaces behind a VPN and block external access to port 443/80 at the firewall level immediately. It's a band-aid, but it stopped the bleeding.
If you can't patch immediately, I recommend checking admin/modules/framework/core/module_functions.php for unauthorized modifications, as that's a common target for persistence.
Good point on the logs. I've been focusing on the rest.php endpoints specifically. Many of these web shells are being injected via the backup module or the custom extensions context.
If you have Syslog forwarded to a SIEM, run a quick query for anomalous user agents on those endpoints. Attackers often use curl or python-requests instead of a browser.
DeviceProduct == "FreePBX" and UrlPath contains "rest.php" and HttpUserAgent in ("curl", "python-requests", "wget")
It turned up three compromised boxes for us that automated file-integrity monitoring missed.
Validating endpoints is crucial, but you also need to spot the shells fast. For MSPs auditing multiple servers, I run this quick command to find recently modified PHP files in the web root, which usually catches the obfuscated scripts:
find /var/www/html -type f -name "*.php" -mtime -7 -exec ls -l {} \;
It's not perfect, but it highlights files changed in the last week for immediate review.
Rachel's tip on timestamps is a good start, but attackers often use touch to reset file dates. To bypass that, I recommend scanning for specific obfuscation patterns instead. Most web shells rely on eval and base64_decode to hide their intent.
Running this recursive grep usually highlights the worst offenders immediately:
grep -Rl --include="*.php" "eval.*base64_decode" /var/www/html
Verified Access Required
To maintain the integrity of our intelligence feeds, only verified partners and security professionals can post replies.
Request Access