What Happened
Last week, attackers compromised the distribution pipeline for Rank Math PRO, one of the most widely deployed commercial SEO plugins for WordPress. Downloads of the plugin served directly from the vendor's official website were trojanized with malicious code that plants a hidden backdoor on any site where the poisoned package was installed or updated.
This is a supply-chain attack in the purest sense: customers who did everything right — downloading a paid, legitimate plugin from the vendor's own site — received malware. There is no phishing email to blame, no weak credential, no unpatched CVE. The trust relationship between vendor and customer was the attack vector.
If your organization, your clients, or your hosting customers installed or updated Rank Math PRO during the affected window, treat those sites as compromised until proven otherwise. A backdoored WordPress site means full remote code execution as the web server user, which on most shared and VPS hosting translates to database access, credential theft from wp-config.php, lateral movement to co-hosted sites, and a persistent foothold for follow-on payloads.
Why This Matters to Defenders
WordPress powers a substantial share of the public web, and Rank Math has millions of active installs across free and paid tiers. Three characteristics of this incident make it particularly dangerous:
- The payload arrived through a trusted channel. Standard controls — download from official sources, verify it's the "real" plugin, keep plugins updated — all fail when the vendor's own distribution server is the source of the malware.
- Backdoors in WordPress are trivially monetizable. Expect injected SEO spam, credit-card skimmers on WooCommerce checkouts, redirect chains to malware, and resale of access to ransomware affiliates.
- Persistence outlives the plugin. Attackers who achieve code execution on a WordPress host rarely limit themselves to one backdoor. Dropping secondary webshells in
wp-content/uploads/, creating rogue admin users, and abusing cron are standard practice. Simply "updating to the clean version" does not evict an attacker.
Technical Analysis
Affected Component
- Product: Rank Math PRO (commercial tier of the Rank Math SEO plugin for WordPress)
- Distribution channel: Official vendor website / in-dashboard update mechanism pulling from the vendor's servers
- Exposure window: Downloads and updates during the compromise period last week (check the vendor's official statement and the BleepingComputer report for the exact timestamps; if you installed or auto-updated PRO within that window, you are in scope)
- Platforms: Any WordPress deployment — Linux/Apache/Nginx + PHP-FPM, Windows/IIS, managed WordPress hosting, containerized installs
No CVE has been assigned to this incident — this is a distribution compromise, not a vulnerability in the plugin code itself. Do not wait for a CVE to act.
Attack Chain (Defender's View)
- Initial access: The victim installs or updates Rank Math PRO. The tampered package contains a malicious payload not present in the legitimate build.
- Backdoor installation: On plugin load (i.e., the next page request after installation), the malicious code writes a concealed backdoor — typically a PHP file dropped outside the plugin directory (uploads,
mu-plugins, theme directories, or the web root) so it survives plugin updates and removal. - Remote access: The attacker issues HTTP requests to the backdoor, achieving arbitrary PHP execution as the web server account (
www-data,apache,nginx, or the pooled PHP-FPM user). - Post-exploitation: From this foothold, expect credential harvesting from
wp-config.php(database creds, auth salts), creation of hidden administrator accounts, injection of malicious JavaScript into theme files or the database, and establishment of redundant persistence (cron entries, additional webshells, modified core files).
Exploitation Status
This is confirmed active compromise in the wild — malicious packages were served to real customers from the vendor's infrastructure. Any site that pulled the tampered build should be treated as actively breached, not merely "at risk."
Detection & Response
The detections below target the behaviors that matter: web server processes executing shells, PHP files appearing where they shouldn't, and unexpected outbound connections from PHP. These are the observable fingerprints of a WordPress backdoor regardless of the specific payload variant.
SIGMA Rules
---
title: Web Server or PHP Process Spawning Shell or Downloader
id: 8c4e2a71-3b6f-4d9a-a1c2-7e5f0b8d3a44
status: experimental
description: Detects PHP-FPM, Apache, or Nginx worker processes spawning shells, downloaders, or interpreters — consistent with webshell/backdoor execution following a compromised WordPress plugin install such as the trojanized Rank Math PRO packages.
references:
- https://www.bleepingcomputer.com/news/security/wordpress-seo-plugin-rank-math-compromised-to-backdoor-websites/
- https://attack.mitre.org/techniques/T1505/003/
author: Security Arsenal
date: 2026/02/12
tags:
- attack.persistence
- attack.t1505.003
- attack.execution
logsource:
category: process_creation
product: linux
detection:
selection_parent:
ParentImage|endswith:
- '/php-fpm'
- '/php-fpm8.1'
- '/php-fpm8.2'
- '/php-fpm8.3'
- '/apache2'
- '/httpd'
- '/nginx'
ParentImage|contains:
- 'php-fpm'
selection_child:
Image|endswith:
- '/bash'
- '/sh'
- '/dash'
- '/zsh'
- '/curl'
- '/wget'
- '/nc'
- '/ncat'
- '/python'
- '/python3'
- '/perl'
- '/base64'
condition: selection_parent and selection_child
falsepositives:
- Legitimate WordPress plugins invoking system commands (backup, image processing, cache tooling) — baseline per host
level: high
---
title: PHP File Created in WordPress Uploads or Must-Use Plugins Directory
id: 2f7b9c15-6e4a-4c81-b3d9-1a8f5e2c7b90
status: experimental
description: Detects creation of PHP files in wp-content/uploads, wp-content/mu-plugins, or the WordPress web root — a hallmark of backdoors dropped by compromised plugins that need persistence outside the plugin directory. The Rank Math PRO supply-chain payload follows this pattern of planting a hidden backdoor separate from the plugin itself.
references:
- https://www.bleepingcomputer.com/news/security/wordpress-seo-plugin-rank-math-compromised-to-backdoor-websites/
- https://attack.mitre.org/techniques/T1505/003/
author: Security Arsenal
date: 2026/02/12
tags:
- attack.persistence
- attack.t1505.003
logsource:
category: file_event
product: linux
detection:
selection_paths:
TargetFilename|contains:
- '/wp-content/uploads/'
- '/wp-content/mu-plugins/'
- '/wp-content/upgrade/'
- '/wp-includes/images/'
selection_ext:
TargetFilename|endswith:
- '.php'
- '.phtml'
- '.phar'
- '.php5'
- '.php7'
condition: selection_paths and selection_ext
falsepositives:
- Rare; some page builders write compiled PHP templates into uploads — review content of flagged files
level: high
---
title: Outbound Network Connection from PHP-FPM or Web Server Process
id: 5a1d8e36-9c2b-4f47-8e6a-3d7c1b9f5e28
status: experimental
description: Detects PHP-FPM or web server worker processes initiating outbound connections to non-standard destinations — consistent with a WordPress backdoor beaconing to attacker infrastructure or retrieving second-stage payloads after a supply-chain compromise.
references:
- https://www.bleepingcomputer.com/news/security/wordpress-seo-plugin-rank-math-compromised-to-backdoor-websites/
- https://attack.mitre.org/techniques/T1071/001/
author: Security Arsenal
date: 2026/02/12
tags:
- attack.command_and_control
- attack.t1071.001
logsource:
category: network_connection
product: linux
detection:
selection:
Image|contains:
- 'php-fpm'
- '/apache2'
- '/httpd'
- '/nginx'
DestinationPort:
- 80
- 443
- 8080
- 8443
filter_known_updates:
DestinationHostname|contains:
- 'wordpress.org'
- 'api.wordpress.org'
- 'rankmath.com'
- 'wordpress.com'
condition: selection and not filter_known_updates
falsepositives:
- Plugins performing legitimate external API calls (SEO analysis, payment gateways, SMTP relays) — maintain an allowlist of known plugin API endpoints
level: medium
KQL — Microsoft Sentinel / Defender
This query hunts for web server and PHP processes spawning shells or making unexpected outbound connections. It works against endpoints onboarded to Defender for Endpoint and against Linux web servers forwarding Syslog/CEF into Sentinel.
let Lookback = 14d;
let WebParents = dynamic(["php-fpm", "php-fpm8.1", "php-fpm8.2", "php-fpm8.3", "apache2", "httpd", "nginx", "php-cgi"]);
let ShellChildren = dynamic(["bash", "sh", "dash", "curl", "wget", "nc", "ncat", "python", "python3", "perl", "base64"]);
let ProcEvents = DeviceProcessEvents
| where TimeGenerated > ago(Lookback)
| where InitiatingProcessFileName has_any (WebParents)
| where FileName has_any (ShellChildren)
| project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, AccountName;
let NetEvents = DeviceNetworkEvents
| where TimeGenerated > ago(Lookback)
| where InitiatingProcessFileName has_any (WebParents)
| where RemotePort in (80, 443, 8080, 8443)
| where not(RemoteUrl has_any ("wordpress.org", "rankmath.com", "wordpress.com"))
| project TimeGenerated, DeviceName, InitiatingProcessFileName, RemoteIP, RemoteUrl, RemotePort;
union ProcEvents, NetEvents
| order by TimeGenerated desc
For Syslog-ingested Linux web servers without Defender, hunt authentication and process logs for web-context command execution:
let Lookback = 14d;
Syslog
| where TimeGenerated > ago(Lookback)
| where ProcessName has_any ("php-fpm", "apache2", "httpd", "nginx")
or SyslogMessage has_all ("www-data", "sh -c")
| where SyslogMessage has_any ("sh -c", "bash -c", "curl ", "wget ", "base64 -d")
| project TimeGenerated, Computer, ProcessName, SyslogMessage
| order by TimeGenerated desc
Velociraptor VQL
Deploy this hunt across WordPress hosts to surface recently created or modified PHP files in high-risk directories — the primary artifact of a planted backdoor. Adjust the date filter to the start of the compromise window announced by the vendor.
-- Hunt for PHP files created or modified in high-risk WordPress directories
-- during the Rank Math PRO supply-chain compromise window
SELECT FullPath, Size, Mtime, Ctime, Btime
FROM glob(globs=[
'/var/www/*/wp-content/uploads/**/*.php',
'/var/www/*/wp-content/mu-plugins/**/*.php',
'/var/www/*/wp-content/upgrade/**/*.php',
'/var/www/*/wp-includes/images/**/*.php',
'/home/*/public_html/wp-content/uploads/**/*.php',
'/home/*/public_html/wp-content/mu-plugins/**/*.php'
])
WHERE Mtime > '2026-02-01'
ORDER BY Mtime DESC
Complement the file hunt with a process check for live webshell activity:
-- Identify web server / PHP processes with suspicious child processes
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ '(bash|sh|curl|wget|nc |python|perl|base64)'
AND (Exe =~ 'php-fpm|apache2|httpd|nginx'
OR Username =~ 'www-data|apache|nginx|nobody')
Remediation & Verification Script
Run the following on each Linux-hosted WordPress instance that installed or updated Rank Math PRO during the affected window. It inventories the plugin, surfaces recently modified PHP files, flags rogue admin accounts, and checks persistence locations. This is triage, not cleanup — a positive finding means full IR, not file deletion.
#!/bin/bash
# Rank Math PRO supply-chain compromise triage script
# Run as root or with sudo on the web host. Adjust WP_PATH as needed.
WP_PATH="/var/www/html"
WINDOW_START="2026-02-01" # Adjust to vendor-confirmed compromise window start
OUT="/root/rankmath_triage_$(date +%Y%m%d_%H%M%S).txt"
echo "=== Rank Math PRO Compromise Triage — $(hostname) — $(date) ===" | tee "$OUT"
# 1. Identify installed Rank Math version and install/update timestamps
echo -e "\n[*] Rank Math plugin inventory" | tee -a "$OUT"
if command -v wp &>/dev/null; then
sudo -u www-data wp plugin list --path="$WP_PATH" --format=table 2>/dev/null | grep -i "rank" | tee -a "$OUT"
fi
stat "$WP_PATH/wp-content/plugins/"*rank* 2>/dev/null | grep -E "File|Birth|Modify" | tee -a "$OUT"
# 2. PHP files created/modified in high-risk directories since the compromise window
echo -e "\n[*] PHP files modified since $WINDOW_START in uploads/mu-plugins/web root" | tee -a "$OUT"
find "$WP_PATH/wp-content/uploads" "$WP_PATH/wp-content/mu-plugins" -type f \( -name "*.php" -o -name "*.phtml" -o -name "*.phar" \) -newermt "$WINDOW_START" -ls 2>/dev/null | tee -a "$OUT"
find "$WP_PATH" -maxdepth 1 -name "*.php" -newermt "$WINDOW_START" -ls 2>/dev/null | tee -a "$OUT"
# 3. All PHP files modified anywhere in wp-content during the window (tamper check)
echo -e "\n[*] All PHP modified in wp-content since window start (sample 50)" | tee -a "$OUT"
find "$WP_PATH/wp-content" -name "*.php" -newermt "$WINDOW_START" -printf '%T+ %p\n' 2>/dev/null | sort | head -50 | tee -a "$OUT"
# 4. Rogue administrator accounts
echo -e "\n[*] WordPress administrator accounts — verify each is expected" | tee -a "$OUT"
if command -v wp &>/dev/null; then
sudo -u www-data wp user list --role=administrator --path="$WP_PATH" --format=table 2>/dev/null | tee -a "$OUT"
fi
# 5. Persistence: system cron, user cron, WP cron
echo -e "\n[*] Cron persistence check" | tee -a "$OUT"
crontab -l 2>/dev/null | tee -a "$OUT"
ls -la /etc/cron.d/ /var/spool/cron/ 2>/dev/null | tee -a "$OUT"
if command -v wp &>/dev/null; then
sudo -u www-data wp cron event list --path="$WP_PATH" 2>/dev/null | grep -vE "wp_|recovery|delete_expired|scheduled_delete" | tee -a "$OUT"
fi
# 6. wp-config.php integrity — check for tampering and note salt exposure
echo -e "\n[*] wp-config.php modification time (rotate DB creds + salts regardless)" | tee -a "$OUT"
stat "$WP_PATH/wp-config.php" 2>/dev/null | grep -E "Modify|Change" | tee -a "$OUT"
# 7. Live suspicious processes under the web server user
echo -e "\n[*] Suspicious processes running as web user" | tee -a "$OUT"
ps auxww | grep -E "www-data|apache|nginx|nobody" | grep -E "bash|sh |curl|wget|nc |python|perl" | grep -v grep | tee -a "$OUT"
# 8. Outbound connections from PHP-FPM / web server
echo -e "\n[*] Active outbound connections from web/PHP processes" | tee -a "$OUT"
ss -tupn 2>/dev/null | grep -E "php-fpm|apache2|httpd|nginx" | grep -v ":443.*ESTAB" | tee -a "$OUT"
echo -e "\n=== Triage complete. Review $OUT. Any hit in sections 2, 4, 5, or 7 = escalate to IR. ===" | tee -a "$OUT"
Remediation
If you installed or updated Rank Math PRO during the compromise window, patching alone is insufficient. Follow this sequence:
- Isolate the site. Take it offline or place it behind a maintenance page/WAF block at the edge while you triage. Preserve evidence — do not delete files before capturing a forensic image or at minimum a full file listing with timestamps and the web access logs covering the window.
- Remove the compromised plugin entirely. Delete the Rank Math PRO plugin directory. Do not "update over" the tampered build — reinstall only after confirming the vendor has published a verified clean build and stated the window of exposure. Check the vendor's official channels (rankmath.com) and the BleepingComputer coverage for confirmation of the clean version and affected window.
- Hunt for the planted backdoor. The entire point of the payload is persistence that survives the plugin. Run the file-integrity triage above: audit
wp-content/uploads/,wp-content/mu-plugins/, theme directories,wp-includes/, and the web root for PHP files created or modified since the window opened. Diff the full installation against a known-clean WordPress core and theme source. - Rotate everything the attacker could have touched. With code execution as the web user, assume full credential exposure: rotate all WordPress admin passwords, database credentials, and the security keys/salts in
wp-config.php(rotating salts invalidates all active sessions). Rotate any API keys, SMTP credentials, payment gateway secrets, and FTP/SFTP accounts stored in the database or config. - Audit accounts and persistence. Enumerate WordPress administrator accounts and remove any you cannot attribute. Check system crontabs,
/etc/cron.d/, and WordPress scheduled events for attacker-established tasks. - Review egress and access logs. Pull web server access logs for requests to unusual PHP files, POST requests to files in uploads, and spikes in outbound connections from the host during and after the window. This scoping work tells you whether the backdoor was actually used and what data was touched — critical for breach-notification obligations if customer data (WooCommerce orders, PII) resided on the site.
- Rebuild if in doubt. For high-value sites (e-commerce, membership, anything handling regulated data), the defensible answer is a clean rebuild: fresh WordPress core, clean plugin and theme copies from verified sources, database restored after content review, and all credentials rotated. Anything less leaves residual risk you cannot quantify.
- Harden going forward. Deploy a WAF with virtual patching, enforce least-privilege on the web user (read-only core directories, no write access to
wp-includesor themes at runtime), disable plugin auto-updates for commercial plugins where your change-control process requires validation, and implement file-integrity monitoring onwp-contentso the next supply-chain event is detected in minutes, not weeks.
The Bigger Lesson
This incident is the third lesson in as many years that "download from the official vendor" is no longer a sufficient control. Supply-chain compromises of WordPress plugin and theme vendors work precisely because the ecosystem is built on blind trust in update channels. Defenders need compensating controls that assume the vendor will eventually be compromised: file-integrity monitoring, egress filtering on web servers, runtime process anomaly detection on PHP, and an IR playbook that treats "trusted update" as a possible initial-access vector. If your current monitoring can't tell you when PHP-FPM starts spawning curl, this is your wake-up call.
Related Resources
Security Arsenal Incident Response Services AlertMonitor Platform Book a SOC Assessment incident-response Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.