VYPR
Critical severityNVD Advisory· Published Apr 6, 2018· Updated Sep 16, 2024

CVE-2018-1270

CVE-2018-1270

Description

Spring Framework STOMP over WebSocket endpoints in spring-messaging allow remote code execution via crafted messages.

AI Insight

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

Spring Framework STOMP over WebSocket endpoints in spring-messaging allow remote code execution via crafted messages.

Vulnerability

CVE-2018-1270 affects Spring Framework versions 5.0 prior to 5.0.5 and 4.3 prior to 4.3.15, as well as older unsupported versions. The vulnerability resides in the spring-messaging module when applications expose STOMP over WebSocket endpoints with a simple, in-memory STOMP broker. The DefaultSubscriptionRegistry class inappropriately used a StandardEvaluationContext for SpEL expression evaluation, which could lead to arbitrary code execution [1] [2] [3].

Exploitation

An attacker must have network access to the STOMP endpoint and the ability to send a crafted STOMP message. The attack involves sending a specially crafted subscription selector header containing a SpEL expression that is evaluated by the DefaultSubscriptionRegistry without proper sandboxing. The attacker does not require authentication if the endpoint is exposed publicly [1] [2] [3].

Impact

Successful exploitation allows an attacker to achieve remote code execution (RCE) on the server hosting the vulnerable Spring application. The attacker gains the ability to execute arbitrary commands in the context of the application, potentially leading to full compromise of the affected system [1] [4].

Mitigation

Spring Framework released fixed versions 5.0.5 and 4.3.15 to address the vulnerability. The fix involved replacing the StandardEvaluationContext with a SimpleEvaluationContext in the DefaultSubscriptionRegistry, which restricts SpEL evaluation to safe operations [2] [3]. Red Hat also released an advisory (RHSA-2018:2939) for Red Hat FIS 2.0 on Fuse 6.3.0 addressing this and related vulnerabilities [4]. Users should upgrade to the patched versions immediately.

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
>= 5.0.0.RELEASE, < 5.0.5.RELEASE5.0.5.RELEASE
org.springframework:spring-messagingMaven
< 4.3.16.RELEASE4.3.16.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

27

News mentions

0

No linked articles in our index yet.