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

CVE-2026-46691: EspoCRM Authorization Bypass Vulnerability

CVE-2026-46691 is an authorization bypass flaw in EspoCRM that allows authenticated users to access import data from other users. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-46691 Overview

CVE-2026-46691 is a missing authorization vulnerability [CWE-862] in EspoCRM, an open source customer relationship management application. The flaw affects versions prior to 9.3.7. An authenticated user with Import scope access can call POST /api/v1/Import/{id}/exportErrors using another user's import identifier. The endpoint returns an attachment identifier for a CSV containing rows from that import. The vulnerability stems from a missing entity-level ownership check that sibling Import endpoints and the Import AccessChecker normally enforce. Version 9.3.7 remediates the issue.

Critical Impact

Authenticated low-privilege users can retrieve CSV rows from other users' imports, exposing confidential CRM data such as customer records, contacts, and lead information.

Affected Products

  • EspoCRM versions prior to 9.3.7
  • EspoCRM instances exposing the Import API to authenticated users
  • Deployments granting Import scope access to non-owner accounts

Discovery Timeline

  • 2026-10-15 - CVE-2026-46691 published to NVD
  • 2026-10-15 - Last updated in NVD database

Technical Details for CVE-2026-46691

Vulnerability Analysis

The vulnerability resides in the Import service method exportErrors, defined in application/Espo/Tools/Import/Service.php. The endpoint POST /api/v1/Import/{id}/exportErrors accepts an import identifier and returns an attachment identifier referencing a CSV of error rows from that import. EspoCRM enforces two authorization layers: scope-level checks that validate whether a role can access an entity type, and entity-level ownership checks that validate whether a user owns or is granted access to a specific record. The exportErrors endpoint performed only the scope-level check.

Root Cause

The original implementation loaded the Import entity directly via entityManager->getRepositoryByClass(ImportEntity::class)->getById($importId). This bypassed the Import AccessChecker logic used by sibling endpoints. Any authenticated user with Import scope access could pass an arbitrary importId belonging to another user and receive a valid attachment identifier for the error CSV.

Attack Vector

An attacker needs valid credentials and Import scope access in their assigned role. The attacker enumerates or obtains import identifiers belonging to other users and issues POST /api/v1/Import/{id}/exportErrors. The response contains an attachment ID that can then be downloaded through the standard attachment endpoint, exposing CSV content from the victim's import job.

php
// Patched exportErrors method - application/Espo/Tools/Import/Service.php
 * @param string $importId An import ID.
 * @return ?string An attachment ID.
 * @throws NotFound
+ * @throws Forbidden
 */
public function exportErrors(string $importId): ?string
{
-    $import = $this->entityManager
-        ->getRepositoryByClass(ImportEntity::class)
-        ->getById($importId);
-
-    if (!$import) {
-        throw new NotFound();
-    }
+    $import = $this->getImport($importId);

    $count = $this->entityManager
        ->getRDBRepositoryByClass(ImportEntity::class)

Source: GitHub Commit 71ad766. The patch replaces the direct repository fetch with getImport($importId), which invokes the Import AccessChecker and throws Forbidden when the caller lacks entity-level ownership.

Detect Identity Attacks the Moment Authentication Fails

Singularity™ Identity surfaces credential misuse, lateral movement, and privilege escalation in real time, so you can contain the breach before it spreads.

Stop Identity Attacks

Detection Methods for CVE-2026-46691

Indicators of Compromise

  • Requests to POST /api/v1/Import/{id}/exportErrors where the authenticated user is not the owner of the referenced Import record.
  • Sequential or enumerated access to multiple distinct Import identifiers from a single user session within a short window.
  • Attachment download requests immediately following exportErrors responses that reference imports owned by other users.

Detection Strategies

  • Correlate web server access logs for /api/v1/Import/*/exportErrors with the EspoCRM database to identify requests where the import's createdById does not match the authenticated user.
  • Alert on any user account issuing exportErrors requests against more than a small threshold of distinct import IDs per hour.
  • Review AuthLogRecord and audit tables for Import entity reads that lack corresponding ownership relationships.

Monitoring Recommendations

  • Enable verbose HTTP access logging for the /api/v1/Import/ route and retain logs for forensic review.
  • Monitor attachment download activity for CSV files generated by Import operations and correlate against the originating user.
  • Track role and permission changes that grant Import scope access to additional users.

How to Mitigate CVE-2026-46691

Immediate Actions Required

  • Upgrade EspoCRM to version 9.3.7 or later, which enforces the entity-level ownership check in exportErrors.
  • Audit role assignments and remove Import scope access from users who do not require it.
  • Review historical access logs for exportErrors calls to identify potential prior exposure of import data.

Patch Information

The fix is included in EspoCRM 9.3.7, published via the GitHub Release 9.3.7. The code change is documented in the GitHub Commit 71ad766 and the GitHub Security Advisory GHSA-rrcj-rhc2-ch5q. The patch replaces the direct entity fetch with the getImport() helper, which routes the lookup through the Import AccessChecker.

Workarounds

  • Restrict Import scope access to administrators until the upgrade to 9.3.7 is deployed.
  • Place the EspoCRM API behind a reverse proxy that blocks POST requests to /api/v1/Import/{id}/exportErrors for non-administrative users.
  • Rotate any sensitive data potentially exposed via prior error exports and notify affected record owners.
bash
# Verify installed EspoCRM version and upgrade
cat /path/to/espocrm/application/Espo/Core/Application.php | grep -i version

# Example upgrade via composer/release package
wget https://github.com/espocrm/espocrm/releases/download/9.3.7/EspoCRM-9.3.7.zip
unzip EspoCRM-9.3.7.zip -d /tmp/espocrm-9.3.7
# Follow the official EspoCRM upgrade procedure before replacing files

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

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.