
DNS-Covert-Channel-Implantat für Red Teams.
WEASEL ist ein kleiner In-Memory-Implant, der Python 3 ohne Abhängigkeiten nutzt. Der Beacon-Client sendet eine kleine Menge identifizierender Informationen über seinen Host an eine DNS-Zone, die Sie kontrollieren. Der WEASEL-Server kann Clients beauftragen, vorgefertigte oder beliebige Befehle auszuführen.
WEASEL ist ein Stage-1-Payload, das schwer zu erkennen sein soll und nützlich ist, um wieder Zugriff zu erlangen, wenn Ihre lauten, vollwertigen Stages entdeckt wurden.
Status
Siehe die Nutzung im Client-README und Server-README für spezifische Anweisungen.
Um den Server oder Client zu starten, führen Sie die Skripte direkt aus oder übergeben Sie sie dem Python-Interpreter.
Stellen Sie sicher, dass die C2-Domänen jeweils einen NS-Eintrag mit der IP-Adresse des Hosts haben, auf dem server.py ausgeführt wird.
WEASEL benötigt Python 3.6+.
Der Client ist eigenständig und verwendet nur Standardbibliotheken, daher kann er auf macOS, Linux usw. ausgeführt werden.
Der Server hat einige Abhängigkeiten von pip, die in server/requirements.txt enthalten sind. Der Server sollte unter Linux ausgeführt werden, aber es spricht nichts dagegen, ihn unter macOS oder anderen *nix-Systemen zu betreiben.
Es ist in diesem Fall nicht nötig, den Beacon zu obfuskatieren und zu minimieren. Print-Anweisungen bleiben erhalten. Wie unter „Verwendung“ oben, stellen Sie sicher, dass NS-Einträge für die Domäne(n) in servers in beacon.py auf die IP-Adresse von server.py zeigen.
Auf dem Server-Host:
sudo python3 server.py
Auf dem Opfer-Host (kann derselbe wie der Server sein):
python3 beacon.py
Sie müssen nichts davon verstehen, um WEASEL zu nutzen.
Beacon kommuniziert über DNS mit AAAA-Anfragen und -Antworten. Es verwendet keine TXT-Einträge, da diese dafür bekannt sind, von DNS-Malware und -Tunnel genutzt zu werden. Blue Teams haben oft DNS-Tunnel-Erkennungen, die bei großen TXT-Anfragen alarmieren.
Die Client-Seite benötigt keine Root-Rechte, verwendet keine Raw-Sockets und erstellt keine fehlerhaften DNS-Pakete. Es werden reguläre, vom System und der Sprache bereitgestellte Schnittstellen für DNS-Anfragen verwendet. Die Informationen sind in den Einträgen selbst kodiert + verschlüsselt.
Dieser Beacon ist als langsam und leise mit geringer Bandbreite konzipiert. Er soll uns mitteilen, auf welchen Hosts er sich befindet, und uns eine Möglichkeit geben, bei Bedarf weitere Stages zu starten – und nicht mehr. Obwohl er beliebige Befehle unterstützt, ist er nicht als reguläre interaktive Shell oder Kommunikationskanal gedacht.
WEASEL ist eine Stage 1, die Sie laufen lassen, um dauerhaften Zugriff zu gewährleisten, während Ihre vollwertigen (und daher lauteren) Stages entdeckt werden.
WEASEL war ursprünglich für Server mit hoher Betriebszeit gedacht, bei denen wir einen zuverlässigen Fußpunkt/Ausnutzungsvektor hatten. Die Umgehung forensischer Untersuchungen hatte hohe Priorität. Daher verfügt es über keine nativen Persistenzfunktionen.
Sie können es persistent machen, indem Sie seine Ausführung zu Ihrer bevorzugten Persistenztechnik hinzufügen – dies bleibt dem Leser als Übung überlassen :)
Eine Anfrage (vom Client) ist eine einzelne AAAA-Abfrage für einen Namen, der wie folgt formatiert ist:
<Präambel><Daten>.<Stream>.<Sitzung>.domain.tld
Die Präambel ist 2 Byte lang. Präambel[0] ist die Sequenznummer dieses Pakets. Präambel[1] ist die Gesamtanzahl der Pakete in diesem Stream.
Daten sind auf 50 Byte (konfigurierbar) begrenzt und enthalten die Nutzlast. Die Nutzlast ist mit einem benutzerdefinierten Alphabet base32-kodiert.
Zuerst werden alle 'w'-Zeichen durch '-' ersetzt.
Als nächstes wird das Padding-Zeichen von '=' zu 'w' geändert, um dem DNS-Zeichensatz [a-z0-9] und [-] zu entsprechen.
Wir ersetzen '=' nicht direkt durch '-', da das Padding immer am Ende der Zeichenkette steht und ein Hostname, der auf '---' endet, sowohl verdächtig als auch nicht RFC-konform ist. Auf diese Weise endet eine Zeichenkette mit Padding auf 'www', was weniger verdächtig und RFC-konform ist.
Eine Antwort (vom Server) besteht aus einem oder mehreren AAAA-Antworten.
Jede AAAA-Antwort ist eine 16 Byte lange verschlüsselte Nutzlast, die als IPv6-Adresse mit socket.inet_ntop dargestellt wird. Antworten in einer DNS-Antwort behalten ihre Reihenfolge während der Übertragung nicht bei, daher werden sie wie die Client-Anfragen sequenziert und wieder zusammengesetzt.
Die Transportnutzlast ist eine Zeichenkette von Datenelementen, die durch ein ^-Zeichen getrennt sind.
Anfragen und Antworten folgen diesem Format:
<Typ>|<Daten>
Sitzungen sind langlebig: Ein Client initiiert eine Sitzung, wenn der Beacon zum ersten Mal ausgeführt wird, und diese Sitzung sollte für die gesamte Zeit bestehen bleiben, in der der Beacon auf diesem Client aktiv ist. Beachten Sie, dass die Sitzungsdaten, da der Beacon im Speicher und nicht persistent ist, im Speicher des Python-Prozesses gespeichert werden. Jeder neue Aufruf des Beacons initiiert eine neue Sitzung.
Die Initiierung einer Sitzung beinhaltet, dass der Client eine Nachricht mit einer eindeutig identifizierenden Nicht-Daten-Präambel erstellt (um dem Server zu signalisieren, dass dies eine neue Sitzung ist): die Verkettung eines 32 Byte langen Diffie-Hellman-öffentlichen Schlüssels und eines 16 Byte langen zufälligen AES-IV.
Der Server empfängt dies und antwortet mit seinem eigenen 32 Byte langen öffentlichen Schlüssel. An diesem Punkt haben Client und Server einen gemeinsamen Sitzungsschlüssel etabliert, der für die Lebensdauer dieser Sitzung verwendet wird, um Daten-Nutzlasten mit AES-128 im CTR-Modus zu verschlüsseln. Der ephemerale Diffie-Hellman-Austausch stellt sicher, dass jede Client-Server-Verbindung einen eindeutigen Sitzungsschlüssel mit perfekter Vorwärtssicherheit verwendet.
Die Kryptographie ist aus mehreren Gründen absichtlich schlecht:
Hier sind einige bekannte Probleme mit dem Kryptoschema:
p ist RFC 3526 Group 5, abgeschnitten auf die ersten 32 Byte. Dies begrenzt nicht nur die öffentlichen und privaten Schlüssel auf 32 Byte, sondern Group 5 ist bereits veraltet und wird nicht mehr empfohlen. Ich nenne diese schlechte Entscheidung "Group 1".a anstelle eines CSPRNG.Jede zwischen einem Client und einem Server gesendete Nachricht muss in Pakete von maximal 50 Byte aufgeteilt werden, um unter der 52-Byte-Grenze für gängige DNS-Covert-Channel-Erkennungen zu bleiben. Alle Pakete für eine bestimmte Nachricht gehören zum selben Stream. Nachricht == Stream.
Streams werden mit einer 2 Byte großen zufälligen Hex-Zahl identifiziert. Erinnern Sie sich an das Format der Client-Anfrage:
<Präambel><Daten>.<Stream>.<Sitzung>.domain.tld
Die 2 Byte große Präambel jedes Pakets im Stream enthält eine Sequenznummer und die Gesamtanzahl der Pakete in diesem Stream. Dies ermöglicht es dem Server zu wissen, wann alles angekommen ist.
Da dies alles über UDP erfolgt, das keine Garantien für die Reihenfolge des Eintreffens von Datagrammen bietet, muss WEASEL die Sequenzierung, Wiederzusammensetzung und Verfolgung mehrerer Streams von vielen Beacons berücksichtigen.
Jeder Stream initialisiert eine global gemeinsam genutzte AES-128-CTR-Chiffre neu, um Nutzlasten zu verschlüsseln/zu entschlüsseln.
Die Nutzlast kann erst entschlüsselt werden, wenn ein Stream vollständig ist (alle Pakete sind angekommen). Wenn es nicht base32 gäbe, könnten wir das, was wir von der Nachricht haben, auch dann entschlüsseln, wenn Pakete fehlen (da AES-CTR eine Stromchiffre ist), aber wir können keine partiellen Streams base32-dekodieren. Schade. Aufgrund der Natur von DNS-Clients werden Anfragen mehrmals (im Allgemeinen 2- oder 4-mal) gesendet, bis eine Antwort empfangen wird, sodass wir eine gute Wahrscheinlichkeit haben, alle Pakete in einem Stream zu empfangen, da jedes Paket vom Client mindestens zweimal gesendet werden sollte. Wenn wir Pakete oder Streams verlieren, ist das kein großes Problem; der Beacon wird sich später wieder melden und dann wahrscheinlich mehr Glück haben.
Siehe die Datei CONTRIBUTING für Informationen, wie Sie helfen können.
WEASEL ist unter der MIT-Lizenz lizenziert, wie in der Datei LICENSE zu finden.
| Typ | Bedeutung (Absender) | AKA | Daten |
|---|
| 0 | Bestätigt | ACK | Zufälliges Hex |
| 1 | Checkt ein (Client) | PING | Zufälliges Hex |
| 2 | Beende dich selbst (Server), Beende mich selbst (Client) | FIN | |
| 3 | Initialisierungsnachricht (Client) | SYN | Version|Hostname|Kernel |
| 4 | Wieder verbinden (Server) | RST | |
| 5 | Callback-Intervall setzen (Server) | Sekunden | |
| 6 | Netzwerkschnittstellendaten abrufen | eth0 1.2.3.4/24\neth1 fe80:::/64\n... | |
| 8 | Beliebigen Python3-Code bis 666 Byte ausführen (Server), die ersten 400 Byte der Ausgabe zurückgeben (Client) | EVAL | Python3-One-Liner-Skript |
| 9 | Beliebigen Befehl bis 666 Byte ausführen (Server), die ersten 400 Byte der Ausgabe zurückgeben (Client) | EXEC | Bash-Befehl |