Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-72909

CVE-2026-72909: ERPNext Auth Bypass Vulnerability

CVE-2026-72909 is an authentication bypass flaw in ERPNext that allows authenticated users to access unauthorized cross-company financial data. This article covers technical details, affected versions, and mitigation.

Updated:

CVE-2026-72909 Overview

CVE-2026-72909 is a broken access control vulnerability [CWE-284] in ERPNext, a free and open source Enterprise Resource Planning (ERP) tool developed by Frappe. The flaw resides in the ReceivablePayableReport.prepare_conditions path within erpnext/accounts/report/accounts_receivable/accounts_receivable.py. The code fails to apply Customer and Supplier user permissions to the Payment Ledger Entry dynamic-link party field. As a result, any authenticated user can read unauthorized cross-company financial data through the Accounts Receivable and Accounts Payable reports. The issue is fixed in versions 15.112.0 and 16.23.0.

Critical Impact

Authenticated low-privilege users can enumerate confidential financial records across customers, suppliers, and companies they are not permitted to view.

Affected Products

  • Frappe ERPNext versions prior to 15.112.0 (v15 branch)
  • Frappe ERPNext versions prior to 16.23.0 (v16 branch)
  • Deployments exposing the Accounts Receivable and Accounts Payable reports to non-admin users

Discovery Timeline

  • 2026-08-10 - CVE-2026-72909 published to NVD
  • 2026-08-11 - Last updated in NVD database

Technical Details for CVE-2026-72909

Vulnerability Analysis

The ReceivablePayableReport class builds a query against the Payment Ledger Entry (PLE) doctype to produce the Accounts Receivable and Accounts Payable reports. The party field on Payment Ledger Entry is a dynamic link, meaning it references either a Customer or Supplier depending on the value of party_type. Because the field is dynamic, Frappe's standard match-condition mechanism cannot automatically inject Customer and Supplier user-permission filters. The report code did not compensate with manual permission enforcement, so restricted users receive rows for parties and companies they should not access. The result is unauthorized disclosure of ledger balances, outstanding invoices, and cross-company financial posture through a legitimate report interface [CWE-284].

Root Cause

The root cause is missing enforcement of Frappe user permissions on a dynamic link field. prepare_conditions applied filters for cost centers, projects, and accounting dimensions but never queried get_user_permissions() to constrain the party values a caller may read.

Attack Vector

An attacker only needs an authenticated ERPNext account with permission to run the Accounts Receivable or Accounts Payable report. By opening the report, the user retrieves Payment Ledger Entry rows for all parties and companies, bypassing the Customer and Supplier restrictions their role assignment implies. No special crafting or injection is required; the report itself returns the excess data.

python
# Security patch: enforce user permissions on the dynamic party field
# Source: https://github.com/frappe/erpnext/commit/b05abbc53b3655b02db17ba2e8165519f195c1c2

         if self.filters.project:
             self.qb_selection_filter.append(self.ple.project.isin(self.filters.project))

+        self.add_user_permission_filters()
+
         self.add_accounting_dimensions_filters()

+    def add_user_permission_filters(self):
+        # Party is a dynamic link, so match conditions cannot auto-apply
+        # Customer/Supplier user permissions
+        from frappe.core.doctype.user_permission.user_permission import get_user_permissions
+        from frappe.permissions import get_allowed_docs_for_doctype
+
+        user_permissions = get_user_permissions()
+        if not user_permissions:
+            return
+
+        for party_type in self.party_type:
+            if party_type not in user_permissions:
+                continue
+
+            allowed_parties = get_allowed_docs_for_doctype(
+                user_permissions[party_type], party_type
+            )
+            self.qb_selection_filter.append(
+                (self.ple.party_type != party_type)
+                | self.ple.party.isin(allowed_parties or [""])
+            )

The patch introduces add_user_permission_filters(), which resolves the caller's allowed Customer and Supplier documents and appends a query-builder clause restricting party accordingly.

Detection Methods for CVE-2026-72909

Indicators of Compromise

  • Access log entries showing non-admin users invoking the Accounts Receivable or Accounts Payable report endpoints at unusual frequency.
  • Report executions where the returned party set exceeds the caller's assigned Customer or Supplier user permissions.
  • Repeated queries against /api/method/frappe.desk.query_report.run referencing the Accounts Receivable or Accounts Payable report names from limited-scope accounts.

Detection Strategies

  • Audit Frappe activity logs and web server access logs for report executions by users whose role profile is not intended to access consolidated financial data.
  • Compare user permission assignments for Customer and Supplier doctypes against the parties returned in exported report data.
  • Review database or application logs for Payment Ledger Entry queries that lack a party restriction clause on pre-patch installations.

Monitoring Recommendations

  • Enable and centralize ERPNext application logs, including frappe.log and web server logs, in a SIEM for retention and correlation.
  • Alert on bulk exports (CSV, Excel, PDF) originating from the Accounts Receivable or Accounts Payable reports.
  • Track deviations in report row counts per user to flag accounts suddenly returning enterprise-wide financial data.

How to Mitigate CVE-2026-72909

Immediate Actions Required

  • Upgrade ERPNext to 15.112.0 or 16.23.0 (or later) on all production and staging instances.
  • Inventory user accounts with access to Accounts Receivable and Accounts Payable reports and remove report permissions where not required.
  • Review Customer and Supplier user permission assignments to confirm they match business need-to-know.

Patch Information

The fix is delivered in Frappe ERPNext Release v15.112.0 and Frappe ERPNext Release v16.23.0. The underlying code changes are tracked in pull request #55696 and commits b05abbc and c03a66a. Additional context is available in GitHub Security Advisory GHSA-p577-cxv9-h82f.

Workarounds

  • Revoke report-level access to the Accounts Receivable and Accounts Payable reports for any role that does not require full company-wide visibility until patching completes.
  • Restrict authenticated access to the ERPNext instance from untrusted networks using reverse proxy or VPN controls.
  • Monitor and rate-limit calls to frappe.desk.query_report.run for the affected report names.
bash
# Upgrade an existing bench-based ERPNext deployment to the patched release
# v15 branch
bench get-app --branch version-15 erpnext
bench update --reset
bench version | grep erpnext   # confirm >= 15.112.0

# v16 branch
bench get-app --branch version-16 erpnext
bench update --reset
bench version | grep erpnext   # confirm >= 16.23.0

# Restart services after upgrade
bench restart

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.