Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-95663 — Runnable proof-of-concept reproducer for CVE-2026-95663, where Apache Camel camel-keycloak served cached token introspection results past token expiry, with Spring Boot and Quarkus demos. | Kitploit
Tools/GitHubGitHub/oscerd/cve-2026-95663
Defensive ToolsVulnerability AnalysisWeb SecurityAuthenticationLearning & Education
GitHuboscerd/cve-2026-95663

CVE-2026-95663

Runnable proof-of-concept reproducer for CVE-2026-95663, where Apache Camel camel-keycloak served cached token introspection results past token expiry, with Spring Boot and Quarkus demos.

View Repository
1 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-95663 — camel-keycloak: a cached introspection result was served past the token's expiry

Runnable proof-of-concept reproducer for the Apache Camel vulnerability where KeycloakSecurityPolicy with token introspection enabled (useTokenIntrospection=true) kept honouring an access token after it had expired, because the cached introspection result outlived the token.

RuntimeDirectoryStack
Camel Spring Bootcamel-spring-boot/Spring Boot 3.5.13 + camel-keycloak 4.22.1
Camel Quarkuscamel-quarkus/Camel Quarkus 3.39.0 (Quarkus 3.39.0, Camel 4.22.0)

What it demonstrates

On the introspection path, Keycloak reports a token as inactive once its exp has passed — so the active flag is what enforces expiry. Introspection results are cached by default (introspectionCacheEnabled=true, introspectionCacheTtl=60), and both shipped caches (ConcurrentMapTokenCache, CaffeineTokenCache) expired an entry a fixed time after it was inserted, without bounding that lifetime by the exp claim the cached response already carried. KeycloakSecurityProcessor in turn checked only active and never compared exp with the current time.

A response cached shortly before the token expired was therefore served to later requests carrying the same token, which were admitted with the roles and permissions the expired token had carried — although RFC 7662 states that a response containing exp must not be cached beyond that time.

cd camel-spring-boot          # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
until [ "$(curl -s http://localhost:8080/exploit/ready)" = "Started" ]; do sleep 1; done
curl -s http://localhost:8080/exploit/attack      # takes ~4s: it waits for the token to expire
docker compose down

Output on an affected build (identical on both runtimes):

1) Request while the token is still valid
     introspection calls so far: 1
     outcome: [ADMITTED - the protected route ran]

   ... the token's exp has now passed. The cache entry is still inside its 60s TTL.

2) The same token again, now expired, against the SAME route
     introspection calls so far: 1  (unchanged - the endpoint was never asked again)
     outcome: [ADMITTED - the protected route ran]
     an expired token was admitted: true

3) CONTROL - the same expired token against a route with introspectionCacheEnabled=false
     introspection calls so far: 2  (the endpoint was asked)
     outcome: [REJECTED - CamelAuthorizationException: Token is not active (may be revoked or expired)]
     rejected once the endpoint was actually consulted: true

>>> PROVEN: ...

The introspection call counter is the evidence: it does not move between probe 1 and probe 2. The identity provider was never asked a second time, so its verdict — which by then is "inactive" — never reached the policy. Probe 3 proves that verdict exists: the same expired token against a route with caching off is rejected, which is also the workaround the advisory gives for deployments that cannot upgrade yet. Lowering introspectionCacheTtl only narrows the window; it does not close it.

How it is wired

No Keycloak server and no network. The app hosts a stand-in for Keycloak's introspection endpoint, on the exact path KeycloakTokenIntrospector builds ({serverUrl}/realms/{realm}/protocol/openid-connect/token/introspect), which behaves as Keycloak does: active until exp has passed, inactive afterwards, with exp, iss and realm_access.roles in the response. The real KeycloakSecurityPolicy, the real KeycloakTokenIntrospector and the real cache run.

Introspection does not require the token to be a JWT, so the token here is an opaque string — the exp that matters lives in the introspection response, which is exactly what the cache was failing to respect.

Scope

The issue grants nothing beyond what the token already carried: no token forgery and no privilege escalation. The window is bounded by introspectionCacheTtl — 60 seconds by default, 300 to 600 where the documentation's recommendation for internal services was followed — and it opens only for a token that the same Camel process introspected and cached while it was still active. Token introspection is not enabled by default. Hence the medium severity.

The local JWT verification path, used when introspection is disabled, is not affected.

Vulnerability Summary

PropertyValue
Componentcamel-keycloak (KeycloakSecurityPolicy with useTokenIntrospection=true)
CWECWE-613 (Insufficient Session Expiration)
SeverityMedium
Attack vectorA caller re-presenting a bearer token that was valid moments earlier, within the introspection cache's TTL
ImpactRequests admitted after the token's expiry, with the roles and permissions that token carried
Affected VersionsFrom 4.16.0 before 4.18.5, from 4.19.0 before 4.22.2
Fixed Versions4.18.5, 4.22.2, 4.23.0
JIRACAMEL-24902
CreditAndrea Cosentino from Apache Software Foundation

Advisory: https://camel.apache.org/security/CVE-2026-95663.html

The fix

Two independent changes:

  1. Both cache implementations now bound each entry's lifetime by the shorter of introspectionCacheTtl and the time remaining until the token's exp, and a result whose token has already expired is never served from the cache.
  2. Independently of the cache, KeycloakSecurityProcessor now rejects a token whose exp lies in the past on every path that uses introspection, so expiry is enforced there as it already was on the local JWT verification path.

A response that carries no exp claim keeps the configured time-to-live.

Fixed on main in apache/camel#26747 (7bfacb70), backported to camel-4.22.x in #27182 (66fd7034) and to camel-4.18.x in #27183 (6be02c19).

Checking a fixed build

Set camel.version to 4.22.2 and run it again. Verified against 4.22.1 → 4.22.2:

2) The same token again, now expired, against the SAME route
     introspection calls so far: 2
     outcome: [REJECTED - CamelAuthorizationException: Token is not active (may be revoked or expired)]
     an expired token was admitted: false

>>> NOT reproduced: the expired token was rejected.

Note the counter moving from 1 to 2: the cache entry's lifetime was bounded by the token's exp, so the second request re-introspected and got the endpoint's real verdict.

Disclaimer

This repository is published for educational and defensive purposes: to help Apache Camel users understand the vulnerability, verify whether they are affected, and confirm that upgrading resolves it. Do not use this material against systems you do not own or operate.

Download Tool