Port knocking con un sólo paquete cifrado (SPA)

Tradicionalmente hablamos de port knocking o golpeo de puertos como un mecanismo para abrir puertos mediante una secuencia de conexiones preestablecida a otros cerrados. Por ejemplo, para abrir el puerto TCP 22 de SSH tendremos que intentar conectarnos primero al 7000, luego al 8000 y finalmente al 9000. Esto lo hemos visto recientemente con Knockd para securizar openssh en modo paranoico y hace tiempo en nuestra pequeña Raspberry Pi...
Pero "aporrear" varias veces la puerta para entrar es muy escandaloso y poco caballeroso ¿verdad?. Existe una forma más fina y elegante mediante Single Packet Authorization (en adelante SPA), una variante de port knocking con la cual sólo será necesario un sólo 'knock' mediante el envío de un único paquete cifrado.

Para ello utilizaremos fwknop (FireWall KNock OPerator), la herramienta de facto que implementa este esquema de autenticación y que por tanto nos permitirá, con un sólo paquete, abrir un servicio oculto detrás de un firewall que por defecto deniega su acceso (INPUT a DROP). Básicamente por defecto el cliente mandará al puerto UDP 62201 un paquete cifrado con Rjindael (simétrico) o GnuPG (asimétrico), no reproducible y autenticado por medio de HMAC. Mientras, el servidor fwknopd estará esnifando el tráfico de forma pasiva mediante libpcap y, si el paquete es válido, ejecutará el iptables, PF o ipfw de turno para permitir el acceso al puerto durante el tiempo especificado.

Además, la cantidad de información que puede ser incluída en el paquete sólo se encuentra limitada por su MTU, es decir, además de la información de acceso también podríamos incluir en el paquete comandos que sean ejecutados en el servidor SPA. Y al usar un único paquete será más difícil de detectar y mucho más rápido. Todo ventajas.

Vale, ya nos hemos convencido de las bondades de fwknop y ahora nos preguntamos cómo hemos podido ser capaces de seguir con nuestras mundanas vidas sin utilizarlo... vamos a ponerle remedio...

En nuestro escenario de ejemplo vamos a configurar port knocking con SPA entre un servidor fwknopd en una máquina virtual con Kali Linux y un cliente fwknop en Windows 7 (so nativo). También y para más seguridad, usaremos cifrado asimétrico con claves GnuPG.

Lo primero que debemos hacer es generar un par de claves en cada máquina.

Generando las claves GnuPG en el cliente y en el servidor 

En el caso del cliente, si ya tienes una clave GnuPG para el correo electrónico (o cualquier otro) la podrás utilizar con seguridad porque sólo se usará para la firma de mensajes de fwknop. Si por el contrario todavía no la tienes, puedes crearla fácilmente con la herramienta Kleopatra que viene con Gpg4win (puedes ver un tutorial en este enlace):



Una vez creadas las claves, no olvides exportar tu clave pública a un fichero ascii:

Ahora le toca el turno al servidor fwknopd. En este caso si tendrás que crear una clave de GnuPG que se utilice exclusivamente para las comunicaciones de fwknop. La razón es que la contraseña utilizada para desbloquear esta clave debe estar almacenada en texto plano en el archivo /etc/fwknop/access.conf porque fwknopd la utilizará para descifrar los mensajes que hayan sido cifrados por el cliente fwknop con la clave pública del servidor. 

Es recomendable que el tamaño de la clave sea 2048 bits o menos porque os recordamos que los mensajes SPA deben caber en un sólo paquete IP.
root@kali:~# gpg --gen-key
gpg (GnuPG) 1.4.12; Copyright (C) 2012 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: directory `/root/.gnupg' created
gpg: new configuration file `/root/.gnupg/gpg.conf' created
gpg: WARNING: options in `/root/.gnupg/gpg.conf' are not yet active during this run
gpg: keyring `/root/.gnupg/secring.gpg' created
gpg: keyring `/root/.gnupg/pubring.gpg' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 
Key does not expire at all
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: Vicente
Email address: hackplayers@ymail.com
Comment: 
You selected this USER-ID:
    "Vicente <hackplayers@ymail.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

Not enough random bytes available.  Please do some other work to give
the OS a chance to collect more entropy! (Need 283 more bytes)

gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key 04E602A1 marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   2048R/04E602A1 2014-01-20
      Key fingerprint = 4F3E 847C EE9B EB8C DFD4  F86E 467A CF40 04E6 02A1
uid                  Vicente <hackplayers@ymail.com>
sub   2048R/71374B8D 2014-01-20

Después de completar el proceso comprobamos que las claves se han generado con éxito:
root@kali:~# gpg --list-keys
/root/.gnupg/pubring.gpg
------------------------
pub   2048R/04E602A1 2014-01-20
uid                  Vicente <hackplayers@ymail.com>
sub   2048R/71374B8D 2014-01-20

Y también y al igual que con el cliente, exportamos la clave pública generada a un fichero ascii:
root@kali:~# gpg -a --export 04E602A1 > servidor.asc

root@kali:~# cat servidor.asc 
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.12 (GNU/Linux)

mQENBFLdoG4BCAC6Tz7R9tlpuGsUpt9r/OlcEK8QpsIC5BjtYAZuzBqoav8Ce4vH
1VbXtTPwYwqqj7MkCz79/RKTBfQN+2CyJzSXmLCShJ+W6py9K/sjPx4AWH9KFwHy
IUQowTQbD6LssOF/fJZjKxQhqZGc8McPAFvxTogRdUFMSF01augPM/iYqx27/TZL
MqVUnVR2v47PQ93Ge1wS6dh2qRKyrd+KWtMC7x3ZQAfqw6VEPQG3cWmS6is3mVdK
M7dYxicVJWG7tkAF9o0ZU7kJp/bQrs39IKUNjFosMHMnIq966E38sBNd6SOuUYN+
CVEArizrTieG7NTbWn/uosL+4PfX6/XDt1XnABEBAAG0H1ZpY2VudGUgPGhhY2tw
bGF5ZXJzQHltYWlsLmNvbT6JATgEEwECACIFAlLdoG4CGwMGCwkIBwMCBhUIAgkK
CwQWAgMBAh4BAheAAAoJEEZ6z0AE5gKhGJwH/Rr/SIZNEXNHhioh8Ufnv9uOMg74
+2XB2k7Icl2sB7vdGIyVKiC9nDVLHr0w2+fMddTyF7Q+JyNhepcHbwYNT025ol6J
V4TNiFRplfvoac3aOdzuga/LfYYax72OVYbJ/i5rMmFrCN1NXuXJPGMle6y2UYw8
LwTWqG1UCkXhn3zZ+cq3HlT4bugUmrW+uLwTs8ul3rZJOlOx0ZI7UDJ3UAmSG7cr
14FjNS5ypj1JDpCDd2es2c7OO554utMT7OIFQyIzCTDCBXe88eRd2hJM8/sQztge
+VXD1/3lrUWWbRRFEjRsTyBjayATgTTqMxnHOgbbd3m/EPe+sUKR/CzDYUm5AQ0E
Ut2gbgEIAJxbLq3urlQbEkxI0rTN2vrdRwYGyn4J+N+1zoNdXB9i2f6sfOWZrkmz
UnL+QL4SddE3op/c+YnYLk3lKVwTVUO5aiXANiVy4IkNqyT6JBJKK4nHHHwxS5g3
5aWwL9p4CDkul88KQB9k5aQa71/ftaZINSfXejnLSZIyWKLk+glTsZbzTuktLDjv
5f0F9ZAjd+nKL10d46GmstC63A8x87ZpFJfMsnEfRM0l3ZfRIcYyIaruPiisvH4s
1jaS+1CE5lyDWmRRfkEZ6VxFPsy+JL3YqlxGc0ycWx8QlOgn1DGfRi4FCBsWGR4T
TLR46Yu9ZWHwOIo3fRl41NIRLOLzPV0AEQEAAYkBHwQYAQIACQUCUt2gbgIbDAAK
CRBGes9ABOYCoc8SB/sEOJycx6p9JPwo/eN1PP9DDyBQ73ZIpgMTcRCg6fneJ8hq
4LoHMy5Y/o5+gwhwkZzv7lp7yeMP5CPD+7NQ/ggoWMOHRXXiwIWwfPBF94VCGaJk
GjLU9zsI0yWv6GXmzdgFw5zrkx9psLnuK/GipshBbvyc+0lK4HpMLWHAHtkFpW9q
bAZvFGNpmYa0uLBeScSJdXcEBA6eu9hUgZohP1zChXIlcYXdCSC3QOVBwt/qdQCg
PzUHdGGR7mhI15Fowug3hULhejH1La7hNeXBDQICC/BQWumu6wuc/T+Tz13WKnUR
imSmtFemZ7DtjDdj+gBefGopd5QnfT0//E/+Bj+S
=pBiY
-----END PGP PUBLIC KEY BLOCK-----
root@kali:~#

Una vez que hayamos creado las claves necesarias en el cliente y en el servidor, tendremos que intercambiar las públicas por scp (o por donde quieras) y firmarlas.

En el servidor importamos y firmamos la clave pública del cliente:
root@kali:~# gpg --import cliente.asc
gpg: key 68E338DE: public key "Vicente2 (Cliente) <hackplayers@ymail.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1  (RSA: 1)

root@kali:~# gpg --edit-key 68E338DE
gpg (GnuPG) 1.4.12; Copyright (C) 2012 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


pub  2048R/68E338DE  created: 2014-01-20  expires: never       usage: SCE 
                     trust: unknown       validity: unknown
[ unknown] (1). Vicente2 (Cliente) <hackplayers@ymail.com>

gpg> sign

pub  2048R/68E338DE  created: 2014-01-20  expires: never       usage: SCE 
                     trust: unknown       validity: unknown
 Primary key fingerprint: 9757 064C 2B1B 843C 4ACB  401E 1545 3B76 68E3 38DE

     Vicente2 (Cliente) <hackplayers@ymail.com>

Are you sure that you want to sign this key with your
key "Vicente <hackplayers@ymail.com>" (04E602A1)

Really sign? (y/N) y

You need a passphrase to unlock the secret key for
user: "Vicente <hackplayers@ymail.com>"
2048-bit RSA key, ID 04E602A1, created 2014-01-20

Y en el cliente importamos y firmamos la clave pública del servidor:

Instalando y configurando el servidor fwknopd

Una vez intercambiadas las claves, vamos a instalar y preparar fwknopd en el servidor:
root@kali:~# apt-get install fwknop-server libpcap-dev

Empezamos editando el fichero /etc/default/fwknop-server e indicamos que fwknopd se ejecute al inicio:
START_DAEMON="yes"

El siguiente paso será especificar el interface y el puerto a la escucha editando el fichero /etc/fwknop/fwknop.conf:
PCAP_INTF                   eth0; 
PCAP_FILTER                 udp port 62201;
 
Finalmente sólo nos queda configurar las directivas de /etc/fwknop/access.conf de tal manera que fwknopd use GnuPG para verificar y descifrar los paquetes SPA. Fíjate que el ID de la clave del servidor es 04E602A1 y el ID de la clave del cliente es 68E338DE:
SOURCE: ANY;
OPEN_PORTS: tcp/22;
DATA_COLLECT_MODE: PCAP;
GPG_REMOTE_ID: 68E338DE;
GPG_DECRYPT_ID: 04E602A1;
GPG_DECRYPT_PW: password123;
GPG_HOME_DIR: /root/.gnupg;
FW_ACCESS_TIMEOUT: 60;

Probando el acceso al servicio ssh mediante SPA

Si habéis seguido estos sencillos pasos ya tendréis todo el entorno montado. Para probarlo vamos a arrancar fwknopd y configurar el firewall del servidor con una política por defecto de denegación en la entrada:
root@kali:~# service fwknop-server start 
root@kali:~# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
root@kali:~# iptables -A INPUT -i ! lo -j DROP

Como podréis comprobar, el acceso al puerto SSH desde el cliente está inicialmente cerrado:
C:\Users\vmotos>nmap -p 22 192.168.225.129

Starting Nmap 5.51 ( http://nmap.org ) at 2014-01-21 00:57 Hora estßndar romance

Nmap scan report for 192.168.225.129
Host is up (0.00s latency).
PORT   STATE    SERVICE
22/tcp filtered ssh
MAC Address: 00:0C:19:8D:52:DC (VMware)

Nmap done: 1 IP address (1 host up) scanned in 3.55 seconds

Ahora tendremos que generar y enviar desde el cliente el paquete SPA correspondiente. Para ello utilizaremos Morpheus, un IU para Windows escrito por Daniel López que además es de código abierto.

Como veréis en el siguiente pantallazo, su configuración no puede ser más simple. Especificaremos el puerto y la dirección IP que podrá acceder a fwknopd, el tipo de cifrado PGP y la ruta de la clave privada del cliente y la clave pública del servidor:


Al introducir la frase de paso enviaremos el paquete SPA y, casi por arte de magia, el puerto 22/TCP del servicio SSH se abrirá durante el perido de tiempo especificado:


Podéis comprobarlo volviendo a escanear el puerto:
C:\Users\vmotos>nmap -p 22 192.168.225.129

Starting Nmap 5.51 ( http://nmap.org ) at 2014-01-21 01:12 Hora estßndar romance

Nmap scan report for 192.168.225.129
Host is up (0.00038s latency).
PORT   STATE SERVICE
22/tcp open  ssh
MAC Address: 00:0C:19:8D:52:DC (VMware)

Nmap done: 1 IP address (1 host up) scanned in 3.23 seconds

C:\Users\vmotos>

Y bueno, para terminar verificamos el log en el servidor...
root@kali:~# tail -f /var/log/syslog | grep fwknop

Jan 20 19:00:47 kali fwknopd[13583]: SPA Packet from IP: 192.168.225.1 received.
Jan 20 19:00:48 kali fwknopd[13583]: Added Rule to FWKNOP_INPUT for 192.168.225.1, tcp/22 expires at 1390262508
Jan 20 19:01:48 kali fwknopd[13583]: Removed rule 1 from FWKNOP_INPUT with expire time of 1390262508.

...  así como las reglas de iptables creadas:
root@kali:~# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
FWKNOP_INPUT  all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             state RELATED,ESTABLISHED
DROP       all  --  anywhere             anywhere            

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FWKNOP_INPUT (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  192.168.225.1        anywhere             tcp dpt:ssh /* _exp_1390262645 */

Sencillo ¿verdad? 

¿A qué hora llamaréis a las puertas de vuestros servicios más sigilosamente? ;)

Fuentes: 
- Single Packet Authorization - A Comprehensive Guide to Service Concealment with fwknop
- fwknop: Single Packet Authorization y port knocking
- Firewall improvement with fwknop  
- Fwknop versus Knockknock
- SinglePacketAuthorization

2 comentarios :