TL;DR: Three controls, one shared assumption. The CDN and the WAF only work if the attacker knocks on the front door — the origin server was directly reachable, so both were optional. Consent was not bypassed; it was never installed — analytics and video frames ran before any banner existed. Mail trust rested on
p=noneand a softfail, so a forged message was delivered. Nothing here is a CVE. The exposure is architectural.
The scenario
A mid-sized WordPress publisher asked for an external review of its public web presence. On paper it looked healthy: the core was current, the focus plugins carried no open advisories, and the site sat behind a CDN and the host's managed web application firewall. There was no "hack the box" finding waiting — and that turned out to be the point.
I started with the edge, because that is what the operator sees when they verify their setup. The response headers were tidy: HTTPS enforced, HTTP/2 and HTTP/3 negotiated, the CDN answering, the WAF present. Every green light an admin checks was green. Then I stopped checking the front door and went looking for the path around it.
I removed all identifying information. The target is described only by sector and technology class so the pattern can be reused.
Within minutes the origin server was answering directly, on its own address, with the CDN and the WAF nowhere in the path. The same review surfaced two more gaps that shared the same shape: tracking that fired with no consent gate present, and mail in the brand's name that the world would happily accept. Three findings, three controls, and every one of them assumed the attacker would behave.
The pattern
A control that sits beside the only path is not a control
The CDN and the WAF were real and correctly configured. They were also optional, because they were not on the only path to the application. Anyone who resolved the origin address — a certificate transparency log, an old DNS record, a subdomain that never went behind the proxy — could talk to the server directly. The edge then stops being a security control and becomes a performance feature: it protects the requests that were already going to be polite.
The lesson is not "the WAF was weak". It is that a control is only as strong as the number of paths to what it guards. Enumerate the paths first, then place the control on all of them.
"No consent" is not a bypass — it is a missing gate
It is tempting to file "analytics loads before consent" as a bypass. It was not. There was no consent manager on the site at all. The analytics tag and the video embeds ran on first paint, wrote device storage, and pinged third-party ad endpoints — with no banner, no opt-in, and no configuration that could ever have gated them. You cannot bypass a gate that was never built.
That distinction matters for review: a "bypass" implies a working control with a hole. A missing gate means the risk was never modelled. The pages where it hurt most were the ones a privacy-conscious visitor opens precisely to understand what happens to their data.
Mail trust is only as strong as the weakest of three records
The domain published an SPF record — with a softfail. It published a DMARC record — with p=none. It published no DKIM selector. Individually each looks like "something is configured". Together they mean the receiving world has no instruction to reject a message that claims to come from the brand. SPF softfail says "I did not send this, but do what you like"; p=none says "take no action"; no signature means there is nothing to verify against. A forged invoice or support mail lands in the inbox.
Findings
| Severity | Finding | Evidence |
|---|---|---|
| HIGH | Origin server directly reachable — CDN and WAF bypassable by connecting to the origin address (CVSS 7.5); origin certificate expired | Direct request to the origin address returns the app; edge headers absent |
| HIGH | Analytics loads and stores before any consent; no consent manager present; device-storage cookie without Secure; pings to ad endpoints | Fresh headless profile: third-party requests and cookies before any interaction, no banner |
| HIGH | Video embeds load unconditionally and set third-party cookies before opt-in | Fresh profile: embed frames load, VISITOR_INFO1_LIVE / YSC / __Secure-YNID set |
| MEDIUM | REST API reflects an arbitrary Origin with Access-Control-Allow-Credentials: true | ?rest_route= and /wp-json/* both reflect the requestor |
| MEDIUM | Consent architecturally impossible — pre-consent storage is not covered by any Consent-Mode denial | Headless network capture; no CMP configuration |
| MEDIUM | Mail is spoofable: SPF softfail, DMARC p=none, no DKIM | DNS TXT records; a forged message from the brand is deliverable |
| MEDIUM | Verified targeting chain: anonymous identity, media and comment enumeration plus a public debug log | REST index + author enumeration + a public wp-content/debug.log |
| MEDIUM | Internal knowledge-graph endpoint public (~400 nodes / links) | Anonymous GET returns the graph |
| MEDIUM | Internal AI/automation endpoints exposed in the REST index | /wp-json/ namespace listing |
| MEDIUM | Missing security headers site-wide (HSTS, CSP, XFO, nosniff, Referrer-Policy, Permissions-Policy) | Response headers |
The attack path
The comfortable path is the one the operator wired up. The real path is the one that skips it.
Reproducing it in the lab
Every probe below targets a lab instance I control. None of it is directed at a live system, and none of it is a payload — the point is that no payload is needed.
# 1. the front door looks correct
curl -sSI https://lab.example-publisher.test/ | grep -iE 'server|cf-|x-ws-|strict-transport'
# <- CDN answering, WAF present, HTTPS enforced
# 2. does the origin answer without the edge?
dig +short origin.lab.example-publisher.test
curl -sSI --resolve 'lab.example-publisher.test:443:<origin-ip>' \
https://lab.example-publisher.test/ | grep -iE 'server|x-ws-|cf-'
# <- the application answers; no edge headers; WAF not in the path
# 3. consent: fresh profile, no interaction, per route class
TARGET=https://lab.example-publisher.test node consent-audit.js
# <- third-party requests before consent, cookies written, banner: none
# 4. mail: three records, read together
dig +short TXT lab.example-publisher.test | grep -i spf # <- ~all (softfail)
dig +short TXT _dmarc.lab.example-publisher.test # <- p=none
# no DKIM selector published
# consent-audit.js asserts, before any interaction:
# third-party requests == 0
# cookies written == 0
# a route that violates either assertion is a finding, banner or not.
Detecting it in your own estate
- Enumerate the paths to your origin, not the controls you bought. Ask: from the public internet, how many addresses answer for this application? If any answer without the edge in front, the edge is decorative.
- Prove consent by outcome, not by the presence of a banner. A fresh profile that sees zero third-party requests and zero cookies before interaction is the only honest test — and it has to pass on every route class, including the legal pages.
- Never read an SPF, DMARC or DKIM record in isolation. Softfail plus
p=noneplus no signature is a send-anywhere configuration dressed up as three ticks in a checklist. - Watch for the pattern rather than the individual finding: a control that is present on the happy path but absent from a parallel one. The origin is the obvious case; an unproxied subdomain, a staging host, or a direct-IP endpoint are the same bug in different clothes.
What the gap is worth
None of this needs a vulnerability. It needs patience and a quiet window.
T+0m origin address resolved and confirmed answering
T+2m WAF, rate-limits and bot defence removed from the equation
(they only ever saw the polite traffic)
T+30m the site is read freely: authors, media, comments, the internal
knowledge-graph, the AI/automation endpoints
T+1d mail in the brand's name is drafted and delivered — no signature
to fail, no policy to reject it
T+n every later flaw is exercised against the raw origin: unlogged,
unfiltered, at whatever rate the attacker likes
The cost is not a defaced homepage. It is a brand that can be impersonated, readers that can be phished, internal structure that is already mapped, and a monitoring story that is blind precisely because the traffic never touched the edge.
The concrete remediation and hardening is a separate, commissioned engagement — this piece deliberately shows only the exposure and what it is worth to an attacker.
Once an attacker gains access, they can perform the following actions:
Takeaways
- Run the review from the attacker's position, not the admin's. The dashboard only shows the front door; the finding lived at the address beside it.
- Separate "bypassed" from "never installed". A missing consent gate is a modelling failure, and it will not show up in a banner audit.
- Three mail records are one system. Softfail,
p=noneand no DKIM are not partial credit — together they are an open door for impersonation. - The unifying question is cheap and powerful: for every control, how many paths lead around it? If the answer is more than zero, the control is a suggestion.
- A patched site is not a defended site. Currency closes CVEs; architecture decides whether anyone ever needed one.