Image
Túneles en la sombra 2: NGINX

Túneles en la sombra 2: NGINX

¡Hola de nuevo a todos! Hoy toca publicar el segundo post de la serie de 3 artículos que comencé hace un par de semanas, en los que os explico una técnica para crear y "ocultar" túneles HTTP en los que encapsular cualquier tipo de tráfico, por medio de la herramienta Chisel y el servidor NGINX.

En el primer post os comenté cómo utilizar Chisel para crear estos túneles y vimos un ejemplo de ejecución básica de esta herramienta, así como diversas opciones que ofrece para llevar esta a cabo en modo servidor y en modo cliente. En esta segunda entrada me centro en NGINX y en cómo usar este servidor para “camuflar” los túneles HTTP creados con la citada herramienta.

NGINX es una aplicación multifunción que puede actuar como balanceador de carga, caché de contenidos y/o servidor web. Para nuestro objetivo nos interesa, especialmente, la parte de servidor web. No obstante, también vamos a comentar la parte de balanceador de carga, ya que puede ser útil usar NGINX como proxy inverso.

Dependiendo de la versión de SO, instalar NGINX puede ser tan sencillo como ejecutar apt install nginx en Debian o Kali. Una vez instalado el paquete, se puede utilizar el comando nginx que, entre otras cosas, permite validar los archivos de configuración si se le pasa el parámetro -t.



La mayoría de archivos relacionados con NGINX, para Debian y Kali, se encuentran en /etc/nginx/. Entre ellos se encuentran los archivos de configuración de servidores o sites. La carpeta /etc/nginx/sites-available/ contiene estos archivos, mientras que en la carpeta /etc/nginx/sites-enabled/ debe existir un enlace simbólico a dichos archivos para que NGINX los reconozca como activados.





Un ejemplo muy sencillo de archivo de configuración para un servidor web HTTP sirviendo en el puerto 80 podría ser el siguiente: 

server{

          listen 80;
          server_name example.net;
          root /var/www/example;
          index index.html;
}

Con el anterior bloque estamos configurando un servidor web HTTP que escucha en el puerto 80. Este responde al nombre de dominio example.net, sirve el contenido de la carpeta local /var/www/example y muestra el archivo index.html como página de inicio.

Ahora vamos a comentar dos directivas interesantes de NGINX: upstream y map. Por una parte, como NGINX tiene funcionalidad de balanceador de carga, la directiva upstream permite darles a un grupo de servidores o “endpoints” un nombre. De esta forma, podemos manejar y referirnos a estos grupos en otras directivas usando únicamente ese nombre. Por otra parte, aunque NGINX tiene una directiva if para usar condicionales dentro de archivos de configuración, el comportamiento de esta directiva parece inestable y caótico. La directiva map es una alternativa a if y tiene cierto parecido con las sentencias switch de algunos lenguajes de programación. La directiva map toma dos variables y a la segunda variable le asigna un valor dependiendo del valor de la primera. Vamos a ver un par de ejemplos de estas directivas:

upstream web {
           server 127.0.0.1:81;
}

upstream tun {
           server 127.0.0.1:82;
}

En este ejemplo de upstream simplemente estamos creando dos grupos llamados web y tun. Estos nombres los podremos usar en otros puntos del mismo archivo de configuración y su valor se sustituirá por el que hemos definido, que en este caso será web por 127.0.0.1:81 y tun por 127.0.0.1:82. En este ejemplo se ha utilizado la dirección de localhost, pero se puede utilizar cualquier IP, nombre de dominio e incluso sockets Unix.

map $http_host $name {
          default 0;
          example.com 1;
          example.org 2;
          example.net 3;
}

En este ejemplo de map estamos utilizando el valor de la cabecera HTTP Host para asignarle un valor. En este caso es numérico, pero puede ser también una cadena de texto, a la variable name. Si dicha cabecera coincide con alguno de los nombres de dominio que hemos indicado, se le asignará a la variable name el valor correspondiente, por ejemplo, 1, si la cabecera Host tiene el valor example.com. Si el valor de esta cabecera no coincide con ninguno de los valores indicados, se activará el valor por defecto (default) y a la variable name se le asignará el valor 0.

Y, para ir cerrando este artículo, vamos a combinar todo lo anterior con chisel. Usando NGINX y las directivas que hemos comentado, podemos conseguir un servidor web aparentemente normal que sirve contenido HTTP en el puerto 80, pero que, si se lanza un cliente chisel contra dicho servidor, la conexión se redirigirá hacia un servidor chisel. Para conseguir esto, podríamos usar la siguiente configuración:

upstream web {
         server 127.0.0.1:81;
}
upstream chisel {
         server 127.0.0.1:82;
}
map $http_sec_websocket_protocol $target {
         default                   "web";
         chisel-v3       "chisel";
}
server{
         listen 80;
         location / {
                  proxy_pass http://$target;
                  proxy_http_version 1.1;
                  proxy_set_header Upgrade $http_upgrade;
                  proxy_set_header Connection $http_connection;

         }
}
server{
          listen 81;
          server_name totesnotevil.net;
          root /var/www/niceweb;
          index index.html;
}

Veamos este archivo de configuración por partes. Las primeras dos directivas upstream nos sirven para definir dos “endpoints”: el web, que es donde estará escuchando nuestro servidor web inofensivo; y el de chisel, que es donde tendremos un servidor chisel en ejecución esperando conexiones. Como hemos comentado antes, aquí se ha utilizado localhost, pero se podría utilizar cualquier tipo de dirección que acepte NGINX. De hecho, sería recomendable no tener el “redirector web” y el servidor de chisel en la misma máquina.

Después de las directivas upstream tenemos una directiva map, la cual está mirando la cabecera Sec-WebSocket-Protocol para ver si existe y si su valor es “chisel-v3”. En caso afirmativo, la variable target toma el valor “chisel”. En cualquier otro caso, toma el valor “web”. Con esto, lo que conseguimos es distinguir si el tráfico que está llegando a nuestro servidor NGINX proviene de un cliente chisel o no. En este caso hemos utilizado la cabecera Sec-WebSocket-Protocol para hacer esta distinción, pero se podría utilizar cualquier otra que permita hacer esta distinción. Si queremos hacer esto, hay que tener en cuenta que NGINX cambia los nombres de las cabeceras cuando crea sus variables internas de la siguiente manera: las mayúsculas se vuelven minúsculas, los guiones (-) se vuelven barras bajas (_) y se le añade $http_ al principio. De esta forma, la cabecera HTTP Sec-WebSocket-Protocol se convierte en la variable NGINX $http_sec_websocket_protocol.

Además de las directivas upstream y map, tenemos una directiva server. Dentro del bloque de esta directiva estamos creando un servidor web que escucha en el puerto 80, pero no sirve ningún contenido. En su lugar, hemos creado un bloque con la directiva location, que actúa como proxy inverso. La configuración dentro de este bloque hace que todas las peticiones a / sean redirigidas a $target, que habrá tomado el valor correspondiente de acuerdo a la directiva map anterior. En esta redirección, establecemos la versión del protocolo HTTP, como la 1.1, necesaria para las conexiones websocket que utiliza chisel. Además, propagamos las cabeceras Upgrade y Connection con el mismo valor con el que nos han llegado, ya que no se retransmiten automáticamente y se perderían en el salto entre el servidor NGINX y el servidor chisel. Con este bloque server y la configuración de proxy conseguimos que, dependiendo de las cabeceras que nos hayan llegado, la conexión se retransmita al servidor chisel escuchando en 127.0.0.1:82 o al servidor web escuchando en 127.0.0.1:81.

La última directiva server en nuestra configuración define un servidor web simple que ofrece su contenido a cualquier conexión que haya llegado a nuestro servidor en el puerto 80 y no sea una conexión entrante de un cliente chisel.



Y con esto último finaliza este segundo post de la serie. Sin embargo, antes de dejaros me gustaría comentar que esta “técnica” puede ser interesante en escenarios de Command & Control en entornos donde existan controles de listas blancas y/o categorización de páginas. Por poner un ejemplo, podemos crear una página web y mandarla a algún servicio de categorización para que “nos la den como buena” y configurar nuestro NGINX para poder recibir conexiones de chisel. El resultado final puede ser que seamos capaces de
establecer canales de C&C HTTP utilizando nuestra web en lista blanca. Adicionalmente, podemos utilizar esta “técnica” para crear un esquema de “redirectores web” que podamos utilizar para ocultar nuestra infraestructura de ataque y control de posibles “curiosos”.

Ahora sí, me despido hasta la próxima y última entrada sobre este tema, en la que os hablaré sobre Domain Fronting y mostraré un ejemplo práctico que recoja todo lo explicado.

¡Nos vemos!


David Bueno

David Bueno