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

CVE-2026-55739: Crater Authentication Bypass Vulnerability

CVE-2026-55739 is an authentication bypass flaw in Crater that allows users from one company to access, modify, or delete customer records from other companies. This article covers technical details, impact, and mitigation.

Published:

CVE-2026-55739 Overview

CVE-2026-55739 is a broken access control vulnerability in Crater, an open-source invoicing platform that isolates data per company_id. The flaw resides in CustomerPolicy view, update, and delete methods, which omit the required company-ownership check and rely only on a blanket Bouncer ability check. Route-model-bound customer lookups and the bulk Customer::deleteCustomers() method also use unscoped self::find($id) queries. Any authenticated user of one company can read, reassign, or delete another company's customer records. Deletion cascades to the affected customer's invoices and payments, causing data destruction across tenants.

Critical Impact

Authenticated tenants can access, hijack, or delete customer records belonging to other companies, cascading into loss of invoices and payment data.

Affected Products

  • Crater (open-source invoicing application)
  • CustomerPolicy view/update/delete methods
  • Customer::deleteCustomers() bulk deletion method

Discovery Timeline

  • 2026-08-05 - CVE-2026-55739 published to NVD
  • 2026-08-05 - Last updated in NVD database

Technical Details for CVE-2026-55739

Vulnerability Analysis

Crater implements multi-tenant data isolation through a company_id column on tenant-owned models. Its InvoicePolicy, EstimatePolicy, PaymentPolicy, and ExpensePolicy correctly enforce two conditions: a Bouncer ability check and a $user->hasCompany($model->company_id) ownership check. CustomerPolicy deviates from this pattern. The view, update, and delete methods evaluate only the ability check, skipping the ownership verification. This gap classifies the issue as an Authorization Bypass Through User-Controlled Key [CWE-639].

Route-model binding in the customer controller compounds the flaw. Lookups execute self::find($id) without a where('company_id', ...) scope. The bulk Customer::deleteCustomers() method behaves the same way, iterating over caller-supplied identifiers with no tenant filter. Any authenticated user who holds the generic customer ability in their own company can operate on customer records belonging to any other company.

Root Cause

The root cause is a missing tenant-scoping check inside CustomerPolicy and the customer lookup functions. The policy enforces "can this user act on customers?" but not "does this customer belong to this user's company?" Because find($id) accepts any primary key, an attacker chooses arbitrary customer IDs and bypasses tenant isolation entirely.

Attack Vector

An authenticated low-privilege user in Company A sends standard API requests referencing customer IDs owned by Company B. The controller resolves the model without a company filter, the policy approves the action, and the request succeeds. Attackers can read customer PII, reassign customers to their own company, or invoke the bulk delete endpoint. Deletes cascade to associated invoices and payments, resulting in cross-tenant data destruction.

See the Crater GitHub repository for source code and issue tracker details.

Detection Methods for CVE-2026-55739

Indicators of Compromise

  • Application logs showing customer read, update, or delete actions where the acting user's company_id differs from the target customer's company_id.
  • Sudden disappearance of customer, invoice, or payment records without a corresponding administrative action.
  • Requests to /customers/{id}, /customers/delete, or bulk delete endpoints referencing IDs outside the authenticated user's tenant range.

Detection Strategies

  • Instrument CustomerPolicy and the customer controller to log every action with both the authenticated user's company and the target record's company, then alert on mismatches.
  • Run database audits comparing customers.company_id against the acting user's tenant at request time via middleware or query listeners.
  • Correlate spikes in cascading invoice and payment deletions with the originating user account and source IP.

Monitoring Recommendations

  • Ingest Crater application and web server logs into a centralized SIEM and alert on cross-tenant object access patterns.
  • Track failed and successful bulk customer delete operations, particularly those affecting large ID ranges.
  • Baseline per-tenant API call volume and flag users touching records outside their historical company_id scope.

How to Mitigate CVE-2026-55739

Immediate Actions Required

  • Upgrade Crater to a patched release once the maintainers publish a fix that adds the hasCompany check to CustomerPolicy.
  • Audit customers, invoices, and payments tables for records missing or mismatched against expected company_id values.
  • Review recent application logs for cross-tenant customer access and rotate credentials for accounts implicated in suspicious activity.

Patch Information

Monitor the Crater GitHub repository for the security release addressing CVE-2026-55739. The fix must add $user->hasCompany($model->company_id) to the view, update, and delete methods of CustomerPolicy and scope customer lookups with a where('company_id', $user->company_id) clause in both route-model binding and Customer::deleteCustomers().

Workarounds

  • Add a global Eloquent query scope on the Customer model that filters by the authenticated user's company_id on every query.
  • Wrap the customer controller actions in middleware that rejects requests where the resolved customer's company_id does not match the caller's tenant.
  • Restrict the customer management ability in Bouncer to trusted administrators until the policy fix is deployed.
bash
# Example Laravel global scope enforcing tenant isolation on Customer queries
php artisan make:scope CompanyScope
# In app/Models/Customer.php boot() method:
#   static::addGlobalScope(new CompanyScope);
# CompanyScope::apply() should call:
#   $builder->where('company_id', auth()->user()->company_id);

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.