Miggo Logo

CVE-2025-41254: Spring Framework STOMP over WebSocket applications may allow attackers to send unauthorized messages

4.3

CVSS Score
3.1

Basic Information

EPSS Score
0.11809%
Published
10/16/2025
Updated
10/17/2025
KEV Status
No
Technology
TechnologyJava

Technical Details

CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Package NameEcosystemVulnerable VersionsFirst Patched Version
org.springframework:spring-websocketmaven>= 6.2.0, < 6.2.126.2.12
org.springframework:spring-websocketmaven>= 6.1.0, <= 6.1.21
org.springframework:spring-websocketmaven>= 6.0.0, <= 6.0.23
org.springframework:spring-websocketmaven<= 5.3.39

Vulnerability Intelligence
Miggo AIMiggo AI

Miggo AIRoot Cause Analysis

The vulnerability, CVE-2025-41254, allows an attacker to send unauthorized STOMP messages over a WebSocket connection. The root cause lies in a state management flaw within the StompSubProtocolHandler class in spring-websocket.

My analysis of the patch between the vulnerable version (6.2.11) and the fixed version (6.2.12) pinpoints the commit c88bfc54c9256e3c07511aa303be2b136c03e0e2, which refactors this state management.

In the vulnerable version, the handler used separate maps to track message channels and authenticated STOMP users (stompAuthentications). A user was associated with a session via a callback after a CONNECT message was sent for processing. This created a race condition. An attacker could send a WebSocket message containing multiple STOMP frames, such as a SEND or SUBSCRIBE frame before the initial CONNECT frame was fully processed and authenticated.

The handleMessageFromClient function would process these frames sequentially. Before the STOMP user was established in the stompAuthentications map, the getUser function would fall back to using the principal from the underlying WebSocket session. This could allow messages to be processed with incorrect permissions, either those of an anonymous user or a less-privileged user associated with the WebSocket handshake, bypassing the intended STOMP-level authorization.

The fix introduces a SessionInfo class that atomically holds all session-related state, including the message channel and the user principal. This SessionInfo object is created and stored in a map only upon receiving a CONNECT frame. For any subsequent frames, the code now asserts that a session must already exist. This enforces a strict protocol flow, ensuring no STOMP messages are processed before a session is properly established and authenticated, thus closing the security bypass.

The primary vulnerable function is handleMessageFromClient as it's the entry point for processing malicious STOMP messages. The getUser function is also a key part of the vulnerable logic.

Vulnerable functions

org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient
spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java
The vulnerability lies in this function's handling of STOMP messages. Before the patch, it did not ensure that a STOMP session was properly established via a CONNECT frame before processing other message types like SEND or SUBSCRIBE. The state of the session, including the authenticated user, was managed in separate, non-atomic data structures (`stompAuthentications` and `orderedHandlingMessageChannels`). This allowed an attacker to send a message that would be processed before STOMP-level authentication completed, causing the system to fall back to the WebSocket session's principal, thus bypassing authorization. The patch fixes this by introducing a `SessionInfo` object that is created atomically on CONNECT and is required for processing any subsequent messages.
org.springframework.web.socket.messaging.StompSubProtocolHandler.getUser
spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java
This function is part of the vulnerable logic. In the unpatched version, it would retrieve the authenticated user from the `stompAuthentications` map. This map was populated asynchronously after a CONNECT message was processed. If a message was processed before this map was populated for the session, this function would return the principal from the underlying WebSocket session, which could have different (and likely lower) privileges, leading to an authorization bypass. The fix ensures the user is retrieved from the new atomic `SessionInfo` object.

WAF Protection Rules

WAF Rule

STOMP ov*r W**So*k*t *ppli**tions m*y ** vuln*r**l* to * s**urity *yp*ss t**t *llows *n *tt**k*r to s*n* un*ut*oriz** m*ss***s. ### *****t** Sprin* Pro*u*ts *n* V*rsions Sprin* *r*m*work: * *.*.* - *.*.** * *.*.* - *.*.** * *.*.x - *.*.**

Reasoning

T** vuln*r**ility, *V*-****-*****, *llows *n *tt**k*r to s*n* un*ut*oriz** STOMP m*ss***s ov*r * W**So*k*t *onn**tion. T** root **us* li*s in * st*t* m*n***m*nt *l*w wit*in t** `StompSu*Proto*ol**n*l*r` *l*ss in `sprin*-w**so*k*t`. My *n*lysis o* t*