
Cloud Vulnerability DB
A community-led vulnerabilities database
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).
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).
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).
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).
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.{"event_type":"transaction.completed","data":{}}) and record the current Unix timestamp (TS=$(date +%s)).Paddle-Signature headers: Generate candidate h1 values — 64-character hex strings — starting with all zeros (0000...0000), then systematically varying each hex character position.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.String#== returns false. Identify the longest-timing guess at each byte position to recover one hex character at a time.subscription.created) and submit it with Paddle-Signature: ts=<ts>;h1=<recovered_hmac>. The request passes valid_signature? and is accepted as legitimate.Pay::Webhooks::ProcessJob, causing the application to provision paid features, record refunds, or alter subscription state as directed by the forged event (GitHub Advisory).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.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).Pay::Webhooks::ProcessJob jobs enqueued for webhook events that do not correspond to legitimate Paddle activity (GitHub Advisory).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).
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).
Source: This report was generated using AI
Free Vulnerability Assessment
Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.
Get a personalized demo
"Best User Experience I have ever seen, provides full visibility to cloud workloads."
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
"We know that if Wiz identifies something as critical, it actually is."