DEV Community

OnaEiuspkz
OnaEiuspkz

Posted on

CVE-2026-66066: an image upload path that reads /proc/self/environ

CVE-2026-66066: an image upload path that reads /proc/self/environ

The shortest way to describe CVE-2026-66066 is that a framework default turned an ordinary avatar upload into a file read primitive, and that the file it can read holds the key to everything else. The chain has been nicknamed KindaRails2Shell and it is scored 9.5.

Technical context

The identifier affects Ruby on Rails applications that use Active Storage with libvips configured as the variant processor. Rails 7.0 and later enable that combination by default. libvips can parse a range of scientific and data formats, including HDF5 and MATLAB files, and those formats support references to external files. The library was not restricting that capability for untrusted uploads.

Four conditions have to hold together for a deployment to be exposed: Active Storage is in use, the variant processor is vips, an upload route is reachable by untrusted users, and libvips is older than 8.13. Corrected Rails releases are 7.2.3.2, 8.0.5.1 and 8.1.3.1, and the setting Vips.block_untrusted = true closes the parsing path.

Explanation and the exploitation path

An attacker uploads a file that presents as an image and passes extension and basic type checks, but contains an HDF5 structure with an external file reference. When the application generates a variant, for example a thumbnail, libvips resolves the reference and reads the referenced file. The useful target is /proc/self/environ, which exposes the process environment and therefore SECRET_KEY_BASE.

That value is the application's signing key. With it, an attacker can forge a valid signed variant parameter, and variant parameters are processed through Ruby Marshal deserialization. Constructing a gadget chain that reaches MiniMagick yields command execution with the privileges of the Rails process.

Two details in the public research are worth noting. The vulnerability class is registered under CWE-1188, insecure default initialization of a resource, which frames the problem as a default rather than an oversight in application code. And the automated tooling moved quickly: a Metasploit module was submitted to Rapid7 shortly after disclosure, so the barrier to use is low even though the underlying chain is intricate.

Defensive implications

Upgrading and setting Vips.block_untrusted = true are the primary measures. Because the leaked value is a signing secret, remediation also requires rotating SECRET_KEY_BASE and any other secret readable from the process environment; an upgrade alone leaves a stolen key valid.

For verification, an operator can determine exposure locally: check the framework and component versions, confirm whether the variant processor is vips or mini_magick, and look for a direct uploads route in the routing table. A route that answers means untrusted upload reaches the processing path.

The broader lesson is about which libraries are allowed to parse untrusted input. A format-specific parser that supports external references is a capability a web application almost never needs, and the safe default is to disable that capability rather than to rely on the upload path being unreachable.

References

[1] GitHub advisory GHSA-xr9x-r78c-5hrm.

[2] NVD entry for CVE-2026-66066.

Top comments (0)