'Someone Bought 30 WordPress Plugins and Planted a Backdoor in All of Them'

Austin Ginder, Anchor Hosting:

Last week, I wrote about catching a supply chain attack on a WordPress plugin called Widget Logic. A trusted name, acquired by a new owner, turned into something malicious. It happened again. This time at a much larger scale.

[…]

The injected code was sophisticated. It fetched spam links, redirects, and fake pages from a command-and-control server. It only showed the spam to Googlebot, making it invisible to site owners. And here is the wildest part. It resolved its C2 domain through an Ethereum smart contract, querying public blockchain RPC endpoints. Traditional domain takedowns would not work because the attacker could update the smart contract to point to a new domain at any time.

[…]

Two supply chain attacks in two weeks. Both followed the same pattern. Buy a trusted plugin with an established install base, inherit the WordPress.org commit access, and inject malicious code. The Flippa listing for Essential Plugin was public. The buyer’s background in SEO and gambling marketing was public. And yet the acquisition sailed through without any review from WordPress.org.

WordPress.org has no mechanism to flag or review plugin ownership transfers. There is no “change of control” notification to users. No additional code review triggered by a new committer. The Plugins Team responded quickly once the attack was discovered. But 8 months passed between the backdoor being planted and being caught.

It’s truly astonishing that WordPress, despite its scale, has such exploitable supply-chain security. I’m aware of a similar npm supply-chain risk with Gobbler, though I am using both Dependabot and Socket.dev to mitigate it.1


  1. I am also reminded of my own brief stint with WordPress in mid-2025 — I was quite excited. However, after four days I was already concerned about its security and installed wpfail2ban.

So, I Switched (Back) To WordPress

So, I Switched (Back) To WordPress

After around six years dabbling with Ghost and static site builders I’ve migrated back to WordPress.

First impressions of the wp-admin panel were “meh”. It’s not really changed in the last few years and it’s really not as nice as Ghost’s admin panel. It does the job, though.

The Gutenberg editor, however, is amazing. It’s such a massive improvement—at least from memory—over the old WYSWYG editor.

To get things just so, I’ve had to install a few plugins:

  • Yoast SEO

  • Advanced Custom Fields

  • I’ve created an external_url field so that I can use it in the JSON feed 1.

  • JSON Feed (by Manton Reece and Daniel Jalkut)

  • Code Block Pro

  • Secure Passkeys

  • Various plugins to add Fediverse tags to the <head> element

  • ActivityPub

To get footnotes to render correctly in RSS readers—i.e., appear as pop-ups—I’ve added the below code to functions.php:

functions.php
function clean_gutenberg_footnotes_for_rss($content) {
if (!is_feed()) return $content;
$refIndex = 1;
$uuidToIndex = [];
// Step 1: Replace <sup> references with numbered links and track mapping
$content = preg_replace_callback(
'/<sup data-fn="([^"]+)" class="fn">\s*<a[^>]*>(\d+)<\/a>\s*<\/sup>/i',
function ($matches) use (&$uuidToIndex, &$refIndex) {
$uuid = $matches[1];
if (!isset($uuidToIndex[$uuid])) {
$uuidToIndex[$uuid] = $refIndex++;
}
$n = $uuidToIndex[$uuid];
return "<sup id=\"fnr-{$uuid}\"><a href=\"#fn-{$uuid}\">{$n}</a></sup>";
},
$content
);
// Step 2: Rewrite footnote section and replace ↩ links
$content = preg_replace_callback(
'/<ol class="wp-block-footnotes">(.*?)<\/ol>/is',
function ($matches) use (&$uuidToIndex) {
$footnotes = $matches[1];
preg_match_all('/<li id="([^"]+)">(.*?)<\/li>/is', $footnotes, $liMatches, PREG_SET_ORDER);
$output = '<div class="footnotes"><ol>';
foreach ($liMatches as $li) {
$id = $li[1];
$rawText = $li[2];
$n = $uuidToIndex[$id] ?? '?';
// Replace ↩ <a> link with formatted Unicode arrow link
$fixedText = preg_replace(
'/<a[^>]+href="#' . preg_quote($id, '/') . '-link"[^>]*>.*?<\/a>/is',
'<a href="#fnr-' . $id . '" class="footnoteBackLink" title="Jump back to footnote ' . $n . ' in the text.">↩︎</a>',
$rawText
);
$output .= '<li id="fn-' . esc_attr($id) . '"><p>' . trim($fixedText) . '</p></li>';
}
$output .= '</ol></div>';
return $output;
},
$content
);
return $content;
}
add_filter('the_content', 'clean_gutenberg_footnotes_for_rss');

  1. I’ll add it to the RSS xml feed soon.