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-94386 — Runnable Spring Boot and Quarkus reproducer for CVE-2026-94386, demonstrating cross-scope OAuth token reuse in Apache Camel camel-oauth's static token cache. | Kitploit
Tools/GitHubGitHub/oscerd/cve-2026-94386
Defensive ToolsVulnerability AnalysisWeb SecurityAuthenticationLearning & Education
GitHuboscerd/cve-2026-94386

CVE-2026-94386

Runnable Spring Boot and Quarkus reproducer for CVE-2026-94386, demonstrating cross-scope OAuth token reuse in Apache Camel camel-oauth's static token cache.

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-94386 — camel-oauth: cross-scope token reuse via an under-keyed JVM-global token cache

Runnable proof-of-concept reproducer for the Apache Camel vulnerability where OAuthClientCredentialsTokenResolver kept the client-credentials tokens it obtained in a static cache shared by every resolver instance and every CamelContext in the JVM, and keyed that cache on the token endpoint and the client ID only — although the token request also carries the client secret and the requested scope.

// affected
record TokenCacheKey(String tokenEndpoint, String clientId) { }
RuntimeDirectoryStack
Camel Spring Bootcamel-spring-boot/Spring Boot 3.5.13 + camel-oauth 4.22.1
Camel Quarkuscamel-quarkus/Camel Quarkus 3.39.0 (Quarkus 3.39.0, Camel 4.22.0)

What it demonstrates

Two profiles that share a token endpoint and a client ID but request different scopes, or authenticate with different client secrets, mapped to the same cache entry. The profile that resolved first populated it; the other was then handed that token, with no token request made under its own configuration, for as long as the token remained valid.

Token caching is on by default, and pointing several profiles at one identity provider client is an ordinary use of the profile support rather than an opt-in to an insecure setting.

cd camel-spring-boot          # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
until curl -sf http://localhost:8080/exploit/ready >/dev/null; do sleep 1; done
curl -s http://localhost:8080/exploit/attack
docker compose down

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

1) Profile 'invoices' resolves first
     token handed to it: TOKEN-FOR[scope=invoice.read;secret=...aaaa]
     token requests the IdP has received: 1

2) Profile 'admin' resolves next, through a DIFFERENT resolver instance
     token handed to it: TOKEN-FOR[scope=invoice.read;secret=...aaaa]
     token requests the IdP has received: 1  (unchanged - no request was made under its own config)
     it was handed the token issued for the OTHER profile's scope: true

3) Profile 'partner' - same endpoint and clientId, DIFFERENT client secret (...-zzzz)
     token handed to it: TOKEN-FOR[scope=invoice.read;secret=...aaaa]
     it was served the token obtained with the other secret, without ever
     authenticating with its own: true

4) CONTROL - profile 'admin' again with cache-tokens=false
     token handed to it: TOKEN-FOR[scope=admin.write;secret=...aaaa]
     it got a token for its own scope: true

Everything the IdP was actually asked for:
  - client_id=camel-integration-client scope=invoice.read;secret=...aaaa
  - client_id=camel-integration-client scope=admin.write;secret=...aaaa

>>> PROVEN: ...

The token value names the scope and secret it was issued for, so you can see which profile's configuration each token actually came from. The request log at the end is the hard evidence: with caching on, the identity provider was never asked for the admin profile's token at all — only the first profile's request and the control's appear.

Probe 2 uses a second resolver instance deliberately: the cache is static, so a separate instance — and a separate CamelContext — shares it.

Who is affected

The resolver obtains tokens on behalf of the components that authenticate their outbound calls with a named OAuth profile — camel-openai, camel-langchain4j-agent, camel-huggingface, camel-docling, camel-a2a, camel-spring-ai-chat and the IBM watsonx components — and of applications calling the OAuthClientAuthenticationFactory SPI directly.

Reaching the issue requires a deployment that defines more than one profile sharing a token endpoint and client ID while differing in scope or client secret.

Threat model

The profiles are configured by the deployment, which is trusted, so no untrusted input reaches the cache. The party that gains is the service that receives a token it was not meant to receive, which, if hostile or compromised, can replay that token within its scope. The outcome is an outbound request carrying a token issued for another profile's scope — broader authority than the calling profile was configured for, or a service the token was not issued for. With identity providers that derive a token's audience from the requested scope, a different scope can mean a token for a different service altogether.

How it is wired

No identity provider and no network. The app hosts a stand-in for the provider's token endpoint serving the client-credentials grant, which issues a token whose value names the scope and client secret it was issued for, and records every request. The real OAuthClientCredentialsTokenResolver and the real static cache run.

The stand-in reads the client credentials from the HTTP Basic authorization header, not from form parameters, because that is how camel-oauth sends them.

Vulnerability Summary

PropertyValue
Componentcamel-oauth (OAuthClientCredentialsTokenResolver)
CWECWE-524 (Use of Cache Containing Sensitive Information)
SeverityMedium
Attack vectorTwo or more OAuth profiles sharing a token endpoint and client ID while differing in scope or client secret
ImpactAn outbound request carrying a token issued for another profile's scope or obtained with another profile's secret
Affected VersionsFrom 4.19.0 before 4.22.2
Fixed Versions4.22.2, 4.23.0
JIRACAMEL-24872
CreditAndrea Cosentino from Apache Software Foundation

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

The fix

The cache key now holds every input of the token request: the token endpoint, the client ID, a SHA-256 fingerprint of the client secret — so the raw secret is not kept in the static cache — and the requested scope. An absent, empty or blank scope counts as one value, because each of them leaves the scope parameter out of the request; any other scope is matched exactly. Profiles that agree on all four still share a token across resolver instances, while profiles that differ in credentials or scope obtain separate tokens.

Fixed on main in apache/camel#26681 (5e896b2d), backported to camel-4.22.x in #27779 (b2d9957b).

Checking a fixed build

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

2) Profile 'admin' resolves next, through a DIFFERENT resolver instance
     token handed to it: TOKEN-FOR[scope=admin.write;secret=...aaaa]
     token requests the IdP has received: 2
     it was handed the token issued for the OTHER profile's scope: false

3) Profile 'partner' ... token handed to it: TOKEN-FOR[scope=invoice.read;secret=...zzzz]
     it was served the token obtained with the other secret ...: false

>>> NOT reproduced: each profile got a token for its own scope and secret.

All four token requests now reach the identity provider, each under its own configuration.

Mitigation without upgrading

Disable token caching on every OAuth profile that shares a token endpoint and client ID with another profile — camel.oauth.NAME.cache-tokens=false for a named profile, camel.oauth.cache-tokens=false for the default one — at the cost of a token request on every resolution (probe 4 shows this working). Alternatively, register a separate identity provider client for each profile that needs a different scope or client secret, so that no two such profiles share a client ID.

Disclaimer

Download Tool