Paste the JWE (the Authorization header value the eToro SPA attaches to every authed
request — copy from DevTools → Network → any /api/* request). The PoC is on a different origin from
eToro; without the CORS bypass, the browser would block reading the response. With the bypass, the
attacker page reads it.
The eToro page https://www.etoro.com/app/chat-bot serves no X-Frame-Options
header, so it is iframeable from any origin. The page's window.onmessage handler accepts
a {type:"chat-bot-start", accessToken, language, agentType, ...} message with
no origin check, then invokes botAPI.liveAgentAPI.startChat, sending the
fields verbatim to Salesforce LiveAgent including extraPrechatFormDetails.value: accessToken
as STS_Token__c. An attacker page can therefore push arbitrary content (including a
sentinel STS Token) into eToro's Salesforce support queue from any origin — this confirms
the cross-origin postMessage path is wide open and the JWE travels to Salesforce as a transcript field.
*.etoro.com: confirmed bypass-scope
The CORS regex is enforced at the Cloudflare edge layer, applied uniformly across the entire
etoro.com zone. Below is a live OPTIONS-preflight sweep across known live subdomains
(run via residential-proxy out-of-band; results frozen at the times the PoC was deployed). Green =
attacker origin reflected with credentials. The list includes auth/STS issuers, trading APIs, the
PCI deposit host, the cashier, the wallet, and many more.
/embed/* and /app/chat-bot are iframeable (no X-Frame-Options)
eToro's main Angular SPA shell is also served from https://www.etoro.com/embed/,
/embed/chat, /embed/widget — none carry XFO or
frame-ancestors, so any origin can iframe them. Combined with the global postMessage
firehose found in main.js (the postMessageService registers a single
window.addEventListener("message", …) with no origin filter, then routes every message
into multiple in-app subscribers), an attacker iframe controls the same-origin SPA's internal
pubsub fabric — including chat-link-clicked → $location.url(…) /
window.open(…) sinks.
Probes JWE-issuing endpoints with the visitor's cookies only (no JWE pasted). Shows that none of them auto-mint a JWE based on cookies alone — eToro auth is JWE-in-header (no eToro session cookie), so the chain partner has to be JWE-leakage, not a cookie-only mint. Reported to map the architecture.
| Property | State |
|---|---|
| Bypass scope | ~114 *.etoro.com hosts including www, sts, tapi-real, tapi-demo, kyc, billing-pci, cashier, wallet, public-api, signin, registration, raf-azure, news, embed, push-lightstreamer, plus the partner/affapi backend and the credit-card view SPA. The misconfig is at the CF edge, applied uniformly to the entire etoro.com zone. |
| Credentials reflected | Access-Control-Allow-Credentials: true on every bypassed host — the browser SOP is removed. |
| What an attacker can do today (no chain) | (1) From www0etoro.com (or any www<c>etoro<c>com the attacker registers), CORS-fetch every authed endpoint with the visitor's __cf_bm/__cflb cookies — DataDome and JWE-required endpoints reject, but every public/unauth endpoint becomes readable cross-origin with credentials, opening XS-Leak / oracle attacks. (2) Iframe /app/chat-bot from any origin and inject arbitrary "STS Tokens" into eToro's Salesforce LiveAgent support transcripts (see Demo B). (3) Iframe /embed/* and feed messages into the SPA's postMessageService firehose — internal pubsub events fire without origin verification. |
| What the bug enables when chained | Any JWE-leak primitive (a found XSS, an OAuth redirect mis-validation, a token-disclosure endpoint, the existing Salesforce transcript exposure) becomes full ATO from any third-party origin, on real-money brokerage accounts. |
| The single fix | Anchor the regex (^https://www\.etoro\.com$) — or replace the regex with an exact-string allow-list. Restrict ACAC:true to legitimately first-party origins. Don't reflect arbitrary origins matching a loose pattern. |