Security context
Low· 3.7GHSA-659m-px2c-25wj CVE-2026-41848CWE-1333Published Jun 9, 2026

Spring Framework Denial of Service via AntPathMatcher

Research this vulnerability

Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.

Affected versions

7.0.0 → fixed in 7.0.86.2.0 → fixed in 6.2.196.1.0 and later (no fix listed)

Details

Applications may be vulnerable to a Regular Expression Denial of Service (ReDoS) attack if an attacker is able to provide a pattern which is then directly or indirectly supplied to one of the following methods in AntPathMatcher: match(String pattern, String path), matchStart(String pattern, String path), extractUriTemplateVariables(String pattern, String path). Affected versions: Spring Framework 7.0.0 through 7.0.7; 6.2.0 through 6.2.18; 6.1.0 through 6.1.27; 5.3.0 through 5.3.48.

The fix

Avoid too many character access attempts in AntPathMatcher

Sam Brannen· May 11, 2026, 09:51 AM+55112b44f2545
spring-core/src/main/java/org/springframework/util/AntPathMatcher.java+55 1
@@ -723,7 +723,7 @@ public boolean matchStrings(String str, @Nullable Map<String, String> uriTemplat
return this.caseSensitive ? this.rawPattern.equals(str) : this.rawPattern.equalsIgnoreCase(str);
}
else if (this.pattern != null) {
- Matcher matcher = this.pattern.matcher(str);
+ Matcher matcher = this.pattern.matcher(new MaxAttemptsCharSequence(str));
if (matcher.matches()) {
if (uriTemplateVariables != null) {
if (this.variableNames.size() != matcher.groupCount()) {
@@ -748,6 +748,60 @@ else if (this.pattern != null) {
return false;
}
+
+ private static class MaxAttemptsCharSequence implements CharSequence {
+
+ private static final int MAX_ATTEMPTS = 1_000_000;
+
+ private final String text;
+
+ private final Counter counter;
+
+
+ MaxAttemptsCharSequence(String text) {
+ this(text, new Counter());
+ }
+
+ private MaxAttemptsCharSequence(String text, Counter counter) {
+ this.text = text;
+ this.counter = counter;
+ }
+
+
+ @Override
+ public int length() {
+ return this.text.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ if (this.counter.value++ >= MAX_ATTEMPTS) {
+ throw new IllegalStateException(
+ "Too many character access attempts encountered during pattern matching");
+ }
+ return this.text.charAt(index);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.text.isEmpty();
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ return new MaxAttemptsCharSequence(this.text.substring(start, end), this.counter);
+ }
+
+ @Override
+ public String toString() {
+ return this.text;
+ }
+
+
+ private static class Counter {
+ private int value;
+ }
+ }
}

References