Rank Math PRO 3.0.123 Backdoor: Six Hunts to Run


What this means for your clients: any client website that switched on a Rank Math PRO 3.0.123 copy downloaded or updated during the affected window on October 15 or 16 should be treated as hacked, and installing the newer version alone does not clean it up.
Picture a small agency that looks after a stack of client sites, every one running the same premium WordPress plugin, with updates switched on. On a Thursday afternoon, the agency's scheduled update job pulls the latest build from the vendor and installs it everywhere. Nobody reviews it, because it came from the vendor's own servers under the license the agency pays for. The next day the vendor posts a security notice about that build.
Rank Math's notice says a limited number of users who downloaded Rank Math PRO 3.0.123 between 20:02 UTC on October 15, 2026 and 07:12 UTC on October 16 received plugin files that "contained malware that could compromise your website if the plugin was activated." That is a window of 11 hours and 10 minutes. Rank Math pulled the files and shipped a clean 3.0.124.
There is no bug to patch
The malware shipped inside the files, and the vendor's own update channel delivered it, which makes this a trojanized update. Wordfence, relaying Rank Math's notice, says the affected copies were "downloaded or installed through an update" in that window. It lists the product as Rank Math SEO PRO and tracks the incident as CVE-2026-111862 under the title "Unauthenticated Embedded Malicious Code."
Site owners who got hit had kept a plugin current, and if auto-updates or a maintenance run fired in the window, the build arrived without anyone touching it.
Who is exposed
Only Rank Math PRO 3.0.123 obtained inside the window is named, and the version number alone is not proof. Wordfence says 3.0.123 by itself does not show a site was compromised, because Rank Math limits the incident to copies downloaded or updated during that period. Rank Math names only the PRO plugin.
Rank Math sorts sites into three cases:
Downloaded or updated in the window and activated: treat the site as compromised and follow the full recovery.
Installed but never activated: delete the plugin files, install 3.0.124 from your Rank Math account and run a security scan.
Downloaded but never installed: Rank Math says there is no indication from this incident alone that the site was compromised.
Wordfence says Rank Math has records of who downloaded an affected copy. If Rank Math emailed you, follow its recovery even if the site now runs a different version.
Updating is not the cleanup
Installing 3.0.124 replaces the Rank Math package. Wordfence says it does not remove files, administrator accounts or scheduled tasks the malware may already have created elsewhere. Rank Math says the malware "can recreate its hidden backdoor whenever someone visits the website."
Rank Math's first choice is a restore from a backup made before the window opened. If you cannot restore, its manual cleanup kills the rebuild first: delete the whole rank-math-pro folder (deactivating is not enough), delete any of the three malicious wp_options rows that exist and block outbound traffic to rank1cdn[.]com if your hosting provider or firewall supports it. Only then does it send you hunting for files, because removing them first may not stick.
The web server account is the blast radius
Rank Math tells owners to assume that every credential and secret the compromised site could reach may have been exposed. Its list covers WordPress passwords, the database credentials in wp-config.php, the security keys and salts, payment gateway keys, SMTP credentials, API keys, hosting control panel logins, FTP, SFTP and SSH credentials, and secrets in environment variables. No source shows that data was taken, but the guidance is to assume exposure.
The notice also looks past WordPress, to SSH authorized_keys files, other users' crontabs, running processes and anything outside the site the web server user can write. On shared hosting, Rank Math says to ask the host to check those.
What Rank Math published, and what it did not
Wordfence's announcement relies on Rank Math's notice, repeats its indicators and adds that their absence "does not, by itself, prove that a website is clean." SecurityWeek covered it on October 19 as a supply chain attack.
Rank Math gives no count beyond "a limited number of users", names no actor and does not say how the files were altered. Wordfence assigned CVE-2026-111862 and published the record on cve.org at 16:35 UTC on October 19, with a 9.8 score of its own. When we checked NVD at 9:52 AM MST that day, it had no entry, and the CISA KEV catalog (version 2026.10.16) did not list it. The CVE record carries Wordfence's CVSS 3.1 score of 9.8. No file hashes were published, and the indicators below are the only ones Rank Math lists.
Rank Math also says some users may need to reset their Rank Math account password, and that it will never send login or download links by email. Our reading is that it says so because a public incident like this is good cover for phishing, so treat any unsolicited reset or download email about it as suspect.
The indicators Rank Math lists
Network: outbound connections to rank1cdn[.]com.
Database: wp_options rows with option_name _uf6qdn2hmm, t8krdk990phb or _np5pvf754c. Your table prefix may not be wp_.
Users: administrator usernames beginning wpsupport_ followed by random characters, or emails ending in @wp-mail[.]net.
Files: wp-includes/class-wp-cache-util.php, which is not a real WordPress core file, and suspicious PHP in wp-content/mu-plugins/, in other plugins' folders, in the inc/, includes/ and lib/ folders of non-default themes, in Rank Math's includes/ folder, and any PHP in uploads/.
Code markers: base64_decode combined with XOR loops, the key xmmqhn, proc_open or PHP-FPM socket code.
Server config: .htaccess rules in the root or any subdirectory that let .cache files run as PHP, and .cache files that contain PHP.
Cron: system crontab entries for the web server user tagged #ms6v46.
What to change this week
Find every site that runs Rank Math PRO, including client sites you manage as an MSP, and check whether 3.0.123 arrived inside the window. Your Rank Math account download history, the vendor's email and the date on the rank-math-pro folder all help.
If an affected copy was activated, restore the site and database from a backup made before 20:02 UTC on October 15, scan it, rotate credentials and install 3.0.124 from your Rank Math account.
If you cannot restore, follow Rank Math's order. Delete the rank-math-pro folder, delete any of the three wp_options rows that exist and, if your host or firewall supports it, block rank1cdn[.]com before you hunt for anything else, then work through the indicators above. Keep copies of what you remove.
Rotate everything the site could reach, even after a clean restore. That covers the database password, the keys and salts, SMTP and payment credentials, API keys and the hosting, SFTP and SSH logins. Review two-factor authentication for every administrator.
Verify with wp core verify-checksums and wp plugin verify-checksums --all, compare premium plugins against a clean copy from the vendor, and watch outbound traffic and the removed files for a few days. If anything suspicious comes back, the cleanup may be incomplete.
Download 3.0.124 only by going to rankmath.com directly, and tell your team to ignore emailed reset or download links.
Six hunts to run (test first)
These are our own threat hunting queries built from Rank Math's indicators, and we have not run them on live customer data. Treat every one as a template, replace each YOUR_* placeholder and confirm the field names in your own data. A hit needs investigation. Where a search has a time filter, it starts at 20:02 UTC on October 15, 2026 (epoch 1792094520), or 20:00 for the file-date checks, and runs through today. The table under the hunts, "Interpret before you escalate", says what is benign and what is not.
Many WordPress sites live on shared or managed hosting with little or no EDR, Linux EDR on web servers is often thin, and endpoint sensors do not see database rows. The best evidence here is the files, the database and egress logs, which is why the first two hunts run on the host itself. An empty result does not prove a site is clean.
Hunt 1 on the host: files, .htaccess, cron and keys (test first)
Our earlier post, Hunt Bricksforge CVE-2026-85097: GIF Upload Turns Into PHP, covers a different flaw, but the same check for PHP where it does not belong applies here.
Search 1: the published file and code markers (run from the WordPress root)
test -f wp-includes/class-wp-cache-util.php && echo "FOUND class-wp-cache-util.php"
grep -rIl --include='*.php' -e 'xmmqhn' -e 'rank1cdn' wp-content wp-includes 2>/dev/null
grep -rIl --include='*.php' -e 'proc_open' wp-content/mu-plugins wp-content/uploads wp-content/plugins/rank-math-pro 2>/dev/null
find . -type f -name '*.cache' -exec grep -l '<?php' {} + 2>/dev/null
find wp-content/uploads -type f -name '*.php'
find . -type f -name '*.php' -newermt '2026-10-15 20:00 UTC' -not -path './wp-content/cache/*'
Search 2: .htaccess, cron, keys and the web user (needs shell access to the server; replace www-data with your web user)
find . -name .htaccess -exec grep -HniE '(AddHandler|AddType|SetHandler|ForceType|FilesMatch).*cache|cache.*(php|x-httpd)' {} +
sudo crontab -l -u www-data | grep -n 'ms6v46'
sudo grep -rn 'ms6v46' /var/spool/cron /etc/cron* 2>/dev/null
sudo find / -xdev -path '*/.ssh/authorized_keys' -exec ls -l --time-style=full-iso {} + 2>/dev/null
ps -u www-data -o pid,ppid,lstart,args
sudo find / -xdev -user www-data -type f -newermt '2026-10-15 20:00 UTC' -not -path '/var/www/*' 2>/dev/null
Search 3: WordPress on Windows and IIS (PowerShell, from the site root)
Get-ChildItem -Recurse -File -Filter *.php | Select-String -Pattern 'xmmqhn','rank1cdn' -List | Select-Object Path
Get-ChildItem -Recurse -File -Filter *.cache | Select-String -Pattern '<?php' -SimpleMatch -List | Select-Object Path
Test-Path .\wp-includes\class-wp-cache-util.php
On IIS, read web.config for handler mappings that cover .cache files instead of .htaccess. Test first by putting a harmless .php file containing the text xmmqhn-test under wp-content in a lab copy of a site and confirming Search 1 finds it, then delete it.
The markers may sit inside encoded blobs, so a plain grep can miss them, and proc_open has legitimate uses in some plugins, so read every hit before you call it. The newer-than search will also list files from any normal update since October 15, so read those by path and by what wrote them.
Caching plugins put plenty of "cache" lines in .htaccess, and the line that matters is one that hands .cache files to the PHP handler, so read the whole block around each hit. Root admins legitimately edit crontabs, but the ms6v46 marker has no benign reading we know of. If every search comes back empty, confirm you ran them from the right site root, as a user that can read every folder, and on every site that ran 3.0.123.
Hunt 2 with WP-CLI and SQL: options, admins and scheduled events (test first)
Search 1: WP-CLI, per site
for o in _uf6qdn2hmm t8krdk990phb _np5pvf754c; do wp option list --search="$o" --fields=option_name,size_bytes,autoload; done
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
wp cron event list --fields=hook,next_run_gmt,recurrence
wp core verify-checksums
wp plugin verify-checksums --all
Search 2: SQL, if you only have database access (replace wp_ with the $table_prefix from wp-config.php)
SELECT option_name, LENGTH(option_value) AS value_bytes
FROM wp_options
WHERE option_name IN ('_uf6qdn2hmm', 't8krdk990phb', '_np5pvf754c');
SELECT ID, user_login, user_email, user_registered
FROM wp_users
WHERE user_login LIKE 'wpsupport\_%'
OR user_email LIKE '%@wp-mail.net'
OR user_registered >= '2026-10-15 20:00:00';
Field mapping: WordPress stores user_registered in UTC, which is why the SQL compares against the UTC start of the window. Both searches print the size of the option rows, not their contents, so you do not copy a payload into your notes. On multisite, each site has its own tables (wp_2_options and so on), so add --url= for each site in WP-CLI or query each prefix in SQL. MSPs can loop the WP-CLI search over every install with wp --path=YOUR_SITE_ROOT and collect the output in one file.
Rank Math notes that checksums may not exist for every plugin, particularly premium ones, so plugin verify-checksums may report Rank Math PRO and other premium plugins as unverifiable; compare those against a clean copy from the vendor. A new administrator account that you or a client created this week is the usual benign hit on the registration line. Unfamiliar cron hooks are common on busy sites, so match each one to a plugin you run. An empty result is a good sign only if you queried the right prefix on every site in the window.
Hunt 3 in Splunk: DNS and proxy egress to rank1cdn[.]com (test first)
Search 1: raw DNS and proxy events
(index=YOUR_DNS_INDEX OR index=YOUR_PROXY_INDEX) earliest=1792094520 rank1cdn
| eval target=lower(coalesce(query, url, dest, dest_host, ""))
| where like(target, "%rank1cdn.com%")
| eval client=coalesce(src, src_ip, clientip)
| stats count AS events, min(_time) AS first_seen, max(_time) AS last_seen, values(client) AS clients, values(sourcetype) AS sourcetypes BY host
| convert ctime(first_seen) ctime(last_seen)
Search 2: CIM Network_Resolution data model
| tstats count AS events, min(_time) AS first_seen, max(_time) AS last_seen from datamodel=Network_Resolution where DNS.query="*rank1cdn.com" earliest=1792094520 by DNS.src, DNS.query
| convert ctime(first_seen) ctime(last_seen)
Search 3: CIM Web data model (proxy logs)
| tstats count AS events, min(_time) AS first_seen, max(_time) AS last_seen from datamodel=Web where Web.url="*rank1cdn.com*" earliest=1792094520 by Web.src, Web.dest
| convert ctime(first_seen) ctime(last_seen)
Field mapping: query, url, dest and src follow common DNS and proxy add-ons, so run Search 1 for one day without the where line and confirm the fields exist. The data model searches only work if your add-ons are CIM-mapped. Test first by confirming your web servers' DNS or egress is logged at all: shared hosting usually is not, and a server that resolves through its host's own resolver may never reach your DNS logs. Traffic to rank1cdn[.]com has no benign reading we know of, so any row is a lead. An empty result means nothing until you have proved the web hosts' traffic reaches these indexes.
Hunt 4 in Kibana: egress and file events (test first)
Search 1: DNS and network events to the domain
(dns.question.name:*rank1cdn.com or url.domain:*rank1cdn.com or destination.domain:*rank1cdn.com) and @timestamp >= "2026-10-15T20:02:00Z"
Search 2: file events on the web host (Elastic Defend or Auditbeat file integrity)
event.category:file and (file.name:"class-wp-cache-util.php" or file.name:".htaccess" or file.extension:"cache" or file.path:*mu-plugins*) and @timestamp >= "2026-10-15T20:02:00Z"
Field mapping: both use ECS fields, and the file name fields are case-sensitive keywords. Leading wildcards need the Kibana advanced setting that allows them, which is on by default. The time picker uses your browser's time zone by default, so set it wide enough to include the start. Add the columns @timestamp, host.name, source.ip, process.name, user.name and file.path.
Caching plugins write .cache files and .htaccess changes during normal work, so line up Search 2 by process.name and by time, and look hardest at a .cache file that a PHP process wrote outside a plugin's own cache folder. Test first by confirming the data streams exist for the web host, then by touching a test file under mu-plugins on a lab site. An empty result only counts if the agent runs on that host.
Hunt 5 in Microsoft Defender for Endpoint KQL (only where the sensor runs on the web host, test first)
Search 1: network connections to the domain
DeviceNetworkEvents
| where Timestamp >= datetime(2026-10-15T20:02:00Z)
| where RemoteUrl has "rank1cdn.com"
| summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Events=count() by DeviceName, InitiatingProcessFileName, InitiatingProcessAccountName, RemoteIP
Search 2: the fake core file, .cache files and .htaccess changes
DeviceFileEvents
| where Timestamp >= datetime(2026-10-15T20:02:00Z)
| where FileName =~ "class-wp-cache-util.php"
or (FileName endswith ".cache" and FolderPath has "wp-")
or (FileName =~ ".htaccess" and ActionType in ("FileCreated", "FileModified"))
| project Timestamp, DeviceName, ActionType, FolderPath, FileName, InitiatingProcessFileName, InitiatingProcessAccountName
| order by Timestamp asc
Search 3: the web server user starting shells or editing cron
let webUsers = dynamic(["www-data", "apache", "nginx", "YOUR_WEB_USER"]);
DeviceProcessEvents
| where Timestamp >= datetime(2026-10-15T20:02:00Z)
| where AccountName in~ (webUsers) or AccountDomain =~ "IIS APPPOOL"
| where FileName in~ ("sh", "bash", "dash", "crontab", "cmd.exe", "powershell.exe") or ProcessCommandLine has "ms6v46"
| summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Events=count(), SampleCommands=make_set(ProcessCommandLine, 10) by DeviceName, AccountName, FileName, InitiatingProcessFileName
| order by FirstSeen asc
Field mapping: these tables need Defender for Endpoint on the web host itself, Linux or Windows. Advanced hunting keeps 30 days, so export results now if you need them later. RemoteUrl can be empty for some connections, so on Windows also check DeviceEvents with ActionType DnsQueryResponse. Run Search 3 for one day without the FileName line to confirm the account name your web server really uses.
Search 3 is the noisy one. PHP sites run shells for image tools, backups and health checks, so name the job behind each row before you clear it. A row with the ms6v46 marker matches Rank Math's published cron indicator. Any other crontab edit by the web user needs an authorized job to explain it. If all three come back empty, confirm the sensor is reporting from that host.
Hunt 6 in Falcon CQL (only where a sensor runs on the web host, test first)
Search 1: DNS requests for the domain
#event_simpleName=DnsRequest DomainName=/rank1cdn\.com$/i
| groupBy([aid, ComputerName, ContextBaseFileName], function=[count(as=Events), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
Search 2: the cron marker and crontab use on Linux
#event_simpleName=ProcessRollup2 event_platform=Lin
| CommandLine=/ms6v46|crontab/i
| table([@timestamp, ComputerName, UserName, CommandLine, ParentBaseFileName])
Field mapping: this is Falcon sensor data in Next-Gen SIEM, so set the time range to start on October 15, 2026, and note FirstSeen and LastSeen come back as epoch milliseconds. If ContextBaseFileName is empty in your DNS events, drop it from the groupBy. Falcon does not see database rows or most of what happens inside PHP, so it backs up Hunts 1 and 2 and does not replace them.
Admins run crontab all the time, so narrow Search 2 to your web user if it is noisy, while any row carrying ms6v46 is a lead. If both come back empty, confirm the sensor reports from that host before you trust it.
Interpret before you escalate
Finding | Benign read | Escalate when |
Any indicator Rank Math lists: one of the three wp_options rows, a wpsupport_ admin or @wp-mail[.]net email, wp-includes/class-wp-cache-util.php, the ms6v46 cron marker, traffic to rank1cdn[.]com, or an .htaccess rule that maps .cache files to PHP | None we know of | Treat the site and host as compromised until proven otherwise: stop the rebuild first in Rank Math's order, keep copies, restore from a pre-window backup and rotate everything. For traffic, find the host and process; for cron, check other users' crontabs too |
.cache file holding PHP code | A caching plugin you can name that stores PHP in its own cache folder | It sits outside a known cache folder or appeared after the window opened |
PHP file in uploads | Some plugins and old installs leave PHP there | Investigate: it has causes beyond this incident, but treat it as a backdoor until you have read it |
proc_open in a PHP file | Many legitimate plugins use it | It sits with base64_decode, XOR loops or the key xmmqhn, or in a folder Rank Math lists |
Empty across all six hunts | Nothing matched in the data you have | Not proof the site is clean: confirm the site never ran 3.0.123 from the window (download history, the vendor email, folder dates), confirm your telemetry covers the host, and still install 3.0.124 |
Sources
Rank Math, Rank Math PRO 3.0.123 Security Incident: How to Secure Your Website: https://rankmath.com/kb/pro-3-0-123-security-incident/
Wordfence, PSA: Malware Found in Some Rank Math SEO PRO 3.0.123 Downloads (October 16, 2026, US time): https://www.wordfence.com/blog/2026/10/rank-math-seo-pro-3-0-123-malware/
Wordfence Intelligence, Rank Math SEO PRO 3.0.123 Unauthenticated Embedded Malicious Code (CVE-2026-111862): https://www.wordfence.com/threat-intel/vulnerabilities/id/81cd7a2c-ad0c-4dbc-b5dc-07ec866d5c1f
CVE.org, CVE-2026-111862 (record published by Wordfence at 16:35 UTC on October 19): https://www.cve.org/CVERecord?id=CVE-2026-111862
NVD, CVE-2026-111862 (no record in the API at 9:52 AM MST on October 19): https://nvd.nist.gov/vuln/detail/CVE-2026-111862
CISA Known Exploited Vulnerabilities Catalog: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
SecurityWeek, Rank Math PRO WordPress Plugin Compromised in Supply Chain Attack (October 19, 2026, secondary): https://www.securityweek.com/rank-math-pro-wordpress-plugin-compromised-in-supply-chain-attack/
WP-CLI, wp option list: https://developer.wordpress.org/cli/commands/option/list/
WP-CLI, wp user list: https://developer.wordpress.org/cli/commands/user/list/
WP-CLI, wp cron event list: https://developer.wordpress.org/cli/commands/cron/event/list/
WP-CLI, wp core verify-checksums: https://developer.wordpress.org/cli/commands/core/verify-checksums/
WP-CLI, wp plugin verify-checksums: https://developer.wordpress.org/cli/commands/plugin/verify-checksums/
Splunk, CIM Network Resolution data model: https://docs.splunk.com/Documentation/CIM/latest/User/NetworkResolution
Elastic Common Schema, field reference: https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html
Microsoft Learn, DeviceNetworkEvents table: https://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-devicenetworkevents-table
Microsoft Learn, DeviceFileEvents table: https://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-devicefileevents-table
Microsoft Learn, DeviceProcessEvents table: https://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-deviceprocessevents-table
What This Means for Your Team
Find every Rank Math PRO install, work out whether 3.0.123 arrived in the window, and if it ran, restore or clean in Rank Math's order before you rotate every secret the site could reach. Run it as an incident response, and treat an empty hunt as a gap in coverage until you have confirmed the files, the database and the logs were in scope.
Inception Protection
Inception Protection is the Inception Security managed detection and response service, and it works with the Microsoft licenses you already have. We watch Defender and sign-in data in your Microsoft environment, dig into anything odd and work through containment with your team. A WordPress site usually sits outside that environment, and its hosting, patching and logs stay with you or your host.
Inception Foresight
If a WordPress site sent mail or connected to services through Microsoft 365, the SMTP passwords, app passwords and API keys it held can reach back into that environment. To find out whether your Microsoft environment would catch suspicious activity, try the free Inception Foresight M365 Assessment: https://www.inceptionsecurity.com/m365assessment.



