Neutron, our AI engine, scored 97.1% on UC Berkeley's CyberGym benchmark. Learn more

Deep Dive

Deep Dive

OpenAM Session Token Theft and SAML Bypass (CVE-2026-123551)

OpenAM before 16.1.4 leaks live session tokens and accepts unsigned SAML assertions, letting unauthenticated attackers hijack sessions; fixed in 16.1.4.

TL;DR

  • OpenAM before 16.1.4 broadcasts live session tokens to policy-agent topics and accepts unsigned SAML assertions on the ECP and HTTP-Artifact bindings. An unauthenticated attacker can use either flaw to take over user and administrator sessions.
  • The same release closes a self-registration flaw that lets an anonymous user join an administrative group. OpenIdentityPlatform published the advisories and OpenAM 16.1.4 on 2026-10-22.
  • The CVE records rate the token disclosure CVSS v4.0 10.0 and the other two 9.3. The vendor advisories rate the token flaw CVSS v3.1 10.0 and the other two 9.8.
  • No public proof of concept and no exploitation in the wild are reported. The flaws are not in CISA KEV as of 2026-11-08. Upgrade to OpenAM 16.1.4.

Am I affected?

All of these must hold:

  • You run OpenIdentityPlatform OpenAM at a version before 16.1.4.
  • For the session-token flaw, you run 14.0.0 through 16.1.3 and the notification WebSocket is reachable. Agent notifications are enabled by default, and that is what puts session identifiers on the agent topics.
  • For the SAML flaw, OpenAM acts as a hosted SAML v2.0 service provider. Its metadata advertises the ECP (PAOS) assertion consumer service, which the generated metadata does by default. The service provider has WantAssertionsSigned set to false, which the generated metadata also does by default, and an encryption key configured so identity providers can encrypt assertions to it.
  • For the self-registration flaw, self-registration is enabled in the realm. It is off by default.
Branch Affected versions Fixed version
OpenAM 14.0.0 through 16.1.3 All three flaws 16.1.4 (2026-10-22)
OpenAM before 14.0.0 SAML assertion bypass and self-registration escalation; the notification WebSocket does not exist 16.1.4 (2026-10-22)

The version endpoint answers without a session. Ask the server directly:

curl -s https://openam.example.com:8443/openam/json/serverinfo/version

The response carries version, revision and date. In the container image the same request runs against the local port:

docker exec openam curl -s http://localhost:8080/openam/json/serverinfo/version

How do I fix it?

Upgrade to OpenAM 16.1.4 or later. The vendor released it on 2026-10-22, the same day as the advisories.

Timeline from the fix commits to the CVE records: fixes committed 2026-10-21, OpenAM 16.1.4 and the advisories on 2026-10-22, CVE records on 2026-11-08
Figure 1: OpenAM fixed all three flaws before the CVE records appeared. Source: OpenAM commits, the 16.1.4 release and the CVE records, checked November 8, 2026.

  1. For the container image, set the service to openidentityplatform/openam:16.1.4 in your Compose file. Pull the configured image and recreate the container:
docker compose pull openam
docker compose up -d openam
  1. For a WAR deployment, download openam-server-only 16.1.4 from Maven Central and deploy the new openam.war over the existing web application. Restart the container after the deployment.

  2. For a Maven build that depends on OpenAM artifacts, set org.openidentityplatform.openam:openam-server-only and the other OpenAM modules to 16.1.4.

If you cannot upgrade immediately, these mitigations reduce exposure but do not fix the code:

  • Set WantAssertionsSigned to true on every hosted SAML v2.0 service provider and enable response signing. This covers the assertion flaw. It does not cover the notification WebSocket or self-registration.
  • Restrict the /openam/notifications WebSocket and the legacy Liberty ID-FF endpoints to policy-agent hosts at the reverse proxy or network layer. This narrows the token-disclosure path. It does not stop an attacker who can reach those endpoints from another route.
  • Disable self-registration in every realm. This closes the anonymous registration path only.
  • Treat every OpenAM instance that faced the internet before the upgrade as exposed, and review its sessions and administrators after upgrading.

What is the root cause?

OpenAM enforced SAML signature inheritance only on the HTTP-POST binding. Every other binding was left to a metadata flag that its generated service-provider metadata sets to false. The check lives in SAML2Utils.verifyResponse in the federation library, at OpenAM 16.1.3:

if (wantAssertionsSigned && !(responseIsSigned || allAssertionsSigned)) {
    throw new SAML2Exception(bundle.getString("assertionNotSigned"));
}

if (profileBinding.equals(SAML2Constants.HTTP_POST)) {
    boolean wantPostResponseSigned = SAML2Utils
            .wantPOSTResponseSigned(orgName, hostEntityId, SAML2Constants.SP_ROLE);
    if (wantPostResponseSigned && !responseIsSigned) {
        throw new SAML2Exception(bundle.getString("responseNotSigned"));
    }
    if (!responseIsSigned && !allAssertionsSigned) {
        throw new SAML2Exception(bundle.getString("assertionNotSigned"));
    }
}

The first block rejects an unsigned response only when the service provider's WantAssertionsSigned metadata is true. The second block rejects an unsigned response or unsigned assertions regardless of that flag, but it runs only for HTTP_POST. On the ECP (PAOS) and HTTP-Artifact bindings neither block requires a signature unless the metadata flag is on.

The notification WebSocket trusted the container to run its filter, and the filter was mapped to request dispatches only. The WebSocket handler subscribed the client to any topic it named:

String topic = json.get("topic").asString();
subscription.bindTo(Topic.of(topic));

The security filter reads the agent cookie and checks that the token belongs to a J2EE or web policy agent. In OpenAM 16.1.3 its mapping in web.xml named no dispatcher, so the container ran it on REQUEST dispatches alone.

An internal forward to /notifications arrives as a FORWARD dispatch. The filter was skipped, and the WebSocket upgrade proceeded without the agent check. OpenAM's legacy Liberty ID-FF endpoints perform such forwards. The advisory names an unvalidated internal forward as the way to reach the socket without credentials.

The anonymous self-registration action created the account with the system administrator token. It never applied the attribute allowlist. In IdentityResourceV2.anonymousCreate at 16.1.3 the request body went straight to the identity service:

SSOToken admin = RestUtils.getToken();
return createInstance(admin, jVal, realm, context, null)

The allowlist lived in IdentityRestUtils and applied only to requests that carried a SelfServiceContext. The legacy action carries none, so a caller could set any attribute. A memberof value does not remain an attribute: the identity layer turns it into group membership. A self-registering user could therefore join an administrative group.

How is it exploited?

The SAML path needs no credentials and no user interaction:

  1. The attacker finds a hosted SAML v2.0 service provider on an affected OpenAM instance. It advertises ECP (PAOS), has WantAssertionsSigned off, and has an encryption key configured.
  2. The attacker builds a SAML response with an assertion that names a target account, including an administrator, and sends it to the ECP or HTTP-Artifact endpoint without a signature.
  3. OpenAM checks the issuer against the circle of trust but not the signature on that binding. It maps the assertion to the local account and issues a session.
  4. The attacker uses that session against OpenAM and every application that relies on it for single sign-on.

The notification path needs a route to the WebSocket:

  1. The attacker sends a request to a legacy Liberty ID-FF endpoint that forwards internally to /notifications. The forward skips the filter that checks for a policy-agent token.
  2. The attacker subscribes to an internal topic. Before the fix, the handler accepted any topic name.
  3. OpenAM publishes the full iPlanetDirectoryPro value on session creation, logout, timeout and property change, because agent notifications are on by default.
  4. The attacker replays a captured value as a bearer credential against OpenAM's REST endpoints and against applications that trust the same session.

The self-registration path needs self-registration enabled:

  1. The attacker completes the email verification step for an address they control.
  2. The attacker sends the anonymousCreate action with a memberof value that names an administrative group.
  3. OpenAM creates the account with its system administrator token and writes the group membership. The new account can administer the realm.

No public proof of concept exists for any of the three flaws. The advisories say technical details and proof-of-concept code are withheld to give operators time to upgrade.

How does the attack flow?

An unsigned SAML assertion reaches a service provider that checks signatures only on another binding, maps to a local account and becomes a session for every application behind OpenAM.

flowchart LR A["Attacker builds an unsigned
SAML assertion"] --> B["Sends it to the ECP
or HTTP-Artifact endpoint"] B --> C["SP checks the signature
only on HTTP-POST"] C --> D["no signature check
on this binding"] D --> E["Assertion maps to a local user,
including an administrator"] E --> F["OpenAM issues a session"] F --> G["Attacker reaches every
application trusting OpenAM"]

What can an attacker do?

The SAML flaw lets an unauthenticated attacker obtain a session as any account the service provider can map, including an administrator. Which accounts are reachable depends on the mapping.

Auto-federation or a NameID used as the user identifier reaches any account. A federation identifier that is an email address or username reaches any account whose identifier the attacker knows or can guess. The attacker inherits every application that trusts the hijacked session.

The notification flaw lets an unauthenticated attacker read the live session token of every user whose session is created, changed or ended while the WebSocket connection is open. That includes administrators. A token is a bearer credential against OpenAM REST and against every application behind the same session. The attacker can also subscribe to internal topics.

The self-registration flaw lets an anonymous attacker create an account that is a member of any existing group, including an administrative group. Where such a group exists, the result is full administrative control of the realm. The attacker needs self-registration to be enabled, which is not the default.

The three CVE records score every flaw as critical: CVSS v4.0 10.0 for the token disclosure and 9.3 for the other two.

Bar chart of the three CVSS v4.0 scores: 10.0 for session token disclosure, 9.3 for SAML assertion bypass and 9.3 for self-registration escalation
Figure 2: The three CVE records score every flaw as critical. Source: the three CVE records, checked November 8, 2026.

None of the three flaws requires a local position. The SAML and notification flaws are network-reachable. The self-registration flaw is network-reachable when the feature is on.

Why does the fix work?

The SAML fix moves the signature decision ahead of every other check and applies it to every binding. The commit adds isMissingRequiredSignature. It rejects a response when neither the response nor all its assertions are signed, unless the binding is the back-channel HTTP-Artifact binding and its configuration allows it. The excerpt is trimmed to that check and its throw:

+boolean allAssertionsSigned = true;
+List<String> unsignedAssertionIDs = new ArrayList<>();
+for (Assertion assertion : assertions) {
+    if (!assertion.isSigned()) {
+        allAssertionsSigned = false;
+        unsignedAssertionIDs.add(assertion.getID());
+    }
+}
+if (isMissingRequiredSignature(responseIsSigned, allAssertionsSigned,
+        () -> isAssertionSignatureRequired(wantAssertionsSigned, profileBinding, spConfig, orgName,
+                hostEntityId))) {
+    throw new SAML2Exception(bundle.getString("assertionNotSigned"));
+}

The same commit binds an HTTP-Artifact response to the identity provider that resolved the artifact, so one trusted partner cannot answer with a response issued in another partner's name. The fix is complete for the service-provider response path the advisories describe.

The WebSocket fix adds an allowlist for topics and makes the filter run on every dispatch type:

+private static final String ALLOWED_TOPIC_PREFIX = "/agent/";
+if (!topic.startsWith(ALLOWED_TOPIC_PREFIX)) {
+    sendError(session, id, "topic not permitted");
+    return;
+}
 <filter-mapping>
     <filter-name>NotificationsWebSocketFilter</filter-name>
     <url-pattern>/notifications</url-pattern>
+    <dispatcher>REQUEST</dispatcher>
+    <dispatcher>FORWARD</dispatcher>
+    <dispatcher>INCLUDE</dispatcher>
+    <dispatcher>ASYNC</dispatcher>
+    <dispatcher>ERROR</dispatcher>
 </filter-mapping>

The federation code also refuses a forward into a root that carries a request-only filter. The fix covers both ways the advisory describes to reach the socket.

The self-registration fix applies the allowlist explicitly, outside any SelfServiceContext:

+if (!USER_TYPE.equals(objectType)) {
+    throw new BadRequestException("Self-registration is only supported for users");
+}
+removeIgnoringCase(jVal, EMAIL);
+removeIgnoringCase(jVal, FIELD_MAIL);
+jVal.put(FIELD_MAIL, email);
+enforceSelfRegistrationWhiteList(configHandler, realm, jVal, objectType);

The allowlist check also rejects group membership that is not allowed, compares attribute names case-insensitively, and refuses a body it cannot convert. The identity is still created with the administrator token, but only with attributes the allowlist permits.

How do I check if I was compromised?

No indicators of compromise are published, and the advisories say no exploitation has been reported. Treat these as leads, not proof, and note that a failed attempt may leave no trace.

Check the version first, because every other check depends on it. Then look for sessions created through the federation endpoints without a matching identity-provider login. Look also for self-registered accounts that are members of administrative groups, and for WebSocket connections to /openam/notifications from hosts that are not policy agents.

Reverse-proxy access logs record the WebSocket path:

grep -iE "GET /openam/notifications|anonymousCreate" /var/log/nginx/access.log

OpenAM's audit log records identity and session events as JSON. Search the audit directory for identity creation and session events around the period you are investigating:

grep -hE '"eventName": ?"AM-(IDENTITY|SESSION)"' /path/to/openam/audit/*.json | tail -n 200

If the audit log is not enabled, enable it before the upgrade and keep the existing logs. A session token captured before a logout remains usable until it expires, so revoke active sessions after upgrading if you have any reason to suspect exposure.

Timeline

  • 2026-10-21: OpenAM commits the fixes for the SAML, notification WebSocket and self-registration flaws.
  • 2026-10-22: OpenAM 16.1.4 is released and the three advisories are published.
  • 2026-11-08: the CVE records are published by VulnCheck, and the GitHub Advisory Database lists them.

FAQ

Does this affect ForgeRock AM or another OpenAM fork?

The advisories cover OpenIdentityPlatform OpenAM. They do not name ForgeRock AM or any other fork. If you run a different fork, ask its vendor whether the same fixes are present before you rely on this advisory.

Is 16.1.4 enough?

Yes. OpenAM 16.1.4 contains the fixes for all three flaws, and the vendor released it on the same day as the advisories. Use 16.1.4 or later.

Am I affected if I do not use SAML?

Possibly. The notification WebSocket flaw affects OpenAM 14.0.0 through 16.1.3 regardless of SAML use, and the self-registration flaw applies wherever self-registration is enabled. Check the version and the feature settings.

Are there public exploits?

No public proof of concept is published for any of the three flaws. The advisories withhold technical details and exploit code to give operators time to upgrade. The flaws are not in CISA KEV as of 2026-11-08.

Can I mitigate without upgrading?

Only partially. Setting WantAssertionsSigned, disabling self-registration and restricting /openam/notifications each closes one path, but none fixes the code. Upgrade to 16.1.4 as soon as you can.

Why did the CVE records appear after the fixed release?

The vendor published the advisories with the 16.1.4 release on 2026-10-22. VulnCheck assigned and published the CVE records on 2026-11-08, which is when scanners that read only CVE feeds could first flag the versions.

Find exposed OpenAM servers with Ostorlab

Ostorlab's attack surface discovery inventories your internet-facing hosts so you can see which OpenAM deployments are exposed and prioritise the ones to upgrade first. It shows the exposed services, not whether a specific instance is vulnerable.

References

  • OpenIdentityPlatform advisory GHSA-5c3q-hp86-xjr8, "OpenAM: unauthenticated disclosure of live session tokens via the notification WebSocket", CVSS v3.1 10.0, affected 14.0.0 to 16.1.3, fixed in 16.1.4: https://github.com/OpenIdentityPlatform/OpenAM/security/advisories/GHSA-5c3q-hp86-xjr8 (checked November 8, 2026)
  • OpenIdentityPlatform advisory GHSA-3j2f-qhrc-rjfj, "OpenAM: SAML v2.0 service provider accepts unsigned assertions on some bindings", CVSS v3.1 9.8, fixed in 16.1.4: https://github.com/OpenIdentityPlatform/OpenAM/security/advisories/GHSA-3j2f-qhrc-rjfj (checked November 8, 2026)
  • OpenIdentityPlatform advisory GHSA-jv8f-m88g-h6mm, "OpenAM: self-registration does not enforce the allowed attribute list", CVSS v3.1 9.8, fixed in 16.1.4: https://github.com/OpenIdentityPlatform/OpenAM/security/advisories/GHSA-jv8f-m88g-h6mm (checked November 8, 2026)
  • CVE-2026-123551 record, session token disclosure, CVSS v4.0 10.0 and v3.1 10.0: https://www.cve.org/CVERecord?id=CVE-2026-123551 (checked November 8, 2026)
  • CVE-2026-123550 record, SAML assertion bypass, CVSS v4.0 9.3 and v3.1 9.8: https://www.cve.org/CVERecord?id=CVE-2026-123550 (checked November 8, 2026)
  • CVE-2026-123549 record, self-registration escalation, CVSS v4.0 9.3 and v3.1 9.8: https://www.cve.org/CVERecord?id=CVE-2026-123549 (checked November 8, 2026)
  • Fix commit for the SAML flaw, 3bf8e61a7e, 2026-10-21: https://github.com/OpenIdentityPlatform/OpenAM/commit/3bf8e61a7e (checked November 8, 2026)
  • Fix commit for the notification WebSocket, 7d6a031c8e, 2026-10-21: https://github.com/OpenIdentityPlatform/OpenAM/commit/7d6a031c8e (checked November 8, 2026)
  • Fix commit for self-registration, 4c8a33822a, 2026-10-21: https://github.com/OpenIdentityPlatform/OpenAM/commit/4c8a33822a (checked November 8, 2026)
  • OpenAM 16.1.4 release, 2026-10-22: https://github.com/OpenIdentityPlatform/OpenAM/releases/tag/16.1.4 (checked November 8, 2026)
  • OpenAM 16.1.3 SAML2Utils.java, the vulnerable signature logic: https://github.com/OpenIdentityPlatform/OpenAM/blob/16.1.3/openam-federation/openam-federation-library/src/main/java/com/sun/identity/saml2/common/SAML2Utils.java (checked November 8, 2026)
  • OpenAM 16.1.3 web.xml, the filter mapping without dispatchers: https://github.com/OpenIdentityPlatform/OpenAM/blob/16.1.3/openam-server-only/src/main/webapp/WEB-INF/web.xml (checked November 8, 2026)
  • OpenAM ServerVersionResource, the unauthenticated version endpoint: https://github.com/OpenIdentityPlatform/OpenAM/blob/16.1.4/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/server/ServerVersionResource.java (checked November 8, 2026)
  • CISA Known Exploited Vulnerabilities catalog, catalog version 2026.11.06, no OpenAM entry: https://www.cisa.gov/known-exploited-vulnerabilities-catalog (checked November 8, 2026)
  • OpenAM repository reach figures, 902 stars and 474,791 Docker Hub pulls: https://github.com/OpenIdentityPlatform/OpenAM and https://hub.docker.com/r/openidentityplatform/openam (checked November 8, 2026)
  • Figure 1 source, the three fix commits and the 16.1.4 release: https://github.com/OpenIdentityPlatform/OpenAM/compare/16.1.3...16.1.4 (checked November 8, 2026)