CVE-2025-61921
NoneSinatra is vulnerable to ReDoS through ETag header value generation
CVSS
—
EPSS
0.5%
Published
Oct 10, 2025
CISA due date
—
Cloudanix analysis
Some Ruby web applications can be knocked offline by specially crafted web requests unless their web framework or runtime is updated.
- Exploitation
- Remote Unauthenticated
- Attacker needs
- None
- Complexity
- Low
- Impact
- Denial Of Service
- Component
- Web Framework
- Class
- Denial of service
- Remediation
- Upgrade
- Effort
- Medium
- Patch risk
- Low
What it actually does
The flaw is a denial-of-service condition in Sinatra's parsing of conditional HTTP request headers, specifically If-Match and If-None-Match, when an application uses Sinatra's etag method to construct responses and runs on Ruby versions earlier than 3.2. A crafted request can trigger excessive parsing work, an exception path, or resource exhaustion in the ETag handling path, degrading availability of the affected Sinatra application. The issue does not indicate code execution, privilege escalation, or direct data disclosure; the primary impact is application availability.
Why it matters to the business
An exposed Sinatra service that uses ETags on Ruby versions before 3.2 could be made slow or unavailable by unauthenticated HTTP requests, potentially interrupting user-facing applications, APIs, health checks, background integrations, or dependent services. The currently reported exploitation maturity is none, it is not listed in CISA KEV, and EPSS is low at 0.00458, so broad active exploitation is not indicated; however, denial-of-service bugs in internet-facing web frameworks can be easy to trigger once details are public, and this issue has been public for 303 days, increasing the need to remove lingering exposure.
How an attack unfolds
An attacker first identifies a Sinatra-based web application that returns ETag headers, which suggests the application may be using Sinatra's etag helper or related conditional response handling. The attacker then sends HTTP requests containing crafted If-Match or If-None-Match headers to routes that generate ETagged responses. On vulnerable combinations of Sinatra and Ruby earlier than 3.2, parsing those headers can trigger the denial-of-service condition, consuming server resources, causing request processing failures, or making application workers unavailable. The attacker can repeat the request pattern, potentially from multiple clients, to sustain degraded service or outage until traffic is blocked, workers are restarted, ETag handling is disabled, Ruby/Sinatra is upgraded, or the fixed Sinatra code is deployed.
How to fix it
Upgrade to a Sinatra version or source revision that includes the fix. For sinatra/sinatra from Git, ensure the code includes commit f2ad45f7d2456172974a30d300e9f82424336e09. Where packaged gem releases are used, select a release that includes that commit or the vendor-designated fixed version. Also upgrade Ruby to 3.2 or later where feasible, because the vulnerability is described as affecting deployments using Ruby versions earlier than 3.2. As interim mitigations, disable or avoid Sinatra's etag method on exposed routes, reject or normalize suspicious If-Match and If-None-Match headers at a reverse proxy or WAF, apply request header length/count limits, rate-limit repeated conditional requests, and ensure application process supervisors can restart unhealthy workers. Mitigations should not be treated as a substitute for applying the Sinatra fix.
How to confirm the fix worked
Confirm dependency state by reviewing the application lockfile, vendored Sinatra source, container image, or build manifest to verify that Sinatra includes commit f2ad45f7d2456172974a30d300e9f82424336e09 or a release containing it. Confirm runtime state by checking that Ruby is version 3.2 or later, or that the patched Sinatra code is deployed if Ruby remains below 3.2. Identify application routes that call etag or return ETag headers and test them in a staging environment with benign conditional requests containing If-Match and If-None-Match headers to ensure they return normal 200/304/412-style responses without worker instability or abnormal latency. At the edge, validate that header-size limits, WAF rules, and rate limits are enforced before requests reach the application. Do not perform stress testing against production without change approval and safeguards.
Typical attack path
- Discover an HTTP service implemented with Sinatra or infer framework behavior from response headers, error pages, routing behavior, or application fingerprints.
- Identify routes that emit ETag response headers or support conditional request handling.
- Send crafted HTTP requests containing malicious or pathological If-Match and/or If-None-Match headers to those routes.
- Observe elevated latency, increased error responses, worker crashes, CPU or memory pressure, or reduced application availability.
- Repeat or parallelize the requests to maintain a denial-of-service condition against vulnerable application instances.
How to detect it
To determine whether exploitation has already been attempted, review HTTP access logs from web servers, reverse proxies, load balancers, CDN/WAF platforms, and application logs for unusual or repeated requests containing If-Match or If-None-Match headers, especially against routes that return ETag headers. Correlate those requests with abnormal increases in 500, 502, 503, or 504 responses, request timeouts, application worker restarts, elevated CPU or memory use, and health-check failures. Sinatra/Rack application logs may show request failures or exceptions around conditional request processing, ETag handling, or malformed HTTP header parsing. Since no public exploit-specific payload string is provided, detection should focus on behavioral indicators: high-volume or malformed conditional headers, oversized ETag lists, repeated requests from the same source or botnet-like sources, and availability degradation immediately following those requests.
- If-Match:
- If-None-Match:
- HTTP_IF_MATCH
- HTTP_IF_NONE_MATCH
- ETag:
- 500
- 502
- 503
- 504
- Sinatra::
- Rack::
- etag
- conditional request
- worker timeout
- worker restart
- upstream timed out
- connection reset by peer
- malformed If-Match
- malformed If-None-Match
What your security team should do
- Inventory Sinatra applications, especially internet-facing APIs and services, and identify those running Ruby versions earlier than 3.2 and using ETag responses.
- Prioritize patching exposed services by upgrading Sinatra to a build containing commit f2ad45f7d2456172974a30d300e9f82424336e09 or a fixed release, and upgrade Ruby to 3.2 or later where practical.
- Add temporary reverse-proxy or WAF controls to limit, block, or rate-limit abnormal If-Match and If-None-Match headers until all affected services are patched.
- Search recent access logs for spikes in requests containing If-Match or If-None-Match headers correlated with 5xx responses, worker restarts, request timeouts, or CPU/memory exhaustion.
- Add monitoring for unusual conditional request rates, application worker churn, elevated p95/p99 latency, and increased 500/502/503/504 responses on Sinatra services.
- Because EPSS is low and there is no KEV listing or known exploit maturity, treat this as a routine-to-expedited availability fix, escalating priority for public-facing or business-critical services.
The record
There is a denial of service vulnerability in the `If-Match` and `If-None-Match` header parsing component of Sinatra, if the `etag` method is used when constructing the response and you are using Ruby < 3.2.
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|---|---|---|
| RubyGems | sinatra | 0 | 4.2.0 |
| Chainguard | gitaly-config-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitaly-config-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitaly-config-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitaly-config-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitaly-config-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-base-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-base-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-base-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-base-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-base-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-certificates-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-certificates-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-certificates-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-certificates-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-certificates-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-cfssl-self-sign-scripts-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-cfssl-self-sign-scripts-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-cfssl-self-sign-scripts-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-cfssl-self-sign-scripts-18.6 | 0 | 18.6.1-r1 |
| Chainguard | gitlab-cfssl-self-sign-scripts-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-cng-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-cng-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-cng-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-cng-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-cng-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-cng-18.9 | 0 | 18.9.1-r0 |
| Chainguard | gitlab-container-registry-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-container-registry-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-container-registry-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-container-registry-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-container-registry-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-container-registry-scripts-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-container-registry-scripts-18.4 | 0 | 18.4.4-r0 |
| Chainguard | gitlab-container-registry-scripts-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-container-registry-scripts-18.6 | 0 | 18.6.1-r1 |
| Chainguard | gitlab-container-registry-scripts-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-exporter-scripts-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-exporter-scripts-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-exporter-scripts-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-exporter-scripts-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-exporter-scripts-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-gitaly-scripts-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-gitaly-scripts-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-gitaly-scripts-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-gitaly-scripts-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-gitaly-scripts-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-logger-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-logger-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-logger-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-logger-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-logger-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-pages-scripts-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-pages-scripts-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-pages-scripts-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-pages-scripts-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-pages-scripts-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-rails-scripts-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-rails-scripts-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-rails-scripts-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-rails-scripts-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-rails-scripts-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-shell-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-shell-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-shell-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-shell-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-shell-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-shell-scripts-18.3 | 0 | 18.3.6-r0 |
| Chainguard | gitlab-shell-scripts-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-shell-scripts-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-shell-scripts-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-shell-scripts-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-sidekiq-ce-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-sidekiq-ce-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-sidekiq-ce-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-sidekiq-ce-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-sidekiq-ce-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-toolbox-ce-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-toolbox-ce-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-toolbox-ce-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-toolbox-ce-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-toolbox-ce-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-toolbox-ce-18.7 | 0 | 18.7.1-r0 |
| Chainguard | gitlab-webservice-ce-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-webservice-ce-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-webservice-ce-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-webservice-ce-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-webservice-ce-18.7 | 0 | 18.7.0-r0 |
| Chainguard | gitlab-workhorse-scripts-18.3 | 0 | 18.3.5-r0 |
| Chainguard | gitlab-workhorse-scripts-18.4 | 0 | 18.4.3-r0 |
| Chainguard | gitlab-workhorse-scripts-18.5 | 0 | 18.5.1-r0 |
| Chainguard | gitlab-workhorse-scripts-18.6 | 0 | 18.6.2-r0 |
| Chainguard | gitlab-workhorse-scripts-18.7 | 0 | 18.7.0-r0 |
| Chainguard | logstash-8.17 | 0 | 8.17.10-r4 |
| Chainguard | logstash-8.17-bitnami-compat | 0 | 8.17.10-r4 |
| Chainguard | logstash-8.17-compat | 0 | 8.17.10-r4 |
| Chainguard | logstash-8.17-env2yaml | 0 | 8.17.10-r4 |
| Chainguard | logstash-8.17-iamguarded-compat | 0 | 8.17.10-r4 |
| Chainguard | logstash-8.17-with-output-opensearch | 0 | 8.17.10-r4 |
| Chainguard | logstash-8.18 | 0 | 8.18.8-r2 |
| Chainguard | logstash-8.18-bitnami-compat | 0 | 8.18.8-r2 |
| Chainguard | logstash-8.18-compat | 0 | 8.18.8-r2 |
| Chainguard | logstash-8.18-env2yaml | 0 | 8.18.8-r2 |
| Chainguard | logstash-8.18-iamguarded-compat | 0 | 8.18.8-r2 |
| Chainguard | logstash-8.18-with-output-opensearch | 0 | 8.18.8-r2 |
| Chainguard | logstash-8.19 | 0 | 8.19.5-r2 |
| Chainguard | logstash-8.19-compat | 0 | 8.19.5-r2 |
| Chainguard | logstash-8.19-env2yaml | 0 | 8.19.5-r2 |
| Chainguard | logstash-8.19-iamguarded-compat | 0 | 8.19.5-r2 |
| Chainguard | logstash-8.19-with-output-opensearch | 0 | 8.19.5-r2 |
| Chainguard | logstash-9.0 | 0 | 9.0.8-r2 |
| Chainguard | logstash-9.0-bitnami-compat | 0 | 9.0.8-r2 |
| Chainguard | logstash-9.0-compat | 0 | 9.0.8-r2 |
| Chainguard | logstash-9.0-env2yaml | 0 | 9.0.8-r2 |
| Chainguard | logstash-9.0-iamguarded-compat | 0 | 9.0.8-r2 |
| Chainguard | logstash-9.0-with-output-opensearch | 0 | 9.0.8-r2 |
| Chainguard | logstash-9.1 | 0 | 9.1.5-r2 |
| Chainguard | logstash-9.1-bitnami-compat | 0 | 9.1.5-r2 |
| Chainguard | logstash-9.1-compat | 0 | 9.1.5-r2 |
| Chainguard | logstash-9.1-env2yaml | 0 | 9.1.5-r2 |
| Chainguard | logstash-9.1-iamguarded-compat | 0 | 9.1.5-r2 |
| Chainguard | logstash-9.1-with-output-opensearch | 0 | 9.1.5-r2 |
| Wolfi | logstash-9.1 | 0 | 9.1.5-r2 |
| Wolfi | logstash-9.1-bitnami-compat | 0 | 9.1.5-r2 |
| Wolfi | logstash-9.1-compat | 0 | 9.1.5-r2 |
| Wolfi | logstash-9.1-env2yaml | 0 | 9.1.5-r2 |
| Wolfi | logstash-9.1-iamguarded-compat | 0 | 9.1.5-r2 |
| Wolfi | logstash-9.1-with-output-opensearch | 0 | 9.1.5-r2 |
Related vulnerabilities
- CVE-2024-21510 Versions of the package sinatra from 0.0.0 are vulnerable to Reliance on Untrusted Inputs… Also affects sinatra
- CVE-2025-11065 A flaw was found in github.com/go-viper/mapstructure/v2, in the field processing… Also affects gitaly-config-18.3
- CVE-2025-61919 Rack is vulnerable to a memory-exhaustion DoS through unbounded URL-encoded body parsing Also affects gitaly-config-18.3
- CVE-2025-61780 Rack has a Possible Information Disclosure Vulnerability Also affects gitaly-config-18.3
- CVE-2025-61770 Rack's unbounded multipart preamble buffering enables DoS (memory exhaustion) Also affects gitaly-config-18.3
- CVE-2025-61771 Rack: Multipart parser buffers large non‑file fields entirely in memory, enabling DoS… Also affects gitaly-config-18.3
- CVE-2025-61772 Rack's multipart parser buffers unbounded per-part headers, enabling DoS (memory… Also affects gitaly-config-18.3
- CVE-2025-50181 urllib3 redirects are not disabled when retries are disabled on PoolManager instantiation Also affects gitaly-config-18.3