
Implante de canal encubierto DNS para Equipos Rojos.
WEASEL es un pequeño implante en memoria que utiliza Python 3 sin dependencias. El cliente baliza envía una pequeña cantidad de información identificativa sobre su host a una zona DNS que usted controla. El servidor WEASEL puede asignar a los clientes la ejecución de comandos predefinidos o arbitrarios.
WEASEL es un payload de etapa 1, diseñado para ser difícil de detectar y útil para recuperar el acceso cuando sus ruidosas etapas completas son detectadas.
Estado
Consulte el uso en el README del cliente y el README del servidor para instrucciones específicas.
Para iniciar el servidor o el cliente, ejecute los scripts directamente o pásalos al intérprete de Python.
Asegúrese de que cada dominio C2 tenga un registro NS con la dirección IP del host que ejecuta server.py.
WEASEL requiere Python 3.6+.
El cliente es autónomo y solo utiliza bibliotecas estándar, por lo tanto, puede ejecutarse en macOS, Linux, etc.
El servidor tiene algunas dependencias de pip, incluidas en requirements.txt del servidor. El servidor debe ejecutarse en Linux, pero nada impide que se ejecute en macOS u otros *nix.
No es necesario ofuscar y minimizar la baliza en este caso. Las declaraciones de impresión se conservan. Al igual que en el Uso anterior, asegúrese de que los registros NS del dominio(s) en servidores en beacon.py apunten a la dirección IP de server.py.
En el host del servidor:
sudo python3 server.py
En el host de la víctima (puede ser el mismo que el servidor):
python3 beacon.py
No necesita entender nada de esto para usar WEASEL.
La baliza se comunica a través de DNS utilizando consultas y respuestas AAAA. No utiliza registros TXT debido a que son conocidos por ser utilizados por malware y túneles DNS. Los equipos azules suelen tener detecciones de túneles DNS que alertan sobre consultas TXT grandes.
El lado del cliente no necesita root para funcionar, no utiliza sockets sin procesar y no crea paquetes DNS malformados. Utiliza interfaces regulares del sistema y del lenguaje para realizar solicitudes DNS. La información está codificada y cifrada en los propios registros.
Esta baliza está diseñada para ser lenta y discreta, con poco ancho de banda. Debe decirnos en qué hosts se encuentra y darnos una manera de lanzar más etapas según sea necesario, y nada más. Aunque tiene soporte para comandos arbitrarios, no está destinada a usarse como un shell interactivo regular o canal de comunicaciones.
WEASEL es una etapa 1 que dejas ejecutando, asegurando acceso continuo mientras tus etapas completas (y por lo tanto más ruidosas) son detectadas.
WEASEL fue inicialmente dirigido a servidores de alta disponibilidad donde teníamos un vector de punto de apoyo/explotación confiable. Evadir el análisis forense era una alta prioridad. Como resultado, no tiene características de persistencia nativas.
Puedes hacerlo persistente agregando su ejecución a tu técnica de persistencia favorita, lo cual se deja como ejercicio para el lector :)
Una solicitud (del cliente) es una única consulta AAAA para un nombre formateado como:
<preamble><data>.<stream>.<session>.domain.tld
El preámbulo tiene 2 bytes. Preámbulo[0] es el número de secuencia de ese paquete. Preámbulo[1] es el número total de paquetes en esa secuencia.
Los datos están limitados a 50 bytes (configurable) y contienen la carga útil. La carga útil está codificada en base32 con un alfabeto personalizado.
Primero, todos los caracteres 'w' se intercambian por '-'.
Luego, el carácter de relleno se intercambia de '=' a 'w' para ajustarse al conjunto de caracteres DNS: [a-z0-9] y [-].
No intercambiamos '=' con '-' directamente porque el relleno siempre estará al final de la cadena, y terminar un nombre de host con '---' es sospechoso y va en contra de la RFC de DNS. De esta manera, cuando una cadena tiene relleno, terminará en 'www', lo cual es menos sospechoso y cumple con la RFC.
Una respuesta (del servidor) está compuesta por una o más respuestas AAAA.
Cada respuesta AAAA es una carga útil cifrada de 16 bytes representada como una dirección IPv6 usando socket.inet_ntop. Las respuestas en una respuesta DNS no mantienen su orden en tránsito, por lo que se secuencian y reensamblan como las solicitudes del cliente.
La carga útil de transporte es una cadena de elementos de datos separados por un carácter ^.
Las solicitudes y respuestas siguen este formato:
<type>|<data>
| Type | Significado (remitente) | AKA | Datos |
|---|---|---|---|
| 0 | Reconocido | ACK | Hex aleatorio |
| 1 | Registrándose (cliente) | PING | Hex aleatorio |
| 2 | Termínate a ti mismo (servidor), terminándome a mí mismo (cliente) | FIN | |
| 3 | Mensaje de inicialización (cliente) | SYN | `version |
| 4 | Reconectar (servidor) | RST | |
| 5 | Establecer intervalo de devolución de llamada (servidor) | segundos | |
| 6 | Obtener datos de interfaz de red | eth0 1.2.3.4/24\neth1 fe80:::/64\n... | |
| 8 | Evaluar código Python3 arbitrario hasta 666 bytes (servidor), devolviendo los primeros 400 bytes de salida (cliente) | EVAL | script de una línea python3 |
| 9 | Ejecutar comando arbitrario hasta 666 bytes (servidor), devolviendo los primeros 400 bytes de salida (cliente) | EXEC | comando bash |
Las sesiones son de larga duración: un cliente inicia una sesión cuando la baliza se ejecuta por primera vez y esa sesión debe durar todo el tiempo que la baliza esté activa en ese cliente. Tenga en cuenta que, dado que la baliza está en memoria y no es persistente, los datos de la sesión se almacenan en la memoria de ese proceso de Python. Cualquier nueva invocación de la baliza iniciará una nueva sesión.
Iniciar una sesión implica que el cliente elabore un mensaje con un preámbulo no de datos que identifique de manera única (para señalar al servidor que esta es una nueva sesión): la concatenación de una clave pública Diffie-Hellman de 32 bytes y un IV AES aleatorio de 16 bytes.