Distilled from the top WordPress security sources (Wordfence, Sucuri, Patchstack, MalCare, BlogVault,
Jetpack, real 2026 incident write-ups). This is the domain knowledge that makes the skill an expert:
what the families are, how each hides, and the exact signal that catches it. Pair with
scripts/signatures.json.
- 91% of new WordPress vulnerabilities are in plugins; ~3% themes; core negligible. (Source: Patchstack State of WordPress Security 2026.)
- 11,334 new vulns in 2025 (+42% YoY); 17% high-severity mass-exploitation risk (+113% YoY).
- Median time to mass exploitation: 5 hours; ~50% of high-impact vulns exploited within 24h; 46% are unpatched at disclosure. → The window is tiny; version cross-check matters.
- Premium/freemium components: 29% of reports, 76% exploitable, 3× more known-exploited vulns than free. → Nulled/premium plugins are a top vector; signature-based scans generally can't see inside them.
- Top exploited types: Broken Access Control, unauth Stored XSS, Authorization Bypass, Arbitrary File Upload, Privilege Escalation, PHP Object Injection → RCE.
- Traditional WAFs blocked only ~12% of WordPress-specific attacks. → A WAF is not a scanner.
- ~49% of compromised sites contain at least one backdoor; cleaned files often reinfect within hours (memory-resident/persistent creators). → Always re-scan 24–48h post-clean.
- Mechanism: PHP router or fake plugin serves spam only to crawlers (UA contains
Googlebot/Bingbot) or mobile; clean content to everyone else and to scanners. - Hides in: web-root shadow folders, a fake plugin named after the victim domain
(
wp-content/plugins/example-com/example-com.php),.htaccess,wp_options, injected posts. - Signals:
cloaking_router,seo_spam_keywords,payload_hosts,sitemap_injection; Layer 1 bot-vs-browser diff is the definitive catch. Roguesitemap*.xmlwith spam URLs;robots.txtpointing at foreign sitemaps; Japanese charset in posts (post_content REGEXP '[ぁ-んァ-ヶ]'). - Real 2026: the "domain-mimic" SEO-spam plugin fetching instructions from
metainfo.jpg, C2 base64-encoded (mag1cw0rld[.]com).
- Mechanism: persistent remote access. From full file-managers (GYOKKA, b374k, WSO, c99, alfa,
IndoXploit) to 500-byte auto-login files (
wp_set_auth_cookieon the first admin). - Hides in:
wp-content/uploads, inactive plugins, theme folders,wp-admin/maint/, disguised as.ico.php/.png.php/hidden.*.php, or core-filename typosquats (wp-comments-posts.phpvs core's singularwp-comments-post.php; thewp-vcdfamily). - Signals:
shells,auto_login_backdoor,cookie_auth_markers,double_extension_files,core_filename_typosquat. Single indicator is enough — don't require two.
- Mechanism: code on every load recreates a rogue admin or re-drops the payload → malware "returns" after you delete the user. Memory-resident variants (e.g. Lock360) reinfect cleaned files.
- Hides in: theme
functions.php, mu-plugins,wp_optionsautoloaded values, a cron hook. - Signals:
persistent_admin_creator,malicious_cron; checkmu-pluginsand autoloaded options. Re-scan after 24–48h to confirm it didn't respawn.
- Mechanism: appends executable code after
require_once ABSPATH . 'wp-settings.php';— surviveswp core download --forcebecause wp-config is preserved. - Signals:
wp_config_injection. Read wp-config tail specifically; nothing executable belongs after the wp-settings require.
- Mechanism: conditional redirect (by referrer, first-visit, mobile, geo, crawler) to spam/ casino/phishing. Owner logged-in often exempted, so the owner "sees nothing."
- Hides in:
.htaccess(301/302),wp_optionssiteurl/home, themeheader.php/functions.php, obfuscated JS in header/footer, injected post<script>. - Signals:
htaccess_injection,db_injection(siteurl/home hijack, foreign<script>/iframe),obfuscation. Trace the redirect chain as a crawler AND a mobile UA.
- Mechanism: attacker buys/compromises a popular plugin, ships a backdoor via a trusted
update. Example (Essential Plugin / Flippa, ~400k installs): unauth REST route
/wp-json/wpos/v1/analyticswithpermission_callback => '__return_true'→ PHP Object Injection via@unserialize()→ gadget chain writes ~6KB intowp-config.phpand dropswp-comments-posts.php→ Googlebot cloaking for gambling/crypto/pharma. 8-month dormancy, then a ~7h activation window. - Signals:
rogue_rest_route,object_injection,wp_config_injection,core_filename_typosquat. Cross-check installed plugin versions against the Wordfence feed; watch access logs for POSTs to odd/wp-json/*routes.
- Mechanism: JS on WooCommerce checkout captures card/CVV fields and exfiltrates to a foreign
host. Often injected via DB (
wp_options, post content) or a compromised payment plugin. - Signals:
card_skimmer. Flag only when captured data goes to a host that is not the site's own payment processor.
- Mechanism: server-side PHP miner or browser JS (Coinhive-style) hijacks CPU.
- Signals:
cryptominer; corroborate with sustained 90–100% CPU without a traffic spike.
These are techniques practitioners on r/ProWordPress / r/Wordpress reported as hard to detect — several got past the scanners and host teams involved before being found manually. Make sure the skill catches each.
- Stream-wrapper + image payload. Payload is a compressed PHP file hidden in
screenshot.webp, executed viacompress.bzip2://screenshot.webpthroughload_template(). On disk it's an "image," so file scanners see nothing; the loader is cloaked as a fakewpengine_update()function. In the reported case it went unresolved until a developer decompressed the webp and found it. Signals:php_stream_wrapper,fake_core_function_loader. Decode any "image" that a PHP file references orincludes. - DB-rebuilt loader. The malicious function builds its target path from
wp_options(get_option('wpengine_' . get_stylesheet() . '_data')→['screenshot','webp','bzip2','compress']) and reconstructs itself after you delete files — persistence lives in the DB, execution in an image. Clean the option AND the file AND flush cache, or it respawns. - Query-string (
?g=) cloaking. 900k spam URLs generated purely from?g=params, served only to Googlebot; nothing in posts, sitemap, or source; a garbage?g=xURL renders spam to a crawler. Paired with a.htaccessDirectoryIndex wp-ingat.php index.phphijack that runs a malicious script before WordPress. Signals: Layer 1 query-string probe as Googlebot +htaccess_injection(DirectoryIndex). - Server-level / above-WordPress persistence. Reinfects
wp-config.phpevery few hours; a clean WordPress audit shows nothing. Real case: a webmail log file turned into a root backdoor; another via~/.bashrc, system cron, or mail services. If it keeps coming back after a clean WP pass, the persistence is outside WordPress — check.bashrc, system crontab, mail logs, and service-startup scripts (seeedge-cases.md). - Practitioner-validated workflow (matches this skill's Layers 11–12 + hardening): Patchstack the vulns → get the payload's file-creation timestamp → cross-reference access logs (mind the FS-vs-log timezone difference) → force-logout everyone + rotate all passwords, SSH keys, FTP → fix permissions → clean uploads → update everything → install an activity-log plugin for early warning.
- Scan MODIFIED core/plugin files, not just added ones. Attackers inject into legitimate files; deletion-only remediation misses it. Use checksum verify (Layer 3/4), not "new file" heuristics alone.
- Remote/surface scanners can't see: file-timestamp manipulation, server-side-only payloads, database infections, cloaked content. That's four whole families invisible to a browser-style scan.
- Signature-only scanners miss: premium/nulled plugins (no signatures), novel/obfuscated code, and DB malware — and produce false positives on custom code. The verify gate (read-in-context) is what gives low false positives without a signature monopoly.
- Timing beats signatures: with a 5-hour median to mass exploitation, the Wordfence vuln cross-check (is an installed version in a known-exploited range?) often finds the door faster than any file scan finds the payload.
wp-config.php: DISALLOW_FILE_EDIT, DISALLOW_FILE_MODS (during lockdown), FORCE_SSL_ADMIN,
disable XML-RPC if unused, move/chmod 400 wp-config, rotate salts. Perms: dirs 755, files 644,
wp-config 400/440, no 777. DB user limited to SELECT/INSERT/UPDATE/DELETE. Enforce 2FA
(stolen creds are a top vector). Rename the admin user. Block direct PHP execution in
wp-content/uploads (nginx location ~* /uploads/.*\.php$ { deny all; } or .htaccess). Update or
remove every component flagged by the Wordfence cross-check within the 24h window. Monitor logs +
re-scan 24–48h for reinfection.
Patchstack State of WordPress Security 2026 · Sucuri hacked-site cleanup guides · Wordfence Intelligence + learning center · MalCare / BlogVault malware & scanner guides · Jetpack redirect-spam guide · Malcure Japanese-keyword-hack guide · 2026 Essential Plugin supply-chain write-up · securityonline domain-mimic SEO-spam analysis.