
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 :