
Implant de canal caché DNS pour les équipes rouges.
WEASEL est un petit implant en mémoire utilisant Python 3 sans dépendances. Le client de la balise envoie une petite quantité d'informations d'identification de son hôte vers une zone DNS que vous contrôlez. Le serveur WEASEL peut ordonner aux clients d'exécuter des commandes pré-définies ou arbitraires.
WEASEL est une charge utile de stade 1, conçue pour être difficile à détecter et utile pour retrouver un accès lorsque vos étapes complètes et bruyantes sont détectées.
Statut
Voir l'utilisation dans le README du client et le README du serveur pour des instructions spécifiques.
Pour démarrer le serveur ou le client, exécutez les scripts directement ou passez-les à l'interpréteur Python.
Assurez-vous que chaque domaine C2 possède un enregistrement NS pointant vers l'adresse IP de l'hôte exécutant server.py.
WEASEL nécessite Python 3.6+.
Le client est autonome et n'utilise que des bibliothèques standard, il peut donc être exécuté sur macOS, Linux, etc.
Le serveur a quelques dépendances pip, incluses dans requirements.txt du serveur. Le serveur doit être exécuté sur Linux, mais rien n'empêche de le faire fonctionner sur macOS ou d'autres *nix.
Pas besoin d'obfusquer et de minimiser la balise dans ce cas. Les instructions print sont conservées. Comme pour l'utilisation ci-dessus, assurez-vous que les enregistrements NS pour le(s) domaine(s) dans servers de beacon.py pointent vers l'adresse IP de server.py.
Sur l'hôte serveur :
sudo python3 server.py
Sur l'hôte victime (peut être le même que le serveur) :
python3 beacon.py
Vous n'avez pas besoin de comprendre tout cela pour utiliser WEASEL.
La balise communique via DNS en utilisant des requêtes et réponses AAAA. Elle n'utilise pas d'enregistrements TXT car ceux-ci sont connus pour être utilisés par les malwares et tunnels DNS. Les équipes bleues ont souvent des détections de tunneling DNS qui alertent sur les grandes requêtes TXT.
Le côté client n'a pas besoin de root pour fonctionner, n'utilise pas de sockets bruts et ne crée pas de paquets DNS malformés. Il utilise des interfaces système et linguistiques standard pour effectuer des requêtes DNS. Les informations sont encodées + chiffrées dans les enregistrements eux-mêmes.
Cette balise est conçue pour être lente et discrète, avec peu de bande passante. Elle doit nous indiquer sur quels hôtes elle se trouve et nous donner un moyen de lancer d'autres étapes si nécessaire, et rien de plus. Bien qu'elle prenne en charge les commandes arbitraires, elle n'est pas destinée à être utilisée comme un shell interactif régulier ou un canal de communication.
WEASEL est un stade 1 que vous laissez tourner, garantissant un accès continu pendant que vos étapes complètes (et donc plus bruyantes) sont détectées.
WEASEL était initialement ciblé pour des serveurs à haute disponibilité où nous avions un point d'appui/vecteur d'exploitation fiable. Éviter les analyses forensiques était une priorité élevée. En conséquence, il n'a pas de fonctionnalités de persistance natives.
Vous pouvez le rendre persistant en ajoutant son exécution à votre technique de persistance préférée, ce qui est laissé comme exercice au lecteur :)
Une requête (du client) est une simple requête AAAA pour un nom formaté comme suit :
<préambule><données>.<flux>.<session>.domaine.tld
Le préambule fait 2 octets. Préambule[0] est le numéro de séquence de ce paquet. Préambule[1] est le nombre total de paquets dans ce flux.
Les données sont limitées à 50 octets (configurable) et contiennent la charge utile. La charge utile est encodée en base32 avec un alphabet personnalisé.
Tout d'abord, tous les caractères 'w' sont remplacés par '-'.
Ensuite, le caractère de remplissage est remplacé de '=' à 'w' pour se conformer au jeu de caractères DNS : [a-z0-9] et [-].
Nous ne remplaçons pas '=' par '-' directement car le remplissage sera toujours à la fin de la chaîne, et terminer un nom d'hôte par '---' est à la fois suspect et contraire à la RFC DNS. De cette façon, lorsqu'une chaîne a un remplissage, elle se termine par 'www', ce qui est à la fois moins suspect et conforme à la RFC.
Une réponse (du serveur) est composée d'une ou plusieurs réponses AAAA.
Chaque réponse AAAA est une charge utile chiffrée de 16 octets représentée comme une adresse IPv6 en utilisant socket.inet_ntop. Les réponses dans une réponse DNS ne conservent pas leur ordre en transit, elles sont donc séquencées et réassemblées comme les requêtes client.
La charge utile de transport est une chaîne d'éléments de données séparés par un caractère ^.
Les requêtes et réponses suivent ce format :
<type>|<données>
| Type | Signification (expéditeur) | Aussi appelé | Données |
|---|---|---|---|
| 0 | Accusé de réception | ACK | Hexadécimal aléatoire |
| 1 | Enregistrement (client) | PING | Hexadécimal aléatoire |
| 2 | Terminez-vous (serveur), je me termine (client) | FIN | |
| 3 | Message d'initialisation (client) | SYN | `version |
| 4 | Reconnexion (serveur) | RST | |
| 5 | Définir l'intervalle de rappel (serveur) | secondes | |
| 6 | Obtenir les données d'interface réseau | eth0 1.2.3.4/24\neth1 fe80:::/64\n... | |
| 8 | Évaluer du code Python3 arbitraire jusqu'à 666 octets (serveur), retourner les 400 premiers octets de la sortie (client) | EVAL | script One-liner Python3 |
| 9 | Exécuter une commande arbitraire jusqu'à 666 octets (serveur), retourner les 400 premiers octets de la sortie (client) | EXEC | commande bash |
Les sessions sont de longue durée : un client initie une session lorsque la balise est exécutée pour la première fois et cette session doit durer tout le temps que la balise est active sur ce client. Notez que comme la balise est en mémoire et non persistante, les données de session sont stockées dans la mémoire de ce processus Python. Toute nouvelle invocation de la balise initiera une nouvelle session.