Sep 3, 2026·8 min read·16 visits
A logical validation flaw in the Grav Login Plugin allows an attacker with valid primary credentials to out-of-band regenerate and read the victim's 2FA secret, successfully bypassing multi-factor protection.
CVE-2026-62669 is a critical Improper Authentication vulnerability (CWE-287) in the Grav Login Plugin for Grav CMS. Prior to version 3.8.11, the plugin's key rotation task failed to verify if a user session was fully authorized before regenerating and returning two-factor authentication (2FA) secrets. Consequently, an attacker possessing a victim's primary credentials could invoke this endpoint to replace the 2FA secret, retrieve the replacement, and bypass the MFA constraint entirely.
The Grav Login Plugin is a core extension for Grav CMS designed to manage administrative access, session validation, user profiles, and security mechanisms including Multi-Factor Authentication (MFA). It acts as the primary gatekeeper for the administrative dashboard, which exposes extensive site controls. The plugin implements Time-Based One-Time Password (TOTP) verification to secure administrative accounts.
This vulnerability, classified under CWE-287 (Improper Authentication), exists within the administrative controller task responsible for regenerating 2FA secrets. When a user authenticates, the system utilizes a two-stage process: validating the primary password, followed by a secondary TOTP challenge. During this secondary stage, the user is authenticated but not yet authorized.
The attack surface is exposed because the application handles administrative task routes before confirming that the user has completed the secondary authentication step. This logical flaw allows a partially authenticated session to trigger tasks reserved for fully authorized sessions. As a result, the protective boundaries of 2FA are weakened, exposing administrative sessions to complete hijack if primary credentials are compromised.
This flaw affects getgrav/grav-plugin-login versions prior to 3.8.11, as well as getgrav/grav installations prior to version 2.0.4. Because the Grav administrative dashboard provides direct facilities for theme modification, configuration changes, and file uploads, bypassing the login sequence yields total control over the server hosting the CMS environment.
To understand the root cause, it is necessary to examine how Grav CMS structures and serializes user sessions during login. When a user submits their correct username and password, Grav instantiates a user object and stores it in the global container under $this->grav['user']. At this specific execution point, the system evaluates the helper $user->exists() as true because a valid, matching database record was found.
However, because the account has 2FA enabled, the login controller does not yet grant full authorization. The login state explicitly declares the session as unauthorized, setting $user->authorized = false and redirecting the browser to the TOTP token prompt page. The server expects the user to submit a valid six-digit token before altering this authorization state flag to true.
The core architectural failure is in classes/Controller.php within the method taskRegenerate2FASecret(). The logic verified only whether the user session existed using $user->exists(), rather than checking if the user was fully authorized using $user->authorized === true. Because the attacker's session meets the existence condition immediately after inputting the correct primary password, the controller permits access to the secret regeneration logic.
Additionally, this endpoint lacked Cross-Site Request Forgery (CSRF) protection. The application did not require a cryptographically signed transaction nonce to authorize the secret regeneration. This allowed the administrative endpoint to be reached via GET requests, enabling attackers to execute cross-site request attacks on authenticated sessions.
The code-level flaw resides in the Controller class of the Grav Login Plugin. In vulnerable versions, the method handling 2FA secret regeneration was implemented as follows:
// Vulnerable Implementation in classes/Controller.php
public function taskRegenerate2FASecret()
{
/** @var UserInterface $user */
$user = $this->grav['user'];
if ($user->exists()) {
/** @var TwoFactorAuth $twoFa */
$twoFa = $this->grav['login']->twoFactorAuth();
$secret = $twoFa->createSecret();
// ... saves the secret to user configuration and outputs JSONThe patched version modifies this evaluation gate to strictly check both existence and session authorization state:
// Patched Implementation in classes/Controller.php
public function taskRegenerate2FASecret()
{
/** @var UserInterface $user */
$user = $this->grav['user'];
// Require a fully authorized session, not merely an existing one.
// Gating on exists() alone let an unauthorized attacker overwrite and read the secret.
if ($user->exists() && $user->authorized === true) {
/** @var TwoFactorAuth $twoFa */
$twoFa = $this->grav['login']->twoFactorAuth();
$secret = $twoFa->createSecret();
// ...Furthermore, the patch hardened the routing flow in login.php to prevent CSRF exploitation. It enforces that regenerate2FASecret requests must use the HTTP POST method and include a valid security nonce:
// Hardening routing gate in login.php
case 'regenerate2FASecret':
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST'
|| !isset($post['login-form-nonce'])
|| !Utils::verifyNonce($post['login-form-nonce'], 'login-form')) {
$this->grav['messages']->add($this->grav['language']->translate('PLUGIN_LOGIN.ACCESS_DENIED'), 'info');
return;
}
break;In the frontend template 2fa_secret.html.twig, a CSRF token helper was injected to supply the required validation parameter:
{# Injection in templates/forms/fields/2fa_secret/2fa_secret.html.twig #}
<div class="danger twofa-wrapper">
<button class="button button-small" data-2fa-regenerate><i class="fa fa-refresh"></i> Regenerate</button>
{{ nonce_field('login-form', 'login-form-nonce')|raw }}
</div>This multi-layered patch ensures that the secret-rotation action is inaccessible during the incomplete, pre-authorization login phase and blocks any unauthorized state transitions.
Exploiting this flaw requires that the attacker has already obtained the victim's correct primary credentials (username and password). The attack does not bypass the initial password verification phase. Instead, it targets the subsequent step where the system relies on TOTP to prevent unauthorized login attempts from actors who have acquired the password.
First, the attacker authenticates using the known credentials, which causes the target server to initialize a session cookie and prompt for the 2FA token. The attacker extracts this session cookie from the HTTP response headers. At this point, the session state satisfies the $user->exists() condition but does not have the authorized flag set.
Next, the attacker submits an out-of-band POST request targeting /admin/task:login.regenerate2FASecret, appending the captured session cookie. Because the application evaluates only the existence of the session user, it processes the request, overwrites the user's authenticating secret in the database, and returns the new base32 secret inside the JSON response body:
POST /admin/task:login.regenerate2FASecret HTTP/1.1
Host: target-grav.local
Cookie: grav-site-xxxx=attacker_captured_cookie
Content-Type: application/x-www-form-urlencoded
login-form-nonce=dummy_or_ignoredResponse:
{
"status": "success",
"secret": "KVKVE43VNZSXIYTM",
"image": "data:image/png;base64,..."
}The attacker inputs the extracted secret (KVKVE43VNZSXIYTM) into a local generator tool to calculate the active six-digit token. Finally, the attacker submits this token to the standard 2FA verification page, satisfying the challenge and gaining full control of the administrative panel.
The technical impact of CVE-2026-62669 is highly severe. It allows a complete bypass of multi-factor authentication, which is often deployed as a primary control to protect highly privileged administrative accounts. Bypassing this control results in an immediate breakdown of the zero-trust boundary surrounding administrative access.
Once inside the Grav CMS administrative interface, the attacker has unrestricted administrative rights. The administrative panel provides functionality to edit page content, change site-wide configurations, create new high-privilege users, and modify installed extensions. Attackers can leverage these features to execute arbitrary code on the underlying host, typically by uploading malicious PHP webshells or modifying active theme templates to execute system commands.
The CVSS v3.1 base score of 7.4 reflects high confidentiality and integrity impact. The Attack Complexity is classified as High because the exploit cannot be performed entirely blindly; the attacker must already possess the victim's correct primary authentication credentials. No user interaction or special privileges are required.
While there is no evidence of active, automated exploitation in the wild, the simple logic required to execute this bypass makes it highly attractive for targeted attacks or persistent threat campaigns. The exposure of Grav administrative interfaces on the public internet makes immediate remediation critical.
The primary remediation for this vulnerability is upgrading the Grav Login Plugin to version 3.8.11 or later. Organizations running Grav CMS must also ensure the core package is updated to version 2.0.4 or higher to ensure full compatibility with the patched authentication and session-handling methods.
If immediate patching is not feasible, temporary mitigation can be achieved at the network layer. Security teams should deploy Web Application Firewall (WAF) or reverse-proxy rules to block any incoming HTTP requests that contain the path pattern task:login.regenerate2FASecret or /task/login.regenerate2FASecret. This path should be restricted strictly to trusted administrative IP addresses.
Detection should focus on monitoring web server logs for anomalous administrative traffic. Search access logs for any POST requests targeting the task:login.regenerate2FASecret endpoint that do not originate from the expected administrative profile configuration sub-paths (e.g., /admin/user/). Requests hitting this endpoint immediately following a login attempt indicate an exploit attempt.
Furthermore, system administrators should audit configuration files for unauthorized modifications. Periodically verify user configuration files under user/config/accounts/ to confirm that the twofa_secret fields have not been modified unexpectedly. Any sudden desynchronization of user authenticators should be flagged and investigated immediately as a potential account takeover incident.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
grav-plugin-login getgrav | < 3.8.11 | 3.8.11 |
grav getgrav | < 2.0.4 | 2.0.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-287 |
| Attack Vector | Network |
| CVSS Score | 7.4 (High) |
| EPSS Score | 0.00386 |
| Impact | Two-Factor Authentication Bypass |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
The software does not prove, or insufficiently proves, that a user is who they claim to be.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.
ASP.NET Core contains a thread deadlock vulnerability (CWE-833) that allows an unauthenticated remote attacker to cause a denial of service over the network by exhausting server thread pool resources.
CVE-2026-70035 is a high-severity Remote Code Execution (RCE) vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio caused by improper handling during the deserialization of untrusted data (CWE-502). An unauthenticated attacker can execute arbitrary code within the context of the affected process by delivering a crafted serialized payload.