CVE-2026-22860
High · 7.5CVSS
7.5
EPSS
0.8%
Published
Feb 18, 2026
CISA due date
—
Cloudanix analysis
Some Ruby web applications may accidentally show files from nearby folders that were never meant to be public.
- Exploitation
- Remote Unauthenticated
- Attacker needs
- None
- Complexity
- Low
- Impact
- Data Exposure
- Component
- Web Framework
- Class
- Path Traversal
- Remediation
- Upgrade
- Effort
- Medium
- Patch risk
- Low
What it actually does
Rack::Directory incorrectly validates requested filesystem paths by comparing the expanded requested path to the configured root using a simple string prefix check. If a sibling or otherwise adjacent path begins with the same byte sequence as the intended root path, a crafted request containing traversal such as `/../root_example/` can pass the check even though the resolved path is outside the configured directory. The primary impact is unauthorized disclosure through directory listing and file enumeration outside the intended web-exposed root. The vulnerability is remotely reachable, requires no authentication or user interaction, and has confidentiality impact but does not directly provide code execution, data modification, or service disruption.
Why it matters to the business
Applications that expose Rack::Directory may unintentionally reveal files and directory names outside the intended public directory. This can expose source code, configuration files, secrets, deployment artifacts, internal documentation, backups, or other sensitive files depending on filesystem layout and application permissions. Although EPSS is currently low and the issue is not listed in CISA KEV, it has been public for 172 days and is straightforward to test, so internet-facing Ruby services using vulnerable Rack versions should be remediated promptly, especially where directory browsing is enabled or static file roots sit near similarly named directories.
How an attack unfolds
An attacker first identifies a Ruby/Rack application or component that serves directory listings using Rack::Directory. They then infer or brute-force the configured directory root name from URLs, page titles, directory index output, common deployment paths, or error behavior. Because vulnerable Rack versions only check whether the expanded candidate path starts with the configured root string, the attacker requests a traversal path that resolves to a different directory whose absolute path still begins with the same string, such as moving from a configured root named `root` to a sibling named `root_example`. If the target directory is readable by the application process and Rack::Directory renders it, the attacker receives an index listing or files from outside the intended root. The attacker can then enumerate directories, download exposed files, and use disclosed information such as credentials, source code, or infrastructure details for follow-on attacks.
How to fix it
Upgrade Rack to a fixed release: 2.2.22 or later for the 2.2.x branch, 3.1.20 or later for the 3.1.x branch, or 3.2.5 or later for the 3.2.x branch. If consuming Rack directly from Git, include commit `0cc2e00b22dffc33955ef912569f01e515a406e1` or a later commit containing the fix. Update dependency lockfiles such as `Gemfile.lock`, rebuild application images, and redeploy all affected services. If immediate upgrade is not possible, disable Rack::Directory, remove public directory listing functionality, ensure web-exposed static roots do not have similarly prefixed sibling directories, restrict filesystem permissions so the application process cannot read sensitive adjacent directories, and block traversal patterns such as `/../`, `%2e%2e%2f`, and `%2e%2e/` at a reverse proxy or WAF as a temporary defense. Treat proxy/WAF filtering as mitigation only, not a substitute for upgrading.
How to confirm the fix worked
Confirm the deployed Rack version is 2.2.22, 3.1.20, 3.2.5, or newer by checking dependency manifests and runtime package inventory, for example `bundle info rack`, `bundle exec ruby -e 'require "rack"; puts Rack.release'`, `Gemfile.lock`, SBOMs, or container image package scans. Verify that applications no longer expose vulnerable Rack::Directory behavior by testing a non-production or authorized environment with a benign sibling-directory scenario and confirming traversal requests such as `/../root_example/` are rejected, normalized safely, or return 403/404 rather than a directory listing. Confirm reverse proxies, CDN caches, and application containers have all been redeployed and are not still running an older bundled Rack version. Also validate that directory listing is disabled unless explicitly required and that the application process cannot read sensitive files outside intended public directories.
Typical attack path
- Identify a Rack-based Ruby application exposing directory browsing or static directory indexes.
- Probe for Rack::Directory behavior by requesting paths that return generated directory listings, parent-directory links, or file index pages.
- Craft traversal requests using `../` or URL-encoded equivalents that resolve outside the configured root while still matching the root path prefix string.
- Target sibling paths with similar prefixes to the configured root, for example `/../root_example/` when the configured root path ends in `root`.
- Review HTTP responses for directory indexes, file names, source files, configuration files, backup archives, or secret material.
- Download exposed files and use any disclosed credentials, keys, application code, or infrastructure details for lateral movement or additional compromise.
How to detect it
To determine whether exploitation was attempted or successful, review HTTP access logs from web servers, reverse proxies, CDNs, WAFs, load balancers, and application logs for requests containing path traversal sequences directed at routes handled by Rack::Directory. Focus on requests with `../`, URL-encoded dot-dot sequences, double-encoded traversal, or paths resembling `/../<root-like-name>/`. Prioritize responses with HTTP 200, 206, or unusually large response bodies, as these may indicate successful file or directory disclosure; 301/302, 403, and 404 may still indicate probing. Look for Rack-generated directory index responses in application logs or captured HTTP telemetry, including HTML directory listings, parent-directory links, and file names outside expected static roots. Correlate suspicious requests with subsequent downloads of configuration files, source files, archives, hidden files, credentials, or keys. If file access auditing is enabled, look for the application process reading directories or files outside the intended public root at the same timestamps as suspicious web requests. No specific public threat actors or ransomware campaigns are currently associated with this issue in the provided data.
- /../root_example/
- ../root_example/
- %2e%2e%2froot_example%2f
- %2e%2e/root_example/
- ..%2froot_example%2f
- %252e%252e%252froot_example%252f
- GET /../
- GET /%2e%2e%2f
- GET /%2e%2e/
- GET /..%2f
- GET /%252e%252e%252f
- ../
- %2e%2e%2f
- ..%2f
- %252e%252e%252f
- Rack::Directory
- <title>Directory:
- <h1>Directory:
- Parent Directory
What your security team should do
- Inventory all Ruby applications, containers, gems, and vendored dependencies that include Rack, prioritizing internet-facing services and applications that serve directory indexes or static files through Rack::Directory.
- Upgrade affected Rack installations to 2.2.22, 3.1.20, 3.2.5, or later, and regenerate lockfiles and images to ensure the fixed dependency is actually deployed.
- Search application code and middleware configuration for `Rack::Directory` usage and disable it where directory listing is not a business requirement.
- Review filesystem layouts for public directories with similarly prefixed sibling directories, such as `root` and `root_example`, and remove sensitive readable content from adjacent paths.
- Add temporary WAF or reverse-proxy detections for traversal patterns while patching, but avoid relying on filtering as the permanent control.
- Review web access logs since public disclosure for traversal attempts, encoded dot-dot sequences, and successful 200 responses returning directory index content outside expected static paths.
- If suspicious access is found, investigate whether sensitive files were disclosed, rotate exposed credentials or keys, and review subsequent authentication and infrastructure logs for follow-on activity.
The record
Rack is a modular Ruby web server interface. Prior to versions 2.2.22, 3.1.20, and 3.2.5, `Rack::Directory`’s path check used a string prefix match on the expanded path. A request like `/../root_example/` can escape the configured root if the target path starts with the root string, allowing directory listing outside the intended root. Versions 2.2.22, 3.1.20, and 3.2.5 fix the issue.
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|---|---|---|
| RubyGems | rack | 0 | 2.2.22 |
| RubyGems | rack | 3.0.0.beta1 | 3.1.20 |
| RubyGems | rack | 3.2.0 | 3.2.5 |
| Chainguard | gitlab-exporter-18.6 | 0 | 18.6.6-r1 |
| Chainguard | gitlab-exporter-18.7 | 0 | 18.7.4-r1 |
| Chainguard | gitlab-exporter-18.8 | 0 | 18.8.4-r1 |
| Chainguard | gitlab-rails-ce-18.8 | 0 | 18.8.7-r0 |
| Chainguard | gitlab-rails-ce-18.9 | 0 | 18.9.3-r0 |
| Chainguard | gitlab-rails-ce-fips-18.8 | 0 | 18.8.7-r0 |
| Chainguard | gitlab-rails-ce-fips-18.9 | 0 | 18.9.3-r0 |
| Chainguard | logstash-8.17 | 0 | 8.17.10-r12 |
| Chainguard | logstash-8.17-iamguarded-compat | 0 | 8.17.10-r12 |
| Chainguard | logstash-8.17-with-output-opensearch | 0 | 8.17.10-r12 |
| Chainguard | logstash-8.19 | 0 | 8.19.11-r1 |
| Chainguard | logstash-8.19-iamguarded-compat | 0 | 8.19.11-r1 |
| Chainguard | logstash-8.19-with-output-opensearch | 0 | 8.19.11-r1 |
| Chainguard | logstash-9.0 | 0 | 9.0.8-r11 |
| Chainguard | logstash-9.0-iamguarded-compat | 0 | 9.0.8-r11 |
| Chainguard | logstash-9.0-with-output-opensearch | 0 | 9.0.8-r11 |
| Chainguard | logstash-9.1 | 0 | 9.1.10-r1 |
| Chainguard | logstash-9.1-bitnami-compat | 0 | 9.1.10-r1 |
| Chainguard | logstash-9.1-iamguarded-compat | 0 | 9.1.10-r1 |
| Chainguard | logstash-9.1-with-output-opensearch | 0 | 9.1.10-r1 |
| Chainguard | logstash-9.2 | 0 | 9.2.5-r1 |
| Chainguard | logstash-9.2-iamguarded-compat | 0 | 9.2.5-r1 |
| Chainguard | logstash-9.2-with-output-opensearch | 0 | 9.2.5-r1 |
| Chainguard | logstash-9.3 | 0 | 9.3.1-r1 |
| Chainguard | logstash-9.3-iamguarded-compat | 0 | 9.3.1-r1 |
| Chainguard | logstash-9.3-with-output-opensearch | 0 | 9.3.1-r1 |
| Chainguard | ruby3.2-rails-7.1 | 0 | 7.1.6-r2 |
| Chainguard | ruby3.2-rails-7.2 | 0 | 7.2.3-r2 |
| Chainguard | ruby3.2-rails-8.0 | 0 | 8.0.4-r2 |
| Chainguard | ruby3.2-rails-8.1 | 0 | 8.1.2-r1 |
| Chainguard | ruby3.3-rack-2.2 | 0 | 2.2.22-r0 |
| Chainguard | ruby3.3-rails-7.1 | 0 | 7.1.6-r2 |
| Chainguard | ruby3.3-rails-7.2 | 0 | 7.2.3-r2 |
| Chainguard | ruby3.3-rails-8.0 | 0 | 8.0.4-r2 |
| Chainguard | ruby3.3-rails-8.1 | 0 | 8.1.2-r2 |
| Chainguard | ruby3.4-rails-7.1 | 0 | 7.1.6-r2 |
| Chainguard | ruby3.4-rails-7.2 | 0 | 7.2.3-r2 |
| Chainguard | ruby3.4-rails-8.0 | 0 | 8.0.4-r2 |
| Chainguard | ruby3.4-rails-8.1 | 0 | 8.1.2-r1 |
| Chainguard | ruby4.0-rails-7.1 | 0 | 7.1.6-r1 |
| Chainguard | ruby4.0-rails-7.2 | 0 | 7.2.3-r2 |
| Chainguard | ruby4.0-rails-8.0 | 0 | 8.0.4-r1 |
| Chainguard | ruby4.0-rails-8.1 | 0 | 8.1.2-r1 |
| Wolfi | logstash-9.1 | 0 | 9.1.10-r1 |
| Wolfi | logstash-9.1-bitnami-compat | 0 | 9.1.10-r1 |
| Wolfi | logstash-9.1-iamguarded-compat | 0 | 9.1.10-r1 |
| Wolfi | logstash-9.1-with-output-opensearch | 0 | 9.1.10-r1 |
| Wolfi | logstash-9.2 | 0 | 9.2.5-r1 |
| Wolfi | logstash-9.2-iamguarded-compat | 0 | 9.2.5-r1 |
| Wolfi | logstash-9.2-with-output-opensearch | 0 | 9.2.5-r1 |
| Wolfi | logstash-9.3 | 0 | 9.3.1-r1 |
| Wolfi | logstash-9.3-iamguarded-compat | 0 | 9.3.1-r1 |
| Wolfi | logstash-9.3-with-output-opensearch | 0 | 9.3.1-r1 |
| Wolfi | ruby3.2-rails-8.0 | 0 | 8.0.4-r2 |
| Wolfi | ruby3.2-rails-8.1 | 0 | 8.1.2-r1 |
| Wolfi | ruby3.3-rack-2.2 | 0 | 2.2.22-r0 |
| Wolfi | ruby3.3-rails-8.0 | 0 | 8.0.4-r2 |
| Wolfi | ruby3.3-rails-8.1 | 0 | 8.1.2-r2 |
| Wolfi | ruby3.4-rails-8.0 | 0 | 8.0.4-r2 |
| Wolfi | ruby3.4-rails-8.1 | 0 | 8.1.2-r1 |
| Wolfi | ruby4.0-rails-8.1 | 0 | 8.1.2-r1 |
| openSUSE:Tumbleweed | rubygem-rack-2.2 | 0 | 2.2.22-1.1 |
Related vulnerabilities
- CVE-2026-25500 Rack is a modular Ruby web server interface. Also affects rack
- CVE-2025-61919 Rack is vulnerable to a memory-exhaustion DoS through unbounded URL-encoded body parsing Also affects rack
- CVE-2025-61780 Rack has a Possible Information Disclosure Vulnerability Also affects rack
- CVE-2025-61770 Rack's unbounded multipart preamble buffering enables DoS (memory exhaustion) Also affects rack
- CVE-2025-61771 Rack: Multipart parser buffers large non‑file fields entirely in memory, enabling DoS… Also affects rack
- CVE-2025-61772 Rack's multipart parser buffers unbounded per-part headers, enabling DoS (memory… Also affects rack
- CVE-2025-59830 Rack has an unsafe default in Rack::QueryParser allows params_limit bypass via… Also affects rack
- CVE-2025-46336 Rack::Session is a session management implementation for Rack. Also affects rack