Skip to content

Add wp-abuse-and-compromise-response skill (response counterpart to planned wp-security)#62

Open
Vytas-cs wants to merge 4 commits into
WordPress:trunkfrom
Vytas-cs:add-wp-abuse-and-compromise-response-skill
Open

Add wp-abuse-and-compromise-response skill (response counterpart to planned wp-security)#62
Vytas-cs wants to merge 4 commits into
WordPress:trunkfrom
Vytas-cs:add-wp-abuse-and-compromise-response-skill

Conversation

@Vytas-cs
Copy link
Copy Markdown

@Vytas-cs Vytas-cs commented Jun 4, 2026

Closes #61

Summary

Adds a new skill wp-abuse-and-compromise-response covering response to a suspected or confirmed WordPress site compromise: defacement, redirect injection, SEO spam, phishing-page host, mailer abuse, cryptominer, and supply-chain plugin attack.

The skill walks AI assistants through:

  1. Triage (with immediate salts rotation to kill active sessions without tipping off the attacker)
  2. Contain — preserve evidence
  3. Classify the compromise type
  4. Investigate (file/DB/log + local-machine malware scan)
  5. Clean (backup-restore path OR manual cleanup)
  6. Full credential rotation
  7. Verify clean
  8. Post-cleanup hardening

This is response-focused. Upfront prevention (nonces, capability checks, sanitization/escaping) is the planned wp-security skill's territory — the two are complementary, not overlapping.

What's included

  • skills/wp-abuse-and-compromise-response/SKILL.md — procedural overview (167 lines, in range with wp-performance and wp-plugin-development)
  • skills/wp-abuse-and-compromise-response/references/:
    • signals.md — full signal-to-category taxonomy across 9 compromise categories
    • investigation.md — DB queries, log triage, file-system patterns
    • common-injection-points.md — catalog of where attackers hide (mu-plugins, autoload options, drop-ins, etc.)
    • hardening.md — post-cleanup hardening checklist
  • skills/wp-abuse-and-compromise-response/scripts/detect_compromise_signals.mjs — deterministic fast-scan helper, JSON output, matches detect_*.mjs convention
  • eval/scenarios/abuse-confirmed-redirect-cleanup.json — happy path
  • eval/scenarios/abuse-suspected-but-unconfirmed.json — cloaking/SEO-spam triage
  • eval/scenarios/abuse-reinfection-after-cleanup.json — focuses AI on persistence mechanisms

Registry updates

  • README.md — added skill to the available-skills table
  • docs/ai-authorship.md — added authorship disclosure row
  • skills/wordpress-router/references/decision-tree.md — added compromise/hacked/defaced/malware/redirect/SEO-spam/phishing/mailer/cryptominer/reinfection intent → route to this skill

Alignment with WordPress.org canonical guidance

The skill was audited against the hacked-site FAQ and the hardening guide before submission. Key alignments:

  • Salts-first then full-credential rotation — kills sessions immediately (matching WP.org's reset-first intent) without sending password-change emails that would tip off the attacker. Full credential rotation runs after cleanup.
  • Local-machine malware scan in investigation — WP.org explicitly flags the owner's box as a major compromise vector.
  • DB user privilege restriction in hardening — runtime user with SELECT/INSERT/UPDATE/DELETE only.
  • HTTP basic auth on /wp-admin/ in hardening.
  • File permission floors — files 644 / dirs 755 / wp-config.php 440 (per WP.org, not 600).

Validation

  • node eval/harness/run.mjs — passes
  • node skills/wp-abuse-and-compromise-response/scripts/detect_compromise_signals.mjs — returns valid JSON
  • All 3 eval scenarios parse as valid JSON
  • SKILL.md headers match repo convention (## When to use, ## Inputs required, ## Procedure with ### 0) Triage first, ## Verification, ## Failure modes / debugging, ## Escalation)
  • Frontmatter contains required WordPress 6.9 and PHP 7.2.24 substrings

AI authorship disclosure

Per the established docs/ai-authorship.md table format:

  • AI tool: Claude Opus 4.7
  • Source material: WordPress.org canonical hardening + hacked-site FAQ docs, WP-CLI command reference, established compromise-response patterns
  • Reviewed by: community contributor with hosting-provider abuse-response operational background
  • Testing: eval scenarios written; not yet run against an AI assistant on real tasks (marked "Pending" in the disclosure)

Opinionated calls (open to feedback)

  1. Salts-first rotation vs WP.org's full-credential reset-first — both are valid. This skill prefers salts-first to avoid attacker-tipoff via password-change emails, with full rotation after cleanup. Acknowledged in SKILL.md §1.
  2. mu-plugins/ listed first in the persistence catalog — most commonly-missed hiding place across the WP community.
  3. wp-config.php reconstructed from scratch, not cleaned — diffing isn't reliable enough; rebuild from known DB creds + fresh salts.
  4. Explicit Y/N confirmation gate before destructive cleanup — prevents AI from being overconfident.
  5. Anonymized real-world supply-chain note in signals.md §8 references the pattern of a multi-plugin slider/popup/widget vendor with tens of thousands of installs becoming a high-value supply-chain target, without naming any vendor or host.

Out of scope

  • Bypassing Cloudflare / bot protection. Inaccessible is a valid verdict.
  • Real-time forensic timeline reconstruction — escalates to specialist services.
  • Multi-site network-level cleanup — single-site focus first.
  • Server-level non-WP malware — escalates to host.

…tion, cleanup, and post-cleanup hardening of compromised WordPress sites (WordPress#61)

Adds a response-time skill complementary to the planned wp-security skill.
Covers: triage with immediate salts rotation, signal-to-category classification
across 9 compromise types, full investigation playbook (file/DB/log), two
cleanup paths (backup-restore + manual), full credential rotation, verification,
and post-cleanup hardening aligned with the WordPress.org hardening guide.

Includes a deterministic fast-scan script that surfaces high-signal compromise
indicators (PHP in uploads, eval+base64 patterns, mu-plugins presence, recent
core mtimes, WP-flavored bait directories, drop-in audit).

Three eval scenarios cover the main entry points: confirmed compromise,
suspected-but-unconfirmed (cloaking pattern), and reinfection-after-cleanup
(persistence mechanism focus).

Audited against WordPress.org canonical sources before submission:
- hacked-site FAQ: https://wordpress.org/documentation/article/faq-my-site-was-hacked/
- hardening guide: https://developer.wordpress.org/advanced-administration/security/hardening/

Registry updates: README skill table, docs/ai-authorship.md disclosure row,
wordpress-router decision-tree routing entry for compromise/hacked/defaced/
malware/redirect/SEO-spam/phishing/mailer/cryptominer/reinfection intent.

Refs WordPress#61
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 4, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @Vytas-cs.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: Vytas-cs.


To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Vytas-cs added 3 commits June 4, 2026 12:34
…--check-vulns

Adds an optional --check-vulns flag to detect_compromise_signals.mjs that:
- Reads installed WP core version from wp-includes/version.php
- Parses plugin headers from wp-content/plugins/<slug>/*.php
- Parses theme headers from wp-content/themes/<slug>/style.css
- Queries wpvulnerability.com (free, no API key, aggregates WPScan +
  Patchstack + WP.org sources) for each detected component
- Filters returned vulnerabilities to those affecting the installed version
- Adds a "known_vulnerabilities" signal block to the JSON report

Why: a breach is often the result of an unpatched CVE in a specific
plugin/theme/core version. Knowing which CVE points at the entry point
and confirms the attack vector, which prioritizes patching during cleanup.

SKILL.md §3 (investigate) now references --check-vulns and lists alternative
data sources (Patchstack, WPScan, Wordfence Intelligence) for users who prefer
direct queries. hardening.md §6 expanded into a fuller ongoing-scanning section
with feed-source comparison and automated scan guidance.

Refs WordPress#61
…ress#62

Blocking fixes:
- Step 5 password reset: scope to admins/editors and --skip-email, so the
  reset no longer blasts a password-change email to every customer
  account on a WooCommerce/membership site (which would also tip off any
  attacker-controlled account).
- detect_compromise_signals.mjs: downgrade recent_core_modifications
  from "high" to "review" — recent mtime in core dirs is just as easily
  a legitimate WP update as it is tampering. Wording now points to
  verify-checksums as the actual signal.
- detect_compromise_signals.mjs: surface truncated / depth_capped /
  files_read_truncated flags from findFilesRecursive + scanMalwarePatterns.
  Summary now refuses to declare "clean" when the scan truncated, and
  recommends running verify-checksums + external scanner instead.
- hardening.md: fix wp-config.php permission notation (440 is -r--r-----,
  not -rw-------) and modernize Apache snippets to Require all denied /
  FilesMatch syntax (the older Order Deny,Allow is mod_access_compat,
  deprecated since Apache 2.4).
- detect_compromise_signals.mjs --check-vulns: mark experimental, add
  privacy note about exfiltrating installed component inventory to the
  third-party API, and fix vulnAffectsInstalledVersion returning true on
  unknown version (now returns null/uncertain, surfaced separately under
  vulnerabilities_uncertain_match so a missing version header doesn't
  flag every historical CVE).

Correctness:
- SKILL.md step 1: softened "salts rotation doesn't tip off the
  attacker" — it kills sessions, which is an observable signal, and
  doesn't defend against backdoors at all. Reworded to acknowledge.
- hardening.md DB privilege restriction: added an explicit caveat that
  many plugins call dbDelta() at runtime (not just at activation), so a
  long-term SELECT/INSERT/UPDATE/DELETE-only runtime user will silently
  break things. Recommends most sites stick with the standard model.
- hardening.md disable_functions: removed curl_multi_exec and
  parse_ini_file from the suggested list (both have legitimate plugin
  uses) and added explicit caveats for exec/shell_exec.

Refs WordPress#61
… change)

Internal cleanup, output shape unchanged for the existing signal fields.

- findFilesRecursive: return { files, truncated, depthCapped } instead of
  mutating the result array. Use a labeled break to drop a fully redundant
  post-loop truncation check (line was dead — truncated is always set inline
  at the cap, never reached otherwise).
- scanPhpInUploads + scanKnownMalwareFilenames: now propagate truncation
  through to the output (previously dropped silently — same class of bug
  reviewer caught for scanMalwarePatterns, fixed in all three places).
- summarize(): treats truncation in any walk-based scan as scan_truncated.
- parseWpPluginHeader + parseWpThemeHeader: unified into parseWpHeader(file,
  kind, maxBytes). Header regex normalized to [\s/*]* (more permissive,
  matches both PHP-comment and CSS-comment prefixes).
- detectInstalledPlugins: drop the manual candidate-dedup loop, just sort
  .php files at the plugin-dir root with <slug>.php first.
- OPERATOR_PREDICATES map collapses the operator if-ladder in
  vulnAffectsInstalledVersion.
- listDirSafe helper: collapses 4 try/readdirSync/catch sites.
- lookupComponentVulns helper: collapses the duplicated plugin/theme lookup
  blocks in checkVulnerabilities; plugin/theme/core lookups now Promise.all
  instead of sequential awaits.
- buildSignals + summarize: pulled out of main() for testability and to keep
  main() at orchestration level (~15 lines).
- TOOL_HEADER constant: dedupes the {name, version} object literal across
  the no-WP-found branch and the main branch.
- compareVersions comment: corrected to be honest about pre-release suffix
  handling (treats "1.2.3-beta" as equal to "1.2.3"; fine for vuln-record
  numeric thresholds).
- flattenVulnRecords: always returns error: null when present, consistent
  shape; skip records confirmed-not-affected (affected === false) instead
  of including them.

Net change: 622 -> 539 lines. Smoke-tested against fake WP install with
known plugin and confirmed --check-vulns round-trips through wpvulnerability.com
correctly (Akismet 5.3 -> 0 vulns, core 6.4.2 -> 0 vulns). Eval harness
still passes.

Refs WordPress#61
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Propose new skill: wp-abuse-and-compromise-response

1 participant