Spring Batch Remote Code Execution — CVE-2026-47875
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Spring Batch is a lightweight batch processing framework for the JVM that provides reusable functions for handling large volumes of records, together with job and step orchestration, chunk-oriented processing, declarative transaction management, and restartability backed by a persistent job repository.
A Remote Code Execution (RCE) vulnerability (CVE-2026-47875) has been identified in the job parameter deserializer used by Jackson2ExecutionContextStringSerializer, which allows attackers who can write to the job repository to have arbitrary classes loaded and populated from serialized job parameter data, including known Jackson gadget classes that lead to code execution.
Per OWASP: The application deserializes untrusted data without sufficiently verifying that the resulting data will be valid. Data which is untrusted cannot be trusted to be well formed. Malformed data or unexpected data could be used to abuse application logic, deny service, or execute arbitrary code, when deserialized.
This issue affects execution context and job parameter deserialization in the job repository persistence layer of Spring Batch.
Details
Module Info
- Product: Spring Batch
- Affected packages:
org.springframework.batch:spring-batch-core - Affected versions: >=6.0.0 <=6.0.4, >=5.0.0 <=5.2.6
- GitHub repository: https://github.com/spring-projects/spring-batch
- Published packages: https://central.sonatype.com/artifact/org.springframework.batch/spring-batch-core
- Package manager: Maven
- Fixed in
- NES for Spring Batch: 5.1.3-spring-batch-5.1.12 on the 5.1.x line and 5.2.6-spring-batch-5.2.8 on the 5.2.x line
- OSS Spring Batch 6.0.5
Vulnerability Info
This Medium-severity vulnerability is found in the org.springframework.batch:spring-batch-core package in the job repository persistence layer of Spring Batch.
Jackson2ExecutionContextStringSerializer reads and writes step and job execution contexts as JSON. Polymorphic typing on the outer object mapper is restricted by a trusted type resolver that only accepts class names present in a small allowlist. The nested deserializer registered for JobParameter values does not take part in that check. It reads the JSON type field directly from the stored document and hands the raw string to Class.forName, then asks the object mapper to materialize the JSON value node as an instance of whatever class was named:
private class JobParameterDeserializer extends StdDeserializer<JobParameter> {
private static final long serialVersionUID = 1L;
JobParameterDeserializer() {
super(JobParameter.class);
}
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Override
public JobParameter deserialize(JsonParser parser, DeserializationContext context) throws IOException {
JsonNode node = parser.readValueAsTree();
boolean identifying = node.get(IDENTIFYING_KEY_NAME).asBoolean();
String type = node.get(TYPE_KEY_NAME).asText();
JsonNode value = node.get(VALUE_KEY_NAME);
try {
Class<?> parameterType = Class.forName(type);
Object typedValue = objectMapper.convertValue(value, parameterType);
return new JobParameter(typedValue, parameterType, identifying);
}
catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to deserialize job parameter " + value.asText(), e);
}
}
}
Any class name that resolves on the application classpath is loaded here, and convertValue then drives that class through its constructor, setters, and creator methods with attacker-chosen JSON. Applications whose job repository is backed by a data source that untrusted parties can write to, or that restore execution contexts serialized elsewhere, can therefore be made to instantiate classes that were never intended to appear in a job parameter, including the gadget types that Jackson deserialization advisories have historically flagged as paths to arbitrary code execution. Exploitation requires write access to the persisted execution context and a suitable gadget class on the classpath, which is why the issue is scored as Medium rather than Critical.
The allowlist that the outer serializer relies on is available to the nested deserializer, but the job parameter path never consults it before loading the class.
Mitigation
Only recent versions of Spring Batch receive community support. Older lines are End-of-Life and will not receive public updates to address this issue.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported version of Spring Batch.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.