Hacktricks-skills java-signedobject-deserialization
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.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/deserialization/java-signedobject-gated-deserialization/SKILL.MDJava 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
wrappersSignedObject - Stack traces show
in the call chainSignedObject.getObject() - Investigating license/authentication endpoints that deserialize user input
- Analyzing applications using Apache Commons IO
or similar wrappersValidatingObjectInputStream - 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
orSignedObject
accepted at the outer layerbyte[] - 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
or uses different validationverify() - 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
andbundle=<signed-data>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
usageSignedObject - Locate
callsgetObject() - 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.,
checks)isServer() - 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
parametersbundle - 302 redirects containing
with base64-encoded databundle= - 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
deserialization pointsSignedObject - Verify signature checks precede
callsgetObject() - 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