Register for the AI for Security Summit: Join Figma, Perplexity & Wiz

CVE-2026-70658
Ruby vulnerability analysis and mitigation

Overview

CVE-2026-70658 is a timing side-channel vulnerability in the Pay gem (a payments engine for Ruby on Rails 6.0+) that allows unauthenticated remote attackers to forge valid Paddle Billing webhook signatures. The flaw exists in Pay::Webhooks::PaddleBillingController#valid_signature?, which compares a computed SHA-256 HMAC against an attacker-supplied header value using Ruby's non-constant-time String#== operator. Affected versions are pay (rubygem) ≤ 11.6.1; the issue was disclosed on May 27, 2026, and fixed in version 11.6.2. It carries a CVSS v3.1 base score of 7.4 (High) (GitHub Advisory).

Technical details

The root cause is CWE-208 (Observable Timing Discrepancy): the valid_signature? method in app/controllers/pay/webhooks/paddle_billing_controller.rb (line 38) uses hmac == h1 — Ruby's String#== — which short-circuits and returns on the first mismatching byte of the 64-character hex HMAC digest. This creates a per-byte timing oracle: an attacker who can measure response times with sub-millisecond precision can iteratively determine which bytes of their guessed h1 value match the real HMAC, recovering the full digest one byte at a time. The attack requires no authentication, as the POST /pay/webhooks/paddle_billing endpoint is publicly exposed by design (Paddle requires it to be internet-reachable). The fix replaces == with ActiveSupport::SecurityUtils.secure_compare, which compares all bytes regardless of mismatch position (GitHub Advisory, Fix Commit).

Impact

A successful attack allows an unauthenticated attacker to forge arbitrary Paddle Billing webhook events (e.g., subscription.created, transaction.completed, refund.created). Forged webhooks pass signature validation and are enqueued via Pay::Webhooks::ProcessJob, enabling the attacker to manipulate billing state, provision paid features without payment, inject fraudulent refunds, or trigger unauthorized customer notifications in the host application. The confidentiality and integrity impacts are both rated High, as the attack leaks the application's HMAC signing secret and allows unauthorized modification of business-critical billing data (GitHub Advisory).

Exploitability

A proof-of-concept exploit is publicly available in the GitHub Security Advisory, including concrete curl-based timing oracle commands and a Ruby microbenchmark demonstrating measurable timing differences between HMAC prefix matches. The attack is not automatable in a trivial sense (requires statistical timing analysis over many requests), but requires no authentication or user interaction. There is no evidence of in-the-wild exploitation at this time, and the vulnerability is not listed in the CISA KEV catalog. The EPSS score is 0.0 as of publication. The NVD SSVC assessment classifies exploitation status as "poc" (GitHub Advisory).

Exploitation steps

  1. Identify the target endpoint: Locate a Rails application using the pay gem (≤ 11.6.1) with Paddle Billing enabled. The endpoint POST /pay/webhooks/paddle_billing is publicly exposed by default when Pay::Engine is mounted.
  2. Craft a webhook payload and timestamp: Prepare a JSON body (e.g., {"event_type":"transaction.completed","data":{}}) and record the current Unix timestamp (TS=$(date +%s)).
  3. Construct guessed Paddle-Signature headers: Generate candidate h1 values — 64-character hex strings — starting with all zeros (0000...0000), then systematically varying each hex character position.
  4. Submit requests and measure response time: For each guessed h1, send: curl -s -w '%{time_total}\n' -o /dev/null -X POST -H "Paddle-Signature: ts=$TS;h1=$guess" -H 'Content-Type: application/json' -d "$BODY" "$WEBHOOK". Collect multiple timing samples per guess to build a statistical distribution.
  5. Infer matching bytes from timing distribution: Guesses whose prefix matches more bytes of the real HMAC take slightly longer before String#== returns false. Identify the longest-timing guess at each byte position to recover one hex character at a time.
  6. Recover the full 64-character HMAC: Repeat across all 64 hex positions (testing up to 16 candidates per position) until the complete digest is recovered.
  7. Forge and submit a malicious webhook: Craft a Paddle event payload of choice (e.g., subscription.created) and submit it with Paddle-Signature: ts=<ts>;h1=<recovered_hmac>. The request passes valid_signature? and is accepted as legitimate.
  8. Trigger billing manipulation: The accepted webhook is enqueued via Pay::Webhooks::ProcessJob, causing the application to provision paid features, record refunds, or alter subscription state as directed by the forged event (GitHub Advisory).

Indicators of compromise

  • Network: High volume of POST requests to /pay/webhooks/paddle_billing from a single IP or small set of IPs, especially with varying Paddle-Signature header values; requests with h1 values that are all-zero or incrementally varying hex strings.
  • Logs: Rails access logs showing repeated POST /pay/webhooks/paddle_billing requests returning 401 or 403 responses in rapid succession, followed by a sudden 200 response; unusual Paddle-Signature header patterns (e.g., h1=0000000000000000000000000000000000000000000000000000000000000000).
  • Application: Unexpected billing state changes such as subscriptions being created or upgraded without corresponding Paddle payment records; fraudulent refund entries; paid features provisioned for accounts with no payment history.
  • Job Queue: Unexpected Pay::Webhooks::ProcessJob jobs enqueued for webhook events that do not correspond to legitimate Paddle activity (GitHub Advisory).

Mitigation and workarounds

Upgrade the pay gem to version 11.6.2 or later, which replaces the vulnerable hmac == h1 comparison with ActiveSupport::SecurityUtils.secure_compare(hmac, h1) and adds a bytesize-equality guard (Fix Commit, Release v11.6.2). As an interim workaround, implement rate limiting on the POST /pay/webhooks/paddle_billing endpoint to reduce the attacker's ability to collect timing samples. Additionally, monitor webhook processing logs for anomalous billing state changes or unexpected Pay::Webhooks::ProcessJob activity as a detection measure (GitHub Advisory).

Community reactions

The vulnerability was reported by security researcher tonghuaroot and published as a GitHub Security Advisory (GHSA-mjgf-xj26-9qf9) on May 27, 2026. The advisory includes a detailed proof-of-concept with both a local Ruby microbenchmark and end-to-end reproduction steps, which contributed to its classification as a high-confidence PoC exploit by Feedly threat intelligence. No significant broader media coverage or notable social media commentary beyond the advisory itself has been identified at this time (GitHub Advisory).

Additional resources


SourceThis report was generated using AI

Related Ruby vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-50276HIGH7.5
  • Ruby logoRuby
  • datadog
NoYesSep 14, 2026
CVE-2026-70658HIGH7.4
  • Ruby logoRuby
  • pay
NoNoSep 14, 2026
CVE-2026-44163MEDIUM5.3
  • Ruby logoRuby
  • fluent-plugin-opentelemetry
NoYesSep 15, 2026
CVE-2026-44282MEDIUM4.8
  • Ruby logoRuby
  • decidim-elections
NoYesSep 15, 2026
CVE-2026-44162LOW2.7
  • Ruby logoRuby
  • fluent-plugin-s3
NoYesSep 14, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management