~/blog/second-pass-three-blind-spotsPOSTED
by Shady Nathan Tawfik · October 5, 2026 · 6 min

The Second Pass: Three Blind Spots a Hardened WordPress Audit Missed

The first pass called the site hardened and rated it HIGH. A deeper second pass found an actively exploited WordPress core RCE, a credential-bound CORS reflection on the REST API and three installed-but-invisible plugins — and moved the rating to CRITICAL.

Interactive[blog-2.0]

TL;DR: My first pass called the site hardened and rated it HIGH. The second pass found an unauthenticated WordPress core RCE that was already in the CISA KEV catalog, a CORS reflection with credentials that I had tested on the wrong URL, and three plugins that never appeared in the HTML. Same surface, different questions, different verdict: CRITICAL.

The scenario

I audited this site once before. The verdict was reassuring: login and admin behind basic authentication, the REST user endpoints closed, xmlrpc shut, comments disabled. For a WordPress estate that is genuinely above average, and the report said so.

Then the client asked me to dig as deep as possible. No new access, no new technology. I started from the same public surface and asked different questions. The result was not an addition to the first report. It was a correction of it.

I removed all identifying information. The target is described only by sector and technology class so the pattern can be reused.

The pattern

A rescan is not "run the first scan again". It is a different checklist. Three blind spots turned the same target from HIGH to CRITICAL.

Blind spot 1: the version was treated as "current enough"

The first pass recorded the core version and moved on, because at the time nothing matched it. The second pass mapped the exact version against the advisories published since — and found a path traversal in the page-template resolution that leads to conditional remote code execution. It was added to the CISA KEV catalog and was being mass-exploited within hours of disclosure. The target's version sat inside the affected range, and the theme pointed at a template directory that satisfies the documented first precondition.

The lesson is not "check the version". It is "check the version against the advisory state at audit time, and understand that a hardening checklist does not cover a core vulnerability".

Blind spot 2: CORS was tested on the wrong URL

My first pass tested cross-origin headers on the homepage and found nothing. That is a natural but wrong test on WordPress. The REST API under /wp-json/ is the endpoint that matters, and the fallback form ?rest_route= must be probed separately. Both reflected an arbitrary origin together with Access-Control-Allow-Credentials: true — a combination that lets any website a logged-in user visits make credentialed cross-origin calls. The WordPress nonce reduces the practical impact, but the misconfiguration is real, and it was invisible from the homepage.

Blind spot 3: the inventory came from the HTML

Fingerprinting in the HTML gave me seven plugins, which is what the page disclosed. The second pass compared existing plugin directories against a non-existent baseline — a missing plugin returns the site's 404 page, an existing one returns a directory response. That differential surfaced three more plugins: a sharing plugin with an open stored-XSS advisory, a custom-fields plugin with two open advisories, and a premium caching plugin whose version I could not determine externally at all. Equally important was what was absent: no backup plugin, no security plugin, no activity log. A compromise on this site would leave no recoverable copy and no trace.

TL;DR: My first pass called the site hardened and rated it HIGH. The second pass found an actively exploited WordPress core RCE, a credential-bound CORS reflection on the REST API and three installed-but-invisible plugins — and turned the rating into CRITICAL.

Findings

SeverityFindingEvidence
KRITISCHUnauthenticated core path traversal, conditional RCE, in CISA KEVVersion inside affected range; theme precondition provable from the public body class
KRITISCHCORS reflection with credentials on the REST APIAny origin reflected with Access-Control-Allow-Credentials: true on /wp-json/ and ?rest_route=
HOCHThree sibling domains without DMARC and with softfail SPFClient phishing possible without any exploitation
HOCHTheme ships a 2012 jQuery that overrides the current core versionKnown XSS and prototype pollution advisories; console warning in production
MITTELThree hidden plugins with open CVEsSharing, custom-fields and caching plugins found only by directory differential
MITTELNo backup, security or audit plugin installedNo recovery and no detection if the RCE is used
MITTELUndeclared call tracking loaded via the tag managerA cookie the privacy policy does not mention
MITTELNo HSTS, no CORS allow-list, no security headers on the main surfaceBasic-auth credentials downgradeable in a hostile network
NIEDRIGFTP open with a banner naming the operatorInfrastructure admin disclosed; brute-force surface

The attack path

Public scan finds WordPress 6.8.3Map version against current advisoriesUnauthenticated path traversal in the change logTheme body class proves the page directory preconditionProbe the PEAR constellation on the serverInclude a local PHP file and drop a webshellRead wp-config and export the client database

Reproducing it in the lab

Every command below targets a lab container I control. None of it is directed at a live system.

~/code/bash bash
# 1. version and theme precondition (a lab WordPress 6.8.3 with a page- template dir)
curl -s http://lab.local/ | grep -E 'generator|body class'
# <- generator 6.8.3, body class contains page-template-page-templates

# 2. CORS differential: homepage vs API root vs fallback
curl -s -D - -o /dev/null -H 'Origin: https://evil.example' http://lab.local/
curl -s -D - -o /dev/null -H 'Origin: https://evil.example' http://lab.local/wp-json/
curl -s -D - -o /dev/null -H 'Origin: https://evil.example' 'http://lab.local/?rest_route=/'
# <- the API responses carry Access-Control-Allow-Origin: https://evil.example

# 3. hidden plugin differential
curl -s -o /dev/null -w '%{http_code} %{size_download}\n' http://lab.local/wp-content/plugins/does-not-exist/
curl -s -o /dev/null -w '%{http_code} %{size_download}\n' http://lab.local/wp-content/plugins/sharing-plugin/
# <- 404 with the site page vs 403 with an empty Apache body

Detecting it in your own estate

  • Map every core and plugin version against the advisory state of the audit date, not against "we updated recently". A CISA KEV entry changes the priority immediately.
  • Probe CORS on /wp-json/ and ?rest_route=, never only on the homepage.
  • Run the directory differential for your own plugin folder and compare the result with the inventory you believe you have.
  • Check the absence list explicitly: backup, security and audit-log plugins. Missing resilience is a finding.
  • Verify every external script against an allow-list and require subresource integrity where the vendor supports it; the 2026 supply-chain campaigns reached sites through update servers and CDNs, not through code.

Remediation

~/code/text text
# within 24 hours
1. update core to the patched release for the affected branch
2. CORS: replace reflection with a fixed allow-list, never with credentials
3. DMARC for every domain in the portfolio, not just the main one
4. gate the database console (vpn or ip allow-list, auth, rate limit)

# within one week
5. update every plugin to the current release; check the premium ones by hand
6. replace the theme jQuery with core jquery and fix the theme scripts
7. ship the header baseline and HSTS on every vhost
8. install backup, security monitoring and an activity log

# within one month
9. rotate credentials and salts after the core update
10. bind both external agencies with processing agreements and MFA, and set a patch SLA

Once an attacker gains access, they can perform the following actions:

Unauthenticated RCE via the core path traversal (CISA KEV)
CORS with credentials: any visited site reads the REST API
Spoofable sibling domains: client phishing without any exploit
Hidden plugins (Shariff, ACF, WP Rocket) with open CVEs
No backup, no security, no audit plugin: no recovery, no detection

Takeaways

  • A hardened perimeter is not a safe core. Basic auth protects the login, not the version.
  • Test the endpoint that matters. On WordPress, CORS lives on the API root, not the homepage.
  • An inventory from the HTML is a marketing list, not an asset list. Use a differential.
  • Ask what is missing. No backup and no log on a site with client data is a risk in itself.
  • A rescan with new questions is worth more than a repeat of the old ones.
Shady Nathan Tawfik

Founder & Cyber IT Security Engineer in Bielefeld. Offensive security, audits and full-stack builds. More at

For educational purposes only. Every reproducible command targets my own lab environment (localhost), never a live system. © 2026