Creando un túnel DNS para navegar saltándose un Firewall.

Buenas tardes,

Hace ya algún tiempo publiqué una entrada haciendo referencia a túneling mediante SSH (https://www.bitsdelocos.es/2015/08/tunnel-ssh-con-raspbian-y-bitvisessh/). En esta entrada, versará sobre túneling sobre DNS (Domain Name Server). El concepto es el mismo: sobre un flujo de peticiones DNS insertar consultas forjadas de un modo conveniente parainyectar información de otro protocolo sobre DNS y así saltarse las protección de un firewall. La definición de un túnel en informática es:

Se conoce como túnel o tunneling a la técnica que consiste en encapsular un protocolo de red sobre otro (protocolo de red encapsulador) creando un túnel de información dentro de una red de computadoras.

Para poder llevar a cabo este escenario, necesitaremos:

  • Servidor: VPS o un equipo linux con una IP de internet estática.
  • Cliente: Sistema operativo linux o una máquina virtual con linux en su defecto.
  • Tener control sobre un dominio y sus entradas de DNS.
  • Tener chrome con la extensión SwitchyOmega.

La idea principal se basa en crear un subdominio al que se le harán las peticiones de DNS y un NS (Name Server o Servidor de nombres) al que enviar las peticiones malformadas. Necesitaremos añadir la siguiente configuración en nuestro DNS público:

  • tunel.bitsdelocos.es (NS apuntando a ns.bitsdelocos.es)
  • ns.bitsdelocos.es (A apuntando a la IP del VPS)

De este modo se conseguirá que todas las peticiones DNS del tipo xxxx.tunel.bitsdelocos.es se envíen a ns.bitsdelocos.es. Lo siguiente que necesitamos es montar un servidor de nombres que haga la función de resolver de paquetes generados maliciosamente que le lleguen. En este caso usaremos iodine (https://github.com/yarrick/iodine) . He usado debian 9 base como servidor de DNS. Para hacer el build de iodine necesitaremos ejecutar como super usuario:

apt-get install gcc make autoconf libz-dev git
git clone https://github.com/yarrick/iodine.git
cd iodine
make && make install

Si todo ha ido bien iodine estará instalado en nuestro sistema:

root@Debi:~# iodine -v
iodine IP over DNS tunneling client
Git version: 27e5d6f

Para hacer funcionar este servidor de DNS bastará con ejecutar:

iodined -c -P <PASSWORD> -n <IP ESTATICA> 172.16.0.1 tunel.bitsdelocos.es -F /var/run/iodine.pid

En el caso de que estemos usando la red local 172.16.0.0/24 podremos cambiar este rango por uno que no estemos usando.

Para comprobar que todo salió bien, se puede ejecutar el comando ip addr show para ver si se asignó una ip a la interfaz virtual dnsX:

root@Debi:~# ip addr show dev dns0
11: dns0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1130 qdisc pfifo_fast state UNKNOWN group default qlen 500
    link/none 
    inet 172.16.0.1/27 scope global dns0
       valid_lft forever preferred_lft forever

En nuestro PC debemos ejecutar el cliente que nos levantará el túnel contra el NS ns.bitsdelocos.es haciendo uso de resoluciones DNS al subdominio tunel.bitsdelocos.es. Para ello lanzaremos iodine en el cliente del siguiente modo:

iodine  -P <PASSWORD> <IP DNS FORWARDER> tunel.bitsdelocos.es -F /var/run/iodine.pid

Donde es la ip del forwarder que use la red a la que estamos conectados o bien un servidor externo como el NS de google (8.8.8.8). Si no ha habido errores se podrá levantar un proxy SOCKS5 con el comando SSH:

ssh -fN -D *:8080 root@172.16.0.1

La IP 172.168.0.1 es la ip que se le habrá asignado a la interfaz dnsX en el servidor.

Para comprobar que todo ha ido bien se debe instalar el plugin switchy omega en el navegador chrome y configurarlo:

La IP que aparece en la captura es la ip de la máquina virtual donde se está ejecutando el cliente de iodine.

Para comprobar si ha ido bien la configruación del proxy, debemos abrir una web y con F12 inspeccionar la ip remota de la página abierta, si corresponde con la ip de nuestro proxy significa que habrá funcionado.

Se ha configurado Switchy omega con Chrome.

Otro modo de comprobar si funciona es usando CURL en un terminal linux

[root@localhost ~]# curl --socks5-hostname  127.0.0.1:8080 www.bitsdelocos.es -v
* About to connect() to proxy 127.0.0.1 port 8080 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.bitsdelocos.es
> Accept: */*
> 
< HTTP/1.1 302 Found
< Server: openresty
< Date: Sun, 21 Oct 2018 15:52:40 GMT
< Content-Type: text/html; charset=iso-8859-1
< Content-Length: 211
< Connection: keep-alive
< Location: https://www.bitsdelocos.es/

Este método de túneling es mucho menos efectivo que el túnneling por ssh ya que el overhead introducido es mucho mayor.

Creando shells inversas

Hace unos días en la casa de campo me instalaron un router 4G al que no tengo acceso de administrador, para realizar cualquier cambio sobre el dispositvo tengo que llamar a la operadora de internet y que me lo hagan remotamente (incluso un cambio de password del WIFI, IBRED atenta contra la LOPD…). Mi intención era dejar una raspberry pi realizando algunas tareas domóticas y de monitorización sencillas, al parecerme un engorro tener que estar haciendo llamadas para que me abran distintos puertos, me reserven una entrda en el dhcp,… decidí buscar una solución alternativa. Esto funcionaría también estando detrás de un firewall con los puertos capados, o en una red NAT sin forwarding de puertos.

En este post se tratará el tema: levantar un túnel inverso y hacer forward de un puerto local a un puerto remoto a través del protocolo SSH. Para ello, es necesario tener un segundo equipo en el que poder hacer login SSH sin credenciales (con claves RSA). Para los que no sepáis que es un túnel SSH, os recomiendo la entrada que publicqué hace un tiempo sobre ello ( Túnel SSH Raspberry )

El siguiente paso es configurar el demonio SSH de la máquina remota para que permita hacer keepaliving de la conexión (permitir alargar el tiempo máximo de conexión sin intercambiar información). Para ello debemos modificar el archivo

/etc/ssh/sshd_config

Añadir/modificar las siguientes líneas:

TCPKeepAlive yes
ClientAliveInterval 30
ClientAliveCountMax 99999
GatewayPorts yes

Y reniciar el servicio SSH

Una vez tenemos configurado el demonio SSH y haber intercambiado las claves públicas entre los equipos es lo único que hay que hacer es ejecutar:

ssh -f -N -R 10000:localhost:22 @ -p 22

Este comando redigirá el puerto 22 local, al puerto 10000 de la máquina remota. Si quisiéramos hacer login en la Raspberry podríamos hacer un SSH user@equipo_remoto -p 10000, en realidad esto estaría redirigiendo el tráfico a través de un túnel al puerto 22 de la raspberry. Se podría usar con cualquier otro puerto 80,443,…

Como mi idea era dejar la Raspberry encendida sin preocuparme si se reinicia y quiero acceder monté un pequeño script en Bash para que lo ejecutara cron:

#!/bin/bash

REMOTE_PORT="10000"
LOCAL_FORWARDED_PORT="22"
REMOTE_SSH_PORT="22"
REMOTE_HOST="host_remoto"
REMOTE_USER="user"

PID=$(ps -ef | grep "${REMOTE_HOST}" | grep -v grep | awk '{print $2}')

if [ "" == "$PID" ]; then
    echo "[+] Excecuting reverse ssh connection"
    ssh -f -N -R "${REMOTE_PORT}:localhost:${LOCAL_FORWARDED_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" -p "${REMOTE_SSH_PORT}"
else
    echo "[-] The connection was stablished before"
fi

La entrada de crontab debe tener la siguiente forma:

* * * * * [ -x /root/reverse_ssh.sh ] &&  /root/reverse_ssh.sh &> /tmp/reverse_ssh.log

Si vamos al servidor remoto deberíamos poder ver

user@host_remoto:~$ netstat -an | grep -i 10000
tcp        0      0 0.0.0.0:10000           0.0.0.0:*               LISTEN     
tcp6       0      0 :::10000                :::*                    LISTEN     

Si queremos usar el servicio al que estamos haciendo forwarding (en este caso SSH) podríamos abrir una shell contra el puerto 10000:

user@host_remoto:~$ ssh root@127.0.0.1 -p 10000
The authenticity of host '[127.0.0.1]:10000 ([127.0.0.1]:10000)' can't be established.
ECDSA key fingerprint is XX:XX:XX:XX:XX:XX:XX:XX:XX.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[127.0.0.1]:10000' (ECDSA) to the list of known hosts.

The programs included with the Kali GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Apr  8 11:25:42 2017 from ::1
root@raspberry:~# 

En este caso veríamos que la shell está funcionando.