CVE-2026-59713
Executive Summary
CVE-2026-59713 is a critical Cross-Site Request Forgery (CSRF) vulnerability in the OpenID Connect (OIDC) login flow of Leantime, an open-source project management tool. The flaw resides in the verifyState() method, which unconditionally returns true without validating the state parameter. This allows an attacker to craft a malicious OIDC callback URL containing an attacker-controlled authorization code. When a victim clicks the link, the attacker can perform session fixation, effectively logging the victim into the attacker's pre-authenticated session. This grants the attacker full access to the victim's Leantime instance, including project data, tasks, and user management.
The vulnerability is classified under MITRE ATT&CK tactic T1190 (Exploit Public-Facing Application) and CWE-352 (Cross-Site Request Forgery). No public exploit code or patch is currently available, but the attack vector is straightforward and requires only user interaction (clicking a link). The CVSS v3.1 score is 8.1 (High) due to the low attack complexity, network vector, and high impact on confidentiality and integrity.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Leantime is a PHP-based application distributed via GitHub and Docker. The OIDC library dependency (typically jumbojett/openid-connect-php) is not directly vulnerable, but the application's misuse of the library's state validation introduces the flaw. Organizations using Leantime should audit their OIDC configuration and ensure the state parameter is properly validated server-side. No malicious packages have been identified in the supply chain.
CVSS Breakdown & Threat Level
- Attack Vector (AV): Network (N) – The attack is executed over the network via a crafted URL.
- Attack Complexity (AC): Low (L) – No special conditions are required; the attacker only needs to craft a URL.
- Privileges Required (PR): None (N) – The attacker does not need any prior authentication.
- User Interaction (UI): Required (R) – The victim must click the malicious link.
- Scope (S): Unchanged (U) – The vulnerability does not cross a trust boundary.
- Confidentiality (C): High (H) – The attacker gains access to all project data.
- Integrity (I): High (H) – The attacker can modify tasks, projects, and user settings.
- Availability (A): None (N) – The vulnerability does not affect system availability.
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N
Base Score: 8.1 (High)
Threat Level: High. The vulnerability is easy to exploit and has significant impact. The lack of a patch increases the risk for unpatched instances.
Deep Dive & Mechanics
The OIDC login flow in Leantime works as follows:
- The user clicks "Login with OIDC" and is redirected to the identity provider (IdP) with a
stateparameter (a random nonce) and aredirect_uri. - After authentication, the IdP redirects the user back to Leantime's callback endpoint with the
stateand anauthorization_code. - Leantime's
verifyState()method should compare the returnedstatewith the one stored in the session to prevent CSRF.
The vulnerability is in the verifyState() method, which is implemented as:
public function verifyState(): bool
{
return true; // Always returns true, no validation
}
This means an attacker can:
- Initiate their own OIDC login flow with their own IdP (or a malicious one) and obtain a valid authorization code.
- Craft a URL like:
https://victim-leantime.com/auth/oidc/callback?state=attacker_state&code=attacker_code - Send this URL to the victim (e.g., via phishing email).
- When the victim clicks the link, Leantime's callback endpoint accepts the attacker's code without validating the state.
- The victim's browser is now logged into the attacker's session, giving the attacker full access.
The attack is a classic session fixation combined with CSRF. The attacker does not need to steal the victim's session; they simply force the victim to use the attacker's session.
Vulnerable vs. Patched Code
Vulnerable Code (Leantime <= affected version):
// File: src/domain/auth/controllers/class.oidc.php (or similar)
public function verifyState(): bool
{
// BUG: No state validation
return true;
}
Patched Code (Hypothetical fix):
public function verifyState(): bool
{
// Retrieve the state from the session
$sessionState = $_SESSION['oidc_state'] ?? null;
// Retrieve the state from the request
$requestState = $_GET['state'] ?? null;
// Compare and return
return $sessionState !== null && $requestState !== null && hash_equals($sessionState, $requestState);
}
The fix uses hash_equals() to prevent timing attacks and ensures both states are non-null.
Exploitation & Lab Setup
Red Team (Attack Simulation)
Prerequisites:
- A Leantime instance with OIDC enabled (any version).
- An attacker-controlled OIDC provider (e.g., a simple PHP script or a public IdP like Auth0).
- A victim who will click a link.
Step-by-Step Exploit:
- Set up a malicious OIDC provider that accepts any redirect and returns a fixed authorization code.
- Initiate an OIDC login from the attacker's browser to the victim's Leantime instance. Capture the
stateparameter from the redirect URL (e.g.,https://victim-leantime.com/auth/oidc/login?state=abc123). - Obtain an authorization code from the malicious IdP (e.g.,
code=xyz789). - Craft the malicious callback URL:
https://victim-leantime.com/auth/oidc/callback?state=abc123&code=xyz789 - Send the URL to the victim via email, social engineering, or a link on a compromised site.
- Victim clicks the link – their browser is now logged into the attacker's session.
- Attacker accesses the victim's Leantime instance using the same session (e.g., by copying the session cookie from the victim's browser or by using the same session ID).
Example Python script to generate the malicious URL:
import urllib.parse
victim_base = "https://victim-leantime.com"
attacker_state = "abc123"
attacker_code = "xyz789"
malicious_url = f"{victim_base}/auth/oidc/callback?state={attacker_state}&code={attacker_code}"
print(f"Malicious URL: {malicious_url}")
Blue Team (Defense & Detection)
Detection Steps:
- Monitor OIDC callback requests for unusual
stateparameters that do not match any recent login attempts. - Log all OIDC login attempts and correlate the
stateparameter with the session. - Implement a WAF rule to block requests where the
stateparameter is not a valid UUID or nonce format. - Audit Leantime code for the
verifyState()method and patch it immediately.
Honeypot Strategy:
- Deploy a fake Leantime instance with OIDC enabled and monitor for callback requests with mismatched
statevalues.
Indicators of Compromise (IOCs)
Network IOCs:
- Multiple OIDC callback requests from different IPs with the same
stateparameter. - Callback requests where the
stateparameter is a simple string (e.g., "test", "123") instead of a random nonce. - Requests to
/auth/oidc/callbackwithout a preceding/auth/oidc/loginrequest from the same session.
- Multiple OIDC callback requests from different IPs with the same
Host IOCs:
- Leantime logs showing
verifyState()returningtruefor all requests. - Session files containing OIDC state values that are not random.
- Leantime logs showing
Log Examples:
[2026-01-15 10:00:00] OIDC callback received: state=abc123, code=xyz789, IP=192.168.1.100 [2026-01-15 10:00:01] OIDC callback received: state=abc123, code=xyz789, IP=10.0.0.50
Detection Engineering
YARA Rule
rule Detect_Leantime_OIDC_CSRF {
meta:
description = "Detects vulnerable Leantime OIDC verifyState() method"
author = "Valters IT"
date = "2026-01-15"
cve = "CVE-2026-59713"
strings:
$vuln_func = "public function verifyState()" nocase
$return_true = "return true;" nocase
condition:
$vuln_func and $return_true
}
Sigma Rule
title: Leantime OIDC CSRF - Suspicious Callback Requests
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects multiple OIDC callback requests with the same state parameter, indicating a CSRF attack.
author: Valters IT
date: 2026-01-15
logsource:
category: webserver
product: apache
service: access_log
detection:
selection:
cs-method: GET
cs-uri-stem: /auth/oidc/callback
cs-uri-query|contains: state=
condition: selection
timeframe: 5m
aggregation:
field: cs-uri-query
condition: gt: 5
falsepositives:
- Legitimate users with the same state (unlikely)
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (
msg:"CVE-2026-59713 Leantime OIDC CSRF - Suspicious Callback";
flow:to_server,established;
content:"/auth/oidc/callback";
http_uri;
content:"state=";
http_uri;
pcre:"/state=[a-zA-Z0-9]{1,10}/";
classtype:attempted-user;
sid:1000001;
rev:1;
)
Remediation & Mitigation
Immediate Actions:
- Disable OIDC login in Leantime until a patch is available.
- Apply a virtual patch using a WAF to block callback requests where the
stateparameter is not a valid UUID (e.g.,/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i). - Monitor logs for suspicious OIDC callback activity.
Long-term Fix:
- Patch the code by modifying
verifyState()to compare thestateparameter with the session-stored value usinghash_equals(). - Update Leantime to the latest version once a patch is released.
- Implement CSRF tokens for all authentication flows.
Workaround:
- If OIDC is required, configure the identity provider to only accept callbacks from trusted IPs or enforce PKCE (Proof Key for Code Exchange) which mitigates CSRF in OIDC flows.
Valters IT
Securing Digital Frontiers