CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-71537

CVE-2026-71537: Credit-Refund Double-Spend Race Condition in Paymenter Service Downgrade

Amit Schendel
Amit Schendel
Senior Security Researcher

Sep 19, 2026·9 min read·5 visits

Executive Summary (TL;DR)

A race condition in Paymenter allows authenticated users to trigger multiple credit refunds for a single service downgrade by making synchronized, concurrent requests to the Livewire downgrade component. This occurs due to lack of row locking during state validation.

A concurrent execution vulnerability (CWE-362) exists in the Paymenter webshop solution within the service downgrade execution path (doUpgrade). Authenticated customers can exploit this concurrency issue by sending concurrent HTTP requests to trigger multiple parallel executions of the refund process. Because the application checks for pending upgrades without database transactional isolation or exclusive row locks, attackers can generate multiple duplicate refunds to their account balance for a single downgrade action. This leads to arbitrary credit inflation on the platform.

Vulnerability Overview

Paymenter is an open-source webshop and management solution designed specifically for hosting services. It features an automated service upgrade and downgrade system handled by Livewire components in the frontend and Laravel controllers in the backend. When a customer elects to downgrade their active hosting package to a lower-cost tier or a different configuration, the application calculates the pro-rata value of the remaining billing cycle. If this calculation results in a negative price differential, the platform is designed to refund the variance to the user's account wallet as credits, provided the configuration setting settings.credits_on_downgrade is enabled.

This functionality introduces a critical attack surface because it deals directly with the manipulation and generation of currency value within the platform database. The logic governing this process is located in the doUpgrade method of the app/Livewire/Services/Upgrade.php component. Prior to version 1.5.7, this logic was executed in an asynchronous, non-transactional environment without exclusive database row locks. As a result, the application was susceptible to concurrency exploits, allowing a customer to manipulate their account credits by sending overlapping HTTP requests.

The vulnerability is classified under CWE-362 (Concurrent Execution using Shared Resource with Improper Synchronization) and specifically represents a Time-of-Check to Time-of-Use (TOCTOU) flaw. When multiple threads process concurrent downgrade actions for the same service, they check the database state simultaneously. Because no lock is placed on the parent service record or the related pending upgrade records, multiple threads see the downgrade as valid and proceed to increment the customer balance multiple times. This results in direct unauthorized manipulation of the application's credit balances.

Root Cause Analysis

The primary root cause of this vulnerability lies in the non-atomic "Check-Then-Act" programming pattern implemented in the original doUpgrade function. Under a standard PHP-FPM and web-server model, HTTP requests are handled concurrently across multiple separate process threads. When a user triggers a downgrade, the application performs a series of database queries to validate the request before updating the state. This verification process must be atomic to ensure that once a check is passed, no other thread can execute the same logic on the same resource.

In the vulnerable implementation, the application first loaded the service model and verified whether there were any active, pending upgrades associated with that service ID. If the database returned no pending upgrade records, the application assumed the request was safe and proceeded. It then saved a new ServiceUpgrade record to the database, calculated the refund amount, and incremented the customer's wallet balance using Eloquent's increment method. However, because these steps occurred outside of a database transaction and lacked pessimistic locking, the state of the database was not secured during the read-to-write interval.

When an attacker sends multiple requests concurrently, the following race sequence occurs. Request A and Request B reach the server at almost the same millisecond. Request A queries the database for pending upgrades and finds none. Request B then performs the exact same query and also finds none, as Request A has not yet written its transaction to the database. Both requests validate successfully. Request A inserts a ServiceUpgrade record and increments the user's wallet. Immediately after, Request B inserts a duplicate ServiceUpgrade record and increments the user's wallet again. The application fails to detect that the downgrade operation has already been executed, resulting in a double-refund scenario.

Code-Level Analysis and Patch Review

Analyzing the patch implemented in commit a42e7f8bafce054ad70de3a2c2ac94d13579f41b reveals how the concurrency window was eliminated. The developer introduced database transactions alongside pessimistic locking on both the service record and the user's credit balance. This ensures that only one request can access and modify these records at any given moment.

// Vulnerable read pattern:
// $this->service was fetched via standard Eloquent without locking.
// If concurrent requests were executed, both would proceed to update the state.
 
// Patched implementation utilizing transaction and pessimistic lock:
DB::beginTransaction();
try {
    // Lock the service record for update to prevent concurrent reads/writes
    $service = Service::where('id', $this->service->id)->lockForUpdate()->first();
 
    // Validate upgrade state again under the lock
    if ($service->upgrade()->where('status', ServiceUpgrade::STATUS_PENDING)->count() != 0) {
        DB::rollBack();
        $this->notify('This service is not upgradable.', 'error', true);
        return $this->redirect(route('services.show', $this->service), true);
    }
    
    // ... [Plan validation and ServiceUpgrade model creation] ...
 
    if ($price->price <= 0) {
        (new ServiceUpgradeService)->handle($upgrade);
 
        if (!config('settings.credits_on_downgrade', true)) {
            DB::commit();
            $this->notify('The upgrade has been completed.', 'success', true);
            return $this->redirect(route('services.show', $service), true);
        }
 
        // Lock the user's wallet balance before executing increment
        $user = User::where('id', Auth::id())->lockForUpdate()->first();
        $credit = $user->credits()->where('currency_code', $price->currency->code)->first();
 
        if ($credit) {
            $credit->increment('amount', abs($price->price));
        } else {
            $user->credits()->create([
                'currency_code' => $price->currency->code,
                'amount' => abs($price->price),
            ]);
        }
 
        DB::commit();
        // ... [Redirect and notification logic] ...
    }
} catch (\Exception $e) {
    DB::rollBack();
    // ... [Exception handling] ...
}

The introduction of lockForUpdate() is critical. When the database processes Service::where(...)->lockForUpdate(), it issues a SELECT ... FOR UPDATE query to the underlying SQL engine (e.g., InnoDB). This places an exclusive write lock on the matching row. If Request B attempts to execute the same query while Request A's transaction is open, the database engine suspends Request B's execution. Request B remains blocked until Request A completes with either a DB::commit() or DB::rollBack(). Once Request A commits, the lock is released, Request B resumes, and immediately encounters the newly created ServiceUpgrade::STATUS_PENDING record, triggering the validation failure and rolling back safely.

Exploitation Methodology

Exploitation of this vulnerability requires standard customer credentials on a Paymenter instance and an active, downgradable hosting service. The objective is to force the server's web worker threads to process multiple downgrade actions simultaneously. This bypasses the logic checking for pending upgrades and results in multiple credit refunds.

An attacker begins by identifying a service plan that can be downgraded to a cheaper tier, which calculates a negative cost differential (a refund). The attacker initiates the downgrade process in the user interface and captures the outgoing Livewire HTTP POST request using an intercepting proxy. This payload contains the necessary validation tokens, component identifiers, and action parameters targeting the doUpgrade method.

To achieve the precision timing necessary for a race condition, the attacker utilizes a multi-threaded execution tool or an HTTP/2-capable client. Using HTTP/2 single-packet compression techniques (such as the single-packet attack), the attacker queues several identical POST requests inside a single TCP frame. This minimizes networking latency jitter. When the target server receives the frame, it distributes the concurrent HTTP requests to its PHP process pool at the exact same moment. Due to the lack of row locks in the vulnerable version, the parallel threads complete their read operations simultaneously. Each thread confirms there is no pending upgrade, creates a duplicate upgrade record, and increments the account's credit balance. This generates a combined refund that is several times larger than the single downgrade's value.

Impact Assessment

The impact of exploiting CVE-2026-71537 is highly severe to the business integrity of the hosting provider. Because account credits are directly tied to financial transactions and can be used to purchase hosting services or licenses, an attacker can exploit this flaw to generate infinite store credit. This credit can then be used to drain the provider's actual computing or licensing resources, causing significant financial loss.

The vulnerability is assessed with a CVSS v3.1 score of 6.5. This reflects medium severity because it requires authenticated customer privileges (PR:L) and does not compromise system confidentiality (C:N) or availability (A:N). However, the integrity impact is rated high (I:H) due to the direct, unauthorized creation of currency value within the database. The scope remains unchanged (S:U) because the exploit operates entirely within the context of the Paymenter database and does not allow lateral movement to the underlying operating system or hypervisors.

Currently, this vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and there is no evidence of active exploitation in the wild. The exploit complexity is low, as the tooling required to synchronize HTTP/2 requests is widely available. Any user with a valid account and a cheap active service can execute the exploit, making immediate remediation critical for operators running affected versions.

Remediation and Defensive Guidance

The primary and recommended mitigation for this vulnerability is to upgrade the Paymenter installation to version 1.5.7 or higher. This release integrates the transactional and pessimistic locking mechanisms within app/Livewire/Services/Upgrade.php that eliminate the race window. Operators should execute standard application update routines, which include running database migrations to ensure any schema adjustments are applied correctly.

If patching immediately is not possible, hosting providers can apply several temporary workarounds to mitigate the risk:

  1. Disable Credits on Downgrade: Administrators can disable the refund feature by setting the settings.credits_on_downgrade configuration option to false. This prevents the application from issuing credit refunds during downgrades, eliminating the financial risk. This option can be configured in the Paymenter administrative dashboard.
  2. Rate Limiting: Implement strict rate-limiting on the Livewire endpoint (/livewire/message/services.upgrade) using a reverse proxy or Web Application Firewall (WAF). Restricting clients to a single request per second on this path prevents the high-frequency concurrent requests required to trigger the race condition.
  3. Transactional Isolation: For advanced configurations, database administrators can temporarily adjust the transaction isolation level for the Paymenter database to SERIALIZABLE. Note that this can introduce performance overhead and potential lock contention issues under high load, so it should be evaluated carefully in a staging environment first.

Post-Exploitation Detection and Auditing

Administrators concerned about potential exploitation of this vulnerability should perform a retro-active audit of their database tables. Because the exploit causes duplicate records to be created, detection can be achieved by querying the database for anomalous, duplicate service upgrade events.

Run the following SQL query against the Paymenter database to identify any instances where multiple pending or completed upgrade records exist for a single service:

SELECT service_id, COUNT(*) as upgrade_count, GROUP_CONCAT(id) as upgrade_ids 
FROM service_upgrades 
GROUP BY service_id 
HAVING upgrade_count > 1;

If the query returns records, administrators should compare the creation timestamps (created_at) of the duplicate upgrades. If multiple upgrades were created for the same service within milliseconds of each other, it indicates a high likelihood of exploitation.

Additionally, examine the user credit transaction logs. Compare the credit increments with the downgrade records. If a customer's balance was increased multiple times for a single downgrade transaction, the account has likely exploited this race condition. In such cases, the operator should suspend the affected customer accounts, audit their credit balances, and reverse any unauthorized credits before upgrading the application to version 1.5.7.

Official Patches

PaymenterGitHub Security Advisory for CVE-2026-71537

Fix Analysis (1)

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N

Affected Systems

Paymenter hosting management webshop installations prior to version 1.5.7

Affected Versions Detail

Product
Affected Versions
Fixed Version
Paymenter
Paymenter
< 1.5.71.5.7
AttributeDetail
CWE IDCWE-362
Attack VectorNetwork (AV:N)
CVSS v3.1 Score6.5
ImpactHigh Integrity (Unauthorized Credit Manipulation)
Exploit StatusNone / Theoretical (No public PoC)
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1068Exploitation for Privilege Escalation
Privilege Escalation
CWE-362
Concurrent Execution using Shared Resource with Improper Synchronization

The application performs a check-then-act sequence across database reads and writes without transactional isolation or exclusive row locks.

Vulnerability Timeline

CVE Published (NVD)
2026-09-18
GitHub Advisory GHSA-5gmm-hjfj-8ff7 Released
2026-09-18

References & Sources

  • [1]Paymenter Security Advisory GHSA-5gmm-hjfj-8ff7
  • [2]Fix Commit a42e7f8bafce054ad70de3a2c2ac94d13579f41b
  • [3]Paymenter Release v1.5.7
  • [4]NVD CVE-2026-71537
  • [5]CVE.org Record for CVE-2026-71537

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 1 hour ago•CVE-2026-63445
7.1

CVE-2026-63445: Arbitrary File Read and Path Traversal in Perses File-System Database Backend

An arbitrary file read and path traversal vulnerability exists in Perses prior to version 0.54.0-rc.0. When configured with a file-system database backend, the application lacks input validation on the request-controlled project query parameter. An authenticated attacker with low privileges can supply directory traversal sequences to read arbitrary JSON or YAML files on the host file system.

Amit Schendel
Amit Schendel
3 views•6 min read
•about 2 hours ago•CVE-2026-59163
9.1

CVE-2026-59163: Critical JWT Authentication Bypass in Mnemosyne Sync Server

CVE-2026-59163 is a critical authentication bypass vulnerability in the Mnemosyne sync server. In versions prior to v3.10.1, the server's authentication logic decoded incoming JSON Web Tokens (JWT) but completely skipped cryptographic signature verification. An unauthenticated remote attacker can exploit this vulnerability to bypass authentication, impersonate arbitrary users, read synchronized AI agent states, or write malicious database updates.

Alon Barad
Alon Barad
5 views•7 min read
•about 3 hours ago•CVE-2026-85058
7.5

CVE-2026-85058: Missing Authorization in Moquette MQTT Broker Last Will and Testament Feature

An authorization bypass vulnerability exists in the Moquette MQTT broker prior to version 0.18.1. When an MQTT client registers a Last Will and Testament (LWT) topic during its connection setup, the broker fails to perform write-access checks on that topic. Upon an abrupt client disconnection, the broker publishes the registered Will message to subscribers of the unauthorized topic, bypassing configured Access Control Lists (ACLs).

Amit Schendel
Amit Schendel
5 views•8 min read
•about 5 hours ago•GHSA-XWMW-PRC4-V3CR
8.8

GHSA-XWMW-PRC4-V3CR: OAuth Dynamic Client Registration Enables API Token Theft via Audience Confusion in Obot Platform

A critical security vulnerability exists in the Obot Platform (versions < 0.23.0) where unauthenticated OAuth dynamic client registration, a consentless authorization flow, and a lack of JWT audience validation enable remote attackers to steal API tokens via audience confusion.

Alon Barad
Alon Barad
6 views•6 min read
•about 6 hours ago•GHSA-PR6H-VR44-XQ8J
5.3

GHSA-PR6H-VR44-XQ8J: Authentication Bypass in Obot Model Context Protocol (MCP) Registry API

An authentication bypass vulnerability in Obot versions <= v0.22.1 allows unauthenticated remote attackers to access Model Context Protocol (MCP) registry metadata and retrieve server lists when OBOT_SERVER_ENABLE_REGISTRY_AUTH is configured. This is due to a routing logic flaw where `/v0.1` paths are incorrectly categorized as public frontend user interface assets.

Amit Schendel
Amit Schendel
5 views•8 min read
•about 11 hours ago•GHSA-JGH3-FGGC-MCPM
7.6

GHSA-jgh3-fggc-mcpm: Non-Blind Server-Side Request Forgery (SSRF) in Obot Platform

An authenticated Server-Side Request Forgery (SSRF) vulnerability in the Obot Platform allows administrative or power users to bypass IP verification and scan or query internal resources, private networks, and cloud instance metadata services (IMDS). Because response bodies and error details are reflected back to the client interface, this constitutes a non-blind SSRF.

Alon Barad
Alon Barad
9 views•8 min read