La vulnérabilité à détecter pour ce challenge était une SSRF exploitable à l’aide d’un contournement utilisant une ré-association DNS (MDCMDU). La résolution de challenge ne demande pas nécessairement de connaissance du langage (Ruby) ou du framework web (Roda). En effet, le problème étant plutôt porté sur des concepts logiques et universels.

Note : Cet article est aussi disponible en anglais 🇬🇧. Le challenge a été annoncé dans ce tweet 🐦.

Explication

Le problème principal réside dans le fait que l’application résout deux fois le nom de domaine : une première fois pour effectuer la vérification de sécurité et la deuxième fois pour effectuer la requête elle-même. Cette double résolution induit une situation de concurrence.

La première résolution DNS a lieu au sein de la fonction trusted?() lorsque Resolv.getaddress est appelé pour vérifier si le domaine inclut dans l’URL demandée appartient à l’un des serveurs hébergeant les sites web internes.
La deuxième résolution DNS est effectuée au moment où la requête HTTP est effectuée (http.get(url)).

Un attaquant peut donc contourner le mécanisme de protection en effectuant une attaque de ré-association DNS (connu sous le nom de DNS rebinding en anglais). Plus spécifiquement une ré-association DNS MDCMDU (Moment Du Contrôle, Moment De l’Utilisation, connu sous le nom de TOCTOU pour time-of-check to time-of-use en anglais) qui est une forme de situation de concurrence.

Le principe de l’attaque repose sur le fait que l’attaquant contrôle un nom de domaine ainsi que le serveur DNS faisant autorité afin de donner des réponses dynamiquement différentes selon certains critères. Par exemple, pour un sous-domaine donné, le serveur va répondre avec une IP A à la première requête DNS qu’il reçoit et une IP B à la deuxième requête ou alors une IP A à la première requête et une IP B après un temps T suivant la première requête et étant originaire d’une même adresse IP.

Un moyen simple de mener cette attaque est d’héberger une instance du service 1u.ms ou d’utiliser la version publique en ligne.

En spécifiant, le sous-domaine suivant, l’application obtiendra la valeur 10.10.0.200 lors de la première requête DNS, permettant de valider le contrôle de sécurité, puis obtiendra 42.42.42.42, ou tout autre adresse IP vers laquelle l’attaquant voudra faire pointer le serveur, pour la seconde requête DNS lorsque la requête HTTP sera effectué. Permettant ainsi à l’attaquant de contourner le mécanisme de sécurité tout en contrôlant la cible de la requête HTTP.

make-10.10.0.200-rebind-42.42.42.42-rr.1u.ms

Ré-association DNS

Afin d’exploiter la SSRF, il suffit donc d’utiliser l’URL suivante :

http://localhost:9292/admin/proxy?url=http://make-10.10.0.200-rebind-42.42.42.42-rr.1u.ms

Si l’attaque ne fonctionne pas quand elle est effectuée plusieurs fois de suite, c’est probablement car le client DNS utilisé par l’application a mis l’entrée DNS en cache et n’est donc plus résolu à chaque requête HTTP. Cependant, 1u.ms prévoit l’ajout de préfixe ou suffixe afin de générer différents sous-domaines ayant le même comportement.

noraj01-make-10.10.0.200-rebind-42.42.42.42-rr.1u.ms

Code corrigé

Les contre-mesures pour se protéger de cette attaque peuvent se révéler être délicates à mettre en place.

  • Bloquer les adresses privées n’est pas toujours possible ou efficace.
  • Utiliser / autoriser exclusivement HTTPS fera échouer les échanges SSL lors d’une attaque.
  • Placer un système d’authentification sur tous les services locaux permet de protéger les données même si l’attaquant arrive à mener son attaque.
  • Spécifier l’adresse du serveur DNS utilisé pour résoudre les requêtes ET configurer un système de cache sur ce serveur. Cependant, il faut que le cache définisse une durée de rétention minimale (ex : 300 secondes), sinon si le cache se base sur le TTL renvoyé par l’autorité, l’attaquant pourra toujours envoyer un TTL de 0 ou 1 seconde. systemd-resolved, dnsmasq, bind utilisent le TTL mais une option --min-cache-ttl ou équivalent est disponible afin d’appliquer une durée de rétention minimale.
  • Il est possible d’implémenter un système de cache au niveau applicatif à l’aide d’un serveur Redis :
    1. Vérifier si l’entrée existe déjà dans Redis
    2. Si elle existe l’utiliser, si elle n’existe pas, passer à l’étape suivante
    3. Résoudre une première fois le sous-domaine
    4. Le stocker dans Redis avec une certaine durée de vie

La solution la plus simple et universelle sera sans doute d’utiliser un cache DNS.

Exemple de configuration dnsmasq avec cache minimum de 30 secondes.

➜ grep -Ev '^#|^$' /etc/dnsmasq.conf
port=5353
listen-address=127.0.0.153
cache-size=1000
min-cache-ttl=30

Cache DNS

Voici donc le code corrigé utilisant le cache DNS configuré précédemment :

Code corrigé

Diff

Le code source est disponible sur le dépôt Github Acceis/vulnerable-code-snippets.

À propos de l’auteur

Article écrit par Alexandre ZANNI alias noraj, Ingénieur en Test d’Intrusion chez ACCEIS.