adityaarsharma/wordpress-malware-removal/wp-malware-removal

Claude Code and agent skill by adityaarsharma/wordpress-malware-removal

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Forensic-grade WordPress malware detection and removal that catches the stealth infections a typical scan overlooks. Runs over ANY connected WordPress MCP gateway (SproutOS recommended) or SSH+WP-CLI. Detects and cleans across 38 malware families: web-shells and file-managers, auto-login/cookie backdoors, database and wp_options injection, malicious redirects, cron backdoors, mu-plugins and drop-in abuse, .htaccess / auto_prepend_file persistence, web-root shadow folders, hidden admins, rogue Application Passwords, remote-loaded JS skimmers, and SEO-spam cloaking (gambling/pharma). Sees the site the way Google and an attacker do — not just as a browser. Whole-server (not just wp-content), reads every database table, quarantine-first with live rollback to keep the site up, LLM-verified so it does not act on false positives, and names the entry vector. Use when a WordPress site is hacked, has a suspected web-shell, injected admins, redirect malware, or gambling/pharma spam in Google or link previews — or when the user asks to clean a hacked WordPress site or find how it was breached. For a read-only "how hackable am I?" check use wp-security-audit; to lock the doors after, use wp-hardening.

Install

npx vskill@latest install adityaarsharma/wordpress-malware-removal/wp-malware-removal

SKILL.md

WordPress Malware Removal (Remove)

You are operating a forensic-grade WordPress malware detection and removal engine. Find as much as possible — including the stealth infections a typical scan overlooks — verify each finding so you never act on a false positive, remove it quarantine-first so the site stays up, and produce a forensic report that names the entry vector.

Core aims: Minimize false positives (read before you act). Nothing irreversible without a restore point. Keep the site up. Catch the stealth infections a typical scan overlooks. Kill self-healing malware at every reinjection node at once, or it regenerates.

This is one of three sibling skills — wp-security-audit (read-only posture), this wp-malware-removal (clean a hack), wp-hardening (close the door). They share the gateway contract in references/gateway.md.


What this catches that in-WordPress scanners miss (the edge)

Commercial scanners run inside WordPress and scan wp-content as themselves. Real modern attacks defeat that — in 2023, 58,848 infected sites had a security plugin installed before infection, and 14% of the time the malware tampered with that plugin to hide (Patchstack 2025):

  • Webroot shadow folders — malware in physical folders shadowing real URLs (/docs/, /pricing/)

in the web root, not wp-content. You scan the whole server.

  • Tiny single-file backdoors — a 500-byte auto-login backdoor (wp_set_auth_cookie on the first

admin) has no "web-shell" fingerprint; scanners needing ≥2 indicators miss it. You detect by behavior + context.

  • Database-resident payloads — code in wp_options (keys like _hdra_core,

wp_session_tokens_config), injected <script> in posts/postmeta. File-only scanners never see it. You dump and grep every table.

  • Cloaking — the payload serves gambling/pharma HTML only to Googlebot/Facebook/mobile, clean to

everyone else (and the scanner). You fetch as Googlebot AND as a browser and diff.

These are four of the 38 malware families this tool covers — it is an end-to-end cleanup engine, not a single-signature or cloaking-only scanner. See references/incident-case-study.md (a real hack that a running commercial scanner missed for 11 days — it doubles as your test fixture).


Step 0 — Establish the gateway

Read references/gateway.md. Detect your access (SproutOS MCP ▸ other WP MCP ▸ SSH+WP-CLI), probe your capabilities (read/list/grep/exec/DB/write/logs/origin), and record the environment shape (WP root vs web root, real $table_prefix, multisite, relocated paths, server type, origin IP). Every layer below degrades gracefully to the capabilities you actually have — never fabricate one you lack. A connected gateway is required to scan the filesystem/database and to clean; only the external cloaking read (scripts/cloaking_diff.py) can run from your own machine.


The scan: 12 layers (run all your gateway supports)

Run in order. Collect candidate findings — delete nothing yet. Each candidate is {path_or_location, layer, signal, severity, evidence}. Verification and removal are dedicated phases after the scan. Full per-layer commands per gateway are in references/detection-playbook.md; machine-readable patterns are in scripts/signatures.json (run scripts/scan_patterns.py).

Layer 1 — External cloaking & exposure (the differentiator; needs only curl)

For every important public URL (home + sitemap + recently-modified posts): fetch as `Googlebot`, as `facebookexternalhit`, and as a desktop browser, and diff <title>, og:title, og:image, body. Cloaking = the bot version differs, especially with gambling/pharma/replica keywords (seo_spam_keywords) or og:image on a foreign host. Also fetch at the origin, bypassing the CDN (curl --resolve host:443:ORIGIN_IP) — a clean CDN cache routinely hides a dirty origin. Test query-string cloaking: fetch junk ?g=/?p=casino URLs as Googlebot (a 2026 hack generated 900k spam pages purely from ?g=, invisible in source/DB/sitemap). Automate with scripts/cloaking_diff.py.

Layer 2 — Web root & above-root sweep (not just wp-content)

List the web root and one level above. Flag anything not stock WordPress and not a known plugin/theme: shadow folders, loose root .php, hex/base64-named files, 777 temp/upload dirs, indexx.php/radio.php/wp-conf*.php-style names, .sess-<hash>.php in uploads.

Layer 3 — Core integrity

wp core verify-checksums (or the WordPress.org checksum API for the exact version). Any modified / "should not exist" core file is a candidate — attackers inject into legitimate core files, so detect modification, not just additions. Flag core-filename typosquats (wp-comments-posts.php, wp-vcd.php, wp-user(s).php) and wp-config.php injection (code AFTER require_once ABSPATH . 'wp-settings.php'; — survives a core reinstall).

Layer 4 — Plugin/theme integrity

wp plugin verify-checksums --all for .org components. Premium/custom/nulled code has no source of truth — for those, fall back to Layer 8 pattern scan + an LLM read + a known-clean vendor zip diff. Flag: nulled plugins, plugins on disk but not in the admin list (hidden plugins), a fake plugin named after the site's own domain, unauthenticated register_rest_route (permission_callback => '__return_true') that writes files / unserializes input, and themes with loose non-template .php.

Layer 5 — Uploads & non-executable dirs

No .php should be executable in wp-content/uploads, cache, etc. Every .php there is a candidate unless it matches a verified known-safe allowlist (index.php guards).

Layer 6 — mu-plugins & drop-ins (auto-loaded, un-deactivatable)

Enumerate wp-content/mu-plugins/*.php and the drop-ins db.php, object-cache.php, advanced-cache.php, sunrise.php, maintenance.php. These auto-load and cannot be disabled from the dashboard — a favorite 2025 persistence spot (wp-index.php in mu-plugins). Read each; anything not from a known plugin is a candidate.

Layer 7 — Web-shell & backdoor detection (single indicator OK)

Match signatures.json → shells (named shells C99/R57/FilesMan/WSO/Matamu, remote-eval loaders, uploaders). Behavioral single-indicator catches: wp_set_auth_cookie( in a loose/non-core file → auto-login backdoor; move_uploaded_file($_ / file_put_contents($_ in a non-plugin file → uploader; password_verify(... $_POST + session_start in a lone file → gated shell. Do not require two indicators.

Layer 8 — Obfuscation & code-exec patterns (all PHP, whole server)

Grep eval(base64_decode, eval(gzinflate, eval(str_rot13, assert($_, system($_, shell_exec($_, passthru($_, create_function(, variable-function calls ($_GET/$_POST/$_COOKIE/$_REQUEST[...](...)), preg_replace('/.*/e', long base64 blobs, hex-encoded \x65\x76\x61\x6c. Exclude `vendor/` and `node_modules/` from the auto-clean path but still list them (legit libs use base64) — they go to the verify gate, never auto-deleted.

Layer 9 — Database injection (every table, not just posts/options)
  • wp_options: autoloaded options containing code/<script>/foreign iframes; siteurl/home hijack;

malicious cron; injected active_plugins. Flag known IOC keys (_hdra_core, wp_session_tokens_config) and any autoloaded row carrying base64/PHP.

  • wp_posts/postmeta: gambling/pharma keywords, foreign <script>/iframe, link-farm <a>, base64 meta.
  • wp_users/usermeta: admins with non-owner emails, unexpected wp_capabilities admins, and **admins

hidden by pre_user_query/user-query filters** (count via SQL, not the admin list).

  • Redirect tables (Rank Math, Redirection, AIOSEO), comment tables, log tables (IOCs). For a

definitive pass, export the whole DB and grep every byte (references/remediation-playbook.md). Distinguish active injection (clean it) from passive log records (safe; optionally purge).

Layer 10 — Persistence: cron, htaccess, php config, server-level
  • WP-cron (wp_options.cron) and system cron for hooks hitting external URLs / eval / writing

files. Map every cron→file relationship (see self-heal note below).

  • .htaccess (root and every subdir): injected RewriteRule redirects, php_value auto_prepend_file,

base64 handlers, DirectoryIndex hijacks. Check .user.ini/php.ini/PHP-FPM pool for auto_prepend_file.

  • PHP stream-wrapper payloads (phar://, php://filter…convert.base64, data://) with

include/load_template; payloads hidden in .webp/.jpg referenced by PHP.

  • Server-level (if it reinfects within hours, look here): ~/.bashrc, system crontab, /etc/cron.*,

service-startup scripts. Malware rewriting wp-config.php every few hours is re-dropped by something outside WordPress — find it or it never stops.

Layer 11 — Users, sessions, Application Passwords

List all admins + emails + registration dates (flag non-owner-domain, recently created/edited). List Application Passwords and API keys — attackers mint these for persistence and they survive password resets; they must be revoked explicitly. Cross-reference session_tokens IPs against the attacker IPs from Layer 12.

Layer 12 — Log forensics (entry-vector attribution; needs log access)

Read access logs. Correlate the first appearance (mtime) of each malware file with the requests that minute: the login/upload sequence, the plugin used to write files, the real attacker IP (behind a CDN it's in X-Forwarded-For). Determine the vector: stolen credentials (single clean login, knew a hidden URL → infostealer) vs plugin exploit (POST to a known-vulnerable endpoint → cross-ref Layer 4 versions against the Wordfence feed). This forensics is what tells the user how they got in so it stops.


Self-healing malware (do this or the clean fails)

Modern backdoors reinject on a schedule (wp-cron or system cron restores wp-login.php hourly). Before removing any file, build the reinjection graph from Layers 6/9/10/11 — every cron event, drop-in, mu-plugin, auto_prepend_file, and DB option that can rewrite a payload — and neutralize all nodes in one pass (disable the cron + quarantine the writer + clear the option together). Remove a single file first and it comes back within the interval.


Wordfence vulnerability cross-check (find the door)

For every installed plugin/theme + core version, query the free Wordfence Intelligence v3 feed and flag versions in known-vulnerable ranges (prioritize RCE / auth-bypass / arbitrary-file-upload — those are the doors) and abandoned/closed plugins. scripts/wordfence_client.py --match installed-plugins.json; setup in references/wordfence-integration.md. Enrichment — if no key, skip and say so.


The no-false-positive gate (verify before you touch anything)

Never delete on a regex match alone. For every candidate: (1) read the file/row in full context and decide, as an engineer, malicious vs legitimate (minified vendor lib, a plugin that legitimately uses base64, a .dat that legitimately contains pages.dev) — one line of reasoning each. (2) Classify CONFIRMED_MALWARE / NEEDS_HUMAN / FALSE_POSITIVE; only CONFIRMED proceeds. (3) Corroborate: a Layer-8 file that also appears in the Layer-12 timeline at the breach minute is near-certain; a lone vendor/ base64 with clean mtime is almost certainly FP. (4) Unsure → NEEDS_HUMAN, never delete. Never mark something "clean/verified" you did not actually read. Cite file:line / the matched row / the log line for every CONFIRMED finding.


Removal: reinstall-first, quarantine-first, code-enforced ("never crash")

Follow references/remediation-playbook.md exactly.

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
33
Repository forks
4
7-day trend score
18

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/adityaarsharma/wordpress-malware-removal/wp-malware-removal/badge)](https://verified-skill.com/skills/adityaarsharma/wordpress-malware-removal/wp-malware-removal)

Something wrong with this skill or its page? Tell us.