Bulwark Webmail: A Foreign Site Overwrites the Victim's Session Cookie

POST /api/auth/session writes the session cookie from an unauthenticated request body and carries no CSRF check

Advisory ID: TP-2026-104
Product: Bulwark Webmail (web interface for the Stalwart mail server, Next.js)
Vulnerability type: Cross-site request forgery (CWE-352)
CVE: requested on 19 September 2026 (GitHub CNA), assignment pending
CVSS 3.1: 6.3 (Medium) · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L
Affected versions: 1.3.0 bis 1.9.2
Vendor advisory: GHSA-qvr9-m8cq-7wvg
Fixed in: 1.10.0
Reported: 16 September 2026

Summary

The endpoint that sets the webmail session cookie takes its content from the body of an unauthenticated request and does not check whether the request comes from its own site. A form on a foreign site produces, with the right encoding type, a body the server parses as JSON. Any site thereby overwrites a signed-in victim's cookie with credentials the attacker chooses. Since the handler skips the upstream re-fetch for an administrator-configured server address, those credentials need not even be valid. The sidebar display stays unchanged, because the identity in browser storage is untouched.

Root cause

POST /api/auth/session writes the session cookie from the request body and carries no check on the request's origin at all. The PUT method in the same file is gated by a triple assertion on the Sec-Fetch headers, and the admin surface ships its own helper for same-origin checking; neither reaches any of the nine POST handlers under /api/auth/. A form with the encoding type text/plain produces a body that request.json() parses, so the request goes through without a preflight. The default cookieSameSite: 'lax' (lib/oauth/cookie-config.ts:6) does not stop it, because the cookie is written by a Set-Cookie on a top-level navigation rather than read from one. Three of the ungated routes write identity cookies, namely /api/auth/session, /api/auth/stalwart-context and /api/auth/totp-token-exchange.

Proof of Concept

<!-- foreign site, visited by the signed-in victim -->
<form action="https://webmail.example/api/auth/session"
      method="POST" enctype="text/plain">
  <input name='{"username":"[email protected]","serverUrl":"'
         value='<the configured server>","x":"}'>
</form>
<script>document.forms[0].submit()</script>

<!-- the body is parsed as JSON and the server sets the session
     cookie to the attacker's mailbox. -->

The session-fixation variant requires the victim to be signed in; the denial variant needs only an open browser. The remember-me option is not required: the normal login writes this cookie only when it is ticked, while the attack writes it unconditionally.

Impact

  • Any site makes the signed-in victim's browser hold a thirty-day session for the attacker's mailbox.
  • Everything the victim writes or files afterwards lands in the attacker's mailbox, while the sidebar still shows their own address.
  • Denial of service through a cookie with no valid mailbox, for which an open browser suffices.
  • Two further ungated routes write identity cookies as well.

References

Is Something Like This in Your Software?

Our team found this vulnerability in the course of its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.