Analysis and Exploitation of Spring Data REST CVE-2017-8046 Remote Code Execution Vulnerability
The article examines Spring Data REST’s CVE‑2017‑8046 remote‑code‑execution flaw, showing how a malicious JSON Patch path is turned into an unchecked SpEL expression that can run arbitrary commands, reproduces the exploit on a sample Spring Boot app, and advises upgrading to versions that include the path‑verification fix.
After the release of Spring 3.0 RC1 in September 2009, Spring introduced the Spring Expression Language (SpEL). While developers welcomed the new feature, operations teams faced new security concerns similar to the OGNL‑related vulnerabilities in Struts 2. Several Spring CVEs (e.g., CVE‑2017‑8039, CVE‑2017‑4971, CVE‑2016‑5007, CVE‑2016‑4977) are tied to SpEL misuse.
This article focuses on CVE‑2017‑8046, which also stems from SpEL handling in Spring Data REST. Readers can consult the official advisory or jump to the “Vulnerability Fix” section for remediation steps.
Spring Data REST Overview
Spring Data REST is a sub‑project of Spring Data that automatically exposes Spring Data repositories as RESTful endpoints. The official mission of Spring Data is to provide a consistent, Spring‑based programming model for data access across relational, non‑relational, map‑reduce, and cloud data stores.
In short, Spring Data abstracts data access, allowing developers to focus on business logic. Spring Data REST builds on this abstraction to generate REST APIs with minimal code.
Demo Setup
The author follows the official documentation to create a Spring Boot application backed by an H2 database. The steps include adding Maven dependencies, defining an entity, a repository, and the application entry point.
Entity definition (Person):
//import 省略
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO) //自增主键
private long id;
private String firstName;
private String lastName; // getter/setter 省略
}Repository interface (exposes "/people" endpoint):
//import 省略
//在 /people 处创建 RESTful 入口点
@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
// 继承了 PagingAndSortingRepository,提供 CRUD、分页、排序
}Spring Boot application entry point:
//import 省略
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}After building and running the application, the author performs a series of data‑operation tests (GET, POST, PATCH) using the generated REST endpoints. Screenshots of the test results are included in the original article.
JSON Patch Background
The JSON Patch method is defined by IETF RFC 6902. A PATCH request must use the application/json-patch+json content type and contain a JSON array of operations, each with an op, path, and optional value. Example:
[
{ "op": "test", "path": "/a/b/c", "value": "foo" },
{ "op": "remove", "path": "/a/b/c" },
{ "op": "add", "path": "/a/b/c", "value": ["foo", "bar"] }
]Vulnerability Analysis
The author reproduces the issue by sending a crafted JSON Patch request that triggers SpEL evaluation on a malicious path. The stack trace generated by the vulnerable version is shown below:
at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.setValue(MethodReference.java:355) ~[spring-expression-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.expression.spel.ast.CompoundExpression.setValue(CompoundExpression.java:95) ~[spring-expression-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.expression.spel.standard.SpelExpression.setValue(SpelExpression.java:438) ~[spring-expression-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.data.rest.webmvc.json.patch.PatchOperation.setValueOnTarget(PatchOperation.java:167) ~[spring-data-rest-webmvc-2.6.6.RELEASE.jar:na]
... (truncated) ...The analysis proceeds by inspecting the relevant source code in JsonPatchHandler and related classes. Key fragments are reproduced verbatim:
public <T> T apply(IncomingRequest request, T target) throws Exception {
Assert.notNull(request, "Request must not be null!");
Assert.isTrue(request.isPatchRequest(), "Cannot handle non-PATCH request!");
Assert.notNull(target, "Target must not be null!");
if (request.isJsonPatchRequest()) {
return applyPatch(request.getBody(), target);
} else {
return applyMergePatch(request.getBody(), target);
}
} public boolean isJsonPatchRequest() {
return isPatchRequest() && RestMediaTypes.JSON_PATCH_JSON.isCompatibleWith(contentType);
} @SuppressWarnings("unchecked")
<T> T applyPatch(InputStream source, T target) throws Exception {
return getPatchOperations(source).apply(target, (Class<T>) target.getClass());
} private Patch getPatchOperations(InputStream source) {
try {
return new JsonPatchPatchConverter(mapper).convert(mapper.readTree(source));
} catch (Exception o_O) {
throw new HttpMessageNotReadableException(
String.format("Could not read PATCH operations! Expected %s!", RestMediaTypes.JSON_PATCH_JSON), o_O);
}
} public Patch(List<PatchOperation> operations) { this.operations = operations; }
public PatchOperation(String op, String path, Object value) {
this.op = op; this.path = path; this.value = value; this.spelExpression = pathToExpression(path);
} private static String pathToSpEL(String path) {
return pathNodesToSpEL(path.split("/"));
}
private static String pathNodesToSpEL(String[] pathNodes) {
StringBuilder spelBuilder = new StringBuilder();
for (int i = 0; i < pathNodes.length; i++) {
String pathNode = pathNodes[i];
if (pathNode.length() == 0) continue;
if (APPEND_CHARACTERS.contains(pathNode)) {
if (spelBuilder.length() > 0) spelBuilder.append(".");
spelBuilder.append("$[true]");
continue;
}
try {
int index = Integer.parseInt(pathNode);
spelBuilder.append('[').append(index).append(']');
} catch (NumberFormatException e) {
if (spelBuilder.length() > 0) spelBuilder.append('.');
spelBuilder.append(pathNode);
}
}
String spel = spelBuilder.toString();
if (spel.length() == 0) spel = "#this";
return spel;
} public <T> T apply(T in, Class<T> type) throws PatchException {
for (PatchOperation operation : operations) {
operation.perform(in, type);
}
return in;
} <T> void perform(Object target, Class<T> type) {
setValueOnTarget(target, evaluateValueFromTarget(target, type));
} protected <T> Object evaluateValueFromTarget(Object targetObject, Class<T> entityType) {
return value instanceof LateObjectEvaluator
? ((LateObjectEvaluator) value).evaluate(spelExpression.getValueType(targetObject))
: value;
} protected <T> Object evaluateValueFromTarget(Object targetObject, Class<T> entityType) {
verifyPath(entityType);
return evaluate(spelExpression.getValueType(targetObject));
}
protected final <T> Object evaluate(Class<T> type) {
return value instanceof LateObjectEvaluator ? ((LateObjectEvaluator) value).evaluate(type) : value;
}
protected final Optional<PropertyPath> verifyPath(Class<?> type) {
String pathSource = Arrays.stream(path.split("/"))
.filter(it -> !it.matches("\\d"))
.filter(it -> !it.equals("-"))
.filter(it -> !it.isEmpty())
.collect(Collectors.joining("."));
if (pathSource.isEmpty()) return Optional.empty();
try {
return Optional.of(PropertyPath.from(pathSource, type));
} catch (PropertyReferenceException o_O) {
throw new PatchException(String.format(INVALID_PATH_REFERENCE, pathSource, type, path), o_O);
}
}The crucial observation is that the path supplied in the JSON Patch is converted to a SpEL expression without sufficient validation. An attacker can craft a path such as /system("touch /tmp/pwned")/…, which the pathToSpEL logic turns into a SpEL that executes arbitrary code.
Vulnerability Reproduction
With the vulnerable Spring Data REST version, sending a PATCH request containing a malicious replace operation and a crafted path leads to remote code execution. The author provides a screenshot of a successful exploit.
Fix
The vulnerability was disclosed on 21 September 2017 and fixed in later releases of spring-data-rest-webmvc. The patch adds a verifyPath check that validates the path against the target entity type before evaluating the SpEL expression. The recommended mitigation is to upgrade to a version that includes this fix.
References:
Official CVE‑2017‑8046 advisory
Commit that introduces the path verification fix
Spring Data REST project page
RFC 6902 – JSON Patch
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Meituan Technology Team
Over 10,000 engineers powering China’s leading lifestyle services e‑commerce platform. Supporting hundreds of millions of consumers, millions of merchants across 2,000+ industries. This is the public channel for the tech teams behind Meituan, Dianping, Meituan Waimai, Meituan Select, and related services.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
