
Impianto di canale nascosto DNS per Red Teams.
WEASEL è un piccolo impianto in memoria che utilizza Python 3 senza dipendenze. Il beacon client invia una piccola quantità di informazioni identificative sul suo host a una zona DNS che controlli. Il server WEASEL può incaricare i client di eseguire comandi preconfigurati o arbitrari.
WEASEL è un payload di stage 1, pensato per essere difficile da rilevare e utile per riguadagnare l'accesso quando i tuoi stage rumorosi e ricchi di funzionalità vengono scoperti.
Stato
Consulta l'uso nel README del client e nel README del server per istruzioni specifiche.
Per avviare il server o il client, esegui gli script direttamente o passali all'interprete Python.
Assicurati che i domini C2 abbiano ciascuno un record NS con l'indirizzo IP dell'host che esegue server.py.
WEASEL richiede Python 3.6+.
Il client è autonomo e utilizza solo librerie standard, quindi può essere eseguito su macOS, Linux, ecc.
Il server ha alcune dipendenze da pip, incluse nel requirements.txt del server. Il server dovrebbe essere eseguito su Linux, ma nulla impedisce di eseguirlo su macOS o altri *nix.
Non è necessario offuscare e minimizzare il beacon in questo caso. Le istruzioni print vengono preservate. Come per l'Utilizzo sopra, assicurati che i record NS per i domini in servers in beacon.py puntino all'indirizzo IP di server.py.
Sull'host del server:
sudo python3 server.py
Sull'host vittima (può essere lo stesso del server):
python3 beacon.py
Non è necessario capire nulla di questo per usare WEASEL.
Il beacon comunica tramite DNS utilizzando query e risposte AAAA. Non utilizza record TXT perché sono noti per essere utilizzati da malware e tunnel DNS. I blue team spesso hanno rilevamenti di tunneling DNS che avvisano su grandi query TXT.
Il lato client non necessita di root per funzionare, non utilizza socket raw e non crea pacchetti DNS malformati. Utilizza interfacce regolari fornite dal sistema e dal linguaggio per effettuare richieste DNS. Le informazioni sono codificate + crittografate nei record stessi.
Questo beacon è pensato per essere lento e a bassa larghezza di banda. Dovrebbe dirci su quali host si trova e fornirci un modo per lanciare ulteriori stage secondo necessità, e niente di più. Sebbene supporti comandi arbitrari, non è destinato a essere utilizzato come shell interattiva regolare o canale di comunicazione.
WEASEL è uno stage 1 che lasci in esecuzione, garantendo l'accesso continuo mentre i tuoi stage completi (e quindi più rumorosi) vengono scoperti.
WEASEL era inizialmente mirato a server con elevato uptime dove avevamo un vettore di sfruttamento/accesso affidabile. Eludere le indagini forensi era una priorità alta. Di conseguenza, non ha funzionalità di persistenza nativa.
Puoi renderlo persistente aggiungendo la sua esecuzione alla tua tecnica di persistenza preferita, che è lasciata come esercizio al lettore :)
Una richiesta (dal client) è una singola query AAAA per un nome formattato come:
<preambolo><dati>.<flusso>.<sessione>.dominio.tld
Il preambolo è di 2 byte. Preambolo[0] è il numero di sequenza di quel pacchetto. Preambolo[1] è il numero totale di pacchetti in quel flusso.
I dati sono limitati a 50 byte (configurabili) e contengono il payload. Il payload è codificato in base32 con un alfabeto personalizzato.
In primo luogo, tutti i caratteri 'w' vengono sostituiti con '-'.
Successivamente, il carattere di padding viene sostituito da '=' a 'w' per conformarsi al set di caratteri DNS: [a-z0-9] e [-].
Non sostituiamo '=' con '-' direttamente perché il padding sarà sempre alla fine della stringa, e terminare un nome host con '---' è sia sospetto che contrario all'RFC DNS. In questo modo, quando una stringa ha del padding, terminerà con 'www', che è sia meno sospetto che conforme all'RFC.
Una risposta (dal server) è composta da una o più risposte AAAA.
Ogni risposta AAAA è un payload crittografato di 16 byte rappresentato come un indirizzo IPv6 utilizzando socket.inet_ntop. Le risposte in una risposta DNS non mantengono il loro ordine durante il transito, quindi vengono sequenziate e riassemblate come le richieste del client.
Il payload di trasporto è una stringa di elementi di dati separati dal carattere ^.
Richieste e risposte seguono questo formato:
<tipo>|<dati>
Le sessioni sono di lunga durata: un client avvia una sessione quando il beacon viene eseguito per la prima volta e quella sessione dovrebbe durare per tutto il tempo in cui il beacon è attivo su quel client. Nota che poiché il beacon è in memoria e non persistente, i dati della sessione sono memorizzati nella memoria del processo Python. Qualsiasi nuova invocazione del beacon avvierà una nuova sessione.
L'avvio di una sessione comporta che il client crei un messaggio con un preambolo non dati che identifica in modo univoco (per segnalare al server che si tratta di una nuova sessione): la concatenazione di una chiave pubblica Diffie-Hellman di 32 byte e un IV AES casuale di 16 byte.
Il server riceve questo e risponde con la propria chiave pubblica di 32 byte. A questo punto il client e il server hanno stabilito una chiave di sessione condivisa che verrà utilizzata per tutta la durata di questa sessione per crittografare i payload di dati utilizzando AES-128 in modalità CTR. Lo scambio Diffie-Hellman Effimero garantisce che ogni connessione client-server utilizzi una chiave di sessione unica con segretezza avanzata.
La crittografia è volutamente scadente per una serie di motivi:
Ecco alcuni problemi noti con lo schema crittografico:
p è RFC 3526 Gruppo 5 troncato ai primi 32 byte. Questo non solo limita le chiavi pubbliche e private a 32 byte, ma il Gruppo 5 è già deprecato e sconsigliato. Chiamo questa cattiva decisione "Gruppo 1".a invece di un CSPRNG.Ogni messaggio inviato tra un client e un server deve essere pacchettizzato in pacchetti di massimo 50 byte, per rimanere sotto il limite di 52 byte per i rilevamenti comuni di canali nascosti DNS. Tutti i pacchetti per un particolare messaggio fanno parte dello stesso flusso. Messaggio == Flusso.
I flussi sono identificati con un numero esadecimale casuale di 2 byte. Ricorda che il formato della richiesta del client è:
<preambolo><dati>.<flusso>.<sessione>.dominio.tld
Il preambolo di 2 byte di ogni pacchetto nel flusso ha un numero di sequenza e il numero totale di pacchetti in quel flusso. Ciò consente al server di sapere quando tutto è arrivato.
Poiché stiamo facendo tutto questo tramite DNS su UDP, che non fornisce garanzie sull'ordine in cui arriveranno i datagrammi. Ecco perché WEASEL deve gestire il sequenziamento, il riassemblaggio e il monitoraggio di più flussi da molti beacon.
Ogni flusso reinizializza un cifrario AES-128-CTR condiviso globalmente per crittografare/decifrare i payload.
Il payload può essere decifrato solo quando un flusso è completo (tutti i pacchetti sono arrivati). Se non fosse per base32, potremmo decifrare ciò che abbiamo del messaggio anche se mancassero pacchetti (perché AES-CTR è un cifrario a flusso) ma non possiamo decodificare base32 su flussi parziali. Pazienza. Per la natura dei client DNS, le richieste vengono effettuate più volte (generalmente 2 o 4 volte) fino a quando non si riceve una risposta, quindi abbiamo una buona probabilità di ricevere tutti i pacchetti in un flusso poiché ogni pacchetto dovrebbe essere inviato dal client almeno due volte. Se perdiamo pacchetti o flussi, non è un grosso problema, il beacon si ripresenterà più tardi e probabilmente avrà più fortuna.
Vedi il file CONTRIBUTING per come aiutare.
WEASEL è concesso in licenza MIT, come riportato nel file LICENSE.
| Tipo | Significato (mittente) | AKA | Dati |
|---|
| 0 | Riconosciuto | ACK | Hex casuale |
| 1 | Check-in (client) | PING | Hex casuale |
| 2 | Terminati (server), mi termino (client) | FIN | |
| 3 | Messaggio di inizializzazione (client) | SYN | `versione |
| 4 | Riconnessione (server) | RST | |
| 5 | Imposta intervallo di callback (server) | secondi | |
| 6 | Ottieni dati interfaccia di rete | eth0 1.2.3.4/24\neth1 fe80:::/64\n... | |
| 8 | Eval codice Python3 arbitrario fino a 666 byte (server), restituisce i primi 400 byte di output (client) | EVAL | script oneliner python3 |
| 9 | Esegui comando arbitrario fino a 666 byte (server), restituisce i primi 400 byte di output (client) | EXEC | comando bash |