CVE-2026-53654 Overview
CVE-2026-53654 is an open redirect vulnerability [CWE-601] in the Grav Login plugin affecting versions prior to 3.8.5. The twofa_cancel task accepts a client-controlled _redirect field without validating a nonce. An unauthenticated attacker can supply an external http, https, or protocol-relative URL as the redirect target. Controller::execute() applies the field when taskTwofa_cancel() sets no redirect, and Grav::getRedirectResponse() accepts the target through Uri::isExternal(). The result: attackers can craft links on a trusted Grav host that redirect victims to attacker-controlled phishing sites.
Critical Impact
Unauthenticated attackers can leverage a trusted Grav domain to redirect users to arbitrary external URLs, enabling credential phishing and malware distribution campaigns.
Affected Products
- Grav CMS Login plugin versions prior to 3.8.5
- Grav file-based Web platform deployments using the Login plugin
- Sites relying on the plugin's two-factor authentication (2FA) flow
Discovery Timeline
- 2026-08-19 - CVE-2026-53654 published to NVD
- 2026-08-19 - Last updated in NVD database
Technical Details for CVE-2026-53654
Vulnerability Analysis
The Grav Login plugin exposes a twofa_cancel task used to abort an in-progress two-factor authentication challenge. Prior to version 3.8.5, this task did not verify the login-form nonce that guards other login tasks. The handler processes a _redirect POST field supplied by the client without validating its origin. When taskTwofa_cancel() completes without explicitly setting a redirect, Controller::execute() falls back to the client-supplied value. Grav then issues an HTTP 303 response pointing to the attacker-controlled target. Because the redirect originates from a legitimate Grav host, users and security tools are more likely to trust it. The classification aligns with URL Redirection to Untrusted Site [CWE-601].
Root Cause
Two control gaps combine to produce the flaw. First, twofa_cancel was omitted from the nonce verification switch in login.php, allowing unauthenticated invocation. Second, the fallback redirect logic in classes/Controller.php accepted any _redirect value, including external URLs approved by Uri::isExternal() as a routable target rather than rejected.
Attack Vector
An attacker crafts a URL or form pointing at the Grav site's login endpoint with task=twofa_cancel and _redirect=https://attacker.example/phish. The victim clicks the link on the trusted Grav domain. Grav processes the request without nonce verification and issues a 303 redirect to the attacker's page. User interaction is required, consistent with phishing scenarios.
// Patch in classes/Controller.php — reject off-site _redirect targets
$this->grav['log']->error('plugin.login: '. $e->getMessage());
}
- if (!$this->redirect && $redirect) {
+ // Never honor a client-supplied `_redirect` that points off-site. This
+ // closes the open-redirect across every login task (e.g. `twofa_cancel`,
+ // which returns without setting its own redirect), not just the one that
+ // was reported. Server-side redirects set by a task are unaffected.
+ if (!$this->redirect && $redirect && !Uri::isExternal($redirect)) {
$this->setRedirect($redirect, 303);
}
Source: Grav Login plugin commit 1535e51
// Patch in login.php — enforce nonce on twofa_cancel
return;
}
break;
+
+ case 'twofa_cancel':
+ // The 2FA form carries the `login-form` nonce, so verify it here
+ // too — `twofa_cancel` was previously unguarded, letting it run
+ // (and act on a client `_redirect`) without a nonce.
+ if (!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;
}
$controller = new Controller($this->grav, $task, $post);
Source: Grav Login plugin commit 1535e51
Detection Methods for CVE-2026-53654
Indicators of Compromise
- Requests to Grav login endpoints containing task=twofa_cancel combined with a _redirect parameter pointing to an external host or protocol-relative URL.
- HTTP 303 responses from a Grav host with a Location header targeting a domain outside the site's allow list.
- Referrer traffic in web logs showing users arriving at unknown external domains via the Grav login path.
Detection Strategies
- Inspect web server access logs for POST or GET requests to the Login plugin task endpoint where _redirect contains http://, https://, or a leading //.
- Alert on twofa_cancel invocations missing a valid login-form-nonce parameter.
- Correlate outbound redirect responses with reputation data on the destination host.
Monitoring Recommendations
- Enable verbose logging on the Grav Login plugin and forward events to a centralized log platform for correlation.
- Baseline normal 2FA cancellation volume and alert on spikes or requests from unauthenticated sessions.
- Monitor phishing intelligence feeds for lookalike domains referencing the organization's Grav-hosted site.
How to Mitigate CVE-2026-53654
Immediate Actions Required
- Upgrade the Grav Login plugin to version 3.8.5 or later on all Grav installations.
- Audit web logs for prior exploitation attempts referencing twofa_cancel with external _redirect values.
- Educate users about phishing links that appear to originate from the organization's trusted Grav domain.
Patch Information
The fix is available in Grav Login plugin 3.8.5. The patch adds Uri::isExternal($redirect) checks in Controller::execute() and adds nonce verification for the twofa_cancel task in login.php. Details are published in the GitHub Security Advisory GHSA-wwcc-xxhc-44j6 and the 3.8.5 release notes.
Workarounds
- Deploy a web application firewall (WAF) rule that blocks requests to the Login plugin endpoint when _redirect contains an absolute or protocol-relative URL.
- Restrict access to the Grav admin and login endpoints by source IP where feasible until the plugin is upgraded.
- Strip or rewrite the _redirect parameter at a reverse proxy for task=twofa_cancel requests.
# Example ModSecurity rule to block external _redirect values on twofa_cancel
SecRule ARGS:task "@streq twofa_cancel" \
"id:1005301,phase:2,chain,deny,status:400,log,msg:'CVE-2026-53654 open redirect attempt'"
SecRule ARGS:_redirect "@rx ^(https?:)?//" "t:none"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

