Spring deserialisation / SpEL / RCE deep dive
TL;DR: Spring (Spring Framework, Spring Boot, Spring Cloud) is the dominant Java web framework. The RCE-class bug surface clusters into: (1) Spring Expression Language (SpEL) injection in routes / Thymeleaf / SpEL-binding, (2) data-binding bypass enabling write to arbitrary properties (Spring4Shell, CVE-2022-22965), (3) Spring Cloud Function / Gateway / Cloud Config injection (CVE-2022-22963, CVE-2022-22947), and (4) classic Java deserialisation via accepted endpoints. Companion to java-deserialization-audit and spring-boot-audit-patterns.
Why Spring deeper
- Most-deployed Java enterprise stack.
- Spring4Shell (2022) demonstrated data-binding RCE with limited prereqs but huge install base.
- SpEL is rich — string-as-expression evaluates to arbitrary code.
- Spring Cloud subsystems each have their own injection classes.
Class 1 — SpEL injection
Spring Expression Language is a runtime expression evaluator. Used in:
@Value("#{...}")annotation.T(...)static-type access.- Thymeleaf
th:utext="${...}"and similar. - Some configuration paths.
If user input becomes part of a SpEL expression evaluated by Spring:
1
2
3
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(userInput);
exp.getValue(); // arbitrary code execution
Payloads like T(java.lang.Runtime).getRuntime().exec("calc") give RCE.
Historical CVE: many. Spring Boot Actuator endpoints that exposed SpEL evaluation (multiple CVEs).
Class 2 — Data-binding (Spring4Shell)
CVE-2022-22965. JDK 9+ removed reflection restriction. Spring’s WebRequest data binding allowed setting any property of the model bean via request parameter name.
With JDK 9+, the chain class.module.classLoader.resources.context.parent.pipeline.first.{...} reached Tomcat’s AccessLogValve. Setting the log file location + log pattern wrote attacker-controlled file (with arbitrary content) — JSP file in webroot. Visit JSP → RCE.
Prereqs were specific but matched many Tomcat-based Spring deployments.
Patch: blocked the class traversal.
Class 3 — Spring Cloud Function (CVE-2022-22963)
Spring Cloud Function’s HTTP dispatcher used a header spring.cloud.function.routing-expression. Value parsed as SpEL → RCE.
Single HTTP request to a Function-exposed endpoint with crafted header.
Class 4 — Spring Cloud Gateway (CVE-2022-22947)
Gateway’s Actuator endpoint, if exposed, accepted creating new routes with predicate / filter definitions including SpEL. Attacker creates route with SpEL filter → triggers route → RCE.
Class 5 — Spring Cloud Config (CVE-2019-3799, CVE-2020-5410)
Spring Cloud Config server had path-traversal bugs.
Class 6 — Thymeleaf SpEL injection
Thymeleaf templates use SpEL for dynamic content:
1
<div th:text="${user.name}"></div>
If user input becomes part of th:text value or attribute:
1
<div th:text="${T(java.lang.Runtime).getRuntime().exec('whoami')}"></div>
RCE via template injection. See ssti.
Class 7 — Reflection-based property setting
PropertyUtils.setProperty and similar in some Spring data-binding chains accept attacker-supplied property paths. Without filter, can reach beans not intended to be mutable.
Class 8 — Classic Java deserialisation
Spring endpoints accepting application/x-java-serialized-object:
- Direct ObjectInputStream.
- Gadget chains via included libraries (Commons Collections, etc.).
See java-deserialization-audit.
Class 9 — XStream / Jackson misconfig
Spring projects often use XStream (XML) or Jackson polymorphic deserialisation:
- Polymorphic without type allowlist = RCE.
- XStream pre-2017 default = RCE.
Class 10 — Actuator endpoint exposure
Spring Boot Actuator exposes management endpoints:
/env— environment variables (often secrets)./heapdump— heap dump (potentially with credentials)./jolokia— JMX over HTTP; class-loading abuse.
In production with public-facing Actuator, multiple paths to compromise.
Audit shape
For a Spring application:
- Endpoints: list
@RestController,@Controller,@RequestMappingpatterns. - SpEL usage: grep for
parseExpression,getValue. - Data binding: identify
@ModelAttribute,@RequestParam, beans bound to. - Actuator: check enabled endpoints, exposure.
- Cloud subsystems: which Spring Cloud projects are used.
- Deserialisation: identify Jackson / XStream / Java-serialised endpoints.
- Template engine: Thymeleaf, Freemarker — SSTI surface.
- Spring version + patch level.
Defensive baseline
- Update Spring promptly; CVEs published quarterly.
- Avoid SpEL on user input without strict allowlist.
- Disable Actuator in production; or restrict access.
- Spring Boot 2.7+ / 3.x — defaults are safer.
- Java serialization filter for ObjectInputStream.
- Polymorphic Jackson with allowlist.
- Thymeleaf: use
th:text(escapes), avoidth:utexton user input. - Static analysis — Snyk, Sonar, Semgrep rules for Spring.
Workflow to study
- Reproduce Spring4Shell in a controlled Docker.
- Practice SpEL injection in a deliberately vulnerable app.
- Audit Spring Cloud Function exposure.
- Read CVE writeups for Spring4Shell + Cloud-Function-RCE.
Tools
- Burp + Spring-specific extensions.
- Semgrep Spring rules.
- GitHub Code Search for
parseExpression. - Trivy for known-CVE scanning.
- Snyk, Sonatype — SCA.
Real-world incidents
- Spring4Shell (2022) — mass exploitation; Volexity / NSFOCUS / others reported active campaigns.
- Spring Cloud Function (CVE-2022-22963) — adjacent mass exploitation.
- Spring Cloud Gateway (CVE-2022-22947) — similar.
Related
- java-code-auditing
- java-deserialization-audit
- spring-boot-audit-patterns
- ssti
- expression-injection
- dotnet-deserialisation-deep