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
SignedObjectwrappers - Stack traces show
SignedObject.getObject()in the call chain - Investigating license/authentication endpoints that deserialize user input
- Analyzing applications using Apache Commons IO
ValidatingObjectInputStreamor 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
// Outer deserialization restricted to SignedObject only
SignedObject so = (SignedObject) JavaSerializationUtilities.deserialize(
payload, SignedObject.class, new Class[]{ byte[].class });
// Signature verification gate
if (!so.verify(pub, sig)) {
throw new IOException("Unable to verify signature!");
}
// Inner object deserialization (potential gadget execution)
SignedContainer inner = (SignedObject) so.getObject();
return inner.getData();
Key characteristics:
- Outer deserializer blocks arbitrary top-level gadget classes
- Only
SignedObjectorbyte[]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>andSet-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
SignedObjectusage - 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
// ALWAYS verify before getObject()
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:
// Use ValidatingObjectInputStream or ObjectInputFilter for inner stream
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;
// Outer layer: only SignedObject and byte[]
if (c == java.security.SignedObject.class || c == byte[].class) {
return ObjectInputFilter.Status.ALLOWED;
}
// Inner layer: strict DTO allow-list
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
// DON'T generate tokens in error handlers for unauthenticated users
public void handleError(HttpServletRequest req, HttpServletResponse resp) {
// Bad: generates token for unauthenticated session
// String token = SessionUtilities.generateLicenseRequestToken(session);
// Good: log error and redirect without token generation
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
bundleparameters - 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"; // Weaker algorithm
}
Risk: Downgrade attacks or algorithm confusion
Pattern 2: Conditional Verification
if (keyCfg.isServer()) {
// Hardened path with additional validation
return deserializeUntrustedSignedObject(...);
} else {
// Client path with basic verification only
if (!so.verify(pub, sig)) throw exception;
return so.getObject(); // No inner filtering
}
Risk: Client path lacks inner stream protection
Pattern 3: Error Handler Token Generation
public void service(HttpServletRequest req, HttpServletResponse resp) {
try {
// Normal flow
} catch (Exception e) {
// Bad: generates token in error handler
String token = generateLicenseRequestToken(session);
resp.sendRedirect("/vendor?bundle=" + token);
}
}
Risk: Pre-auth access to protected endpoints
Testing Checklist
- Identify all
SignedObjectdeserialization points - Verify signature checks precede
getObject()calls - Check for inner stream filtering on deserialized objects
- Map authentication requirements for all related endpoints
- Test error handlers for token/session generation
- Attempt to obtain tokens without authentication
- Verify private key cannot be extracted or obtained
- Check for signing oracle possibilities
- Review serialization filter configurations
- Test with malformed/invalid inputs to trigger error paths
References
- watchTowr Labs – CVE-2025-10035 Analysis
- Fortra Advisory FI-2025-012
- JEP 290 – Serialization Filtering
- Apache Commons IO – ValidatingObjectInputStream
Related Skills
- Java deserialization gadget chain analysis
- Session token security assessment
- Error handler security review
- Signature verification bypass techniques