VulnerabilityJul 30, 20268 min read

Rails Active Storage arbitrary file read (CVE-2026-66066): an image upload can leak your secrets

A crafted image upload can make Rails Active Storage read arbitrary files off the server - including config/master.key and secret_key_base - with no login required. It's rated 9.5 critical, fixes are already out, and full technical details are expected by 28 August 2026. Here's the briefing, the exposure test, and the action list.

ST
Staatse
Security research · Staatse
Contents

The short version

Here's an uncomfortable idea: a feature as ordinary as "let users upload a profile picture" can, under the right conditions, hand an attacker the contents of your server's secret files. No login. No user interaction. Just an upload.

That's the shape of CVE-2026-66066, a critical arbitrary file read in Ruby on Rails' Active Storage component. Where image processing is handled by the libvips library - the default from Rails 7.0 onwards - an application that accepts image uploads from untrusted users can let an unauthenticated attacker read arbitrary files from the server by uploading a specially crafted file.

9.5CVSS 4.0 - critical severity
0Credentials needed - unauthenticated
Aug 28Full technical details expected by
3Fixed lines - 7.2.3.2 / 8.0.5.1 / 8.1.3.1

If you do nothing else today: upgrade the activestorage gem to 7.2.3.2, 8.0.5.1 or 8.1.3.1 (or later on your branch), confirm libvips is 8.13 or newer, and rotate every secret the application process could read - starting with secret_key_base and your master key.

What: Active Storage did not disable libvips operations flagged as unsafe when handling untrusted input, giving an attacker who can upload a crafted file an arbitrary file-read primitive. Status: not known to be exploited in the wild and no public proof-of-concept at time of writing - but fixes are out and full technical details are expected no later than 28 August 2026.

What actually broke

The root cause is a trust boundary that wasn't drawn tightly enough. libvips ships a set of operations that its own maintainers flag as unsafe for untrusted input - the "unfuzzed" operations, so called because they haven't been through the fuzzing regime the rest of the library has. libvips provides a mechanism to block them precisely because processing attacker-supplied files is a different threat model to processing your own.

Rails Active Storage did not disable those operations when handling untrusted input. An attacker who can upload a crafted file and cause a variant to be generated from it can invoke one of them and disclose the contents of arbitrary files that the Rails worker process can read.

That gives an attacker an arbitrary file-read primitive - and in a Rails app, file read is rarely where the damage stops.

Why "just a file read" is a full-blown crisis

The reason this rates 9.5 rather than a comfortable medium is what those files contain. A Rails application keeps the keys to its entire kingdom in files the worker process reads by design:

  • secret_key_base - the signing key for cookies, sessions and message verifiers
  • The Rails master key, and by extension everything in encrypted credentials
  • Database credentials
  • Cloud storage keys - including, neatly, the Active Storage service credentials
  • Third-party API tokens
1

Upload

The attacker sends a specially crafted file to any endpoint that accepts image uploads from untrusted users - an avatar form, a public support-ticket attachment, a listing photo. No account required.

2

Trigger

Active Storage hands the file to libvips for processing. Because the unsafe operations were never blocked, the crafted input invokes one of them.

3

Read

The operation discloses the contents of an arbitrary file readable by the Rails worker process. In a standard deployment that includes config/master.key, config/credentials.yml.enc, environment files, and anything else the app user can open.

4

Escalate

With secret_key_base in hand the attacker can forge trusted, signed data - a well-worn path to code execution in Rails. Database, cloud and third-party credentials carry the compromise outward into every connected system.

So the practical consequences are:

  • Remote code execution. A stolen secret_key_base lets an attacker mint signed payloads the application will trust and deserialise. Rails has a long, well-documented history of that ending in code execution.
  • Lateral movement. Valid database, object-storage and third-party credentials get reused against the systems they unlock, none of which need to be running Rails to be affected.
  • Data exposure. Application and customer data - on the server and in every connected service - becomes accessible and exfiltratable.

A read primitive that leaks your master key isn't a read primitive. It's a skeleton key to the whole application and everything it touches.

Are you affected?

You're exposed when all of the following are true:

  1. Your app uses libvips for Active Storage image processing - config.active_storage.variant_processor = :vips, which has been the default since Rails 7.0.
  2. It accepts image uploads from untrusted or unauthenticated users.
  3. Your libvips build includes one or more of the unsafe operations that can read files.

Applications using the MiniMagick processor instead of libvips are not exposed through this particular path.

Three commands will tell you where you stand:

# Which processor is Active Storage actually using?
bin/rails runner 'puts Rails.application.config.active_storage.variant_processor'

# Which activestorage version is installed?
bundle list | grep activestorage

# Which libvips is on the box (and in your container image)?
vips --version

Run the last one inside the container or on the host that actually processes uploads, not on a developer laptop. A Dockerfile that installs libvips from a distribution package is very often several releases behind what the developer has locally via Homebrew.

Don't lean on "we don't generate variants." Variant generation is part of the mechanism, but it isn't listed as a separate condition for exposure - accepting untrusted image uploads through a libvips-backed Active Storage is the criterion. Thumbnails get generated in more places than most teams remember: admin previews, background jobs, mailer attachments, and any variant call in a view that nobody has looked at since it was written.

Affected gem versions and fixes

Affected activestorage version First fixed release Status
< 7.2.3.2 7.2.3.2 VULNERABLE
8.0.0 - 8.0.5 8.0.5.1 VULNERABLE
8.1.0 - 8.1.3 8.1.3.1 VULNERABLE

A few caveats that catch people out:

  • Rails 7.0 and 7.1 are end-of-life and get no fix. libvips is the default processor on both, which is an unfortunate combination: the branches most likely to be exposed are the ones with nothing to upgrade to in place. Apps on 7.0 or 7.1 must move to 7.2.3.2 or later.
  • Rails 6.0 through 6.1.7.10 are affected only where Active Storage was explicitly configured to use libvips - it wasn't the Rails 6 default, so most 6.x apps are on MiniMagick and out of scope through this path. Check rather than assume; someone may have switched it for performance years ago.
  • Patched installs also require libvips 8.13 or later, and ruby-vips 2.2.1 or later where ruby-vips is in play. Bumping the gem while leaving an ancient libvips underneath it doesn't finish the job.

What to do - in priority order

1. Upgrade

Move activestorage to a fixed release - 7.2.3.2, 8.0.5.1, or 8.1.3.1, or later on your branch - and make sure libvips is 8.13 or newer in every environment that processes uploads. That means the production image, the background-worker image, and any separate media-processing service, not just the web tier.

2. If you can't upgrade Rails immediately, block the unsafe operations

Interim options, depending on what you have available:

  • With libvips 8.13+ present, set the VIPS_BLOCK_UNTRUSTED environment variable to disable the unsafe operations:
export VIPS_BLOCK_UNTRUSTED=1
  • With ruby-vips 2.2.1+, block them from an application initialiser:
# config/initializers/vips.rb
Vips.block_untrusted(true)
  • If only libvips older than 8.13 is available, remove the libvips dependency - for example, drop ruby-vips from the Gemfile and switch the variant processor - until libvips can be upgraded. Earlier versions have no mechanism to block these operations, so there is no configuration that makes them safe.

Treat all three as a bridge to the upgrade, not a substitute for it.

3. Rotate your secrets - this is mandatory, not optional

This is the step people skip, and it's the one that matters most.

Patching does not undo a file that was already read. If anyone exploited this before you upgraded, your secrets left the building and the patch cannot recall them. A stolen secret_key_base keeps working against a fully patched application - that's the whole point of a signing key.

So treat every secret readable by the application as potentially compromised and rotate it: secret_key_base, the master key and everything it decrypts, database credentials, Active Storage service keys, and third-party tokens. And do not keep the old values around as fallbacks - a rotation that leaves the previous key accepted for "compatibility" has rotated nothing.

4. Reduce exposure and watch the logs

Where it's feasible, restrict image uploads to authenticated or otherwise trusted users, and monitor application and system logs for unusual upload activity - malformed files, repeated processing errors, uploads to endpoints that normally see very little traffic.

The clock on this one

This vulnerability has an unusual property: a published deadline.

  1. Now

    Fixes out, no public exploit

    Fixed releases are already available. There's no known exploitation in the wild and no public proof-of-concept, which means right now you are patching ahead of the threat rather than behind it. That is a rare position to be in.

  2. 2026/Aug/28

    Full technical details expected

    Complete details are expected to be published no later than 28 August 2026. The moment the attack chain is public, the gap between "researchers know" and "everyone knows" closes, and opportunistic scanning of upload endpoints becomes far more likely.

The safe read is to patch and rotate before that window opens, not after. "Not currently exploited" is a description of today, not a risk assessment - and on a bug this easy to reach from the open internet, it's a poor reason to wait.

The severity of a bug lives in its consequences, not its category. "Arbitrary file read" sounds contained right up until you remember that a Rails app keeps its master key in a file the worker process opens by design.

- Staatse advisory desk, July 2026

Key takeaways

  • CVE-2026-66066 lets an unauthenticated attacker read arbitrary files from a Rails server by uploading a crafted image, wherever Active Storage processes untrusted uploads through libvips - the default since Rails 7.0.
  • The files it reaches typically include secret_key_base and the master key, which turns a read primitive into forged signed data, remote code execution, and lateral movement through database, cloud and third-party credentials.
  • Upgrade activestorage to 7.2.3.2, 8.0.5.1 or 8.1.3.1 and ensure libvips 8.13+ / ruby-vips 2.2.1+. Rails 7.0 and 7.1 are end-of-life and get no fix - they must move to 7.2.3.2 or later.
  • If you can't upgrade yet, block the unsafe operations with VIPS_BLOCK_UNTRUSTED or Vips.block_untrusted(true). Then rotate every secret the app could read - patching does not recall a file that was already stolen.

The takeaway

CVE-2026-66066 is a good reminder that the interesting attack surface is rarely the code you wrote. It's the library underneath it, running with your process's file permissions, doing exactly what it was asked to do with input you never should have trusted it to parse.

Upgrade activestorage, block the unsafe libvips operations if you can't patch straight away, and - crucially - rotate every secret the application could reach. There's a disclosure deadline on the calendar and no exploit in the wild yet. That combination gives you a head start most advisories don't. Use it.

If you want help working out whether your Rails estate is exposed - or checking whether anyone got there first - our web application security testing and managed security teams can scope a focused review. Get in touch and we'll walk your team through it.

References & further reading

  1. Ruby on RailsSecurity advisory GHSA-xr9x-r78c-5hrm - Active Storage arbitrary file read via libvips
  2. NVDCVE-2026-66066 - Ruby on Rails Active Storage arbitrary file read
Need a hand?

Need help securing your business?

If anything in this report applies to your stack, we can scope a focused assessment within two business days.