<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Pay - CraftedSignal Threat Feed</title><link>https://feed.craftedsignal.io/vendors/pay/</link><description>Trending threats, MITRE ATT&amp;CK coverage, and detection metadata. Fed continuously.</description><generator>Hugo</generator><language>en</language><managingEditor>hello@craftedsignal.io</managingEditor><webMaster>hello@craftedsignal.io</webMaster><lastBuildDate>Fri, 03 Jul 2026 12:39:17 +0000</lastBuildDate><atom:link href="https://feed.craftedsignal.io/vendors/pay/feed.xml" rel="self" type="application/rss+xml"/><item><title>Non-Constant-Time HMAC Comparison in Pay Gem Paddle Billing Webhook Signature Verifier</title><link>https://feed.craftedsignal.io/briefs/2026-07-pay-gem-timing-side-channel/</link><pubDate>Fri, 03 Jul 2026 12:39:17 +0000</pubDate><author>hello@craftedsignal.io</author><guid isPermaLink="true">https://feed.craftedsignal.io/briefs/2026-07-pay-gem-timing-side-channel/</guid><description>A timing side-channel vulnerability in the `Pay` gem's Paddle Billing webhook signature verification component (`Pay::Webhooks::PaddleBillingController#valid_signature?` &lt;= v11.6.1) allows an unauthenticated attacker to recover the HMAC signing secret by observing response time variations in `String#==` comparisons, enabling the forgery of arbitrary webhook events and leading to business logic abuses such as unauthorized feature provisioning or fraudulent refunds.</description><content:encoded><![CDATA[<p>The Ruby <code>Pay</code> gem, specifically versions equal to or older than 11.6.1, is vulnerable to a timing side-channel attack affecting its Paddle Billing webhook signature verification. The <code>Pay::Webhooks::PaddleBillingController#valid_signature?</code> method utilizes Ruby's non-constant-time <code>String#==</code> operator to compare the calculated HMAC with the attacker-supplied <code>Paddle-Signature</code> header. This allows an unauthenticated attacker to discern the correct HMAC character by character by observing subtle variations in response times. Successful exploitation grants the attacker the ability to forge arbitrary Paddle Billing webhook events, such as <code>subscription.created</code> or <code>transaction.completed</code>, which can lead to unauthorized provisioning of services, fraudulent refunds, or other business logic abuses within the victim application. The vulnerability is present in applications that mount <code>Pay::Engine</code> and enable <code>paddle_billing</code>, making the <code>POST /pay/webhooks/paddle_billing</code> endpoint publicly accessible to Paddle and, consequently, to attackers.</p>
<h2 id="attack-chain">Attack Chain</h2>
<ol>
<li><strong>Initial Access:</strong> An attacker identifies a Rails application utilizing the <code>Pay</code> gem (version &lt;= 11.6.1) and exposing the <code>POST /pay/webhooks/paddle_billing</code> endpoint, which is publicly accessible for Paddle to deliver webhook events.</li>
<li><strong>Information Gathering / Reconnaissance:</strong> The attacker crafts a series of requests to the exposed webhook endpoint, supplying a valid timestamp (<code>ts=&lt;now&gt;</code>) and a systematically varying guessed HMAC signature (<code>h1=&lt;guess&gt;</code>) within the <code>Paddle-Signature</code> header.</li>
<li><strong>Exploitation (Timing Side Channel):</strong> The vulnerable application processes each request, internally calculating the true HMAC for the raw post data and comparing it to the attacker's <code>h1</code> guess using Ruby's <code>String#==</code>. Due to the non-constant-time nature of this comparison, the server's response time varies subtly based on how many leading characters of the <code>h1</code> guess match the true HMAC.</li>
<li><strong>Credential Access (HMAC Key Recovery):</strong> By sending a large number of requests and precisely measuring and analyzing the response times for each attempt, the attacker can use this timing oracle to accurately reconstruct the full 64-character hex-encoded SHA-256 HMAC, byte by byte, for a known payload.</li>
<li><strong>Forging Webhooks:</strong> Once the HMAC signing secret is effectively known (as the HMAC for any given payload can be computed), the attacker can generate valid <code>Paddle-Signature</code> headers for arbitrary Paddle Billing webhook payloads that they construct.</li>
<li><strong>Impact (Business Logic Abuse):</strong> The attacker then delivers these forged webhook events (e.g., <code>subscription.created</code>, <code>transaction.completed</code>, <code>invoice.paid</code>) to the <code>/pay/webhooks/paddle_billing</code> endpoint. The vulnerable application processes these forged events as legitimate, leading to unauthorized actions such as provisioning premium features for free, initiating fraudulent refunds, or triggering false customer notifications and database updates.</li>
</ol>
<h2 id="impact">Impact</h2>
<p>This vulnerability (CWE-208: Observable Timing Discrepancy) allows an unauthenticated attacker to fully compromise the integrity of Paddle Billing webhook events. By recovering the HMAC signing secret through a timing side-channel, attackers can forge any webhook event, effectively tricking the application into believing Paddle Billing sent it. This can lead to significant financial losses through fraudulent transactions (e.g., free access to paid features, unauthorized refunds), customer confusion from incorrect notifications, or data integrity issues due to malicious updates to billing states. Since the webhook endpoint must be internet-reachable by design, all <code>Pay</code> gem installations integrating with Paddle Billing are exposed to this risk. No specific victim counts or sectors were identified in the source, but any organization using the affected <code>Pay</code> gem version is at risk.</p>
<h2 id="recommendation">Recommendation</h2>
<ul>
<li>Immediately update the <code>pay</code> gem to a version greater than <code>11.6.1</code> to apply the fix that replaces the non-constant-time <code>String#==</code> comparison with <code>ActiveSupport::SecurityUtils.secure_compare</code>.</li>
<li>Review application logs for the <code>/pay/webhooks/paddle_billing</code> endpoint for unusually high request volumes from single IP addresses or abnormal response time distributions, which could indicate attempts at timing side-channel exploitation.</li>
<li>Review application-level audit logs for the <code>Pay</code> gem for any anomalous <code>subscription.created</code>, <code>transaction.completed</code>, or other financial event entries around the time of the vulnerability disclosure or patch deployment, as these could indicate forged events delivered by an attacker.</li>
</ul>
]]></content:encoded><category domain="severity">high</category><category domain="type">advisory</category><category>timing-attack</category><category>vulnerability</category><category>webhooks</category><category>ruby-on-rails</category><category>server-side</category><category>logic-error</category><category>remote-code-execution</category></item></channel></rss>