Skip to main content

Free 30-min security demo Book Now

Offensive360 Offensive360
ZeroDays CVE-2026-66416
High CVE-2026-66416 CVSS 8.8 Leantime PHP

CSRF Protection Globally Disabled in Leantime

CVE-2026-66416: Leantime 3.6.2 omits Laravel's VerifyCsrfToken middleware globally, enabling unauthenticated attackers to forge state-changing requests as any authenticated user.

Offensive360 Research Team
Affects: <= 3.6.2
Source Code View Patch

Overview

CVE-2026-66416 is a cross-site request forgery (CSRF) vulnerability affecting Leantime 3.6.2, an open-source project management platform built on the Laravel framework. The flaw stems from the deliberate or inadvertent omission of Laravel’s VerifyCsrfToken middleware from the global HTTP kernel middleware stack, meaning that every state-mutating route in the application accepts requests without validating the accompanying CSRF token. Any authenticated session can be silently abused by a third-party page the victim merely visits.

The vulnerability was identified by security researchers and disclosed through a GitHub Security Advisory. Because Leantime is commonly self-hosted by small-to-medium engineering teams and project management offices — many of whom route internal task data, sprint planning, and role assignments through it — the blast radius extends beyond a single user account and into the organizational trust model of the platform itself.

With a CVSS 3.1 base score of 8.8 (High), the rating reflects the combination of no authentication requirement on the attacker’s side, network-level reachability, and the breadth of actions that can be performed: creating and deleting projects, changing user permissions, and altering application settings. Any organization running an internet-exposed or intranet-accessible Leantime instance below the patched version should treat this as an urgent remediation item.

Technical Analysis

Laravel’s CSRF protection works by issuing a per-session token stored in the session store and as a cookie. On every non-idempotent request (POST, PUT, PATCH, DELETE), the VerifyCsrfToken middleware compares the token embedded in the request (via the _token field or the X-CSRF-TOKEN header) against the session value. If they do not match, the request is rejected with a 419 Page Expired response.

In a standard Laravel application the kernel wires this middleware into the web middleware group inside app/Http/Kernel.php:

// Standard Laravel app/Http/Kernel.php — CORRECT configuration
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,   // ← must be present
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
    'api' => [
        'throttle:api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

In Leantime 3.6.2 the VerifyCsrfToken entry is missing from both the global middleware array and the web group:

// Leantime 3.6.2 app/Http/Kernel.php — VULNERABLE configuration
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        // VerifyCsrfToken is absent — all POST/PUT/DELETE routes are unprotected
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

Because the middleware is never registered anywhere in the stack, the Laravel request lifecycle never performs token validation. Every route that relies on the web group — which covers the authenticated project management surfaces — accepts cross-origin POST requests without question. Cookies are sent automatically by the browser due to the same-origin cookie policy, so the server cannot distinguish a legitimate user action from one triggered by a malicious third-party page.

An attacker can craft a minimal proof-of-concept HTML page that auto-submits a form targeting a Leantime endpoint:

<!-- Attacker-controlled page hosted at https://evil.example.com -->
<html>
<body onload="document.forms[0].submit()">
  <form method="POST" action="https://leantime.target.org/projects/create">
    <input type="hidden" name="name"        value="Attacker-Injected Project" />
    <input type="hidden" name="clientId"    value="1" />
    <input type="hidden" name="description" value="Exfil staging area" />
  </form>
</body>
</html>

When a logged-in Leantime user visits this page — via a phishing email link, a malicious advertisement, or a compromised third-party site — the browser immediately sends a cross-origin POST with the victim’s session cookie attached. The server processes the request as fully legitimate.

Impact

The practical impact is wide. Because CSRF protection is absent at the framework level rather than on a subset of routes, every authenticated POST, PUT, and DELETE endpoint is exposed simultaneously. Attackers can:

  • Create or delete projects and milestones — disrupting sprint planning and destroying tracked work.
  • Modify role assignments and permissions — escalating a low-privilege account to administrator or stripping permissions from existing admins.
  • Change global application settings — altering notification targets, integration credentials, or SMTP configuration to redirect sensitive communications.
  • Delete user accounts — causing denial-of-service at the application layer for targeted users.

No exploitation infrastructure beyond a web server hosting a malicious HTML page is needed. The attack requires only that the victim be authenticated to Leantime and visit an attacker-controlled URL, conditions that are trivially achievable through phishing. The CVSS vector AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H captures this well: network-accessible, low complexity, no privilege required on the attacker’s side, single user interaction.

How to Fix It

Upgrade to the patched version. Pull request #3659 re-introduces VerifyCsrfToken into the middleware stack. Upgrade immediately:

composer require leantime/leantime:^3.6.3
# or, if running from source:
git pull origin main
composer install --no-dev --optimize-autoloader

Verify the middleware is registered after upgrading by inspecting app/Http/Kernel.php:

// app/Http/Kernel.php — FIXED configuration
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,   // ← restored
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

If an immediate upgrade is not possible, apply a compensating control by enforcing SameSite=Strict on the session cookie in config/session.php. This prevents the browser from attaching the session cookie to cross-site requests entirely:

// config/session.php
'same_site' => 'strict',

Note that SameSite=Strict is a defense-in-depth measure and not a full replacement for synchronizer-token CSRF protection, particularly in environments where top-level navigations must carry cookies.

Our Take

This vulnerability is a textbook example of how framework security primitives fail silently. Laravel ships with CSRF protection enabled by default — the VerifyCsrfToken middleware is part of every laravel new scaffold. For it to be absent in a production codebase almost certainly means it was removed deliberately at some point, possibly to work around a development inconvenience (API clients, legacy integrations, or test harnesses that did not handle CSRF tokens), and the removal was never reverted before shipping.

This pattern recurs across web frameworks. Developers disable a security control to solve an immediate problem, the disable lands in the default branch, and it ships. The underlying issue is the lack of a policy-level check asserting that mandatory security middleware must be present. No code review catches it unless the reviewer is specifically auditing the kernel configuration.

For enterprises deploying self-hosted project management tooling, the lesson is that “built on a secure framework” does not mean “securely configured.” Framework defaults can be overridden at any layer of the application stack.

Detection with SAST

This vulnerability class falls under CWE-352: Cross-Site Request Forgery. A SAST engine targeting Laravel applications should flag it by performing data-flow and configuration analysis on app/Http/Kernel.php:

  • Middleware inventory check: Parse the $middlewareGroups and $middleware arrays and assert that a class resolving to VerifyCsrfToken (or an equivalent custom implementation) is present in the web group. Absence of the class reference is a direct finding at HIGH severity.
  • Route-level exception audit: Scan for the $except array in any VerifyCsrfToken subclass. Overly broad wildcard patterns (e.g., '*' or '/*') that exempt all routes should be flagged as a medium-severity misconfiguration even when the middleware is nominally present.
  • Dead middleware detection: Identify middleware classes that are defined but never referenced in Kernel.php — a broader pattern that surfaces security controls that exist in the codebase but are not wired into the request lifecycle.

Offensive360’s engine models the Laravel middleware stack as a call graph and traces route resolution through the kernel, making the absence of mandatory security nodes in the graph an explicit finding rather than a heuristic.

References

#CSRF #Laravel #Middleware #Authentication Bypass

Detect this vulnerability class in your codebase

Offensive360 SAST scans your source code for CVE-2026-66416-class vulnerabilities and thousands of other patterns — across 60+ languages.