An Approval Token That Approves Anything: HMAC Confirmation Replay Across Resources in Zscaler’s MCP Server (CVE-2026-59563)

CVE-2026-59563, published 28 September 2026 and rated CVSS 4.6 (Medium), is a small bug with an outsized lesson. Zscaler’s MCP server, versions 0.7.0 and 0.7.1, issued HMAC confirmation tokens that were not bound to the target resource identifier — so a token generated for one resource could be replayed to affect a different resource of the same type. The upstream advisory’s own framing names the threat actor plainly: “allowing an MCP client or agent to replay a token.” Fixed in 0.7.2. Not currently marked as known-exploited.

The CVE record maps Zscaler/zscaler-mcp-server versions >=0.7.0, <0.7.2 as affected with 0.7.2 as the fix, under CWE-305 (authentication bypass by primary weakness). The score is modest. The pattern is not.

The control that failed is the control everyone recommends

A confirmation token exists for one reason: to force a second, deliberate authorisation step before a consequential action — the human-in-the-loop approval that every agent-governance checklist prescribes for destructive tool calls. For that token to mean anything, it must commit to exactly what was approved: which action, which resource, which parameters. An HMAC over an approval decision that omits the resource identifier is a signature on a blank cheque with the payee line left empty. The cryptography worked. The thing it attested to was underspecified.

This is the confused-deputy shape wearing an approval costume. The server verified that approval happened while forgetting to check what was approved — and in an MCP deployment, the party presenting the token is routinely the agent itself, arriving via a prompt-influenced tool call rather than a human hand on a keyboard. The advisory says “client or agent” because in this ecosystem those are often the same thing.

Unbound approvals are a design smell — go look for siblings

When you find one unbound token, audit the whole approval surface for the same omission, because the same developer habit produces all of these:

  • Action unbinding — a token minted for a read reused for a write or delete on the same resource.
  • Parameter unbinding — approval covers “modify firewall rule” but not the rule body that actually ships.
  • Replayability — single approval consumed twice, or valid indefinitely instead of single-use with a short expiry.
  • Principal unbinding — the token does not record who approved, so any holder of the token inherits the approver’s authority.

None of these raise the CVSS of this particular CVE. All of them are the difference between an approval step that constrains an agent and one that merely decorates it. The week’s broader MCP evidence keeps pointing the same way: 29% of the top 8,000 MCP servers carry a risk finding, six of them unintended RCE, and the MCP Python SDK’s OAuth flow let a malicious server take over the account. The ecosystem’s most urgent defects are no longer missing checks — they are checks that do not check what they claim to.

What to do today

  • Update zscaler-mcp-server to 0.7.2. Check lockfiles and deployed manifests for the >=0.7.0, <0.7.2 range; test outside production first, per the usual drill.
  • Bind every approval token to action, resource, parameters, principal, and expiry. The HMAC must cover the full tuple — (approver, action, resource_id, canonical_params, nonce, expires_at) — and the server must re-verify each field at consumption time, not just signature validity.
  • Make approvals single-use with short lifetimes. Consume the nonce on first use; reject anything past its window. An approval that survives indefinitely is a standing privilege, not an approval.
  • Log approval minting and redemption as a pair. If a token minted for resource A is ever presented for resource B, that mismatch is a detection event — but only if both halves are recorded with their full parameters.
  • Inventory your MCP servers against a written baseline. The CIS MCP Server Benchmark v1.0.0 covers execution safety and server configuration with audit procedures; binding checks like this one belong in the recurring pass, alongside the authorisation-bypass class Obot just patched five of.

Sources: