La fragmentación IP es una técnica utilizada para dividir los datagramas IP en fragmentos de menor tamaño. Ésto es necesario ya que cuando los datagramas IP viajan de un lugar a otro, éstos pueden atravesar diferentes tipos de redes y el tamaño máximo -llamado MTU– de estos paquetes puede variar dependiendo del medio físico utilizado para la transmisión.
El valor máximo que técnicamente puede utilizarse para un datagrama IP es de 65536 bytes, aunque en la práctica se utilizan otros tamaños mucho más pequeños:
- Ethernet: 1518 bytes (típicamente 1500 bytes).
- PPPoE: 1492 bytes.
- ATM: 8190 bytes.
- FDDI: 4470 bytes.
- PPP: 576 bytes.
Veamos cómo funciona esta técnica con más detalle. La cabecera IP, que suele tener un tamaño de 20 bytes, contendrá la siguiente información:
- Identificador de fragmento. Cada Fragmento debe asociarse con un único identificador para que el reensamblaje en destino pueda realizarse correctamente.
- Información sobre la posición en el paquete final.
- Información sobre el tamaño de los datos que se transportan en el fragmento.
- Cada fragmento debe contener el bit MF (More Fragments) para saber si el fragmento actual es el último o no.
Así que la figura de un paquete de máximo tamaño que no necesite fragmentación en una red típica Ethernet sería algo así:
Si sumamos la cabecera y los datos encapsulados, tenemos que en total hacen 1500 bytes, por lo que al viajar por una red Ethernet, no sería necesaria su fragmentación.
Los datos encapsulados pueden ser tanto un protocolo IP como TCP, UDP o ICMP. Veamos un ejemplo en el que se tenga que utilizar la fragmentación. Este es un ejemplo anormalmente grande, pero en el que podremos ver cómo se realiza el proceso de fragmentación. Se trata de una petición echo que pasa por una red Ethernet con MTU de 1500 bytes.
En el paquete original, la suma de las cabeceras y los datos ICMP suman 4028 bytes. Este paquete al ser transmitido en una red Ethernet deberá ser fragmentado, generandose así 3 paquetes de 1500 bytes o menos. Cada fragmento llevará obligatoriamente al menos la cabecera IP (necesaria para saber hacia dónde se dirige el fragmento), que en este caso ocupa 20 bytes, así que tendremos realmente 1480 bytes útiles.
El primer fragmento contendrá la Cabecera IP + la cabecera ICMP + la información restante para llegar a 1500 bytes, en este caso 1472 bytes. Puesto que es el primer fragmento, el valor de Offset valdrá 0 y el bit MF valdrá 1 ya que hay más paquetes.
El segundo fragmento contendrá la Cabecera IP + la información restante para llegar a 1500 bytes, en este caso 1480. Ahora el valor de Offset valdrá 1480, ya que es la posición que debe ocupar al ensamblar el fragmento (recordemos que el primer fragmento tenía 8+1472 = 1480). El bit MF valdrá 1 ya que no es el último paquete.
El tercer fragmento contendrá la Cabecera IP + la información restante, en este caso 1048 bytes (ya no hay más bytes). El valor de Offset valdrá 2960, ya que el primer y segundo fragmento ocupaban 1480 cada uno. El bit MF se establece a 0 porque es el último fragmento del paquete.
El campo Protocol sólo indica a qué tipo de protocolo corresponde el paquete original. ID fragmento indica un identificador que será igual para todos los fragmentos del paquete original, así se podrá reconstruir en destino sin confusión. El bit MF (more fragments) se establecerá a 1 siempre, excepto para el último paquete, que será 0. El Offset nos indica la posición que ocupa cada fragmento dentro del paquete original. El campo Tamaño, simplemente registra el tamaño del fragmento actual sin contar la Cabecera IP.
Una vez conocemos a grosso modo cómo funciona esto de la fragmentación, explicaré por encima, cómo un usuario malintencionado puede utilizar este procedimiento para realizar un ataque.
El ataque más conocido que explota la fragmentación IP se llama Teardrop. Este ataque usará información falseada en los fragmentos para poder confundir el reensamblaje en destino y colapsar así el sistema.
Imaginemos que tenemos un MTU de 512 bytes y un paquete que necesita ser dividido en N fragmentos y utilizamos los campos Tamaño y Offset de la siguiente manera:
Offset | Tamaño | |
Fragmento 1 | 0 | 512 |
Fragmento 2 | 500 | 512 |
… | … | … |
Fragmento N | 10 | 100 |
Al reconstruir el paquete en destino se producirá un error de desbordamiento de buffer (buffer overrun), ya que el fragmento N apunta a un lugar en el que ya se había escrito previamente y obliga a sobreescribirse.
Otro ataque interesante es enviar cientos o miles de fragmentos manipulados con diferentes ID de fragmento contra la máquina que se desea atacar, de manera que agotemos los recursos de reensamblaje del equipo atacado; acabaremos colmando la pila en la que reconstruye estos paquetes, y no aceptará ninguno más, generando así un ataque de Denegación de Servicio (DoS).
Afortunadamente, a dia de hoy, este tipo de ataques no suelen ser efectivos. Los sistemas operativos vulnerables son Windows 3.1x, Windows 95, Windows NT y las versiones inferiores a Linux 2.0.32, así como la 2.1.63.
Otro día hablaré del famoso ping de la muerte! 🙂