About Wp Malware Cleanup MCP
Wp Malware Cleanup MCP is an MCP server published by vapvarun in the Content category: mCP server for cleaning malware from WordPress sites via SSH and WP-CLI. Batch operations for 20+ sites.
The repository has 32 stars and 5 forks, with the last commit a month ago. Recent commit activity is the strongest signal that reported issues will get attention.
Install
git clone https://github.com/vapvarun/wp-malware-cleanup-mcpThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Tools (24)
wp_audit_userDetailed audit of user activity
Parameters not published.
wp_batch_scanScan all configured sites
Parameters not published.
wp_check_cron_eventsScan cron events for suspicious hooks
Parameters not published.
wp_export_sitesExport site configurations
Parameters not published.
wp_find_recent_usersFind users created recently
Parameters not published.
wp_fleet_statusFleet scorecard across all sites
Parameters not published.
wp_generate_case_studyGenerate anonymized case study
Parameters not published.
wp_generate_reportGenerate cleanup report
Parameters not published.
wp_get_siteGet site information
Parameters not published.
wp_host_group_statusOne account's sites + reinfection check
Parameters not published.
Show all 24 tools
wp_list_adminsList all administrator users
Parameters not published.
wp_list_scansList scan tasks
Parameters not published.
wp_list_sitesList all configured sites
Parameters not published.
wp_monitor_http_requestsMonitor outgoing HTTP requests
Parameters not published.
wp_read_fileRead file contents for analysis
Parameters not published.
wp_scanAll malware scans via one tool; `scope=` selects depth (quick/full/deep) or a category (uploads/webshells/themes/cron/wpconfig/…)
Parameters not published.
wp_scan_resultGet background scan results
Parameters not published.
wp_scan_statusCheck background scan status
Parameters not published.
wp_test_connectionTest SSH and WP-CLI connectivity
Parameters not published.
wp_threat_check_fileCheck file against threat database
Parameters not published.
wp_threat_db_statsShow threat database statistics
Parameters not published.
wp_threat_exportExport signatures
Parameters not published.
wp_threat_top_signaturesShow most detected signatures
Parameters not published.
wp_versionShow version information
Parameters not published.
Ask AI
Ask AI about Wp Malware Cleanup MCP
Powered by Claude · Grounded in docs
Security checks
Static checks derived from the repository 1 hours ago. Heuristics on the source, not an audit.
- ✓README presentThe repository ships documentation.
- ✓License declaredUsable under a stated license.
- ✓Tests presentA test suite exists in the repository.
- ✓Dependencies pinnedA lockfile pins dependency versions.
- !No dynamic code executionSource scan found eval or shell execution. Review how tool inputs reach it before granting credentials.
- !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.
Releases
README
WordPress Malware Cleanup MCP Server
Find and remove WordPress malware - the whole kill chain, not just the visible shell - from a single site or an entire fleet, with an AI assistant that previews every change before it touches your server.
Free and open source (GPL), for any WordPress site owner: a solo blogger with one site, a freelancer cleaning up a client hack, or a host managing thousands of installs. You drive it in plain language through Claude; it does the SSH and WP-CLI work, shows you a preview, and never does anything irreversible without your confirmation and a backup.
By Wbcom Designs (Varun Dubey) · Ongoing threat research: WP Vanguard
Why this exists
Most scanners grep for known signatures and call a site "clean." Real WordPress infections do not work that way - they reinfect. They leave a fake mu-plugin that re-drops the payload, a rogue admin, a cron job, a poisoned .htaccess, an SSH key, a zero-byte decoy a signature scan cannot even see. Delete the visible shell and it is back within the hour.
This tool hunts the entire persistence chain and removes it safely:
- Finds what signature scanners miss - zero-byte decoys, drop-in backdoors (
object-cache.php/db.php), DB-payload options, cron-guard hooks, themefunctions.phpinjections, rogue admin accounts, SSH-key backdoors, and UA-cloak.htaccessrules. - Safe by construction - scans are read-only by default; every destructive action shows a preview manifest and requires
confirm=True; deletions quarantine first (recoverable) and refuse to run without a recent backup. - Works anywhere - one site or a whole fleet, cPanel or Plesk or plain SSH, over your existing SSH and WP-CLI. Nothing to install on the infected site.
Proven on a real fleet
The SC/SCOCV drop-in backdoor case study documents a live cleanup of a self-reinfecting backdoor family across a 22-account cPanel server: the reinfection vectors, the detection gaps that let it survive earlier cleanups, and how each layer was removed. More write-ups live in docs/case-studies, with new threat research published at WP Vanguard.
How to use it (in plain English)
You do not write code or run commands. Install the server once, then just talk to Claude in plain language - it does the SSH and WP-CLI work and shows you a preview before it changes anything.
1. Install it once (about 5 minutes - see Installation).
2. Add your site so Claude knows how to reach it. Tell it something like:
"Add my site example.com - SSH host 203.0.113.10, port 22, user bob, WordPress at /home/bob/public_html, using my SSH key."
3. Ask for what you need, in plain English. For example:
"Scan example.com for malware and show me what you find." "My site got hacked - find the backdoors and clean it up." "Check all my sites and tell me which ones are infected." "Reset the admin passwords on example.com and remove any rogue admin accounts." "Harden example.com, but keep automatic plugin updates working."
4. Review the preview, then confirm. Scans are always read-only. Anything destructive (deleting files, resetting passwords, replacing core) shows you exactly what it will do and waits for your go-ahead, and it refuses to run irreversible steps without a recent backup.
That is the whole loop: ask, preview, confirm, re-scan. New to it? Start with Your First Scan or the task-by-task Usage Guide.
SAFETY: This tool performs irreversible operations on live sites (file deletion, database modification, password resets, core replacement). Always take a full backup first. Destructive tools require
confirm=Trueand show a preview manifest before running. See the Tool Risk Matrix. The authors are not liable for data loss.
New here? The Usage Guide walks the backup → preview → execute → re-scan workflow task by task. Hosts and agencies: the Wiki has setup guides, daily-scan automation, and the fleet playbook.
Table of Contents
- Before You Start
- Tool Risk Matrix
- Features
- Requirements
- Installation
- Configuration
- Quick Start
- Available Tools
- Usage Examples
- Cleanup Workflow
- Security Considerations
- Troubleshooting
- Project Structure
- Contributing
- License
- Credits
Before You Start
Before using any cleanup or hardening tools on a live WordPress site:
-
Create a full server backup (files + database). Use your hosting provider's backup tool or:
wp_backup_database("site-name")A database backup alone is not sufficient - back up the entire
wp-content/directory as well. -
Test on staging first if possible. Clone the infected site to a staging environment and run cleanup there before touching production.
-
Keep an SSH session open to monitor the server during cleanup. Watch for errors or unexpected behavior.
-
Note current admin passwords before running
wp_reset_passwords. After reset, the old passwords are gone permanently. -
Destructive tools require
confirm=True. Tools likewp_reinstall_core,wp_reset_passwords,wp_complete_cleanup, etc. will show a preview manifest when called withoutconfirm=True. Review the manifest before confirming.
Tool Risk Matrix
All tools are classified into three risk tiers:
SAFE (read-only)
These tools only read data. No files or database records are modified.
| Tool | Description |
|---|---|
wp_scan |
All malware scans via one tool; scope= selects depth (quick/full/deep) or a category (uploads/webshells/themes/cron/wpconfig/…) |
wp_fleet_status |
Fleet scorecard across all sites |
wp_host_group_status |
One account's sites + reinfection check |
wp_check_cron_events |
Scan cron events for suspicious hooks |
wp_list_admins |
List all administrator users |
wp_audit_user |
Detailed audit of user activity |
wp_find_recent_users |
Find users created recently |
wp_list_sites |
List all configured sites |
wp_get_site |
Get site information |
wp_test_connection |
Test SSH and WP-CLI connectivity |
wp_read_file |
Read file contents for analysis |
wp_version |
Show version information |
wp_export_sites |
Export site configurations |
wp_generate_report |
Generate cleanup report |
wp_generate_case_study |
Generate anonymized case study |
wp_batch_scan |
Scan all configured sites |
wp_scan_status |
Check background scan status |
wp_scan_result |
Get background scan results |
wp_list_scans |
List scan tasks |
wp_threat_db_stats |
Show threat database statistics |
wp_threat_check_file |
Check file against threat database |
wp_threat_top_signatures |
Show most detected signatures |
wp_threat_export |
Export signatures |
wp_monitor_http_requests |
Monitor outgoing HTTP requests |
MODERATE (reversible changes)
These tools make changes that can be undone or automatically create backups.
| Tool | Description |
|---|---|
wp_backup_database |
Create database backup |
wp_quarantine_file |
Move file to quarantine (recoverable) |
wp_add_site |
Add a site to configuration |
wp_update_site |
Update site configuration |
wp_remove_site |
Remove site from configuration |
wp_harden_wpconfig |
Add security constants to wp-config.php |
wp_add_security_htaccess |
Add security rules to .htaccess |
wp_install_security_mu_plugin |
Install security MU-plugin |
wp_fix_permissions |
Set secure file permissions |
wp_disable_file_editing |
Disable theme/plugin editor |
wp_threat_db_sync |
Sync community threat signatures |
wp_threat_add_signature |
Add malware signature to local DB |
wp_threat_learn_from_cleanup |
Record malware hash for future detection |
wp_start_background_scan |
Start background scan task |
DESTRUCTIVE (irreversible - requires confirm=True)
These tools permanently delete files, reset credentials, or overwrite data. Always create a backup first. Tools marked with confirm will show a preview manifest when called without confirm=True.
| Tool | confirm gate |
Description |
|---|---|---|
wp_complete_cleanup |
Yes | Full cleanup + hardening (20 destructive steps) |
wp_reinstall_core |
Yes | Overwrite wp-admin/, wp-includes/, root PHP files |
wp_reinstall_all_plugins |
Yes | Reinstall all plugins from WordPress.org |
wp_reinstall_all_themes |
Yes | Reinstall all themes from WordPress.org |
wp_reset_passwords |
Yes | Reset all administrator passwords |
wp_regenerate_salts |
Yes | Replace security salts, invalidate all sessions |
wp_revoke_app_passwords |
Yes | Delete all application passwords |
wp_reinstall_plugin |
Yes (since 2.1.0) | Reinstall a single plugin from repo |
wp_delete_user |
Yes | Delete a WordPress user |
wp_clean_uploads_php |
Yes | Remove all PHP files from uploads |
wp_clean_core_injections |
Yes (since 2.1.0) | Delete injected core files (also supports dry_run=True) |
wp_clean_db_spam |
Yes | Remove spam comments |
wp_delete_malware_options |
Yes (since 2.1.0) | Delete malware markers from wp_options (also supports dry_run=True) |
wp_remove_mu_plugin |
Yes (since 2.1.0) | Quarantine an mu-plugin |
wp_update_all |
- | Update core, plugins, themes |
wp_batch_update |
- | Update all clean sites |
wp_full_harden |
- | Complete hardening in one command |
Features
Multi-Site Management
- Add and manage 20+ WordPress sites from a single interface
- Persistent configuration storage
- Batch operations across all sites
- Status tracking and reporting
Comprehensive Malware Scanning
- Core File Verification - Compare against official WordPress checksums
- Plugin Integrity Check - Verify plugins against WordPress.org
- Known-Vulnerable Plugin Detection - Flag outdated plugins with known CVEs (e.g. the WP File Manager RCE) by version
- File-Manager Plugin Policy - Flag any file-manager-class plugin for removal on sight (a common RCE entry vector, no visitor value)
- MU-Plugins Detection - Scan must-use plugins for backdoors, including zero-byte decoys
- Uploads Security - Find PHP files and time-stamped webshells that should not exist
- Deep Pattern Matching - Detect obfuscated code and known signatures
- Database Injection Scan - Find malicious content in posts/options (aborts loudly rather than reporting "clean" over an unreachable DB)
Persistence and reinfection vectors that signature scanners miss:
- SC/SCOCV drop-in backdoors - obfuscated
object-cache.php/db.php/advanced-cache.php, hidden.sc_*directories, DB-payload options, cron-guard hooks, themefunctions.phpinjection, and self-reinfecting mu-plugins, in one dedicated tool - Rogue admin accounts - the SC/wp2shell/mail-daemon naming families, aligned across scan and clean
- SSH-key backdoors - flags unlabeled or forced-command keys in
~/.ssh/authorized_keys(survives every file/DB cleanup) - UA-cloak
.htaccess- the "Block for Chrome" /#KH..YSmarker rules, including at the site root - Recently Modified Files - identify the infection timeline
User Security
- List and audit administrator accounts
- Track user activity before deletion
- Find recently created suspicious users
- Safe user deletion with content reassignment
Safe Cleanup Operations
- Quarantine System - Move files instead of deleting (allows recovery)
- Automatic Backups - Database export before changes
- Core Reinstallation - Restore official WordPress files
- Plugin Reinstallation - Fresh install from repository
- Permission Fixes - Set secure file permissions
Security Hardening
- Reset all admin passwords
- Regenerate security salts
- Disable admin file editing
- Add .htaccess security rules
- Update WordPress, plugins, and themes
Reporting & Logging
- Detailed cleanup reports
- Action logging per site
- Export configurations
- Status summaries
Requirements
Your Mac (Client)
- macOS 13+ (Ventura or later)
- Homebrew
- Claude Code CLI
- uv (Python package manager)
WordPress Servers
- SSH access (password or key-based)
- WP-CLI installed and in PATH
- WordPress 5.0+
macOS Setup Guide
Complete setup from scratch on a fresh Mac. Takes ~5 minutes.
Prerequisites
- Homebrew installed
- Claude Code CLI installed
- uv installed (
curl -LsSf https://astral.sh/uv/install.sh | sh)
Step 1: Install System Dependencies
# Python 3.12 (macOS system Python is too old for the mcp package)
brew install python@3.12
# sshpass (required for password-based SSH auth)
brew install sshpass
Step 2: Clone the Repository
git clone https://github.com/vapvarun/wp-malware-cleanup-mcp.git ~/.claude/wp-malware-cleanup-mcp
cd ~/.claude/wp-malware-cleanup-mcp
Step 3: Create Virtual Environment & Install Dependencies
# Create venv with Python 3.12
uv venv --python 3.12 .venv
# Install dependencies into venv
uv pip install -r requirements.txt
Step 4: Register MCP Server with Claude Code
# Register globally (available in all Claude Code sessions)
claude mcp add -s user wp-malware-cleanup -- \
~/.claude/wp-malware-cleanup-mcp/.venv/bin/python \
~/.claude/wp-malware-cleanup-mcp/server.py
Step 5: Verify
claude mcp list
# Should show: wp-malware-cleanup: ... ✓ Connected
Step 6: Restart Claude Code
Close and reopen Claude Code so the new MCP tools are loaded.
Configuration
Claude Code (Recommended)
The claude mcp add command in Step 4 automatically writes to ~/.claude.json:
{
"mcpServers": {
"wp-malware-cleanup": {
"command": "/Users/YOU/.claude/wp-malware-cleanup-mcp/.venv/bin/python",
"args": ["/Users/YOU/.claude/wp-malware-cleanup-mcp/server.py"]
}
}
}
Claude Desktop (Optional)
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"wp-malware-cleanup": {
"command": "/Users/YOU/.claude/wp-malware-cleanup-mcp/.venv/bin/python",
"args": ["/Users/YOU/.claude/wp-malware-cleanup-mcp/server.py"]
}
}
}
Note: Replace
/Users/YOUwith your actual home directory path.
Data Storage Locations
~/.wp-sites/
└── sites.json # Site configurations (shared with wp-site-doctor-mcp)
~/.wp-malware-cleanup/
├── sites.json # Legacy copy (kept in sync for backward compatibility)
├── quarantine/ # Quarantined malicious files
├── reports/ # Generated cleanup reports
├── scans/ # Persisted scan results, one JSON per run (since 2.2.0)
│ └── <site>/<scan_id>.json
├── logs/ # Per-site action logs
└── threat_intelligence.db # Local threat intel SQLite DB
Site configurations live at ~/.wp-sites/sites.json so both this MCP and
wp-site-doctor-mcp see the same
sites. A copy is also written to the legacy location so any existing tooling
that reads the old path keeps working.
The shared registry is read tolerantly (since 2.2.0): unknown keys written by another MCP are ignored rather than crashing the loader, so the two servers can evolve their own fields without corrupting the shared file.
Every whole-site wp_scan run (quick/full/deep) also writes a
structured result record to ~/.wp-malware-cleanup/scans/<site>/, so scan
history is queryable and can be diffed before/after a cleanup.
Quick Start
After setup, restart Claude Code and use these tools in conversation:
1. Add Your First Site
With password auth (common for InstaWP, managed hosting):
wp_add_site(
name="my-site",
host="147.182.198.163",
username="sshuser",
wp_path="/home/sshuser/web/example.com",
auth_type="password",
site_url="https://example.com"
)
With SSH key auth (recommended for production):
wp_add_site(
name="client-blog",
host="example.com",
username="deploy",
wp_path="/var/www/html",
auth_type="key",
key_path="~/.ssh/id_rsa"
)
Grouping sites that share a server / cPanel account (host_group):
# Two sites under the same cPanel user - group them so cross-site reinfection
# can be reasoned about. host_group defaults to `host` when omitted.
wp_add_site(name="shop-acme", host="srv12.host.net", username="acme",
wp_path="/home/acme/shop", auth_type="key", host_group="acme")
wp_add_site(name="blog-acme", host="srv12.host.net", username="acme",
wp_path="/home/acme/blog", auth_type="key", host_group="acme")
On shared hosting, one infected site commonly reinfects its siblings under the same account. Setting a shared
host_grouprecords that relationship for fleet-wide reasoning. Update it any time withwp_update_site("blog-acme", host_group="acme").
2. Test the Connection
# Password auth - provide password each time (not stored on disk for security)
wp_test_connection("my-site", password="your-ssh-password")
# Key auth - no password needed
wp_test_connection("client-blog")
Security note: Passwords are never stored on disk. They are passed via the
SSHPASSenvironment variable at runtime and discarded after the SSH session ends.
3. Run a Full Scan
wp_scan("my-site", password="your-ssh-password")
4. View All Sites
wp_list_sites()
5. See the Whole Fleet at a Glance
wp_fleet_status() # instant scorecard from the last stored scans
wp_fleet_status(refresh=True) # quick-scan every site first, then report
wp_host_group_status("acme") # one cPanel account + reinfection assessment
Available Tools
Site Management
| Tool | Description |
|---|---|
wp_add_site |
Add a new WordPress site |
wp_update_site |
Update site configuration |
wp_get_site |
Get detailed site information |
wp_list_sites |
List all sites with status |
wp_remove_site |
Remove a site from configuration |
wp_test_connection |
Test SSH and WP-CLI connectivity |
Fleet (since 2.2.0)
| Tool | Description |
|---|---|
wp_fleet_status |
At-a-glance scorecard across all sites, worst-first, with risk scores. Instant from stored scans; pass refresh=True to quick-scan the fleet first. Flags clean siblings of an infected site as AT RISK. |
wp_host_group_status |
All sites in one server / cPanel account (host_group) plus a cross-site reinfection assessment and clean order. |
Scanning - one tool, many scopes
All scans run through a single tool, wp_scan(name, scope="full"). Pick a
scope for depth or a specific area; if unsure, use the default full.
wp_scan("my-site") # full scan (default)
wp_scan("my-site", scope="quick") # 2-3 min triage
wp_scan("my-site", scope="deep") # most thorough, step-by-step
wp_scan("my-site", scope="uploads") # one targeted category
scope |
Scans |
|---|---|
quick |
Fast triage (core + key plugins + uploads) |
full (default) |
All categories comprehensively |
deep |
Full scan with step-by-step progress |
verbose |
Full scan showing every command run |
core |
WordPress core checksums |
plugins |
Plugins vs WordPress.org |
suspicious-plugins |
Random/malware-named plugin folders |
hidden-plugins |
Self-hiding plugins |
themes |
Malware themes |
mu-plugins |
Must-use plugin malware |
uploads |
Dangerous files in uploads |
webshells |
Web shells / file-manager backdoors |
hidden-admins |
Suspicious admin accounts |
app-passwords |
Suspicious application passwords |
malware-patterns |
Malware signature patterns |
recently-modified |
Recently changed files |
db |
Database injection check |
cron |
Suspicious wp-cron events |
wpconfig |
wp-config.php injection (read-only) |
Whole-site scopes (quick/full/deep/verbose) persist a result for the
fleet scorecard (wp_fleet_status); targeted scopes report without changing the
site's overall verdict.
User Audit Tools
| Tool | Description |
|---|---|
wp_list_admins |
List all administrator users |
wp_audit_user |
Detailed audit of user activity |
wp_find_recent_users |
Find users created recently |
wp_delete_user |
Delete user after audit |
Cleanup Tools
| Tool | Description |
|---|---|
wp_complete_cleanup |
Full cleanup + hardening in one command (20 steps). Use without confirm for preview manifest. |
wp_quarantine_file |
Move malicious file to quarantine (auto-learns hash) |
wp_read_file |
Read file contents for analysis |
wp_remove_mu_plugin |
Quarantine an mu-plugin |
wp_reinstall_core |
Reinstall WordPress core files |
wp_reinstall_plugin |
Reinstall a plugin from repo |
wp_clean_uploads_php |
Remove all PHP from uploads |
wp_clean_core_injections |
Delete injected core files that wp core download --force misses |
wp_clean_db_spam |
Delete spam/trash comments (backup-gated - see below) |
wp_delete_malware_options |
Delete known malware campaign markers from wp_options (backup-gated) |
wp_clean_cron_events |
Delete malicious wp-cron events (the #1 reinfection vector) |
wp_clean_malicious_htaccess |
Quarantine hostile .htaccess/.user.ini in uploads/wp-content |
wp_revoke_app_passwords |
Revoke all application passwords across all users |
wp_fix_permissions |
Fix file/directory permissions |
Backup gate (since 2.2.0). Tools that delete data they cannot restore -
wp_clean_db_spam,wp_delete_malware_options- refuse to run unless a recent backup is found in the action log (within 30 minutes). Runwp_backup_database("site")first, or passforce=Trueto override when you accept the risk. Reversible tools (quarantine-based, likewp_quarantine_fileandwp_clean_uploads_php) are not gated - their preview explains recovery.
Security Hardening
| Tool | Description |
|---|---|
wp_reset_passwords |
Reset all admin passwords |
wp_regenerate_salts |
Regenerate security salts |
wp_disable_file_editing |
Disable theme/plugin editor |
wp_harden_wpconfig |
Apply security constants to wp-config.php |
wp_install_security_mu_plugin |
Install persistent security MU-plugin |
wp_full_harden |
Complete hardening in one command |
wp_update_all |
Update core, plugins, themes |
wp_add_security_htaccess |
Add security rules to .htaccess |
Database Tools
| Tool | Description |
|---|---|
wp_scan (scope="db") |
Scan database for injected malware |
wp_backup_database |
Create database backup |
wp_clean_db_spam |
Remove spam comments |
Batch Operations
| Tool | Description |
|---|---|
wp_batch_scan |
Scan all configured sites |
wp_batch_update |
Update all clean sites |
Reporting
| Tool | Description |
|---|---|
wp_generate_report |
Generate detailed cleanup report |
wp_incident_report |
Client-facing incident report: timeline, IOCs, before/after, clean sign-off |
wp_generate_case_study |
Generate anonymized case study for public sharing |
wp_export_sites |
Export site configurations |
wp_version |
Show version information |
Case Studies
Anonymized malware case studies are available in docs/case-studies/. These document real-world infections with:
- Detection signatures and IOCs
- Code analysis and behavior
- Remediation steps
- Lessons learned
Threat Intelligence Database
| Tool | Description |
|---|---|
wp_threat_db_stats |
Show database statistics (signatures, detections) |
wp_threat_db_sync |
Sync with GitHub community signatures |
wp_threat_add_signature |
Add new malware signature to local database |
wp_threat_learn_from_cleanup |
Learn malware hash from cleanup for future detection |
wp_threat_check_file |
Check specific file against threat database |
wp_threat_export |
Export signatures for GitHub contribution |
wp_threat_top_signatures |
Show most frequently detected signatures |
Usage Examples
Adding Multiple Sites
# Site with SSH key
wp_add_site(
name="site1",
host="server1.example.com",
username="deploy",
wp_path="/var/www/site1",
auth_type="key",
key_path="~/.ssh/id_rsa"
)
# Site with different port
wp_add_site(
name="site2",
host="server2.example.com",
port=2222,
username="admin",
wp_path="/home/admin/public_html",
auth_type="key",
key_path="~/.ssh/server2_key"
)
Batch Scanning All Sites
# Scan all sites
wp_batch_scan()
# Scan only pending sites
wp_batch_scan(status_filter="pending")
Complete Cleanup Workflow
# 1. Backup first!
wp_backup_database("infected-site")
# 2. Run full scan
wp_scan("infected-site")
# 3. Audit suspicious users
wp_list_admins("infected-site")
wp_audit_user("infected-site", "suspicious_user")
# 4. Delete malicious user
wp_delete_user("infected-site", "suspicious_user", "real_admin", confirm=True)
# 5. Clean malicious files
wp_clean_uploads_php("infected-site", confirm=True)
wp_remove_mu_plugin("infected-site", "malware.php")
# 6. Reinstall core and plugins (confirm=True required for destructive tools)
wp_reinstall_core("infected-site", confirm=True)
# 7. Harden security (confirm=True required)
wp_reset_passwords("infected-site", confirm=True)
wp_regenerate_salts("infected-site", confirm=True)
wp_disable_file_editing("infected-site")
wp_add_security_htaccess("infected-site")
# 8. Update everything
wp_update_all("infected-site")
# 9. Verify cleanup
wp_scan("infected-site")
# 10. Generate report
wp_generate_report("infected-site")
One-Command Cleanup (with preview)
# Preview what will happen (dry run - no changes made)
wp_complete_cleanup("infected-site")
# Review the manifest, then execute
wp_complete_cleanup("infected-site", confirm=True)
Cleanup Workflow
Phase 1: Assessment
-
Backup Database
wp_backup_database("site-name") -
Run Full Scan
wp_scan("site-name") -
Check Recently Modified Files
wp_scan("site-name", scope="recently-modified")
Phase 2: User Audit
-
List Admin Users
wp_list_admins("site-name") -
Find Recent Users
wp_find_recent_users("site-name", days=30) -
Audit Suspicious Users
wp_audit_user("site-name", "suspicious_username") -
Delete Malicious Users
wp_delete_user("site-name", "hacker", "legitimate_admin", confirm=True)
Phase 3: File Cleanup
-
Clean Uploads Directory
wp_clean_uploads_php("site-name", confirm=True) -
Remove Malicious MU-Plugins
wp_scan("site-name", scope="mu-plugins") wp_remove_mu_plugin("site-name", "malware.php") -
Quarantine Suspicious Files
wp_quarantine_file("site-name", "/full/path/to/file.php")
Phase 4: Core Restoration
-
Reinstall WordPress Core (preview first, then confirm)
wp_reinstall_core("site-name") # Preview manifest wp_reinstall_core("site-name", confirm=True) # Execute -
Verify Core Files
wp_scan("site-name", scope="core") -
Reinstall Affected Plugins
wp_reinstall_plugin("site-name", "plugin-slug")
Phase 5: Security Hardening
-
Reset All Passwords (preview first, then confirm)
wp_reset_passwords("site-name") # Preview affected users wp_reset_passwords("site-name", confirm=True) # ExecuteSave the new passwords securely!
-
Regenerate Salts (preview first, then confirm)
wp_regenerate_salts("site-name") # Preview wp_regenerate_salts("site-name", confirm=True) # Execute -
Disable File Editing
wp_disable_file_editing("site-name") -
Add Security Rules
wp_add_security_htaccess("site-name") -
Fix Permissions
wp_fix_permissions("site-name")
Phase 6: Updates & Verification
-
Update Everything
wp_update_all("site-name") -
Final Verification Scan
wp_scan("site-name") -
Generate Report
wp_generate_report("site-name")
Security Considerations
SSH Key Authentication (Recommended)
Always use SSH keys instead of passwords:
# Generate SSH key
ssh-keygen -t ed25519 -C "malware-cleanup"
# Copy to server
ssh-copy-id -i ~/.ssh/id_ed25519 user@server
Data Storage Security
- Site configurations are stored locally in
~/.wp-sites/sites.json(shared with wp-site-doctor-mcp) and mirrored to~/.wp-malware-cleanup/sites.jsonfor backward compatibility. - Passwords are NEVER stored - provide them at runtime via the tool's
passwordparameter; they pass through as an env var tosshpass, never appearing on the command line. - Database credentials never appear in
ps- tools that need to run SQL go throughWpConfigService.run_query(), which delegates to WP-CLI'swp db query. Credentials stay inside the WP-CLI process. Fixed in 2.1.0. - Quarantined files are kept on the remote server under
wp-content/quarantine/. Originals are renamed with a timestamp + hash for traceability. - Reports may contain sensitive information - store securely.
Principle of Least Privilege
- Use a dedicated SSH user for cleanup operations
- Grant only necessary permissions
- Consider using sudo for specific WP-CLI commands
Audit Trail
All actions are logged to ~/.wp-malware-cleanup/logs/{site-name}.log
Troubleshooting
"Connection refused" or "Connection timed out"
- Verify the hostname and port are correct
- Check if SSH is running on the server
- Verify firewall allows SSH connections
- Test manually:
ssh -p PORT user@host
"WP-CLI not found"
Install WP-CLI on the WordPress server:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
"Permission denied"
- Check SSH key permissions:
chmod 600 ~/.ssh/id_rsa - Verify the user has access to the WordPress directory
- Check if the user can run WP-CLI:
wp --info
"sshpass not found"
If using password authentication:
- Ubuntu/Debian:
sudo apt install sshpass - macOS:
brew install hudochenkov/sshpass/sshpass - Recommended: Use SSH keys instead
Command Timeout
For large sites, increase the timeout:
- Database exports may take longer
- Full scans on large sites need more time
- Consider running scans during low-traffic periods
False Positives in Malware Scan
Some legitimate code may trigger false positives:
- Base64 encoding in plugins (e.g., for image handling)
- Minified JavaScript
- Vendor libraries
Review each finding manually before taking action.
Threat Intelligence & Auto-Learning
This project includes a threat intelligence database that automatically learns from every cleanup. No manual steps required - every scan and cleanup action feeds data back into the local DB.
Auto-Learning Pipeline
Every detection and cleanup action is automatically recorded to the threat database:
| Action | What Gets Recorded |
|---|---|
| Quarantine a file | File hash + path + auto-added to known_malicious_files for cross-site recognition |
| Scan finds hidden plugin | Plugin slug + risk flags (self-hiding, user-hiding, self-healing) |
| Scan finds malware theme | Theme slug + detection reasons (timestamp name, webshells, wp-config.php) |
| Delete injected core file | File path of each removed injection |
| Delete malware DB option | Option name + value that was removed |
| Revoke app passwords | User ID + username for each revocation |
| Complete cleanup | Summary with total steps completed |
| MU-plugin user-hiding detected | Filenames of MU-plugins with user-hiding hooks |
This means:
- Quarantined file hashes are stored in
known_malicious_filesand can be matched against any file withwp_threat_check_file("site", "/path/to/file"), including on a different site. - Learned and community signatures feed the scanner. Since 2.7.0,
wp_scan(scope="malware-patterns")(and the full scan) greps every active threat-DB signature across the site - so a signature learned while cleaning site A detects on site B. String signatures match literally; regex signatures use PCRE. cleanup_recordsbuilds a searchable history of every action across all sites- All recording is best-effort - a DB failure never blocks a cleanup operation
Community Database
Sync with community-contributed signatures and contribute back:
# Sync latest signatures from GitHub
wp_threat_db_sync()
# Check current database stats (includes cleanup_records count)
wp_threat_db_stats()
# After confirming malware, teach the system
wp_threat_learn_from_cleanup("/path/to/malware.php", "abc123hash", "backdoor", "Description")
# Export for contribution
wp_threat_export()
Community Files
| File | Description |
|---|---|
community-signatures.json |
Malware detection patterns (50+ signatures) |
community-hashes.json |
Known malicious file hashes |
Security Features
- Secure Random Filenames: All backup files, reports, and quarantine directories use cryptographically secure random names to prevent attackers from guessing file locations
- Restricted Permissions: Quarantine and backup directories are created with 700 permissions
- No Predictable Paths: File naming uses SHA256-based random tokens
Project Structure
Since v2.1.0 the codebase is split into a thin entry point plus three packages:
wp-malware-cleanup-mcp/
├── server.py # 60-line entry point: setup_logging() + import tools + mcp.run()
├── config.py # Config dataclass + every malware/cron/HTTP detection constant
├── models.py # SiteConfig, ScanResult, DatabaseCredentials, exception hierarchy
├── database_v2.py # Threat-intel SQLite store (thread-safe, WAL)
├── threat_feeds_v2.py # GitHub community-signature sync + pattern analysis
│
├── core/ # Shared services - pure logic, no MCP coupling
│ ├── sites.py # SiteManager (mtime-cached, dual-write to shared registry)
│ ├── paths.py # WpPaths helpers (uploads_dir, plugins_dir, …)
│ ├── wpconfig.py # WpConfigService.get_db_credentials + run_query via WP-CLI
│ ├── safety.py # require_confirm(), safety_banner(), log_action()
│ ├── reports.py # ScanReport / CleanupReport markdown formatters
│ ├── orchestrator.py # Step / Orchestrator primitives for scan + cleanup pipelines
│ ├── threat_db.py # facade over database_v2 + threat_feeds_v2
│ └── legacy.py # compat shims for helpers still in transition
│
├── tools/ # One module per tool category - each registers @mcp.tool()
│ ├── _registry.py # shared FastMCP instance
│ ├── sites.py # add/update/list/get/remove/test_connection (6 tools)
│ ├── scanning.py # verify/scan_* + quick/deep/full scans (17 tools)
│ ├── cleanup.py # quarantine/clean_*/complete_cleanup/backup (8 tools)
│ ├── hardening.py # permissions/salts/wpconfig/mu-plugin/etc. (7 tools)
│ ├── reinstall.py # core/plugin/all-plugins/all-themes (4 tools)
│ ├── users.py # list_admins/audit/find/delete/reset/revoke (6 tools)
│ ├── threat.py # wp_threat_* (db stats, sync, learn, …) (13 tools)
│ ├── reporting.py # generate_report/case_study/export_sites (3 tools)
│ ├── batch.py # batch_scan/batch_update/update_all (3 tools)
│ ├── background.py # start_background_scan/status/result/list (4 tools)
│ ├── misc.py # version/read_file/check_cron/monitor_http (4 tools)
│ └── interactive_scans.py # internal worker for background scans
│
├── utils/ # Low-level helpers
│ ├── ssh.py # SSHConnectionPool (ControlMaster), run_ssh_command, run_wpcli
│ ├── validation.py # path-traversal defense, shell escaping, log redaction
│ └── progress.py # ProgressReporter, TaskManager, BackgroundTask
│
├── scripts/
│ ├── count_tools.py # walks the FastMCP registry; `--diff` checks tools-baseline.txt
│ └── tools-baseline.txt # frozen 75-tool roster; CI's parity check
│
└── tests/
├── conftest.py # MockSSH fixture + with_site fixture
├── test_sites.py # SiteManager (mtime-invalidation, dual-write, migration)
├── test_wpconfig.py # asserts DB password never appears on `mysql` command line
├── test_safety.py # confirm gate, backup warning, banner
├── test_confirm_gates.py # the four destructive tools that need confirm=True
├── test_scanning_integration.py # MockSSH-driven smoke tests for scanning tools
└── … # paths, reports, orchestrator, validation, models
Dependency rule (one-way): tools/ → core/ → utils/ → models/. Tools
never import from server; server.py only knows about tools._registry.mcp
and side-effect imports the tools package to trigger registration. This lets
you add or rename tool modules without touching server.py.
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes (with tests where it makes sense)
- Run
make smoke && make parity && make testbefore submitting - Submit a pull request
Contributing Signatures
To contribute malware signatures:
- Use
wp_threat_export()to export your local signatures - Fork this repository
- Add your signatures to
community-signatures.jsonorcommunity-hashes.json - Submit a pull request with a description of what malware the signatures detect
Guidelines:
- Only submit confirmed malware signatures
- Include clear descriptions
- Test for false positives before submitting
- Do not include sensitive information
Development Setup
git clone https://github.com/vapvarun/wp-malware-cleanup-mcp.git
cd wp-malware-cleanup-mcp
# Create a venv (Python 3.10+) and install dev dependencies
python3.12 -m venv .venv
source .venv/bin/activate
make install # equivalent to: pip install -e ".[dev]"
Adding a new tool
- Pick the right category module under
tools/(or create a new one - register it intools/__init__.pyso the side-effect import wires it up). - Decorate with
@mcp.tool()(importmcpfromtools._registry). - Use
core.get_site_manager()for site lookups,core.WpPathsfor path construction, and - for destructive tools -core.safety.require_confirm()for the confirm gate. - Add the tool name to
scripts/tools-baseline.txt(or regenerate the file). - Add at least one integration test against the
mock_ssh+with_sitefixtures intests/conftest.py.
Running tests / quality gates
make smoke # python -c "import server"
make parity # asserts tools/__init__ wires up exactly the 75 baseline tools
make test # full pytest run (excludes the 6 known-pre-existing validation
# failures tracked separately)
make test-all # everything, including the known failures
make lint # ruff check (config in pyproject.toml)
MockSSH (in tests/conftest.py) replaces utils.ssh.run_ssh_command and
run_wpcli with a deterministic stub - register responses with
mock_ssh.expect(substr_or_callable, (success, output)) and inspect issued
commands via mock_ssh.calls. Integration tests for scanning/cleanup/threat
tools should go through this fixture rather than hitting a real server.
License
This project is free software, licensed under the GNU General Public License v2.0 or later (GPL-2.0-or-later) - the same license as WordPress core. See the LICENSE file for the full text. You are free to use, study, share, and modify it; derivative works must remain under the GPL.
Credits
Author: Varun Dubey Company: Wbcom Designs
Built With
- Model Context Protocol (MCP) - Protocol for LLM integrations
- WP-CLI - Command-line interface for WordPress
- FastMCP - Python MCP server framework
Acknowledgments
- WordPress Security Team for security best practices
- The WP-CLI community for the excellent command-line tool
- Anthropic for the Model Context Protocol specification
Support
For support, questions, or custom development:
- Website: https://wbcomdesigns.com
- GitHub Issues: Report a bug
Changelog
Version 3.0.0 (2026)
A fleet-operations release that turns the per-site cleanup tool into a fleet incident-response console, plus a deep re-audit that fixed a large amount of functionality the 2.1.0 package-split refactor had shipped silently broken.
Breaking
- Scan tools consolidated under
wp_scan(name, scope="full"). The 17 separate scan tools (wp_full_scan,wp_quick_scan,wp_deep_scan,wp_scan_uploads,wp_scan_webshells, …) are removed as individual tools;scopeselects depth (quick/full/deep/verbose) or a category (uploads,webshells,themes,core,plugins,hidden-admins,cron,wpconfig,db, …). Scripts calling the old tool names must switch towp_scan(scope=...). - Removed 6 non-functional threat tools (
wp_threat_sync_wpscan,wp_threat_sync_patchstack,wp_threat_sync_all,wp_threat_download_plugin_signatures,wp_threat_learn_from_plugins,wp_threat_report) - they called methods that did not exist and crashed at runtime, advertising external feeds the server could not fetch.
New - fleet console
wp_fleet_status- at-a-glance scorecard across all sites, worst-first, risk-scored. Instant from stored scans, orrefresh=Trueto quick-scan first. Clean siblings of an infected site (samehost_group) are flagged AT RISK.wp_host_group_status- one server / cPanel account's sites plus a cross-site reinfection assessment and suggested clean order.host_grouponwp_add_site/wp_update_siteto record which sites share an account.
New - incident response
wp_incident_report- client-facing "proof of clean": infection timeline, IOCs (path + hash), before/after comparison, and a clean sign-off when the latest whole-site scan is clean. Anonymized by default.wp_clean_cron_events- delete malicious wp-cron hooks (the #1 reinfection vector).wp_clean_malicious_htaccess- quarantine hostile.htaccess/.user.inithat re-enable PHP execution or redirect traffic.wp_scan(scope="wpconfig")- read-only scan for injected code in wp-config.php (survives core reinstalls).wp_complete_cleanupnow removes cron +.htaccess, scans wp-config.php, then runs a post-cleanup verification scan and sets status from the result - it no longer reports "clean" unconditionally.
New - threat intelligence
- The threat DB now feeds the scanner (it was write-only).
wp_scan(scope="malware-patterns")greps every active signature - community-synced and learned-from-cleanup - so a signature learned cleaning one site detects on the next. (grep -Ffor strings,grep -Pfor regex, invalid patterns dropped, best-effort.) - Persistent scan results - every whole-site scan writes a structured record to
~/.wp-malware-cleanup/scans/<site>/(capped at 200/site), enabling history and before/after diffs. Targeted scans are recorded but never set the fleet verdict.
Improve
- Detection - web-shell detection requires ≥2 distinct file-manager indicators (cuts false positives from a lone
scandir/is_writable); backup/migration plugins added to the false-positive paths; the obfuscation scan now also catcheseval($var)and variable-function backdoors ($f($_GET[...])). - Safety - backup gate on irreversible data deletion (
wp_clean_db_spam,wp_delete_malware_options) with aforce=Trueoverride. - Resilience - the shared
sites.jsonloader tolerates unknown keys instead of renaming the file to.bak, so it coexists with other MCPs.
Fix (refactor-orphaned silent failures)
wp_complete_cleanupwas a near-no-op - it called ~14 functions never imported into its module; wrapped in try/except, every step failed silently. All imports fixed; a full-chain test now guards it.wp_generate_report,wp_generate_case_study,wp_export_sites,wp_quick_scan,wp_deep_scan,wp_batch_scan,wp_threat_db_sync,wp_threat_export, andwp_full_scansteps all referenced names removed by the refactor and failed silently. Fixed and covered by a static undefined-name (F821) sweep and an execute-every-tool smoke test.wp_audit_usercrashed (UnboundLocalError) on a user with a missing registration date.- Case studies and reports were written into the installed package instead of the data dir.
- Validation:
validate_hostnamerejects invalid IPv4;generate_secure_filenamekeeps the extension dot (backups were missing.sql);sanitize_filenamereturnsunnamedfor blank input.
Tools: 75 → 59. Tests: 152 passing, no skips.
Version 2.1.0 (2026)
- Package-split refactor - server.py shrank from 8,176 to 60 lines; 75 tools moved into per-category modules under
tools/(sites, scanning, cleanup, hardening, reinstall, users, threat, reporting, batch, background, misc). - New
core/package -SiteManager,WpPaths,WpConfigService,require_confirm(),ScanReport,CleanupReport, threat DB facade, scan orchestrator. - Confirm gates added to
wp_reinstall_plugin,wp_clean_core_injections,wp_delete_malware_options,wp_remove_mu_plugin. Each now requiresconfirm=True; without it they return a preview manifest. - DB-password leak fix -
wp_scan_hidden_adminsandwp_scan_app_passwordspreviously builtmysql -u <user> -p<pass>commands visible inpson the target. They now route through WP-CLI viaWpConfigService.run_query(), keeping credentials inside the WP-CLI process. - v1 modules (
database.py,threat_feeds.py) deleted;V2_MODULES_AVAILABLEbranching removed; legacy fallback SSH impls removed. pyproject.toml,Makefile, andMockSSHtest fixture added. Test count: 75 (up from 33 unit tests in 2.0.0).
Version 2.0.0 (2026)
- Auto-learning pipeline - every scan/cleanup action auto-records to threat DB
- Cross-site hash recognition - quarantined files are auto-detected on other sites
- SSH connection pooling and thread-safe DB connections
- Signature caching with TTL and pre-compiled regex
- New tools:
wp_scan_hidden_plugins,wp_scan_themes,wp_clean_core_injections,wp_delete_malware_options,wp_revoke_app_passwords,wp_complete_cleanup,wp_scan_hidden_admins,wp_scan_app_passwords,wp_scan_webshells,wp_harden_wpconfig,wp_install_security_mu_plugin,wp_full_harden,wp_generate_case_study - GitHub community signature sync (
wp_threat_db_sync) - Anonymized case study generation
- v2 config, models, and utils modules
Version 1.0.0 (2024)
- Initial release
- Multi-site management
- Comprehensive malware scanning
- User audit capabilities
- Safe cleanup operations
- Security hardening tools
- Batch operations
- Detailed reporting
README fetched from the repository 1 hours ago. The original is authoritative.