VYPR
Critical severityNVD Advisory· Published Apr 11, 2018· Updated Sep 17, 2024

CVE-2018-1275

CVE-2018-1275

Description

Spring Framework STOMP over WebSocket endpoints allow remote code execution via crafted STOMP messages due to improper SpEL evaluation in subscription selectors.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Spring Framework STOMP over WebSocket endpoints allow remote code execution via crafted STOMP messages due to improper SpEL evaluation in subscription selectors.

Vulnerability

Spring Framework versions 5.0 prior to 5.0.5 and 4.3 prior to 4.3.16 (and older unsupported) that expose STOMP over WebSocket endpoints using a simple in-memory broker via spring-messaging are vulnerable to remote code execution. The vulnerability resides in the DefaultSubscriptionRegistry where selector expressions from STOMP subscriptions are evaluated using SpEL with a StandardEvaluationContext, which allows execution of arbitrary code [1][2][3]. The selector header in STOMP SUBSCRIBE frames is processed without proper restrictions.

Exploitation

An attacker can send a STOMP SUBSCRIBE frame with a malicious selector expression to any WebSocket endpoint that uses the simple broker [1]. No authentication is required if the endpoint is publicly accessible. The selector is parsed and evaluated via SpEL, and due to the use of StandardEvaluationContext, the expression can invoke arbitrary methods. The fix in [2] and [3] shows that replacing with SimpleEvaluationContext limits property access only to message headers.

Impact

Successful exploitation allows remote code execution in the context of the Spring application process [1]. The attacker gains full control over the application, potentially leading to data theft, service disruption, or further compromise of the underlying system.

Mitigation

The vulnerability is fixed in Spring Framework 5.0.5 and 4.3.16 [1]. Upgrading to these versions is recommended. For users of older unsupported versions, migrating to a supported branch is necessary. Oracle has also included fixes in their CPU July 2018 [4]. No workarounds are documented; however, disabling the simple broker or implementing input validation on selectors could mitigate, but patching is the preferred solution.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.springframework:spring-messagingMaven
< 4.3.16.RELEASE4.3.16.RELEASE
org.springframework:spring-messagingMaven
>= 5.0.0.RELEASE, < 5.0.5.RELEASE5.0.5.RELEASE

Affected products

2

Patches

2
0009806debb5

Re-use EvaluationContext in DefaultSubscriptionRegistry

https://github.com/spring-projects/spring-frameworkRossen StoyanchevApr 9, 2018via ghsa
1 file changed · +27 19
  • spring-messaging/src/main/java/org/springframework/messaging/simp/broker/DefaultSubscriptionRegistry.java+27 19 modified
    @@ -26,15 +26,14 @@
     import java.util.concurrent.ConcurrentMap;
     import java.util.concurrent.CopyOnWriteArraySet;
     
    -import org.springframework.expression.AccessException;
     import org.springframework.expression.EvaluationContext;
     import org.springframework.expression.Expression;
     import org.springframework.expression.ExpressionParser;
     import org.springframework.expression.PropertyAccessor;
     import org.springframework.expression.TypedValue;
     import org.springframework.expression.spel.SpelEvaluationException;
     import org.springframework.expression.spel.standard.SpelExpressionParser;
    -import org.springframework.expression.spel.support.StandardEvaluationContext;
    +import org.springframework.expression.spel.support.SimpleEvaluationContext;
     import org.springframework.messaging.Message;
     import org.springframework.messaging.MessageHeaders;
     import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
    @@ -64,6 +63,10 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
     	/** Default maximum number of entries for the destination cache: 1024 */
     	public static final int DEFAULT_CACHE_LIMIT = 1024;
     
    +	/** Static evaluation context to reuse */
    +	private static EvaluationContext messageEvalContext =
    +			SimpleEvaluationContext.forPropertyAccessors(new SimpMessageHeaderPropertyAccessor()).build();
    +
     
     	private PathMatcher pathMatcher = new AntPathMatcher();
     
    @@ -191,7 +194,6 @@ private MultiValueMap<String, String> filterSubscriptions(
     		if (!this.selectorHeaderInUse) {
     			return allMatches;
     		}
    -		EvaluationContext context = null;
     		MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(allMatches.size());
     		for (String sessionId : allMatches.keySet()) {
     			for (String subId : allMatches.get(sessionId)) {
    @@ -208,12 +210,8 @@ private MultiValueMap<String, String> filterSubscriptions(
     					result.add(sessionId, subId);
     					continue;
     				}
    -				if (context == null) {
    -					context = new StandardEvaluationContext(message);
    -					context.getPropertyAccessors().add(new SimpMessageHeaderPropertyAccessor());
    -				}
     				try {
    -					if (expression.getValue(context, boolean.class)) {
    +					if (Boolean.TRUE.equals(expression.getValue(messageEvalContext, message, Boolean.class))) {
     						result.add(sessionId, subId);
     					}
     				}
    @@ -525,7 +523,7 @@ private static class SimpMessageHeaderPropertyAccessor implements PropertyAccess
     
     		@Override
     		public Class<?>[] getSpecificTargetClasses() {
    -			return new Class<?>[] {MessageHeaders.class};
    +			return new Class<?>[] {Message.class, MessageHeaders.class};
     		}
     
     		@Override
    @@ -534,19 +532,29 @@ public boolean canRead(EvaluationContext context, Object target, String name) {
     		}
     
     		@Override
    -		public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
    -			MessageHeaders headers = (MessageHeaders) target;
    -			SimpMessageHeaderAccessor accessor =
    -					MessageHeaderAccessor.getAccessor(headers, SimpMessageHeaderAccessor.class);
    +		public TypedValue read(EvaluationContext context, Object target, String name) {
     			Object value;
    -			if ("destination".equalsIgnoreCase(name)) {
    -				value = accessor.getDestination();
    +			if (target instanceof Message) {
    +				value = name.equals("headers") ? ((Message) target).getHeaders() : null;
     			}
    -			else {
    -				value = accessor.getFirstNativeHeader(name);
    -				if (value == null) {
    -					value = headers.get(name);
    +			else if (target instanceof MessageHeaders) {
    +				MessageHeaders headers = (MessageHeaders) target;
    +				SimpMessageHeaderAccessor accessor =
    +						MessageHeaderAccessor.getAccessor(headers, SimpMessageHeaderAccessor.class);
    +				Assert.state(accessor != null, "No SimpMessageHeaderAccessor");
    +				if ("destination".equalsIgnoreCase(name)) {
    +					value = accessor.getDestination();
     				}
    +				else {
    +					value = accessor.getFirstNativeHeader(name);
    +					if (value == null) {
    +						value = headers.get(name);
    +					}
    +				}
    +			}
    +			else {
    +				// Should never happen...
    +				throw new IllegalStateException("Expected Message or MessageHeaders.");
     			}
     			return new TypedValue(value);
     		}
    
e0de9126ed8c

Re-use EvaluationContext in DefaultSubscriptionRegistry

https://github.com/spring-projects/spring-frameworkRossen StoyanchevMar 22, 2018via ghsa
1 file changed · +29 22
  • spring-messaging/src/main/java/org/springframework/messaging/simp/broker/DefaultSubscriptionRegistry.java+29 22 modified
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2017 the original author or authors.
    + * Copyright 2002-2018 the original author or authors.
      *
      * Licensed under the Apache License, Version 2.0 (the "License");
      * you may not use this file except in compliance with the License.
    @@ -26,15 +26,14 @@
     import java.util.concurrent.ConcurrentMap;
     import java.util.concurrent.CopyOnWriteArraySet;
     
    -import org.springframework.expression.AccessException;
     import org.springframework.expression.EvaluationContext;
     import org.springframework.expression.Expression;
     import org.springframework.expression.ExpressionParser;
     import org.springframework.expression.PropertyAccessor;
     import org.springframework.expression.TypedValue;
     import org.springframework.expression.spel.SpelEvaluationException;
     import org.springframework.expression.spel.standard.SpelExpressionParser;
    -import org.springframework.expression.spel.support.StandardEvaluationContext;
    +import org.springframework.expression.spel.support.SimpleEvaluationContext;
     import org.springframework.lang.Nullable;
     import org.springframework.messaging.Message;
     import org.springframework.messaging.MessageHeaders;
    @@ -65,6 +64,11 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
     	/** Default maximum number of entries for the destination cache: 1024 */
     	public static final int DEFAULT_CACHE_LIMIT = 1024;
     
    +	/** Static evaluation context to re-use */
    +	private static SimpleEvaluationContext evaluationContext = SimpleEvaluationContext.builder()
    +			.propertyAccessor(new SimpMessageHeaderPropertyAccessor()).build();
    +
    +
     
     	private PathMatcher pathMatcher = new AntPathMatcher();
     
    @@ -192,7 +196,6 @@ private MultiValueMap<String, String> filterSubscriptions(
     		if (!this.selectorHeaderInUse) {
     			return allMatches;
     		}
    -		EvaluationContext context = null;
     		MultiValueMap<String, String> result = new LinkedMultiValueMap<>(allMatches.size());
     		for (String sessionId : allMatches.keySet()) {
     			for (String subId : allMatches.get(sessionId)) {
    @@ -209,12 +212,8 @@ private MultiValueMap<String, String> filterSubscriptions(
     					result.add(sessionId, subId);
     					continue;
     				}
    -				if (context == null) {
    -					context = new StandardEvaluationContext(message);
    -					context.getPropertyAccessors().add(new SimpMessageHeaderPropertyAccessor());
    -				}
     				try {
    -					if (Boolean.TRUE.equals(expression.getValue(context, Boolean.class))) {
    +					if (Boolean.TRUE.equals(expression.getValue(evaluationContext, message, Boolean.class))) {
     						result.add(sessionId, subId);
     					}
     				}
    @@ -533,7 +532,7 @@ private static class SimpMessageHeaderPropertyAccessor implements PropertyAccess
     
     		@Override
     		public Class<?>[] getSpecificTargetClasses() {
    -			return new Class<?>[] {MessageHeaders.class};
    +			return new Class<?>[] {Message.class, MessageHeaders.class};
     		}
     
     		@Override
    @@ -542,21 +541,29 @@ public boolean canRead(EvaluationContext context, @Nullable Object target, Strin
     		}
     
     		@Override
    -		public TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
    -			Assert.state(target instanceof MessageHeaders, "No MessageHeaders");
    -			MessageHeaders headers = (MessageHeaders) target;
    -			SimpMessageHeaderAccessor accessor =
    -					MessageHeaderAccessor.getAccessor(headers, SimpMessageHeaderAccessor.class);
    -			Assert.state(accessor != null, "No SimpMessageHeaderAccessor");
    +		public TypedValue read(EvaluationContext context, @Nullable Object target, String name) {
     			Object value;
    -			if ("destination".equalsIgnoreCase(name)) {
    -				value = accessor.getDestination();
    +			if (target instanceof Message) {
    +				value = name.equals("headers") ? ((Message) target).getHeaders() : null;
     			}
    -			else {
    -				value = accessor.getFirstNativeHeader(name);
    -				if (value == null) {
    -					value = headers.get(name);
    +			else if (target instanceof MessageHeaders) {
    +				MessageHeaders headers = (MessageHeaders) target;
    +				SimpMessageHeaderAccessor accessor =
    +						MessageHeaderAccessor.getAccessor(headers, SimpMessageHeaderAccessor.class);
    +				Assert.state(accessor != null, "No SimpMessageHeaderAccessor");
    +				if ("destination".equalsIgnoreCase(name)) {
    +					value = accessor.getDestination();
     				}
    +				else {
    +					value = accessor.getFirstNativeHeader(name);
    +					if (value == null) {
    +						value = headers.get(name);
    +					}
    +				}
    +			}
    +			else {
    +				// Should never happen...
    +				throw new IllegalStateException("Expected Message or MessageHeaders.");
     			}
     			return new TypedValue(value);
     		}
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

23

News mentions

0

No linked articles in our index yet.