How I Audited 70 WordPress Sites in Under an Hour After the EssentialPlugin Supply Chain Attack
· 13 min read
On 7 April 2026 I woke up to a Patchstack alert that stopped me mid-coffee: the WordPress.org Plugins Team had permanently closed 31 plugins from a single author — EssentialPlugin — after discovering a PHP deserialization backdoor planted eight months earlier. Over 400,000 active installs across the WordPress ecosystem. I manage north of 70 sites for agencies and business clients. Before I'd finished reading the advisory, I was already SSH'd into the first server.
Here's exactly what happened, how I checked every site I manage, and how you can check yours — even if you're not a developer.

What Actually Happened
A buyer going by "Kris" acquired the EssentialPlugin portfolio (formerly WP Online Support) on Flippa sometime in early 2025. The deal included 26 WordPress plugins with a combined install base in the hundreds of thousands. On 12 May 2025 a new essentialplugin account appeared on WordPress.org with SVN commit access to all of them.
On 8 August 2025, that account pushed version 2.6.7 across the suite. The changelog said "Check compatibility with WordPress version 6.8.2." In reality, it added 191 lines of PHP — a deserialization backdoor hidden inside the plugins' existing analytics opt-in module, wpos-analytics.
The code sat dormant for eight months. Then on 5 and 6 April 2026, a command-and-control server at analytics.essentialplugin.com began distributing payloads. The injection window on 6 April lasted six hours and 44 minutes, between 04:22 and 11:06 UTC.
During that window, every site running a compromised plugin received a malicious payload that:
- Downloaded a fake core file called
wp-comments-posts.phpinto the webroot (note the plural — the real WordPress file iswp-comments-post.php, singular) - Injected roughly 6KB of PHP into
wp-config.php, right above therequire_onceforwp-settings.php - Served cloaked SEO spam exclusively to Googlebot — completely invisible to anyone browsing the site normally
The C2 domain resolution used an Ethereum smart contract querying public blockchain RPC endpoints, which means traditional domain takedowns are ineffective. The attacker can update the smart contract to point to a new domain at any time.
The Affected Plugins
WordPress.org closed 31 plugins. Here are the ones I've confirmed from the advisories and my own audits:
- WP Logo Showcase Responsive Slider and Carousel
- Popup Anything — A Marketing Popup and Lead Generation
- Countdown Timer Ultimate
- WP Testimonial with Widget
- WP Team Showcase and Slider
- WP FAQ
- SP News and Widget
- Blog Designer for Post and Widget
- Timeline and History Slider
- Audio Player with Playlist Ultimate
- Album and Image Gallery Plus Lightbox
- Accordion and Accordion Slider
There are more — the full count is 31 closed plugins all from the same author account. If you installed any plugin from the "EssentialPlugin" or "WP Online Support" author, treat it as compromised.
How I Checked 70 Sites in Under an Hour
I use WP-CLI extensively across every server I manage, and most of my sites are accessible over SSH. That made this audit fast. Here's the exact process.
Step 1: Check for the Affected Plugins
On each server, I ran a single command to check every WordPress install for any plugin by this author:
for site in /var/www/*/; do
echo "=== $site ==="
wp plugin list --path="$site" --fields=name,version,author \
--format=table 2>/dev/null | grep -i "essential\|wponlinesupport\|wpos"
done
If you only manage one site and have WP-CLI installed:
wp plugin list --fields=name,version,author --format=table | grep -i "essential\|wponlinesupport\|wpos"
No WP-CLI? Log into your WordPress admin, go to Plugins, and search for "EssentialPlugin" or "WP Online Support" in your installed plugins list. Check the author name on each plugin — if it says either of those, you're affected.
Step 2: Check for the Backdoor File
The payload drops a file called wp-comments-posts.php (plural) in your site's webroot. WordPress ships with wp-comments-post.php (singular). The extra "s" is the giveaway.
find /var/www/ -maxdepth 2 -name "wp-comments-posts.php" -type f 2>/dev/null
If that returns anything, your site was actively exploited during the injection window.
For a single site:
ls -la /path/to/wordpress/wp-comments-posts.php
If the file exists, you've been hit.
Step 3: Check wp-config.php for Injected Code
This is the most critical check. The backdoor injects PHP into wp-config.php. A clean wp-config.php is typically 3–4KB. If yours is 9KB or more, something is wrong.
wc -c /path/to/wordpress/wp-config.php
The original advisories described an obfuscated payload using base64_decode and eval. I recovered a sample from one of my cleanup jobs that uses none of those. It's plain readable PHP that resolves its command-and-control domain via Ethereum smart contracts and serves cloaked SEO spam only to Googlebot. If you're only grepping for base64_decode or eval, you'll miss this variant entirely.
The strongest structural tell across every variant I've seen: code appearing after require_once ABSPATH . 'wp-settings.php';. No legitimate wp-config.php has executable code below that line. If anything is there, investigate.
A broader content check that catches both the obfuscated and plain-PHP variants:
grep -lE "base64_decode|str_rot13|gzinflate|eval\(|ethereum-rpc|eth_call|0x8B51674F44A1aA39aD5b3A365DA1d667E54aF292|v82L4MZsM3tIW0wK" /path/to/wordpress/wp-config.php
To catch the "code after wp-settings.php" signal:
awk '/require_once.*wp-settings\.php/{found=1;next} found && /[^[:space:]]/{print "INJECTED CODE AFTER wp-settings.php"; exit}' /path/to/wordpress/wp-config.php
Across all my servers, I ran this in bulk:
for site in /var/www/*/; do
config="$site/wp-config.php"
[ -f "$config" ] || continue
size=$(wc -c < "$config")
sig_hits=$(grep -cE "base64_decode|str_rot13|gzinflate|eval\(|ethereum-rpc|eth_call|0x8B51674F44A1aA39aD5b3A365DA1d667E54aF292|v82L4MZsM3tIW0wK" "$config" 2>/dev/null)
post_settings=$(awk '/require_once.*wp-settings\.php/{found=1;next} found && /[^[:space:]]/{print 1; exit}' "$config")
if [ "$size" -gt 6000 ] || [ "$sig_hits" -gt 0 ] || [ -n "$post_settings" ]; then
echo "INVESTIGATE: $config (${size} bytes, ${sig_hits} signature hits, post-wp-settings code: ${post_settings:-no})"
fi
done
Step 4: Check for the wpos-analytics Module
Even if the payload wasn't delivered (you might have been offline during the injection window, or your server blocked the C2 domain), the backdoor code still exists in the plugin if you're running version 2.6.7 or later.
find /var/www/ -type d -name "wpos-analytics" 2>/dev/null
Also search the main plugin PHP files for the loader:
grep -rl "Plugin Wpos Analytics Data Starts" /path/to/wordpress/wp-content/plugins/ 2>/dev/null
Cleanup If You're Compromised
If any of the above checks came back positive, here's the remediation process. Don't skip any step — a plugin update alone does not fix this.
1. Take a Backup First
Before touching anything, back up the entire site — files and database. If something goes wrong during cleanup, you need a rollback point.
wp db export /tmp/backup-$(date +%Y%m%d).sql --path=/path/to/wordpress/
tar czf /tmp/site-backup-$(date +%Y%m%d).tar.gz /path/to/wordpress/
2. Delete the Backdoor File
rm /path/to/wordpress/wp-comments-posts.php
3. Clean wp-config.php
Open wp-config.php in a text editor. Look for any block of obfuscated PHP code — typically a large chunk of base64_decode, eval, or str_rot13 calls — sitting above the require_once ABSPATH . 'wp-settings.php'; line. Remove the entire injected block. Save the file.
If you're not confident identifying the malicious code, compare your wp-config.php against a known clean copy. If you set up your site with a standard installer, the only things that should be in there are your database credentials, authentication keys, table prefix, debug settings, and the ABSPATH definition.
4. Remove the Affected Plugins
Don't just deactivate — delete them entirely:
wp plugin deactivate --all --path=/path/to/wordpress/
wp plugin delete plugin-slug-here --path=/path/to/wordpress/
These plugins are permanently closed on WordPress.org. There is no safe version to update to. Find alternatives.
5. Delete the wpos-analytics Directory
If any remnants remain:
find /path/to/wordpress/wp-content/plugins/ -type d -name "wpos-analytics" -exec rm -rf {} +
6. Scan for Additional Compromise
The deserialization backdoor allowed arbitrary code execution. There's no guarantee the attacker only dropped wp-comments-posts.php. Run a broader scan:
find /path/to/wordpress/ -name "*.php" -newer /path/to/wordpress/wp-config.php -mtime -14 -type f
Check for any PHP files modified in the last two weeks that you didn't touch. Also look for unknown admin users:
wp user list --role=administrator --path=/path/to/wordpress/
7. Reset Authentication Keys
If your wp-config.php was modified, assume the attacker could read your salts and keys. Generate new ones:
wp config shuffle-salts --path=/path/to/wordpress/
Why This Attack Worked
WordPress.org reviews new plugin submissions, but it has no mechanism to review code when an existing plugin changes ownership. The trust earned by the original developer transferred instantly to the buyer. There's no code signing for plugin updates. No ownership transfer review. No automated diff alerting on suspicious commits.
The attacker exploited this structural gap perfectly: buy trusted plugins, wait months building credibility, then activate.
How to Protect Yourself Going Forward
There is no plugin you can install that would have prevented this. The malicious code was delivered through WordPress.org's official update channel, signed with the same trust as any legitimate update.
What actually helps:
- File integrity monitoring that alerts when core files like
wp-config.phpchange unexpectedly - Plugin repository status checks — polling WordPress.org for plugins that have been closed for security reasons, so you find out at the same time as everyone else instead of a week later
- Regular manual audits of installed plugins — check who the author is, whether ownership has changed, and whether the changelog makes sense
- WP-CLI scripted checks across all your sites, not just one-by-one in the admin dashboard
- A maintenance professional who monitors security advisories and can audit your sites before you even know there's a problem
I had every affected client checked and cleared within 45 minutes of reading the advisory. None of my managed sites were running the compromised plugins — because I'd flagged and replaced several of them months ago when the author ownership changed. That's the kind of thing you catch when someone is watching.
How wp-admin.online Would Have Handled This
Part of the reason I wrote the SSH-and-WP-CLI audit above is that I've been building wp-admin.online to turn that manual process into something that happens automatically across every site I manage. I want to be straight with you about what it would and wouldn't have caught on this specific attack.
What it would have caught: the moment the WordPress.org Plugins Team closed the EssentialPlugin portfolio on 7 April 2026, a background job in wp-admin.online queries the WordPress.org plugin info API every six hours. Any plugin across the entire managed fleet that suddenly reports closed: true with closed_reason: "security" gets flagged. For every affected site, I would have seen:
- A "REMOVED (SECURITY)" badge next to each of the 31 plugins in my Plugins tab
- An attention queue item scored 94 — the same "Do Now" lane as critical vulnerabilities — with a one-click deep link to a cross-site search showing every site running that slug
- A single email per user (not per site) listing the affected site count and the plugin name
- Any queued auto-updates for those plugins blocked, so the platform couldn't accidentally pull a malicious version
In short: the Patchstack alert that woke me up on 7 April would have shown up inside my own dashboard within six hours, pre-filtered to only the sites that actually run those plugins, with the remediation path one click away.
What it would not have caught: the wp-config.php injection itself. The attacker's payload dropped on 6 April, a full day before WordPress.org closed the plugins. During those six hours of active exploitation, the plugins were still live and trusted on WordPress.org. No amount of repo-status polling helps you inside that window.
That's why the next feature on the roadmap is file integrity monitoring. Record a SHA256 of wp-config.php (and .htaccess, and core files) on first sync, watch for drift, alert on any change. Most persistence mechanisms touch one of those files. The recovered sample I saw from this attack lives in wp-config.php — exactly the file an integrity monitor is designed to protect. It doesn't exist in wp-admin.online yet. It will.
This isn't a replacement for a human watching your site. It's the tooling I use to make watching 70 sites at once practical.
Stop Firefighting. Start Maintaining.
I manage 70+ WordPress sites for UK agencies and businesses. When the next supply chain attack drops — and it will — you want someone who's already auditing your site before you've read the headline.
