| name | java-signedobject-deserialization |
| description | Identify and analyze Java SignedObject-gated deserialization vulnerabilities, including pre-auth reachability via error handlers. Use this skill whenever investigating Java deserialization issues, analyzing stack traces with SignedObject.getObject() calls, reviewing license/authentication endpoints, or assessing applications that use java.security.SignedObject for serialization. Trigger on mentions of SignedObject, Java deserialization, license validation, signature verification, or CVE-2025-10035 patterns. |
Java SignedObject-Gated Deserialization Analysis
This skill helps identify and analyze "guarded" Java deserialization patterns built around java.security.SignedObject where seemingly unreachable sinks become exploitable via error-handling flows or signature verification bypasses.
When to Use This Skill
Use this skill when:
- You encounter Java deserialization vulnerabilities with
SignedObject wrappers
- Stack traces show
SignedObject.getObject() in the call chain
- Investigating license/authentication endpoints that deserialize user input
- Analyzing applications using Apache Commons IO
ValidatingObjectInputStream or similar wrappers
- Pre-auth access appears possible through error handlers or exception paths
- You need to assess signature verification bypass possibilities
Vulnerability Pattern Overview
The Guarded Deserialization Pattern
SignedObject so = (SignedObject) JavaSerializationUtilities.deserialize(
payload, SignedObject.class, new Class[]{ byte[].class });
if (!so.verify(pub, sig)) {
throw new IOException("Unable to verify signature!");
}
SignedContainer inner = (SignedObject) so.getObject();
return inner.getData();
Key characteristics:
- Outer deserializer blocks arbitrary top-level gadget classes
- Only
SignedObject or byte[] accepted at the outer layer
- RCE primitive exists in the inner object from
getObject()
- Signature verification gate must be bypassed for exploitation
Exploitation Requirements
To achieve code execution, one of these conditions must be met:
- Private key compromise - Obtain the matching private key used for signing
- Signing oracle - Coerce a trusted service to sign attacker-controlled content
- Alternate reachable path - Find a path that skips
verify() or uses different validation
- Pre-auth token minting - Error handlers that generate session tokens for unauthenticated users
Pre-Auth Reachability Detection
Error Handler Token Minting Pattern
Some applications inadvertently mint session-bound tokens in error handlers:
1. Unauthenticated request to error-prone endpoint
2. Error handler generates session token
3. Token attached to unauthenticated session
4. Token enables access to protected deserialization sink
Proof-of-Reachability Probe
GET /<app>/license/Unlicensed.xhtml/<junk>?javax.faces.ViewState=<junk>&GARequestAction=activate HTTP/1.1
Host: <target>
Expected responses:
- Vulnerable: 302 redirect with
bundle=<signed-data> and Set-Cookie: ASESSIONID=<token>
- Patched: Redirect without bundle parameter or token generation
Stack Trace Indicators
Look for these patterns in logs/stack traces:
java.io.ObjectInputStream.readObject
java.security.SignedObject.getObject
<app>.license.*.BundleWorker.verify
<app>.license.*.BundleWorker.unbundle
<app>.license.*.LicenseController.getResponse
<app>.license.*.LicenseAPI.getResponse
<app>.ui.admin.servlet.LicenseResponseServlet.doPost
Analysis Workflow
Step 1: Identify the Deserialization Sink
- Search codebase for
SignedObject usage
- Locate
getObject() calls
- Check if signature verification precedes the call
- Identify what classes are allowed through the outer filter
Step 2: Assess Signature Verification
- Determine the public key source (hardcoded, configuration, certificate)
- Check for key version/algorithm selection logic
- Look for conditional verification paths (e.g.,
isServer() checks)
- Identify if verification can be bypassed or skipped
Step 3: Map Pre-Auth Reachability
- Find all endpoints that process the serialized payload
- Check authentication requirements for each endpoint
- Trace error handlers and exception paths
- Look for token/session generation in error flows
- Test if tokens can be obtained without authentication
Step 4: Evaluate Exploitation Feasibility
- Can the private key be obtained?
- Is there a signing oracle available?
- Can signature verification be bypassed?
- Are there alternate deserialization paths?
- What gadget chains are available in the classpath?
Hardening Recommendations
1. Maintain Signature Verification
if (!so.verify(pub, sig)) {
throw new IOException("Unable to verify signature!");
}
SignedContainer inner = (SignedContainer) so.getObject();
2. Apply Inner Stream Filtering
Replace direct getObject() calls with hardened wrappers:
SignedContainer inner = (SignedContainer) JavaSerializationUtilities.deserializeUntrustedSignedObject(
so, SignedContainer.class, new Class[]{ byte[].class }
);
3. Implement Strict Serialization Filters
ObjectInputFilter filter = info -> {
Class<?> c = info.serialClass();
if (c == null) return ObjectInputFilter.Status.UNDECIDED;
if (c == java.security.SignedObject.class || c == byte[].class) {
return ObjectInputFilter.Status.ALLOWED;
}
if (c == SignedContainer.class || c == byte[].class) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
};
ObjectInputFilter.Config.setSerialFilter(filter);
4. Remove Error Handler Token Generation
public void handleError(HttpServletRequest req, HttpServletResponse resp) {
log.error("License error", exception);
resp.sendRedirect("/error/license");
}
5. Treat Error Paths as Attack Surface
- Audit all exception handlers
- Remove privileged operations from error flows
- Don't generate security tokens in error handlers
- Log and monitor error handler invocations
Detection Signatures
Log/Stack Trace Patterns
java\.security\.SignedObject\.getObject
java\.io\.ObjectInputStream\.readObject
.*\.BundleWorker\.verify
.*\.LicenseController\.getResponse
Network Indicators
- POST requests to license/authentication endpoints with
bundle parameters
- 302 redirects containing
bundle= with base64-encoded data
- Session cookies set on error page responses
Common Vulnerable Patterns
Pattern 1: Key Version Controls Algorithm
if ("2".equals(keyCfg.getVersion())) {
sigAlg = "SHA512withRSA";
} else {
sigAlg = "SHA1withDSA";
}
Risk: Downgrade attacks or algorithm confusion
Pattern 2: Conditional Verification
if (keyCfg.isServer()) {
return deserializeUntrustedSignedObject(...);
} else {
if (!so.verify(pub, sig)) throw exception;
return so.getObject();
}
Risk: Client path lacks inner stream protection
Pattern 3: Error Handler Token Generation
public void service(HttpServletRequest req, HttpServletResponse resp) {
try {
} catch (Exception e) {
String token = generateLicenseRequestToken(session);
resp.sendRedirect("/vendor?bundle=" + token);
}
}
Risk: Pre-auth access to protected endpoints
Testing Checklist
References
Related Skills
- Java deserialization gadget chain analysis
- Session token security assessment
- Error handler security review
- Signature verification bypass techniques