Share
## https://sploitus.com/exploit?id=5C644F95-6CD7-5EEF-9D19-FF726C1EABE3
# CVE-2026-23552 - Cross-Realm Token Acceptance in camel-keycloak

## Overview

Apache Camel's `KeycloakSecurityPolicy` does not validate the `iss` (issuer) claim of JWT tokens against the configured realm. This means a token issued by one Keycloak realm is silently accepted by a policy configured for a completely different realm, breaking tenant isolation.

Affected versions: 4.15.0, 4.16.0, 4.17.0

Tracked at: https://issues.apache.org/jira/browse/CAMEL-22854

## Root Cause

In `KeycloakSecurityHelper.parseAccessToken()`, when no public key is explicitly provided (which is the default configuration), the token is only decoded from Base64 -- it is never verified:

```java
public static AccessToken parseAccessToken(String tokenString, PublicKey publicKey)
        throws VerificationException {
    if (publicKey != null) {
        return TokenVerifier.create(tokenString, AccessToken.class)
                .publicKey(publicKey)
                .verify()
                .getToken();
    } else {
        // no signature verification, no issuer check
        return TokenVerifier.create(tokenString, AccessToken.class).getToken();
    }
}
```

Since the default code path hits the `else` branch, three things go unchecked:

1. The token signature is never verified
2. The `iss` claim is never compared to the expected `{serverUrl}/realms/{realm}`
3. No JWKS public key is fetched from Keycloak

The role check still passes because it only looks at role names inside the token payload. If the role name (`tenant-user`) happens to match across realms -- which is common in multi-tenant setups -- the request goes through.

## Impact

In a multi-tenant application where each tenant maps to a separate Keycloak realm, a user from tenant A can access routes protected for tenant B. Concretely:

- Cross-tenant data access in multi-tenant SaaS applications
- Privilege escalation when different realms have different role configurations
- Complete bypass of realm-based security isolation

## Reproducer

This project demonstrates the vulnerability using Apache Camel 4.17.0 and a local Keycloak instance.

### Prerequisites

- Java 17+
- Maven 3.9+
- Camel JBang CLI (`jbang app install camel@apache/camel`)
- Docker or Podman (used by `camel infra` under the hood)

### Setup

Start a local Keycloak:

```bash
camel infra run keycloak
```

This starts Keycloak on `localhost:8080` with admin credentials `admin`/`admin`.

### Run

```bash
mvn verify
```

### What happens

The integration test (`CrossRealmTokenBypassIT`) does the following:

1. Connects to the local Keycloak and creates two realms: `acme` and `globex`
2. Creates a confidential client in each realm with direct access grants enabled
3. Creates the `tenant-user` realm role in both realms
4. Creates user `alice` (password `alice123`) in the `acme` realm and assigns her the `tenant-user` role
5. Sets up a Camel route (`direct:globex-protected`) guarded by a `KeycloakSecurityPolicy` bound to the `globex` realm
6. Obtains a JWT token for `alice` from the `acme` realm (issuer: `http://localhost:8080/realms/acme`)
7. Sends that token to the globex-protected route

The request succeeds. The `acme` token passes the `globex` policy without any error.

### Expected test results on 4.17.0

| Test | Result | Meaning |
|------|--------|---------|
| `testAcmeTokenIsObtainable` | PASS | Sanity check -- token acquisition works |
| `testCrossRealmTokenAccepted` | PASS | Confirms the vulnerability is present |
| `testCrossRealmTokenShouldBeRejected` | FAIL | Documents correct behavior -- on 4.17.0 no exception is thrown |

On a patched version (4.18.0+), the second test would fail and the third would pass.

### Cleanup

```bash
camel infra stop keycloak
```

The test also removes the `acme` and `globex` realms in `@AfterAll`.

## Fix

The `KeycloakSecurityPolicy` should reject tokens where the `iss` claim does not match `{serverUrl}/realms/{realm}`. The fix is tracked in CAMEL-22854 and will ship with Camel 4.18.0 (the next LTS release).