domingo, 17 de mayo de 2015

UDP en GNU/Linux Parte I

Hace unos días me encontré con un caso interesante de pérdida de paquetes en transferencias utilizando UDP. Como se imaginaran poco me asombro que se perdieran paquetes UDP, ya que por definición se trata de un protocolo de transporte que no es fiable a la hora de entregar la información, una especie de cartero ebrio digamos. El control de flujo, el orden y la re transmisión no forman parte de las especificaciones de UDP [1], ya que fue ideado para ser simple. Meter los paquetes al cable y que la fuerza los acompañe.

Usar UDP como protocolo de transporte suele dar lugar a las siguientes situaciones:
  • Paquetes que nunca alcanzan el destino. Sin control de conexión y re transmisión, básicamente el emisor pone el paquete en el cable y se olvida por completo del mismo, no hay notificación de recepción ni nada que se le parezca.
  • Paquetes que llegan desordenados. Por esas cosas de las redes, los paquetes A y B que fueron enviados en ese orden podrían ser recibidos por el receptor en el orden inverso.
por lo tanto muchas aplicaciones que utilizan UDP deciden lidiar con estos problemas en la capa de aplicación.

En GNU/Linux, haciendo uso del programa netstat podemos ver las estadísticas UDP del sistema, por ejemplo:

root@ubuntu:/home/juan# netstat -su
Udp:
    23 packets received
    0 packets to unknown port received.
    0 packet receive errors
    23 packets sent
...
root@ubuntu:/home/juan#


En esta caso particular vemos que se enviaron 23 paquetes y se recibieron 23, muy probablemente se trate de consultas y respuestas DNS, ya que acabo de iniciar la máquina virtual. En total son 4 los campos disponibles de estadísticas UDP:
  • Packets received: número paquetes recibidos de manera satisfactoria por el sistema. Esto significa paquetes recibidos y pasados a la aplicación correspondiente.
  • Packets to unknown port received: número de paquetes que fueron recibidos pero no eran esperados por el sistema, es decir no había ninguna aplicación escuchando en ese puerto.
  • Packet receive errors: número de paquetes que no pasaron el checksum o que llegaron cuando el buffer de recepción se encontraba lleno y por lo tanto fueron descartados.
  • Packets sent: número de paquetes enviados por el sistema.

Probando Packets received:

Para probar este campo primero debemos identificar algún servicio escuchando en un puerto UDP, y para eso usamos netstat:

root@ubuntu:/home/juan# netstat -plun
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
udp        0      0 0.0.0.0:68              0.0.0.0:*                           558/dhclient   
udp        0      0 0.0.0.0:5113            0.0.0.0:*                           558/dhclient   
udp6       0      0 :::43512                :::*                                558/dhclient   
root@ubuntu:/home/juan#


Podemos ver que el puerto 68, 5113 y 43512 están en uso así que apuntaremos al puerto 68 y enviaremos 5 paquetes. Para generar tráfico UDP sin demasiadas complicaciones vamos a utilizar Hping3 [2] (aquí hay un post en donde lo utilicé para probar DNS Amplification attacks), para entender los parámetros man hping3.

root@moon:/home/juan# hping3 192.168.0.10 -2 -c 5 -p 68 -V
using wlan0, addr: 192.168.0.2, MTU: 1500
HPING 192.168.0.10 (wlan0 192.168.0.10): udp mode set, 28 headers + 0 data bytes

--- 192.168.0.10 hping statistic ---
5 packets transmitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms
root@moon:/home/juan# 


Ahora veamos cómo se manifiesta esto en las estadísticas UDP:

root@ubuntu:/home/juan# netstat -su
Udp:
    28 packets received
    0 packets to unknown port received.
    0 packet receive errors
    23 packets sent

...
root@ubuntu:/home/juan#


Vemos que los paquetes recibidos se incrementaron en 5, como era de esperarse, ya que los paquetes tenían como destino un puerto donde había una aplicación escuchando (mas allá de que la aplicación haya sido capaz de interpretar el contenido de los paquetes o no).

Probando Packets to unknown port received:

Los campos Packets received y Packets sent son bastante simples de comprobar así que vamos a apuntar a este otro que es un poco mas interesante.El tráfico será:
  • Destinado al puerto 5555.
  • Enviaremos 10 paquetes.
 Shoot!

root@moon:/home/juan# hping3 8.8.8.8 -2 -c 10 -p 5555 -V
using wlan0, addr: 192.168.0.2, MTU: 1500
HPING 8.8.8.8 (wlan0 8.8.8.8): udp mode set, 28 headers + 0 data bytes

--- 8.8.8.8 hping statistic ---
10 packets transmitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms
root@moon:/home/juan# hping3 192.168.0.10 -2 -c 10 -p 5555 -V
using wlan0, addr: 192.168.0.2, MTU: 1500
HPING 192.168.0.10 (wlan0 192.168.0.10): udp mode set, 28 headers + 0 data bytes
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1179 seq=0
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1180 seq=1
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1181 seq=2
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1182 seq=3
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1183 seq=4
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1184 seq=5
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1185 seq=6
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1186 seq=7
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1187 seq=8
ICMP Port Unreachable from ip=192.168.0.10 name=UNKNOWN  
status=0 port=1188 seq=9

--- 192.168.0.10 hping statistic ---
10 packets transmitted, 10 packets received, 0% packet loss
round-trip min/avg/max = 3.4/19.6/161.2 ms
root@moon:/home/juan#


Vemos que el destino nos respondió con un mensaje ICMP Port Unreachable por cada paquete que enviamos, lo cuál nos da dos pautas:
  • Los paquetes llegaron al sistema y el kernel reconoció que no tenía a quién entregarlos.
  • El host no tiene un firewall que descarte los paquetes a puertos que no sean necesarios.
Pero bien, ahora veamos qué pasó en los contadores del host destino!!

root@ubuntu:/home/juan# netstat -su
IcmpMsg:
    OutType3: 10
Udp:
    28 packets received
    10 packets to unknown port received.
    0 packet receive errors
    23 packets sent
...

root@ubuntu:/home/juan#

Exacto! Ahora tenemos 10 paquetes reconocidos como "packets to unknown port received" y también vemos los 10 mensajes ICMP enviados como respuesta.

Probando packet receive errors:

Ahora pongamos a prueba el reconocimiento de paquetes con problemas. Por suerte Hping3 nos permite, con el flag -b, enviar paquetes con el checksum corrupto así que usaremos eso para probar. En este punto haremos dos pruebas:

  • Prueba1
    • 5 paquetes con checksum inválido
    • puerto 68
  • Prueba 2
    • 5 paquetes con checksum inválido
    • puerto 5555
Prueba 1:

/root@moon:/home/juan# hping3 192.168.0.10 -2 -c 5 -p 68 -b -V
using wlan0, addr: 192.168.0.2, MTU: 1500
HPING 192.168.0.10 (wlan0 192.168.0.10): udp mode set, 28 headers + 0 data bytes

--- 192.168.0.10 hping statistic ---
5 packets transmitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms
root@moon:/home/juan#



root@ubuntu:/home/juan# netstat -su
IcmpMsg:
    OutType3: 10
Udp:
    28 packets received
    10 packets to unknown port received.
    5 packet receive errors
    23 packets sent
    InCsumErrors: 5
...
root@ubuntu:/home/juan#

Ahora podemos ver que el valor de "packet receive errors" pasó de 0 a 5, también tenemos un nuevo campo llamado InCsumErrors con valor 5.

Prueba 2:

root@moon:/home/juan# hping3 192.168.0.10 -2 -c 5 -p 5555 -b -V
using wlan0, addr: 192.168.0.2, MTU: 1500
HPING 192.168.0.10 (wlan0 192.168.0.10): udp mode set, 28 headers + 0 data bytes

--- 192.168.0.10 hping statistic ---
5 packets transmitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms
root@moon:/home/juan#



root@ubuntu:/home/juan# netstat -su
IcmpMsg:
    OutType3: 10
Udp:
    28 packets received
    10 packets to unknown port received.
    10 packet receive errors
    23 packets sent
    InCsumErrors: 10
...
root@ubuntu:/home/juan#


El número de packet receive errors (y InCsumErrors) volvió a incrementarse en 5. Vale notar que esta vez no recibimos los mensajes ICMP, básicamente porque los paquetes fueron descartados rápidamente por no tener el checksum inválido.

Un detalle llamativo de estas dos últimas pruebas es el siguiente. Viendo los logs escritos en /var/log/syslog, encontré:

May 17 15:08:25 ubuntu dhclient: 5 bad udp checksums in 5 packets
May 17 15:08:34 ubuntu kernel: [ 9005.777840] UDP: bad checksum. From 192.168.0.2:2868 to 192.168.0.10:5555 ulen 8
May 17 15:08:35 ubuntu kernel: [ 9006.776665] UDP: bad checksum. From 192.168.0.2:2869 to 192.168.0.10:5555 ulen 8
May 17 15:08:36 ubuntu kernel: [ 9007.781846] UDP: bad checksum. From 192.168.0.2:2870 to 192.168.0.10:5555 ulen 8
May 17 15:08:37 ubuntu kernel: [ 9008.776246] UDP: bad checksum. From 192.168.0.2:2871 to 192.168.0.10:5555 ulen 8
May 17 15:08:38 ubuntu kernel: [ 9009.779863] UDP: bad checksum. From 192.168.0.2:2872 to 192.168.0.10:5555 ulen 8


La primera línea indica que dhclient (el servicio escuchando en 68/UDP) recibió, o al menos se enteró de, los 5 paquetes con checksum erróneo, lo cual me deja un poco desconcertado. Aparentemente el kernel pasa estos paquetes de todas maneras a la aplicación, probablemente para que la aplicación decida qué medidas tomar. Las siguientes 5 líneas son del kernel indicando que se recibieron 5 paquetes UDP con bad checksum (dirigidos al 5555/UDP).

Otra forma un poco mas complicada de analizar packet receive erors es desde /proc/net/udp. La ventaja de este método es que la información está mas detallada, por ejemplo por puerto. netstat resume esta información.

root@ubuntu:/home/juan# cat /proc/net/udp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode ref pointer drops            
   67: 00000000:0044 00000000:0000 07 00000000:00000000 00:00000000 00000000     0        0 8110 2 caaa82c0 5                
  248: 00000000:13F9 00000000:0000 07 00000000:00000000 00:00000000 00000000     0        0 8044 2 caaa8000 0                 
root@ubuntu:/home/juan#


En este caso marcado en "negrita" vemos 0044 valor hexadecimal del puerto 68, y por último el número de paquetes descartados para ese puerto 5 (los paquetes enviados con bad checksum). Otros valores importantes que vemos son tx_queue y rx_queue que básicamente indican que las colas están vacías dado que no hay paquetes llegando o saliendo del host.

Cuando describí el campo packet receive errors al comienzo del post, dije que este contador no solo se incrementa por paquetes defectuosos sino también por paquetes que al llegar encuentran el buffer de recepción lleno y son descartados. Esta última condición se genera cuando la tasa de llegada de paquetes es mayor a la tasa de procesamiento de los mismos. Un clásico problema productor-consumidor, si se produce a mayor tasa de la que se consume, el productor debe ser capaz de reconocer la situación y frenar la producción (control de flujo que UDP no posee) o el consumidor debe ser capaz de aumentar su velocidad de consumo o ampliar su capacidad de almacenar productos (incrementar el tamaño de los buffers).

Probar esta última situación resulta un poco mas complicado así que queda pendiente para la próxima publicación :P

Links:

[1] UDP - https://www.ietf.org/rfc/rfc768.txt
[2] Hping3 - http://wiki.hping.org/

sábado, 2 de mayo de 2015

End-To-End security, evitando ataques MiTM

Hace mucho tiempo que Internet dejó de ser ese lugar pacífico y seguro donde protocolos como telnet o ftp se consideraban buenas prácticas. Hoy la red de redes es mas bien un callejón oscuro en una noche lluviosa en una de esas grandes metrópolis que vemos en las películas. Atravesar dicho callejón sin las debidas precauciones podría ser una muy muy mala idea.

La seguridad end-to-end de las comunicaciones es fundamental para proporcionar privacidad y prevenir buena parte de los ataques MiTM (ataques de hombre en el medio).

Se reconoce como ataque MiTM a un ataque donde una tercera parte (el atacante) logra posicionarse en medio de una comunicación entre por ejemplo cliente y servidor. Durante esta intervención el atacante es capaz de ver la comunicación, alterarla y/o interrumpirla. Por definición, una red que se encuentre en poder de un atacante es vulnerable a un ataque MiTM.

El ataque MiTM más conocido es ARP spoofing. En este caso el atacante enviando tramas ARP falsificadas logra engañar a la víctima y se hace pasar por el gateway de la LAN. 

En la actualidad no existe una bala de plata contra los ataques MiTM, sin embargo se pueden tomar medidas que incrementen la dificultad de llevarlos a cabo, haciéndolos a veces hasta impracticables (esta afirmación dependerá de quién es el atacante y de cuántos recursos dispone, no es lo mismo si tu atacante es un niño aburrido o un gobierno).

Existe dos medidas muy utilizadas para atenuar/evitar los ataques MiTM y son el cifrado y el uso de certificados digitales. Estas medidas protegen principalmente la confidencialidad y la integridad de la información en tránsito, complicando claramente el escenario para un atacante. Pero ni lerdos ni perezosos, los atacantes apuntan ahora contra las debilidades en los mecanismos de protección, por ejemplo atacando las CAs, los algoritmos de cifrado o los protocolos que los utilizan. Algunos de esos ejemplos son:

-Ataques a CAs como Comodo o DigiNotar.
-Ataques como BEAST, CRIME and POODLE han dejado a SSL al borde del Knock Out.


Pareciera que la única opción es sentarnos a beber y llorar, pero no! Hay maneras de complicar aún mas el camino a los atacantes. Porque al final del día siempre se trata de eso, la seguridad absoluta no existe.

Según US-CERT estas son las mejores prácticas para aspirar a seguridad End-to-End y mitigar así ataques MiTM.

Actualizar TLS/SSL:

Se recomienda utilizar TLS 1.1 o superior y en lo posible desterrar el uso de TLS 1.0 y SSL 1, 2 y 3.x. Algunos clientes TLS 1.0 pueden usar SSL 3.0 el cuál es vulnerable al ataque POODLE cuando se utiliza el modo de cifrado de bloques encadenado.
Empresas como AWS, CloudFlare y Twitter ya cumplen esta recomendación o están en proceso.

Usar Certificate Pinning:

Esta técnica consiste en asociar un certificado X.509 y su clave pública a por ejemplo un host/servicio. Generalmente, los certificados son validados corroborando la cadena de confianza hasta el certificado raíz. Certificate Pinning esquiva ese proceso de validación y permite al usuario confiar en un certificado particular o en la firma de un certificado particular.

Browsers como Chrome o Firefox utilizan esta técnica. Una de las maneras en que la aplican es pre-cargando los hashes de las claves públicas aceptadas; luego limitan los certificados válidos a aquellos cuyo hash de la clave pública coincida con alguno de los pre-cargados. Chrome por su lado mantiene una lista blanca de las claves públicas válidas para los servicios de Google.

Certificate Pinning destruye en cierta manera el esquema de cadena de confianza que plantea PKI, ya que una aplicación que asocia un certificado o clave pública a un host ya NO depende de otros servicios como DNS o CAs a la hora de aceptar la identidad del mismo.

OWASP lo plantea muy clarito:

The pandemic abuse of trust has resulted in users, developers and applications making security related decisions on untrusted input. The situation is somewhat of a paradox: entities such as DNS and CAs are trusted and supposed to supply trusted input; yet their input cannot be trusted. Relying on untrusted input for security related decisions is not only bad karma, it violates a number of secure coding principals

DNS-Based Authentication of Named Entities (DANE):

 DANE es un protocolo que permite asociar un certificado X.509 a un registro DNS que use DNSSEC. Confiar en un gran número de CAs es un problema, ya que una de ellas podría ser comprometida y emitir certificados para dominios que no fueron solicitados. DANE permite al administrador del dominio definir qué CA puede emitir certificados en su nombre.

Network Notary Servers:

Esta solución apunta a mejorar la seguridad en la comunicación entre clientes y sitios web permitiendo a los navegadores verificar la autenticidad de un sitio sin depender de una CA. Un Network Notary Server se encarga de monitorizar sitios web y construir un historial de sus certificados a través del tiempo. Entoces cuando un navegador web, utilizando network notary, obtiene el certificado de un sitio web puede comunicarse con su Network Notary Server para corroborar el historial de certificado del sitio. Si el certificado provisto por el web server no coincide con el historial del Notary Network Server, se podría tratar de un ataque MiTM.

Fuentes:

https://www.us-cert.gov/ncas/alerts/TA15-120A
http://en.wikipedia.org/wiki/ARP_spoofing
https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning
http://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities 
http://perspectives-project.org/
https://www.dnssec-validator.cz/