CVE-2025-66307 Overview
CVE-2025-66307 is an information disclosure vulnerability in the Grav admin plugin, an HTML-based interface used to configure Grav CMS and manage pages. The flaw resides in the Forgot Password functionality exposed at /admin/forgot. The endpoint returns distinct server responses depending on whether a submitted username exists, and it discloses the associated email address in certain cases. Attackers can enumerate valid accounts and harvest email addresses without authentication. The disclosed data supports targeted follow-on attacks including password spraying, phishing, and social engineering. The vulnerability is fixed in version 1.11.0-beta.1 [CWE-204: Observable Response Discrepancy].
Critical Impact
Unauthenticated remote attackers can enumerate valid Grav administrator usernames and disclose their associated email addresses through response differences in the password reset flow.
Affected Products
- Getgrav grav-plugin-admin versions prior to 1.11.0-beta.1
- Grav CMS installations exposing the /admin/forgot endpoint
- Any Grav deployment relying on the vulnerable admin plugin login controller
Discovery Timeline
- 2025-12-01 - CVE-2025-66307 published to the National Vulnerability Database
- 2026-06-17 - Last updated in NVD database
Technical Details for CVE-2025-66307
Vulnerability Analysis
The vulnerability is an Observable Response Discrepancy in the Grav admin plugin login controller. When a user submits a username or email to /admin/forgot, the application returns different response messages based on whether the account exists and whether it is currently rate-limited. One error path, triggered when the account exists but has exceeded the reset interval, embedded the target email address ($to) directly into the returned message. This behavior confirms account existence and leaks the associated email address to any unauthenticated requester.
Root Cause
The root cause is verbose, state-dependent error messaging in classes/plugin/Controllers/Login/LoginController.php. The translation key PLUGIN_LOGIN.FORGOT_CANNOT_RESET_IT_IS_BLOCKED accepted the account's email address as a parameter and rendered it in the user-facing response. Combined with distinct responses for valid versus invalid users, this design violated the principle of uniform authentication responses.
Attack Vector
An unauthenticated attacker sends crafted POST requests to /admin/forgot with candidate usernames or email addresses. By comparing response strings, status codes, and rate-limiting messages, the attacker classifies each input as valid or invalid. Repeated rate-limit responses reveal the associated email in the message body. The attack requires only network access to the Grav admin interface and no user interaction.
$interval = $config->get('plugins.login.max_pw_resets_interval', 2);
- $this->setMessage($this->translate('PLUGIN_LOGIN.FORGOT_CANNOT_RESET_IT_IS_BLOCKED', $to, $interval), 'error');
+ // Security: Use generic message to prevent email enumeration (GHSA-q3qx-cp62-f6m7)
+ $this->setMessage($this->translate('PLUGIN_ADMIN.FORGOT_CANNOT_RESET_RATE_LIMITED', $interval), 'error');
return $this->createRedirectResponse($current);
}
Source: Grav admin plugin patch commit 99f6532. The patch removes the $to (email) argument from the translated message and switches to a generic rate-limit string, eliminating the disclosure.
Detection Methods for CVE-2025-66307
Indicators of Compromise
- High volumes of POST requests to /admin/forgot originating from a single IP or narrow IP range within a short window.
- Sequential submissions of common usernames (admin, root, editor) or dictionary-based email addresses against the endpoint.
- Server responses containing PLUGIN_LOGIN.FORGOT_CANNOT_RESET_IT_IS_BLOCKED or rendered email addresses returned to unauthenticated clients.
- Unexpected spikes in plugins.login.max_pw_resets_interval rate-limit triggers in application logs.
Detection Strategies
- Deploy WAF rules that alert on repeated /admin/forgot submissions exceeding a configurable threshold per source IP.
- Parse Grav application logs for successive forgot password events referencing multiple distinct usernames from the same session or IP.
- Correlate /admin/forgot traffic with subsequent phishing or credential-stuffing attempts against the same accounts.
Monitoring Recommendations
- Enable verbose logging on the Grav admin plugin and forward events to a centralized log platform for retention and analysis.
- Baseline normal password reset volumes per Grav instance and alert on statistical deviations.
- Monitor outbound password reset emails for unusual bursts that may indicate active enumeration attempts.
How to Mitigate CVE-2025-66307
Immediate Actions Required
- Upgrade the grav-plugin-admin package to version 1.11.0-beta.1 or later on all Grav installations.
- Restrict access to /admin and /admin/forgot to trusted networks or VPN clients while patching is scheduled.
- Enforce rate limiting and IP throttling on password reset endpoints at the reverse proxy or WAF layer.
- Review recent web server logs for suspicious enumeration patterns and rotate any exposed admin email addresses where feasible.
Patch Information
The vendor released a fix in commit 99f653296504f1d6408510dd2f6f20a45a26f9b0, included in grav-plugin-admin1.11.0-beta.1. The patch replaces the email-bearing error message with a generic rate-limit response and unifies responses across valid and invalid users. Full details are published in the GitHub Security Advisory GHSA-q3qx-cp62-f6m7 and the upstream patch commit.
Workarounds
- Place the Grav admin interface behind an authenticated reverse proxy or IP allowlist until the patched plugin is deployed.
- Configure aggressive rate limits on /admin/forgot to slow enumeration attempts by unauthenticated clients.
- Customize the PLUGIN_LOGIN.FORGOT_CANNOT_RESET_IT_IS_BLOCKED translation string locally to remove the email placeholder as an interim measure.
# Nginx rate-limiting example for the vulnerable endpoint
limit_req_zone $binary_remote_addr zone=grav_forgot:10m rate=5r/m;
server {
location = /admin/forgot {
limit_req zone=grav_forgot burst=2 nodelay;
allow 10.0.0.0/8;
deny all;
proxy_pass http://grav_backend;
}
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

