CRZ-008

Auth cookie without SameSite; GET endpoints accept mw alone

medium CVSS 3.1: 4.2 · Asset: admin.corezoid.com

Summary

The admin.corezoid.com application sets two paired authentication cookies with the same value (mw and __Host_mw):

Set-Cookie: mw=<b64...>; Domain=.corezoid.com; Path=/; HttpOnly; Secure           (NO SameSite)
Set-Cookie: __Host_mw=<b64...>; Domain=.corezoid.com; Path=/; HttpOnly; Secure; SameSite=Strict

Testing confirms:

Endpoint Auth result with mw alone Auth result with both cookies
GET /auth/me 200 (full profile + superuser status + siteheart_auth token) 200
GET /auth/me/state_changes 200 (financial data: total_balance: -12000000) 200
GET /system/conf 200 (widget URLs, auth providers) 200
GET /logout 200 (state-changing!) 200
POST /auth/me {"redirect":"/enter"} 200
POST /api/2/json {"proc":"error","description":"cookie or headers are not valid"} 200

What's exploitable in a real browser cross-site attack:

The mw cookie (SameSite=Lax by default) IS sent on top-level cross-site GET navigations. Scenarios:

  1. Forced logout (CSRF). Attacker hosts <img src="https://admin.corezoid.com/logout"> or tricks the user into visiting <a href="https://admin.corezoid.com/logout">click me</a>. With Lax default, the mw cookie is sent → logout endpoint accepts mw alone → user is logged out. Annoyance vector, not data theft.
  2. Information disclosure via subdomain-takeover. The mw cookie has Domain=.corezoid.com. If any subdomain under *.corezoid.com is compromised (subdomain takeover, XSS, abandoned S3 bucket, dev host with weak auth), the attacker JavaScript on that subdomain can:
    • Read mw cookie (actually no, it's HttpOnly — can't be read by JS directly)
    • BUT: fetch('/auth/me', {credentials:'include'}) from the compromised subdomain to admin.corezoid.com — blocked by CORS (no Access-Control-Allow-Origin)
    • Direct GETs from the compromised subdomain to admin.corezoid.com would fire, but the response body stays opaque
    • The real risk: a new XSS on admin.corezoid.com has immediate access to the full authenticated API surface via same-origin requests
  3. Leaked dev endpoint with cookie echo. Any endpoint that reflects arbitrary content from *.corezoid.com + the Lax cookie combines to create a reliable exfiltration channel.

Test payload (NOT deployed — CSRF PoC concept)

<!-- attacker site -->
<iframe src="https://admin.corezoid.com/logout" width=0 height=0></iframe>
<!-- or -->
<a href="https://admin.corezoid.com/logout">Click here for your coupon</a>

Because /logout accepts GET with mw alone, and mw is Lax (sent on top-level nav), the victim is logged out. Low impact but demonstrably exploitable CSRF.

Remediation

Priority 1 — quick wins:

  1. Set SameSite=Strict (or at least Lax) on the mw cookie explicitly. Lax default is not identical to explicit Lax (spec says "Lax" but implementations vary; some browsers treated missing SameSite as None until 2020).
  2. Rename __Host_mw to __Host-mw (underscore → hyphen) so browsers actually enforce host-prefix rules. Currently the double underscore is a misleading label, not a real security feature.
  3. Collapse the dual-cookie scheme. Having two cookies with identical content and different SameSite defeats the purpose — if mw is accepted alone on GET endpoints, the Strict flag on __Host_mw is security theater.

Priority 2 — server-side defenses:

  1. Require both cookies on GET endpoints too. Right now GET /auth/me accepts mw alone. This is the actual bug enabling the information disclosure.
  2. Change /logout to POST only, and require CSRF token in POST body.
  3. Reduce cookie Domain=.corezoid.com scope to just admin.corezoid.com. The session cookie does not need to be sent to book.corezoid.com, market.corezoid.com, etc.

References