The first warning was easy to miss: WordPress reported three registered users, but only two appeared in the Users screen. The missing account was publishing unsolicited advertising posts.
That inconsistency led me to a malicious must-use plugin, an unauthorized administrator, and an infection that had spread beyond the original website into a shared server environment containing 23 separate WordPress installations.
I handled the technical response from investigation to recovery: containing the incident, auditing the wider environment, removing the malware and its persistence mechanisms, preserving business data, replacing an abandoned plugin, and adapting the custom theme so the site could continue working without the vulnerable dependency.
Case facts component
- Environment: 23 separate WordPress sites on a shared agency server
- My role: Senior WordPress Developer and technical incident lead
- Date: April 2026
- Scope: Investigation, containment, malware cleanup, recovery, legacy-code replacement, hardening, and ongoing support
- Tools and systems: WordPress, PHP, MySQL, SSH, WP-CLI, server logs, Wordfence, Sucuri, hosting security tools, Gravity Forms, and Cloudflare
- Confidentiality: Client, agency, domain, and infrastructure details withheld under NDA
The first clue was one user who should not exist
The website was still online. The WordPress dashboard worked, the database was accessible, and there were no obvious signs of a complete takeover. The visible symptom was a collection of unexpected promotional posts in the blog.
While investigating those posts, I noticed that the user count did not match the user list. WordPress said that three accounts existed, but the administration screen displayed only two.
This was not a harmless display issue. The third account had administrator access and was responsible for creating the unwanted content. A malicious file in the must-use plugins directory was modifying the WordPress administration interface to conceal the account while leaving it active.
That small discrepancy changed the scope of the task from “remove a few spam posts” to a full security incident investigation.
One compromised site became a server-wide incident
The affected website was hosted alongside 22 other WordPress sites managed by the same agency. Once I confirmed the hidden administrator and malicious must-use plugin, it was no longer safe to treat the problem as an isolated website infection.
The malware had moved beyond the original installation into the shared hosting environment. I therefore treated all 23 websites as potentially affected and reviewed the portfolio rather than cleaning only the site where the spam first appeared.
This distinction mattered. Removing the visible payload from one website would have produced a clean scan for a moment, but another compromised location on the server could have restored the malware later.
Containment came before cleanup
Before changing files, I documented the symptoms and preserved the information needed to understand the incident. I coordinated with the hosting provider, kept the project manager informed, and helped the agency communicate clearly with affected clients.
The immediate goal was to limit further changes while keeping essential business websites available wherever it was safe to do so.
My initial response included:
- reviewing recent administrator activity, unexpected posts, and database changes;
- identifying and restricting unauthorized access;
- preserving backups and a record of the compromised state before cleanup;
- reviewing hosting and server logs for suspicious requests and file changes;
- rotating relevant WordPress, hosting, database, and file-transfer credentials;
- invalidating active sessions and refreshing WordPress security keys and salts;
- coordinating server-level checks with the hosting provider; and
- expanding the audit from the original website to all 23 WordPress installations.
Finding and removing the persistence mechanisms
I used SSH and WP-CLI alongside Wordfence, Sucuri, the hosting provider’s security tools, server logs, checksum comparisons, targeted file searches, and manual code and database inspection.
Automated scanners were useful for coverage, but they were not the whole investigation. A scanner can flag a suspicious file; it cannot always explain why an account is missing from the dashboard, whether a custom file is legitimate, or which part of a legacy theme depends on an unsafe plugin.
The audit covered:
- WordPress core integrity and unexpected modified files;
- active, inactive, and must-use plugins;
- theme files and custom PHP code;
- visible and hidden WordPress users and their capabilities;
- scheduled tasks and unfamiliar cron events;
- unexpected PHP files, obfuscated code, and recently changed files;
- database content related to spam posts, users, options, and injected settings;
- configuration and rewrite files, including
wp-config.phpand.htaccess; - plugin and theme versions across the shared environment; and
- server logs that could help reconstruct the infection path.
I removed the unauthorized administrator, malicious must-use plugin, injected content, and other compromised files and database records. Where integrity could not be trusted, I replaced WordPress core and extension files with clean copies from trusted sources instead of attempting to repair unknown code in place.
The abandoned plugin was the real repair
The investigation traced the initial entry point to a plugin that had not received an update in roughly ten years. Removing the malware without addressing that dependency would have left the same route open for another compromise.
The plugin could not simply be deleted, however. The website’s custom theme relied on its functionality, so removing it immediately would have broken parts of the live site.
I selected a maintained alternative and refactored the relevant theme integration around it. This closed the known entry point while preserving the website’s existing behavior for visitors and administrators.
That was the most important part of the recovery: not only deleting malicious code, but changing the application so the unsafe component was no longer required.
Preserving forms and business data
The websites were active business tools, not disposable installations. Existing Gravity Forms entries had to survive the cleanup and restructuring.
I preserved and transferred the required form entries, checked the recovered data, and verified that the forms continued to accept and store new submissions correctly. No client data was lost during the recovery.
The investigation did not identify evidence of data exfiltration. That wording is deliberate: an incident review can report what the available files, database records, and logs show, but it should not turn an absence of evidence into an absolute promise that no access ever occurred.
Verification before returning to normal operations
A clean-looking dashboard was not enough to close the incident. I verified the environment through multiple independent checks and continued monitoring it after the immediate recovery.
Verification included:
- repeating server-side and external malware scans;
- verifying WordPress core files against official checksums;
- comparing plugin and theme files with trusted distributions where possible;
- reviewing users, roles, scheduled tasks, and must-use plugins again;
- checking logs for renewed suspicious activity;
- testing the repaired theme and replacement plugin functionality;
- testing forms and confirming that existing Gravity Forms entries remained available;
- reviewing the affected websites from both the public and administrative sides; and
- monitoring the environment for signs of reinfection.
The security warning was removed, the affected websites remained operational, and no repeat infection appeared during the subsequent support period.
Hardening the shared environment
Once the environment was clean, I reduced the likelihood and potential impact of another incident. The exact configuration varied by website, but the hardening work included:
- updating WordPress core, themes, and plugins;
- removing unused and abandoned extensions;
- replacing the vulnerable legacy plugin with a maintained alternative;
- tightening file and directory permissions;
- strengthening administrator access with two-factor authentication and login restrictions;
- reviewing Cloudflare and web application firewall protection;
- improving backup coverage and recovery readiness;
- adding ongoing security monitoring and scheduled checks; and
- reviewing server configuration with the hosting provider.
Security work does not end with a successful scan. The long-term protection came from removing the obsolete dependency, reducing unnecessary access, improving visibility, and continuing to maintain the sites after the incident.
Clear communication during an uncomfortable incident
I was the only developer handling the technical investigation and recovery, but the incident involved more than code. I prepared detailed progress reports for the project manager and stayed in contact with the hosting provider and clients throughout the response.
The reports separated confirmed findings from open questions, explained what had already been contained, and made the next technical steps clear. That gave non-technical stakeholders useful information without overwhelming them with raw scan results or speculation.
The result
- The hidden administrator and malicious must-use plugin were identified and removed.
- The wider 23-site environment was audited instead of treating the incident as a single-site cleanup.
- A ten-year-old abandoned plugin was replaced with a maintained alternative.
- The custom theme was adapted so the website retained its required functionality.
- Gravity Forms entries and other required business data were preserved.
- The affected websites remained operational after recovery.
- The external security warning was cleared.
- No evidence of data loss or exfiltration was identified during the investigation.
- No reinfection was observed during the subsequent support period.
- The agency received detailed technical reporting and continued maintenance after the incident.
