← Back to Feed CACHED · 2026-07-31 01:48:30 · CACHE_KEY CVE-2026-66066
CVE-2026-66066 · CWE-1188 · Disclosed 2026-07-30

Action Pack is a framework for handling and responding to web requests.

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

Your image upload thumbnail generator is an open door to every secret on the server

CVE-2026-66066, dubbed KindaRails2Shell by its discoverers at Ethiack, is a pre-authentication arbitrary file read chained to remote code execution in Ruby on Rails Active Storage. The flaw lives in how Active Storage hands untrusted uploads to libvips without disabling its "unfuzzed" format loaders — handlers for formats like MATLAB/HDF5 that were never security-hardened. An attacker uploads a crafted file to any endpoint that triggers variant generation (thumbnails, avatars, previews), and the unfuzzed loader reads arbitrary files as the Rails worker process. The primary targets are SECRET_KEY_BASE, config/master.key, database credentials, and cloud storage tokens. With SECRET_KEY_BASE in hand, the attacker forges a signed Ruby Marshal deserialization payload to achieve full remote code execution. Affected versions: activestorage < 7.2.3.2, 8.0.0–8.0.5.0, 8.1.0–8.1.3.0, and Rails 6.x only where variant_processor = :vips was explicitly enabled. The vips processor has been the default since Rails 7.0, meaning the vast majority of modern Rails apps running Active Storage are in scope.

GitHub (as CNA) scored this 9.5 on CVSS v4.0. That score is justified. This is a zero-click, unauthenticated, network-accessible chain that ends in full RCE on the web server, requires only that the app accept image uploads (the entire purpose of Active Storage), and runs in the default configuration. The Ethiack team estimates 500,000+ sites are potentially exposed because the vips processor is default in official Rails Docker images and Debian/Ubuntu packages. The only friction is that the app must actually generate variants from uploads — but thumbnail/avatar generation is the canonical Active Storage use case. noisgate independently assesses this as CRITICAL at 9.3.

"Unauthenticated file read to RCE on default Rails 7+/8.x apps accepting image uploads — patch now."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Identify upload endpoint

The attacker locates any form or API endpoint on the target Rails application that accepts image file uploads via Active Storage. This is trivially discoverable through normal application usage — profile photo uploads, document attachments, product image forms, etc. No authentication is required if the upload endpoint is public-facing.
Conditions required:
  • Target runs Rails 7.0+ or 8.x with Active Storage (default config)
  • Application exposes at least one image upload endpoint to untrusted users
Where this breaks in practice:
  • Applications that use CarrierWave, Shrine, or Paperclip instead of Active Storage are not affected
  • Internal-only apps behind VPN reduce the reachable population
Detection/coverage: WAF rules inspecting upload Content-Type vs. actual file magic bytes may flag mismatched uploads, but the crafted file can use a valid image extension.
STEP 02

Upload crafted file to trigger unfuzzed loader

The attacker uploads a malicious file crafted to invoke one of libvips' unfuzzed format handlers (MATLAB/HDF5 family). The file is structured to include directives that reference local filesystem paths. When Active Storage processes the upload to generate a variant (thumbnail, resize), libvips dispatches to the unfuzzed loader which follows the embedded file references. Tool: rails_vips_oast_poc.py (Ethiack PoC).
Conditions required:
  • libvips built with vulnerable third-party loaders (default on Debian/Ubuntu packages)
  • Application generates variants from uploads (thumbnails, avatars, previews)
Where this breaks in practice:
  • Custom libvips builds that exclude MATLAB/HDF5 loaders are not vulnerable, but this is rare
  • Variant generation might be async (Sidekiq/GoodJob), adding a small delay but not blocking exploitation
Detection/coverage: Monitor for variant generation requests that trigger unusual libvips loader paths. Application-level logging of Vips::Error exceptions during variant processing may indicate exploitation attempts.
STEP 03

Read SECRET_KEY_BASE and master key

The unfuzzed loader follows the crafted file's embedded references and reads the contents of targeted files — typically /proc/self/environ (for SECRET_KEY_BASE), config/master.key, or config/credentials.yml.enc. The file contents are embedded in the generated variant image output, which the attacker retrieves via the variant URL. This step yields the cryptographic material needed for step 4.
Conditions required:
  • Rails process has filesystem read access to secret files (always true)
  • Variant output is accessible to the requester (standard Active Storage behavior)
Where this breaks in practice:
  • Containerized deployments may limit readable paths, but /proc/self/environ is almost always available
  • Secret management via external vaults (e.g., AWS Secrets Manager injected at runtime) still exposes env vars
Detection/coverage: File integrity monitoring (FIM) on config/master.key reads. Anomalous reads of /proc/self/environ by the Rails worker process. SIEM correlation of variant generation + unusual file access patterns.
STEP 04

Forge Marshal deserialization payload for RCE

With SECRET_KEY_BASE or the Rails master key in hand, the attacker crafts a signed Ruby Marshal serialization payload. Rails trusts signed cookies and session data deserialized via Marshal.load. The attacker generates a malicious serialized object (e.g., using ysoserial-style Ruby gadget chains) and signs it with the stolen key. Submitting this payload as a session cookie or signed parameter triggers arbitrary code execution as the Rails worker process.
Conditions required:
  • Stolen SECRET_KEY_BASE or master key from step 3
  • Target uses cookie-based sessions or any Marshal-backed signed data (Rails default)
Where this breaks in practice:
  • Applications using JWT-only sessions or database-backed sessions without Marshal may limit this vector, but cookie sessions are the Rails default
  • Modern Rails versions have some Marshal restrictions, but known gadget chains still work
Detection/coverage: Anomalous session cookie sizes or structures. Deserialization error spikes in Rails logs. EDR/HIDS detecting unexpected child processes spawned by the Rails worker (e.g., sh, curl, wget).
STEP 05

Post-exploitation: lateral movement and data exfiltration

With RCE on the web server, the attacker has access to the full application database via stored credentials, cloud storage buckets (S3/GCS/Azure) via Active Storage service keys, and any API tokens in the environment. From here, standard post-exploitation applies: credential harvesting, pivoting to internal networks, accessing cloud metadata services (169.254.169.254), and establishing persistence.
Conditions required:
  • Successful RCE from step 4
Where this breaks in practice:
  • Network segmentation and cloud IAM least-privilege reduce blast radius
  • Container runtimes with read-only filesystems and no-new-privileges limit persistence options
Detection/coverage: Cloud audit logs (CloudTrail, GCP Audit) for anomalous API calls using stolen service credentials. EDR on the host detecting reverse shells or unusual outbound connections from the web tier.
03 · Intelligence Metadata

The supporting signals.

In-the-Wild StatusNo confirmed exploitation as of 2026-07-31. Rails Security Team states "no awareness of exploitation or attempted exploitation." Ethiack withheld full technical details until August 28, 2026 to allow patching.
Proof-of-ConceptAvailable. Ethiack published rails_vips_oast_poc.py with a self-contained lab demonstrating the full file-read-to-RCE chain. A third-party GitHub repo appeared within hours of disclosure (July 29, 17:30 UTC). Weaponization is straightforward.
EPSS ScoreNot yet scored by FIRST (disclosed <48 hours ago). Given the pre-auth/default-config/RCE chain and public PoC, expect high percentile once scored.
KEV StatusNot listed as of 2026-07-31. No confirmed in-the-wild exploitation yet, but the 48-hour window since disclosure is short.
CVSS Vector (v4.0, GitHub CNA)CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H9.5 Critical. Network-accessible, low complexity, no privileges, no user interaction, high impact across all CIA dimensions including subsequent systems.
Affected Versionsactivestorage < 7.2.3.2, 8.0.0–8.0.5.0, 8.1.0–8.1.3.0. Rails 6.x affected only if config.active_storage.variant_processor = :vips was set manually. Rails 7.0–7.1 and 6.x lines have no upstream fix (EOL).
Fixed Versions7.2.3.2, 8.0.5.1, 8.1.3.1. Requires libvips ≥ 8.13 and ruby-vips ≥ 2.2.1. The fix calls Vips.block_untrusted(true) at boot.
Exposure DataEthiack estimates 500,000+ sites potentially exposed. The vips processor is default in official Rails Docker images and Debian/Ubuntu libvips packages. Shodan/Censys fingerprinting of Rails apps is imprecise, but the framework powers a significant share of internet-facing web applications.
Disclosure Timeline2026-07-22: Ethiack reports to Rails. 2026-07-29: Public advisory, patches released. 2026-08-28: Full technical writeup scheduled. Turnaround: 7 days from report to patch — aggressive.
ResearchersEthiack: André Baptista, Bruno Mendes, Rafael Castilho. GMO Flatt Security: RyotaK (independent discovery). Named KindaRails2Shell.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to CRITICAL (9.3/10)

The single most decisive factor is that this is an unauthenticated, pre-auth RCE chain against default-configured, internet-facing Rails applications — the attack requires only an image upload form, which is the canonical use case for the affected component. With 500,000+ estimated exposed sites, a public PoC available within hours, and the vips processor being the default since Rails 7.0, the reachable population and weaponization readiness both support a CRITICAL assessment.

HIGH Vulnerability existence and file-read impact confirmed by vendor advisory and multiple independent researchers
HIGH RCE chain viability — PoC demonstrates full SECRET_KEY_BASE extraction to Marshal deserialization
MEDIUM Exposure population — 500K+ estimate is researcher-sourced, not independently validated via scanner data

Why this verdict

  • Pre-auth, zero-click, network-accessible: The attack requires no authentication, no user interaction, and targets a network-facing upload endpoint. There is no friction from access control — anyone who can reach the upload form can exploit this.
  • Default configuration is vulnerable: Rails 7.0+ ships with variant_processor = :vips as the default. Debian/Ubuntu libvips packages include the vulnerable loaders. The attacker does not need a misconfiguration — they need the *default* configuration.
  • Full RCE chain with public PoC: The file read alone would be HIGH; the chain to SECRET_KEY_BASE extraction → signed Marshal deserialization → RCE elevates this to CRITICAL. The PoC (rails_vips_oast_poc.py) was published within hours of disclosure.
  • Massive reachable population: Ethiack estimates 500,000+ potentially vulnerable sites. Rails is one of the most widely deployed web frameworks, and Active Storage with image uploads is a bread-and-butter feature.
  • Role multiplier: Rails is the *typical* web application tier (line-of-business apps, SaaS products, e-commerce). Some high-value deployments include GitLab (CI/CD + supply chain), Discourse (community platforms), Shopify storefronts, and internal admin tools. While GitLab uses CarrierWave (not Active Storage), many internal Rails tools and SaaS products *do* use Active Storage for file handling. On a typical Rails web server, the blast radius is full server compromise + database access + cloud credential theft. For internal admin tools, the chain can pivot to internal network access. The affected component is a web application framework, where ≥80% of installs are internet-facing application servers — this is a canonical high-value-role component for web-tier attacks. Verdict floor: CRITICAL.

Why not higher?

A 9.3 rather than 9.5+ reflects two minor mitigants: (1) the chain requires the application to actually *generate variants* from uploads, not just store them — apps using has_one_attached without ever calling .variant() are not exploitable, and (2) the RCE step via Marshal deserialization has some deployment-specific variability (apps using non-cookie session stores or with custom Marshal restrictions face a slightly higher bar). These narrow the universality slightly below a perfect 10.

Why not lower?

Downgrading below CRITICAL would require evidence that the reachable population is small or that meaningful friction exists in the chain. Neither is true: the vips processor is the *default*, image uploads with thumbnailing is the *canonical* Active Storage use case, the attack is *unauthenticated*, and a *public PoC* exists. The only prerequisite — that the app accepts and processes image uploads — is satisfied by the overwhelming majority of Active Storage deployments. No modern security tool (WAF, EDR) reliably stops this chain at the upload stage because the malicious file is structurally valid.

05 · Compensating Control

What to do — in priority order.

  1. Set VIPS_BLOCK_UNTRUSTED=true environment variable immediately — This tells libvips ≥ 8.13 to refuse loading via unfuzzed/untrusted format handlers, neutralizing the file-read vector entirely. Deploy within 3 days per the noisgate mitigation SLA for CRITICAL. Verify libvips ≥ 8.13 is installed first — older versions ignore this variable silently.
  2. Add Vips.block_untrusted(true) to a Rails initializer — Alternative to the env var approach. Create config/initializers/vips_security.rb with require 'vips'; Vips.block_untrusted(true). Requires ruby-vips ≥ 2.2.1. This is the code-level equivalent and survives env var misconfiguration.
  3. Rotate all application secrets after patching — Even if you patch before exploitation, assume secrets *may* have been read. Rotate SECRET_KEY_BASE, RAILS_MASTER_KEY, database credentials, S3/GCS/Azure storage keys, and all third-party API tokens. Changing SECRET_KEY_BASE invalidates all active sessions — plan for user re-authentication.
  4. Disable variant generation for untrusted uploads as a stopgap — If patching or setting the env var is not immediately possible, remove .variant() calls on user-uploaded attachments. This breaks thumbnails/previews but eliminates the trigger. Only viable as a <3 day bridge.
  5. Upgrade libvips to ≥ 8.13 — The patched Rails versions require libvips ≥ 8.13 to call block_untrusted. On Debian/Ubuntu, check vips --version. If running older libvips, the Rails patch will raise a boot exception. Upgrade the system package or use a custom build.
What doesn't work
  • WAF upload scanning — the malicious file uses a valid image extension and can pass basic MIME-type checks. WAF rules based on file extension or Content-Type will not catch the crafted payload because the exploit leverages legitimate libvips format parsing, not a malformed upload.
  • Rate limiting uploads — this is a single-request exploit. Rate limiting does not help; the attacker needs exactly one upload to read a file and one more to deliver the RCE payload.
  • Network-level IDS/IPS — the upload and response are standard HTTP/HTTPS traffic. There is no network signature that distinguishes the malicious upload from a legitimate image upload without deep content inspection of the generated variant output.
  • Disabling Active Storage entirely — while effective, this is not a compensating control; it removes application functionality. Use VIPS_BLOCK_UNTRUSTED=true to surgically disable the vulnerable code path without breaking uploads.
06 · Verification

Crowdsourced verification payload.

Run this script on each Rails application host (or in each container) to check the Active Storage version and libvips configuration. Execute as the deploy user or any user with read access to the Gemfile.lock: bash check_cve_2026_66066.sh /path/to/rails/app. No special privileges required.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_66066.sh — Detect CVE-2026-66066 exposure
# Usage: bash check_cve_2026_66066.sh /path/to/rails/app
# Exit codes: 1=VULNERABLE, 0=PATCHED, 2=UNKNOWN

set -euo pipefail

APP_DIR="${1:-}"
if [ -z "$APP_DIR" ] || [ ! -d "$APP_DIR" ]; then
  echo "Usage: $0 /path/to/rails/app"
  exit 2
fi

GEMFILE_LOCK="$APP_DIR/Gemfile.lock"
if [ ! -f "$GEMFILE_LOCK" ]; then
  echo "UNKNOWN — Gemfile.lock not found at $GEMFILE_LOCK"
  exit 2
fi

# Extract activestorage version
AS_VERSION=$(grep -A1 '^ *activestorage ' "$GEMFILE_LOCK" | grep -oP '\(\K[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1)
if [ -z "$AS_VERSION" ]; then
  echo "UNKNOWN — activestorage gem not found in Gemfile.lock (may not use Active Storage)"
  exit 2
fi

echo "Detected activestorage version: $AS_VERSION"

# Version comparison helper
version_gte() {
  printf '%s\n%s' "$2" "$1" | sort -V -C
}

VULNERABLE=false

# Check version ranges
if version_gte "$AS_VERSION" "8.1.0"; then
  if ! version_gte "$AS_VERSION" "8.1.3.1"; then
    VULNERABLE=true
    FIX="8.1.3.1"
  fi
elif version_gte "$AS_VERSION" "8.0.0"; then
  if ! version_gte "$AS_VERSION" "8.0.5.1"; then
    VULNERABLE=true
    FIX="8.0.5.1"
  fi
elif version_gte "$AS_VERSION" "7.2.0"; then
  if ! version_gte "$AS_VERSION" "7.2.3.2"; then
    VULNERABLE=true
    FIX="7.2.3.2"
  fi
elif version_gte "$AS_VERSION" "7.0.0"; then
  VULNERABLE=true
  FIX="7.2.3.2 (requires Rails upgrade — 7.0/7.1 are EOL)"
elif version_gte "$AS_VERSION" "6.0.0"; then
  echo "NOTE — Rails 6.x only vulnerable if variant_processor was manually set to :vips"
  VULNERABLE=true
  FIX="7.2.3.2 (requires Rails upgrade — 6.x is EOL)"
fi

# Check for VIPS_BLOCK_UNTRUSTED mitigation
MITIGATED=false
if [ "${VIPS_BLOCK_UNTRUSTED:-}" = "true" ]; then
  MITIGATED=true
  echo "MITIGATION DETECTED: VIPS_BLOCK_UNTRUSTED=true is set in environment"
fi

# Check libvips version
if command -v vips &>/dev/null; then
  VIPS_VER=$(vips --version | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
  echo "Detected libvips version: $VIPS_VER"
  if ! version_gte "$VIPS_VER" "8.13.0"; then
    echo "WARNING: libvips < 8.13 — VIPS_BLOCK_UNTRUSTED env var will be IGNORED"
    MITIGATED=false
  fi
else
  echo "WARNING: vips CLI not found — cannot verify libvips version"
fi

# Final verdict
if [ "$VULNERABLE" = true ]; then
  if [ "$MITIGATED" = true ]; then
    echo "PATCHED (via mitigation) — activestorage $AS_VERSION is in the vulnerable range but VIPS_BLOCK_UNTRUSTED=true is active with libvips >= 8.13"
    exit 0
  else
    echo "VULNERABLE — activestorage $AS_VERSION is affected by CVE-2026-66066. Upgrade to $FIX or set VIPS_BLOCK_UNTRUSTED=true (requires libvips >= 8.13)"
    exit 1
  fi
else
  echo "PATCHED — activestorage $AS_VERSION is not in the vulnerable range"
  exit 0
fi
07 · Bottom Line

If you remember one thing.

TL;DR
This is a day-one CRITICAL for any Rails shop running Active Storage with image uploads. Monday morning — actually, *today* — your team should inventory every Rails application, run the verification script against each Gemfile.lock, and deploy the VIPS_BLOCK_UNTRUSTED=true environment variable as an immediate compensating control (noisgate mitigation SLA: ≤ 3 days for CRITICAL). Then schedule the actual gem upgrade to activestorage 7.2.3.2 / 8.0.5.1 / 8.1.3.1 within the noisgate remediation SLA of ≤ 90 days, though given the public PoC and trivial exploitation, pushing this into your next deployment cycle — ideally within 1–2 weeks — is strongly advised. After patching, rotate all secrets: SECRET_KEY_BASE, RAILS_MASTER_KEY, database credentials, S3/GCS/Azure keys, and API tokens. If you're running Rails 7.0, 7.1, or 6.x, there is no upstream fix — you must upgrade your Rails version or apply the VIPS_BLOCK_UNTRUSTED=true workaround and plan a framework migration. Do not wait for KEV listing or confirmed exploitation; the PoC is public and weaponization is trivial.

Sources

  1. GitHub Security Advisory (GHSA-xr9x-r78c-5hrm)
  2. Rails Official Patch Announcement
  3. Ethiack KindaRails2Shell Research
  4. HeroDevs Technical Analysis
  5. The Hacker News Coverage
  6. SOCPrime Advisory
  7. NVD Entry
  8. Rails Discussion Forum Advisory
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.