Home/ Blog/ Security news/ Article
Blog · Security news

Unauthenticated attacker can read any file on a Ruby on Rails server via a crafted image (CVE-2026-66066)

CVE-2026-66066 lets an unauthenticated attacker upload a crafted image to a Rails app and read any server file, including its signing key. Patch now.

A photo-shaped envelope slips through a vault door slot toward a glowing master key inside

Image processing is code execution surface. That is the uncomfortable lesson behind CVE-2026-66066, a critical flaw in Ruby on Rails that the framework's security team disclosed on July 29, 2026. An unauthenticated attacker who can get a Rails application to build a single image thumbnail can read any file the server process can reach. The file that matters most is secret_key_base, the one key Rails uses to sign every session and cookie. Read it once and you have not just leaked a file. You have inherited the trust the whole application is built on. GitHub, acting as the numbering authority, scored it 9.5 on the CVSS v4 scale.

The bug lives in Active Storage, the file-attachment layer bundled with Rails. When an app renders a resized or cropped version of an uploaded image, Active Storage hands the raw file to libvips, a fast image library, without switching off the loaders that libvips itself marks as untrusted. Two research teams, Ethiack and GMO Flatt Security, found the same problem independently and reported it on July 22. The Rails team shipped fixes a week later and held the full technical writeup for August 28, but the details did not stay secret: by July 31, a working analysis and a third-party proof-of-concept were already public.

Rails lineVulnerable versionsPatched release
7.0 through 7.27.2.3.1 and earlier7.2.3.2
8.08.0.5.0 and earlier8.0.5.1
8.18.1.3.0 and earlier8.1.3.1
6.xonly if the vips processor was enabled by handupgrade libvips or drop the dependency
Source: Rails security advisory GHSA-xr9x-r78c-5hrm.

Every line since 7.0 is in scope by default: anything at or below 7.2.3.1, 8.0.5.0, or 8.1.3.0. The patch bumps each of those a single notch, so a 7.2 app moves to 7.2.3.2, an 8.0 app to 8.0.5.1, and an 8.1 app to 8.1.3.1. Rails 6.x escapes unless someone deliberately switched its image processor over to vips, which it never used out of the box.

How a picture reads your filesystem

The trick is that the upload is not really a picture. An attacker crafts a MAT version 7.3 file, a scientific data format built on top of HDF5, and labels it with an image/png content type. The file opens with the bytes MATLAB 5.0, which is enough to slip past the first sanity check. libvips then routes it to libmatio, the library that reads MAT files, and libmatio chases an external-dataset pointer buried in that file straight to whatever path the attacker wrote in. Whatever sits at that path comes back inside what the app believes is a harmless rendered thumbnail.

This is why the standard defenses do nothing here. Checking that an upload has an image content type does not help, because the file claims to be a PNG. Checking a magic-byte signature does not help either, because the interesting parsing happens several layers down, inside a format most developers have never heard of. The weakness is not really in Rails' own code. It lives in the seam where Active Storage hands a file off to a general-purpose image library that was never built to face an attacker's input. We saw the same shape in the Budibase symlink read via a crafted archive and in the MCP Atlassian upload flaw: an upload feature becomes a file-read primitive the moment the parser behind it is more powerful than the input it is fed.

Why one file read is a full compromise

An arbitrary file read sounds like a data-leak problem. On Rails it is an authentication problem. secret_key_base is not just another secret; it is the root of Rails' entire signing scheme. Every signed cookie, every session, and the variation_key that authorizes an Active Storage transformation all trace back to it. Recover that one value from the process environment and you can mint valid sessions for any user, including an administrator, without ever touching a password. That is the collapse: a single read does not expose one file, it hands over the key the application uses to trust everyone.

The escalation goes further. Security firm Rapid7, which published an early analysis under the name KindaRails2Shell, showed that the recovered signing material can be turned into remote code execution by forging a malicious image variation, a path that does not even need the older Ruby object-deserialization tricks. Ethiack chained its own read up to code execution through a separate ImageMagick issue, CVE-2025-24293. So the honest way to read this bug is not "attacker can read a file." It is "attacker can become any user, and with a little more work, run code." Anything the process can read is fair game: the database login, the object-storage keys, the mail and payment tokens, the lot. That is the same lesson the Keycloak vault-secret leak taught: the value of a read primitive is set by what it can point at.

The exposure you did not know you had

Here is the part the coverage has mostly skipped. You do not have to build an upload feature to be exposed. Mount Active Storage's routes and the anonymous direct-upload endpoint comes along for free, even in an app whose own screens never once offer a visitor a file picker. Your real attack surface includes a route you may believe you do not use. To fire the bug an attacker also needs a genuine signed variation_key, which they can lift from any public page that renders an Active Storage image, such as an avatar or a product photo. Most apps that store images at all hand one out for free.

Then there is the evidence problem. Rails runs a background job that sweeps away unattached blobs, the stray uploads that never got tied to a record. An attack like this leaves exactly those strays behind, so the sweep is quietly shredding your evidence: the cron cadence, not the intruder, sets the clock on any investigation. Rails shipped a forensic kit, rails-forensics-CVE-2026-66066, so defenders can comb their stored uploads for the malicious files before that job clears them out. Pause the cleanup job before you start hunting. This read-then-abuse-secrets pattern is the same one behind the recent Adminer takeover: the initial bug is only the door.

What to do today

Patch first. Take a 7.2 app to 7.2.3.2, an 8.0 app to 8.0.5.1, an 8.1 app to 8.1.3.1, and check that the box carries libvips at 8.13 or above alongside ruby-vips 2.2.1 or better. Cannot cut a release this hour? Shut off the dangerous loaders in place: export VIPS_BLOCK_UNTRUSTED, or drop a Vips.block_untrusted(true) line into an initializer. If your libvips is too old to know that switch, the only honest fallback is pulling the libvips dependency out and rendering images with something else.

Patching is the cheap part. The expensive part is that you must now treat every secret the app could read as burned. Cycle secret_key_base first, which tears up every signed cookie and boots all users out, then the database login, the storage service keys, and each outside token that shares the process environment. That blast radius is the piece teams underestimate: the fix is one bundle update, but the cleanup is a full credential rotation across everything the process can touch. Before you rotate, run the forensic toolkit against your stored blobs to see whether anyone reached the door before you closed it.

Topics

Frequently asked questions

What is CVE-2026-66066?

CVE-2026-66066 is a critical flaw in Ruby on Rails Active Storage, scored 9.5 on CVSS v4. An unauthenticated attacker uploads a crafted file disguised as an image, and when Rails processes it into a variant the file reads arbitrary files on the server, including secret_key_base.

Which Rails versions are affected and where is the fix?

In the default setup it affects Rails 7.0.0 through 7.2.3.1, 8.0.0 through 8.0.5.0, and 8.1.0 through 8.1.3.0. The 6.x line is only exposed if vips was enabled by hand. The patched releases are 7.2.3.2, 8.0.5.1, and 8.1.3.1.

Is CVE-2026-66066 being exploited?

No in-the-wild exploitation was confirmed before disclosure on July 29, 2026. That window is closing fast: an independent technical analysis and a third-party proof-of-concept were public by July 31, so treat exposed apps as at immediate risk and patch now.

Why does reading one file mean a full compromise?

Because the target file, secret_key_base, is the root of Rails' signing scheme. It signs every session, cookie, and Active Storage variation key. Recovering it lets an attacker forge sessions for any user, including an admin, and researchers have chained it further into remote code execution.

I do not have an upload feature. Am I still exposed?

Possibly yes. When Active Storage routes are mounted, the anonymous direct-upload endpoint is present by default, even if your own screens never accept files. If any public page renders a stored image, an attacker can also lift the signed variation key they need. Patch regardless.

What do I do besides patching?

Rotate every secret the app could read. Start with secret_key_base, which logs all users out, then the database password, Active Storage service keys, and third-party tokens in the same environment. Run the rails-forensics-CVE-2026-66066 toolkit against stored blobs before the scheduled unattached-blob purge erases the evidence.

Ready to meet the Guardians?

Deploys fast - agentless for monitoring and cloud, a lightweight agent for deep endpoint security. Just Suriq, standing watch.