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

CVE-2026-89420: ZenHive MPP Auth Bypass Vulnerability

CVE-2026-89420 is an authentication bypass flaw in ZenHive MPP that enables clients to obtain paid resources without payment by reusing vouchers. This article covers the technical details, affected versions from 0.14.0 to 0.16.1, security impact, and available mitigation strategies.

Published:

CVE-2026-89420 Overview

CVE-2026-89420 is a business logic vulnerability in ZenHive mpp, an Elixir library implementing the Machine Payment Protocol (MPP). The flaw resides in the session voucher acceptance path and allows a client holding an open payment channel to consume paid resources without being charged. A signed voucher whose cumulativeAmount equals the channel's already-accepted amount is treated as an idempotent success, so the credential verifies and the protected resource is served without incrementing spent units. The issue affects mpp versions from 0.14.0 before 0.16.2 and is reachable across Plug, MCP, JSON-RPC, and WebSocket transports.

Critical Impact

A single signed voucher can be replayed indefinitely under fresh per-request challenges, yielding an unbounded number of paid units for zero cost and undermining the integrity of session-based billing.

Affected Products

  • ZenHive mpp 0.14.0
  • ZenHive mpp versions after 0.14.0 and before 0.16.2
  • Any application built on MPP.Session.Method exposing Plug, MCP, JSON-RPC, or WebSocket transports

Discovery Timeline

  • 2026-09-22 - CVE-2026-89420 published to NVD
  • 2026-09-22 - Last updated in NVD database

Technical Details for CVE-2026-89420

Vulnerability Analysis

The vulnerability lives in MPP.Session.Actions.accept_voucher/3 inside lib/mpp/session/actions.ex. When a client submits a voucher whose cumulative_amount equals the channel's current cumulative_amount, the function returns the channel unchanged and skips the call to maybe_spend/2. The credential still verifies, and the protected resource is served, but the channel's spent and units counters never advance.

Compounding the flaw, the server issues a fresh challenge for every request, and the credential replay store keys entries by challenge id and payload. A previously signed voucher therefore does not appear as a replay under any new challenge. A client can present the same voucher repeatedly and receive metered content indefinitely.

The defect is classified as Improper Validation of Specified Quantity in Input [CWE-1284] and manifests as a business logic error in the metered-billing state machine.

Root Cause

The root cause is that the equality branch of the monotonicity check treats payload.cumulative_amount == channel.cumulative_amount as a no-op success rather than an invalid delta. Because no charge is applied and no error is returned, callers cannot distinguish a zero-value voucher from a legitimate paid one at the transport layer.

Attack Vector

An authenticated client with an open session channel signs one voucher whose cumulativeAmount matches the channel's already-accepted value. The client then presents that voucher against each new server challenge across any MPP transport. Every request succeeds, the resource is served, and spent and units remain unchanged.

text
     cond do
       payload.cumulative_amount == channel.cumulative_amount ->
-        {:ok, channel}
+        {:error, Errors.new(:delta_too_small, "voucher must increase the accepted cumulative amount")}

       payload.cumulative_amount < channel.cumulative_amount ->
         {:error, Errors.new(:invalid_payload, "voucher cumulativeAmount is not monotonic")}

Source: GitHub commit 7270edc1 - the patch converts the equality branch from an idempotent success into a :delta_too_small error.

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-89420

Indicators of Compromise

  • Repeated successful voucher acceptances for the same channel where spent and units do not advance across requests.
  • Multiple distinct challenge ids resolving to the same voucher signature or payload hash within a short window.
  • Session channels serving substantial protected content while their cumulative_amount remains static.

Detection Strategies

  • Log every accepted voucher with channel id, challenge id, cumulative_amount before and after, and signature digest, then alert when the before and after values are equal.
  • Correlate request volume per channel against monotonic growth of cumulative_amount; flag channels where request count grows but balance does not.
  • Track unique voucher signatures per channel over time and alert when a single signature is accepted against more than one challenge id.

Monitoring Recommendations

  • Enable structured audit logging on all MPP.Session.Method handlers across Plug, MCP, JSON-RPC, and WebSocket transports.
  • Ship session accounting metrics to a centralized data lake so anomalous ratios of served bytes to charged units are visible across tenants.
  • Review MPP.Session.Store persistence for channels whose lifetime request count materially exceeds their charged units.

How to Mitigate CVE-2026-89420

Immediate Actions Required

  • Upgrade ZenHive mpp to version 0.16.2 or later on all servers exposing session-based endpoints.
  • Audit MPP.Session.Store records for channels that served resources without proportional growth in cumulative_amount and reconcile billing.
  • Rotate or invalidate long-lived session channels opened while a vulnerable version was in production.

Patch Information

The fix is delivered in mpp 0.16.2. The relevant change in lib/mpp/session/actions.ex replaces the idempotent equality branch with an explicit :delta_too_small error, ensuring every accepted voucher must strictly increase the channel's cumulative amount. See the GitHub Security Advisory GHSA-8c63-r789-xrrf, the CNA CVE-2026-89420 entry, and the OSV record EEF-CVE-2026-89420 for full details.

Workarounds

  • If upgrading is not immediately possible, wrap MPP.Session.Actions.accept_voucher/3 with a guard that rejects vouchers whose cumulative_amount is not strictly greater than the stored channel amount.
  • Add a middleware layer that records the highest accepted cumulative_amount per channel and rejects any request whose voucher does not exceed it.
  • Restrict session channel issuance to trusted clients until the patched version is deployed.
bash
# Pin the patched version in mix.exs
# {:mpp, "~> 0.16.2"}
mix deps.update mpp
mix deps.get
mix compile

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.