
Ce projet génère des fichiers de zone DNS avec des paramètres NSEC3 personnalisés pour reproduire et évaluer les attaques de CVE-2023-50868.
Ce projet génère des fichiers de zone DNS avec des paramètres NSEC3 personnalisés pour reproduire et évaluer les attaques dans CVE-2023-50868.
Python3 (testé sur Python3.10)
Dépendances Python installées :
lib : Utilitaires Python, incluant :
keys.py : Fonctions d'encapsulation pour charger/stocker les clés dans des fichiersnsec.py : Implémentation des hachages NSEC DNSSECdnssec.py : Fonctions dnspython modifiées/patchées avec support NSEC3config.py : Utilitaires de chargement de configurationkeysgen_keys.pyzones : Fichiers de zone (générés avec gen_zones.py)config.json : Exemple de configurationConfigurez quelles zones NSEC3 doivent être créées en modifiant config.json (voir Config)
Générer les clés :
$ ./gen_keys.py
Pour chaque zone, une KSK et une ZSK sont générées. Les clés sont réutilisées lors du changement de configuration tant que les noms de zone restent inchangés dans la configuration.
Générer les fichiers de zone :
$ ./gen_zones.py -c
L'option -c active l'exportation des fichiers de configuration (actuellement uniquement pour BIND9)
Utilisez --help pour plus d'options.
La structure de configuration contient deux éléments :
default : Paramètres par défaut pour les zones (tous ne sont pas encore supportés)zones : Liste de toutes les zones à exporterUne zone contient :
name (obligatoire) : Le nom utilisé pour référencer la zone et comme nom de fichier lors de l'exportationorigin (obligatoire) : Le nom de domaine canonique de l'origine de la zoneparent : Le nom de la zone parente (pas l'origine), à laquelle les enregistrements NS, A, DS et NSEC3PARAM de cette zone sont ajoutéskeysize (obligatoire) : La taille de la clé RSA (uniquement RSA pour l'instant)nsec3 : Les paramètres NSEC3 :
iterations : par défaut 0salt : par défaut ''algorithm : Valeur entière, actuellement seul SHA-1 (1) est supportétight : Un booléen spécial qui contrôle si les enregistrements NSEC3 immédiatement après l'origine et avant et après *.origin doivent être ajoutés. Par exemple, si *.origin a un enregistrement NSEC3 1d..ua.origin., alors les enregistrements pour 1d..u0.origin. et 1d..ub.origin. sont également ajoutés au fichier de zone. Cela garantit que chaque preuve NXDOMAIN sur un sous-domaine de origin (par exemple, a.origin.) nécessite trois enregistrements NSEC3, car les enregistrements NSEC3 couvrant l'origine et le wildcard ont une très petite plage jusqu'à next_hashns : Le(s) serveur(s) de noms de cette zone. Une seule valeur ou liste de :
ns : Le nom de domaine d'un serveur de noms, par défaut ns1.originip : L'adresse IPv4 (IPv6 actuellement non supporté), par défaut 172.0.0.1soa : Les données SOA RDATArrsets : Une liste d'ensembles d'enregistrements supplémentaires, donnée sous forme de liste de 5 éléments [nom de domaine, ttl, classe, type, rdata] où toutes les valeurs (sauf éventuellement ttl) sont données sous forme de chaînesPour reproduire l'attaque NSEC3, cette section illustre une configuration personnalisée possible comprenant un serveur de noms DNS et un résolveur victime. Avant de continuer, assurez-vous que l'environnement système dispose d'un pare-feu suffisamment configuré pour ne pas exposer les serveurs publics aux fichiers de zone de l'attaque.
Installer le serveur de noms NSD (version actuelle)
Rendez-vous sur le site web de NLNetlabs (https://nsd.docs.nlnetlabs.nl/en/latest/installation.html) pour les instructions d'installation.
Il est recommandé de déployer le serveur de noms dans une VM ou un conteneur. Comme point de départ, il y a un petit Dockerfile dans docker/nsd.
Construisez le conteneur avec docker build -t <tag> <path_to_dockerfile>, par exemple :
cd docker/nsd && docker build -t nsd .
Exécutez le conteneur avec docker run -it --name <name> nsd bash pour ouvrir une console dans le conteneur.
Ensuite, le serveur de noms doit être configuré pour héberger les fichiers de zone de l'attaquant.
Cela nécessite une configuration correcte des fichiers de zone à générer (le plus important, l'adresse IP donnée dans les enregistrements NS doit correspondre à l'adresse IP du conteneur).
Si aucun réseau n'a été configuré, l'adresse IP du conteneur peut être visualisée avec :
docker container inspect <name> | grep IPAddress
Générez les zones avec la sortie de configuration (./gen_zones.py -c, voir ci-dessus) et copiez le dossier de sortie des zones depuis le répertoire du dépôt dans le conteneur docker :
docker cp ./zones <name>:/etc/nsd
Dans la console du conteneur, la configuration NSD /etc/nsd/nsd.conf dans le conteneur doit être modifiée avec les lignes suivantes :
verify:
enable: no
remote-control:
control-enable: no
include: "/etc/nsd/zones/nsd.conf"
Enfin, exécutez NSD depuis le shell du conteneur avec la commande
/usr/sbin/nsd -d -c /etc/nsd/nsd.conf
Activez la sortie de logs avec l'option -V 4.
Maintenant, si aucun problème n'est survenu, le serveur de noms faisant autorité devrait fonctionner. Vous pouvez le vérifier en exécutant une requête sur l'un des domaines des zones depuis le système hôte en utilisant dig :
dig @<ip-addr-of-nsd-container> <domain>
Installez un résolveur. Dans cette démonstration, nous montrons une approche possible pour Unbound 1.17.1.
Un Dockerfile officiel peut être trouvé ici : https://github.com/NLnetLabs/pythonunbound
Nous avons inclus une version modifiée de ce Dockerfile dans docker/unbound avec une version Ubuntu mise à jour et pré-configuré pour Unbound 1.17.1.
Clonez le dépôt, placez-vous dans son répertoire et construisez le conteneur Unbound :
docker build -t <tag> .
Exécutez le conteneur avec :
docker run --name <name> -it <tag> bash
Ensuite, Unbound doit être configuré pour pouvoir localiser le serveur de noms faisant autorité NSD.
Cela se fait en modifiant le fichier unbound.conf dans le répertoire de travail du conteneur.
Pour cela, assurez-vous que l'entrée server.module-config est supprimée de la configuration.
Pour activer la validation DNSSEC, les enregistrements DNSKEY de la zone parente de l'attaquant doivent être configurés manuellement. Il s'agit de la même clé qui a été utilisée pour générer les signatures, par exemple :
server:
chroot: ""
do-ip6: no
trust-anchor: "attack.er. DNSKEY 257 3 7 AwEAAdqDN3rJYlmGP3jJs5lCZq5NYrCn pCVlV0ko17JnbfYfLCroEF4reO/Xy0MK C9AVvSRTk83MHDuzMYXogm7m/gcn3Mh0 MwB2InP8jkPw5not+TMH/Wrbs31xkT2n RIBJJ+1lPF+e2AvwWvgREcEVTRbdhIqQ iM1StWXoTVudry4V"
De plus, une zone stubbed (stub-zone) doit être configurée pour permettre au résolveur Unbound de trouver le serveur de noms faisant autorité NSD.
Cela se fait en ajoutant ce qui suit au fichier unbound.conf :
Si vous rencontrez des problèmes avec ce guide, n'hésitez pas à nous contacter pour obtenir des conseils supplémentaires.
stub-zone:
name: "attack.er."
stub-prime: yes
stub-addr: <ip-addr-of-nsd-container>
Démarrez unbound dans le conteneur avec :
unbound -vvv (utilisez -dd pour empêcher la démonisation)
Vous devriez maintenant pouvoir interroger unbound avec dig et observer le temps de réponse :
dig @127.0.0.1 attack.er