Domain Fronting basado en TLS 1.3. Parte 2

Introducción

En la primera parte artículo dimos una breve descripción del mecanismo encrypted SNI (eSNI). Mostramos cómo se puede evadir la detección de los modernos sistemas DPI (tomando como ejemplo el DPI de Beeline y el prohibido RKN rutracker), así como investigamos una nueva variante de domain fronting basada en este mecanismo.

En la segunda parte del artículo pasaremos a cosas más prácticas que serán útiles para los especialistas de RedTeam en su difícil trabajo. Al final, nuestro objetivo no es obtener acceso a recursos bloqueados (para tales cosas banales tenemos el viejo y querido VPN). Afortunadamente, existen una gran cantidad de proveedores de VPN, como se dice, para todos los gustos, colores y presupuestos.

Intentaremos aplicar el mecanismo de domain fronting a las herramientas modernas de RedTeam, como Cobalt Strike, Empire, etc., y darles capacidades adicionales para mimetizarse y evadir los modernos sistemas de filtrado de contenido.

La última vez implementamos el mecanismo eSNI en la biblioteca OpenSSL y lo utilizamos con éxito en la conocida utilidad curl. Pero con solo curl, como se dice, no basta. Por supuesto, queremos implementar algo similar en lenguajes de alto nivel. Sin embargo, desafortunadamente, una búsqueda rápida por la red nos decepciona, ya que el soporte del mecanismo eSNI está completamente implementado solo en GOLANG. Así que, nuestras opciones no son muchas: o escribimos en C o C++ puro utilizando la biblioteca OpenSSL parchada, o utilizamos un fork separado de GOLANG de CloudFlare e intentamos portar nuestras herramientas allí. En principio, hay otra opción, más clásica, pero al mismo tiempo laboriosa: implementar soporte para eSNI en Python. Al final, Python también utiliza OpenSSL para trabajar con https. Pero dejaremos esta opción para que alguien más la desarrolle, mientras nosotros estaremos satisfechos con la implementación en golang, especialmente porque nuestro querido Cobalt Strike puede trabajar con canales de comunicación construidos por herramientas externas (canal C2 externo) – de esto hablaremos al final del artículo.

Try Harder…

Una de las herramientas implementadas en Go es nuestro desarrollo para pivoting dentro de la red – el túnel rsockstun, que, por cierto, hoy en día es detectada por las herramientas de Microsoft y Symantec como un software malicioso que tiene como objetivo comprometer la estabilidad mundial…

Domain Fronting basado en TLS 1.3. Parte 2

Sería genial utilizar el desarrollo anterior en este caso. Pero aquí surge un pequeño problema. El hecho es que rsockstun originalmente implica el uso de un canal de comunicación SSL síncrono con el servidor. Esto significa que la conexión se establece una vez y existe durante todo el tiempo de funcionamiento del túnel. Y, como entenderás, el protocolo https no está realmente diseñado para este modo de operación; funciona en modo de solicitud-respuesta, donde cada nueva solicitud http existe en el marco de una nueva conexión tcp.

La principal desventaja de este esquema es que el servidor no puede enviar datos al cliente hasta que el cliente envíe una nueva solicitud http. Pero, afortunadamente, hay muchas soluciones a este problema: la transmisión de datos a través del protocolo http (al fin y al cabo, logramos ver nuestras series favoritas y escuchar música de portales que operan con https, y la transmisión de video y audio no es más que una transmisión de datos). Una de las tecnologías para emular el funcionamiento de una conexión tcp completa sobre el protocolo http es la tecnología de WebSockets, cuya esencia está en organizar una conexión de red completa entre el cliente y el servidor web.

Para nuestra suerte (¡hurra!), esta tecnología está habilitada por defecto en todos los planes de CloudFlare y funciona perfectamente junto con eSNI. Precisamente esta es la que utilizaremos para enseñarle a nuestro túnel a usar el fronting de dominio y ocultarse de los DPI modernos.

Un poco sobre los WebSockets

Primero que nada, explicaremos brevemente y con palabras simples qué son los WebSockets, para que todos tengan una idea de con qué estaremos trabajando.

La tecnología de WebSockets permite alternar temporalmente de una conexión http a una transmisión de datos estándar a través de un socket de red, sin romper la conexión tcp establecida. Cuando el cliente desea cambiar a WebSocket, establece varios encabezados http en su solicitud http. Dos encabezados obligatorios son Connection: Upgrade y Upgrade: websocket. También puede especificar por fuerza la versión del protocolo WebSocket (Sec-Websocket-Version: 13) y algo como un identificador base64 de WebSocket (Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA==). El servidor responde con el código http 101 Switching Protocols y también establece los encabezados Connection, Upgrade y Sec-WebSocket-Accept. El proceso de cambio se ilustra claramente en la imagen a continuación:

Domain Fronting basado en TLS 1.3. Parte 2

Tras esto, la conexión WebSocket se puede considerar completa. Cualquier dato tanto del cliente como del servidor ahora será suministrado no con encabezados http, sino con encabezados WebSocket (que comienzan con el byte 0x82). Ahora el servidor no necesita esperar a que el cliente haga una solicitud para transmitir datos, ya que la conexión tcp no se rompe.

En Go, hay varias bibliotecas para trabajar con WebSockets. Las más populares son Gorilla WebSocket y la estándar WebSocket. Vamos a utilizar esta última, ya que es más sencilla, más ligera y, como dicen, funciona un poco más rápido.

En el código del cliente rsockstun, necesitamos reemplazar las llamadas net.dial o tls.dial por las correspondientes a WebSocket:

Domain Fronting basado en TLS 1.3. Parte 2

Domain Fronting basado en TLS 1.3. Parte 2

Queremos hacer la parte del cliente de nuestro túnel universal y capaz de funcionar tanto a través de una conexión ssl directa como mediante el protocolo WebSocket. Para ello, crearemos una función separada func connectForWsSocks(address string, proxy string) error {…} por analogía con connectForSocks() y la utilizaremos para trabajar con WebSockets en caso de que la dirección del servidor proporcionada al iniciar el cliente comience con ws: o wss: (en el caso de WebSocket seguro).

Para la parte del servidor del túnel, también crearemos una función separada para trabajar con WebSockets. En ella se creará una instancia de la clase http y se configurará el manejador de conexión http (función wsHandler):

Domain Fronting basado en TLS 1.3. Parte 2

Y toda la lógica de gestión de la conexión (autenticación del cliente por contraseña, establecimiento y finalización de la sesión yamux) la colocaremos en el manejador de conexión WebSocket:

Domain Fronting basado en TLS 1.3. Parte 2

Compilamos el proyecto, iniciamos la parte del servidor:

.\/rsockstun –listen ws:127.0.0.1:8080 –pass P@ssw0rd

Y luego la parte del cliente:

.\/rsockstun -connect ws:127.0.0.1:8080 –pass P@ssw0rd

Y comprobamos el funcionamiento en el host local:

Domain Fronting basado en TLS 1.3. Parte 2

Domain Fronting basado en TLS 1.3. Parte 2

Pasemos al dominio-fronto

Con los WebSockets parece que hemos resuelto el tema. Ahora pasemos directamente a eSNI y el dominio-fronto. Como se mencionó anteriormente, para trabajar con DoH y eSNI necesitamos tomar una rama especial de Go de la empresa CloudFlare. Necesitamos una rama con soporte para eSNI (pwu/esni).

La clonamos localmente o la descargamos y descomprimimos el zip correspondiente:

git clone -b pwu/esni https://github.com/cloudflare/tls-tris.git

Luego necesitamos copiar el directorio GOROOT, reemplazar los archivos correspondientes de la rama clonada y configurarlo como principal. Para liberar al desarrollador de este dolor de cabeza, los chicos de CloudFlare prepararon un script especial: _dev/go.sh. Simplemente lo ejecutamos. El script junto con makefile se encargará de todo. Por curiosidad, pueden echar un vistazo al makefile para más detalles.

Después de que el script se ejecute, al compilar el proyecto, será necesario indicar como GOROOT el directorio local preparado por el script. En nuestro caso, esto se ve así:

GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" go build ….

A continuación, necesitamos implementar en el túnel la funcionalidad de solicitud y análisis de claves eSNI públicas para el dominio solicitado. En nuestro caso, serán las claves eSNI públicas de los servidores frontend de CloudFlare. Para esto crearemos tres funciones:

func makeDoTQuery(dnsName string) ([]byte, error)
func parseTXTResponse(buf []byte, wantName string) (string, error)
func QueryESNIKeysForHost(hostname string) ([]byte, error)

Los nombres de las funciones, en principio, hablan por sí mismos. La implementación la tomaremos del archivo esni_query.go, que forma parte de tls-tris. La primera función crea un paquete de red con una solicitud al servidor DNS de CloudFlare, utilizando el protocolo DoH (DNS-over-HTTPS), la segunda analiza los resultados de la solicitud y obtiene los valores de las claves públicas del dominio, y la tercera es un contenedor para las dos anteriores.

A continuación, agregamos en nuestra nueva función la conexión de websocket connectForWsSocks la funcionalidad de solicitud de claves eSNI para el dominio. Donde funciona la parte del servidor, establecemos los parámetros TLS y también asignamos el nombre del "dominio ficticio":

Domain Fronting basado en TLS 1.3. Parte 2

Cabe mencionar que originalmente, la rama tls-tris no está diseñada para el uso de dominio de cobertura. Por lo tanto, no se presta atención al nombre ficticio del servidor (en el paquete client-hello se pasa un campo serverName vacío). Para corregir esto, tenemos que agregar en la estructura TlsConfig el campo correspondiente FakeServerName. No podemos usar el campo estándar ServerName de la estructura, ya que es utilizado por mecanismos internos de tls y si difiere del original, el handshake de tls finalizará con un error. La descripción de la estructura TlsConfig se encuentra en el archivo tls/common.go – y es lo que tenemos que corregir:

Domain Fronting basado en TLS 1.3. Parte 2

Domain Fronting basado en TLS 1.3. Parte 2

Además, tendremos que realizar cambios en el archivo tls/handshake_client.go, para utilizar nuestro campo FakeServerName al formar el handshake TLS:

Domain Fronting basado en TLS 1.3. Parte 2

¡Eso es todo! Puedes compilar el proyecto y verificar su funcionamiento. Pero antes de ejecutar la verificación, es necesario configurar tu cuenta de CloudFlare. Bueno, más que configurarla, simplemente crea una cuenta en CloudFlare y vincula tu dominio a ella. Todas las características relacionadas con DoH, WebSocket y ESNI están habilitadas de forma predeterminada en CloudFlare. Una vez que se actualicen los registros DNS, puedes verificar el funcionamiento del dominio realizando una consulta de claves eSNI:

dig +short txt _esni.df13tester.info 

Domain Fronting basado en TLS 1.3. Parte 2

Si ves algo parecido para tu dominio, significa que todo está funcionando y puedes pasar a las pruebas.

Iniciamos un VPS de Ubuntu, por ejemplo, en DigitalOcean. P.D. En nuestro caso, la dirección IP recientemente asignada por el proveedor estaba en las listas negras del RKN. Así que no te sorprendas si te sucede algo similar. Tuve que usar un VPN para acceder a mi VPS.

Copiamos en la VPS el rsockstun que ya fue compilado (de hecho, esta es otra ventaja de Go: puedes compilar el proyecto en tu máquina y ejecutarlo en cualquier Linux, respetando solo la arquitectura del sistema) y ejecutamos la parte del servidor:

Domain Fronting basado en TLS 1.3. Parte 2

Y luego la parte del cliente:

Domain Fronting basado en TLS 1.3. Parte 2

Como podemos ver, el cliente se ha conectado exitosamente al servidor a través del servidor frontend de CloudFlare usando WebSocket. Para verificar que el túnel funciona como un túnel, puedes realizar una solicitud curl a través del socks5 local, que está abierto en el servidor:

Domain Fronting basado en TLS 1.3. Parte 2

Ahora veamos qué ve el DPI en el canal de comunicación:

Domain Fronting basado en TLS 1.3. Parte 2

Primero, el tunelador utiliza el mecanismo DoH para consultar al servidor DNS de Cloudflare por las claves eSNI para el dominio de destino (paquetes #1-19), y luego se conecta al servidor frontend y establece una conexión TLS, ocultándose detrás del dominio www.google.com (este es el valor predeterminado cuando al iniciar el cliente no se especifica un dominio falso). Para especificar tu propio dominio falso, debes usar el parámetro -fronfDomain:

Domain Fronting basado en TLS 1.3. Parte 2

Domain Fronting basado en TLS 1.3. Parte 2

Ahora, otro punto. De forma predeterminada, en la configuración de la cuenta de CloudFlare está activado el modo Flexible SSL. Esto significa que las solicitudes https a los servidores frontend de Cloudflare desde los clientes se redirigirán sin cifrado (http) a nuestro servidor. Precisamente por esto, ejecutamos la parte del servidor del tunelador en modo non-ssl (-listen ws:0.0.0.0), y no (-listen wss:0.0.0.0).

Domain Fronting basado en TLS 1.3. Parte 2

Para cambiar al modo de cifrado completo, es necesario seleccionar Completo, o Completo (estricto) en caso de que exista un certificado válido en el servidor. Después de cambiar el modo, podremos aceptar conexiones de CloudFlare a través del protocolo https. No olvide generar un certificado autofirmado para la parte del servidor del túnel.

Domain Fronting basado en TLS 1.3. Parte 2

Un lector curioso podría preguntar: "¿Y qué pasa con el cliente para Windows? Sin duda, el uso principal del túnel es establecer una conexión inversa desde máquinas y servidores corporativos, y allí, por lo general, siempre es Windows. ¿Cómo compilo el túnel para Windows, además con una pila TLS específica?" Ahora vamos a presentar otra característica que muestra lo conveniente que es Go. Compilamos para Windows directamente desde Kali, simplemente añadiendo el parámetro GOOS=windows:

GOARCH=amd64 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows  go build -ldflags="-s -w"

O la versión de 32 bits:

GOARCH=386 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows  go build -ldflags="-s -w"

¡Listo! Y no hay más complicaciones. ¡Esto realmente funciona!

Domain Fronting basado en TLS 1.3. Parte 2

Las flags del compilador –w y –s se utilizan para eliminar la basura innecesaria del archivo ejecutable, haciéndolo un par de megabytes más pequeño. Además, se puede empaquetar después con UPX para reducir aún más el tamaño.

En conclusión

En este artículo, con el ejemplo del túnel escrito en Go, hemos demostrado claramente la aplicación de la nueva tecnología de fronting de dominio, implementada en una característica interesante del protocolo TLS 1.3. De manera similar, se puede adaptar la herramienta existente escrita en Go para trabajar a través de servidores de CloudFlare, por ejemplo, Merlin — conocido C2, o forzar a CobaltStrike Beacon a utilizar el fronting de dominio eSNI al trabajar con el Teamserver a través de Canal C2 Externo, implementado en Go, o en el estándar C++ utilizando una versión parcheada de OpenSSL, de la que hablamos en la parte anterior del artículo. En general, la imaginación no tiene límites.

El ejemplo con el túnel y CloudFlare se presenta como un concepto y por ahora es difícil decir sobre las perspectivas a largo plazo de este tipo de fronting de dominio. En este momento, el soporte para eSNI solo se ha implementado en CloudFlare y, en teoría, nada les impide desactivar este tipo de fronting y, por ejemplo, romper las conexiones tls si SNI y eSNI no coinciden. En general, el futuro lo dirá. Pero por ahora, la perspectiva de operar bajo "el disfraz de kremlin.ru" parece bastante atractiva. ¿No es así?

El código actualizado del túnel, así como los archivos ejecutables compilados en formato exe, están disponibles en una rama separada del proyecto en github. Para cualquier problema que surja con el túnel, es mejor reportar un issue en la página del proyecto en GitHub.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster