Ataques de Desbordamiento de Búfer
Los ataques de desbordamiento de búfer representan una amenaza significativa para la seguridad del software. Estos ataques ocurren cuando individuos malintencionados explotan vulnerabilidades de desbordamiento de búfer. Esto puede llevar a fallos en el programa, corrupción de datos, robo de información sensible y acceso no autorizado a sistemas. Este artículo explica los desbordamientos de búfer, el uso que hacen los atacantes de ellos y los métodos para prevenirlos en el desarrollo de software.
Los Fundamentos de los Desbordamientos de Búfer
Un búfer, en el contexto de la informática, es una región de memoria asignada para almacenar temporalmente datos. Los programas utilizan búferes para contener varios tipos de datos, como la entrada del usuario, el contenido de archivos o cadenas de texto.
Cada búfer tiene un tamaño establecido. Los problemas ocurren cuando un programa intenta almacenar más datos de los que el búfer puede contener. En tales casos, el exceso de datos rebasa el búfer, derramándose en ubicaciones de memoria adyacentes. Este comportamiento no intencionado puede llevar a fallos en el programa, corrupción de datos y vulnerabilidades de seguridad.
Considera el siguiente ejemplo en el lenguaje de programación C:
char buffer[8]; scanf("%s", buffer);
Este fragmento de código asigna un búfer de 8 bytes para almacenar una cadena obtenida de la entrada del usuario. Si el usuario escribe más de 8 caracteres, sobrescribirá la memoria cercana.
Explotación de Vulnerabilidades de Desbordamiento de Búfer
Los atacantes explotan las vulnerabilidades de desbordamiento de búfer sobrescribiendo estratégicamente partes de la memoria que contienen código ejecutable, reemplazándolo con código malicioso. El atacante primero identifica un programa con una vulnerabilidad de desbordamiento de búfer conocida. El atacante envía intencionadamente demasiados datos para sobrecargar el sistema y causar la corrupción de la memoria.
El atacante quiere reemplazar el búfer con código malicioso. Este código puede realizar acciones dañinas, como abrir un shell o ejecutar comandos sin permiso. El ataque tiene éxito cuando el atacante cambia una dirección de retorno de función o un manejador de excepciones. Cuando el usuario ejecuta el código alterado, se ejecuta el código malicioso.
Para ilustrar este concepto, considera un programa de servidor que recibe nombres de usuario de clientes. Si el espacio de almacenamiento del nombre de usuario es de 128 bytes, un hacker podría enviar un nombre de usuario dañino en un formato específico.
"A" * 132 + codigo_malicioso + direccion_de_retorno_falsa
La serie de caracteres “A” desborda el búfer, permitiendo que el código malicioso se escriba en la memoria. La dirección de retorno falsa asegura que el código malicioso se ejecute cuando la función sobrescrita termine.
Ejemplos Reales de Desbordamiento de Búfer
Todos los ataques de desbordamiento de búfer se reducen a la misma estrategia:
A continuación, algunos ejemplos reales de vulnerabilidades de desbordamiento encontradas en DBMS populares a lo largo de los años.
MySQL COM_FIELD_LIST
Hasta 2012, MySQL y MariaDB eran susceptibles a la vulnerabilidad COM_FIELD_LIST. Los fundamentos de la vulnerabilidad eran los siguientes:
El perpetrador haría una consulta al servidor MySQL con una cláusula ‘FROM’, especificando un nombre de tabla muy largo para causar un desbordamiento de búfer.
SELECT * FROM 'nombre_de_una_tabla_muy_larga'.
La longitud exacta no se especificó, pero 5000 bytes eran suficientes para causar un desbordamiento. Esto causaría el cierre del servidor, pero en el momento entre el desbordamiento y el cierre, el servidor se vuelve vulnerable a ataques. Aquí es donde los perpetradores insertarían código malicioso en forma de bytes hexadecimales.
SELECT * FROM 'nombre_muy_largo'.\x31\xc0\x50\x68\x2f\x2f\x73'
El código tenía que ejecutar comandos bash, por ejemplo, ‘rm -rf /bin/’.
Para una mayor probabilidad de éxito, los perpetradores añadirían un segmento de No Operación al código, en otras palabras, código que no realiza ninguna acción.
Una consulta completa se vería así:
SELECT * FROM 'AAAAAAAAAAA..' causando desbordamiento de búfer '\x0\x0\x0\x0\x0' - Slide de No Operación '\xe3\x50\x53\x89\xe1\xb0\x0b' - Código malicioso.
La vulnerabilidad se abordó en las versiones de MySQL 5.5.23, 5.1.63 y 5.6.6, así como en las versiones de MariaDB 5.5.23, 5.3.12, 5.2.14 y 5.1.63. Actualizar a estas versiones o posteriores parcheó la vulnerabilidad y protegió las bases de datos contra este ataque específico.
PostgreSQL 9.3 RESETXLOG Buffer Overflow
De manera similar, en 2013 se descubrió y corrigió rápidamente que la función pg_resetxlog() de PostgreSQL era propensa a estos ataques.
postgres=# SELECT pg_resetxlog('A' * 1000 + *codigo_malicioso*);
SQL Server Slammer Worm
En 2003 se descubrió el ataque SQL Server Slammer Worm. Este ataque estaba dirigido al servidor de resolución de SQL Server, que acepta paquetes UDP en el puerto 1434. Cuando se enviaba un paquete especialmente diseñado a este puerto, desbordaba el búfer y permitía al gusano ejecutar código arbitrario. Este código hacía que el servidor enviara más copias del gusano, lo que condujo a una rápida propagación y una congestión masiva de la red. Un ejemplo sería algo así:
payload = create_malicious_packet() sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto('A' * 1000, *code_malicioso*, (target_ip, 1434)) sock.close()
Oracle 9i XDB HTTP Buffer Overflow
En 2003 se descubrió una vulnerabilidad de desbordamiento en el servidor Oracle. Los atacantes podían enviar un nombre de usuario muy largo en una solicitud HTTP GET, lo que desbordaría el búfer y luego ejecutaría código malicioso.
long_username = 'A' * 1000 + *codigo_malicioso* password = 'password' credentials = f"{long_username}:{password}" encoded_credentials = base64.b64encode(credentials.encode()).decode() http_request = ( "GET / HTTP/1.1\r\n" f" Host: {target_ip}\r\n" "User-Agent: Mozilla/5.0\r\n" f" Authorization: Basic {payload}\r\n" "Accept: */*\r\n" "\r\n" ) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((target_ip, target_port)) sock.sendall(http_request.encode())
Este fragmento muestra un script en Python que transmite credenciales al servidor Oracle que desborda su búfer. Como consecuencia, las credenciales ejecutan código malicioso.
Prevención de Vulnerabilidades de Desbordamiento de Búfer
Desarrollar software seguro requiere un enfoque proactivo para prevenir vulnerabilidades de desbordamiento de búfer. Las siguientes estrategias son esenciales para mitigar los riesgos asociados con los desbordamientos de búfer:
- Validación de Entrada: Asegúrate de que todos los datos suministrados por el usuario estén dentro de los límites esperados antes de almacenarlos en búferes. Valida la longitud de la entrada, el formato y el contenido para evitar que datos maliciosos o no intencionados causen desbordamientos de búfer.
- Funciones Seguras de Cadenas y Memoria: Evita utilizar funciones de biblioteca estándar que sean propensas a desbordamientos de búfer. En su lugar, opta por alternativas más seguras, como `strncpy` y `memcpy_s`, que incluyen salvaguardias integradas contra desbordamientos.
- Protecciones a Nivel de Sistema: Habilita características de seguridad a nivel de sistema, tales como la randomización del diseño del espacio de direcciones (ASLR) y la prevención de ejecución de datos (DEP), siempre que sea posible. Estos mecanismos dificultan que los atacantes predigan la disposición de la memoria y ejecuten código malicioso.
- Utiliza lenguajes seguros para la memoria como Rust, Go o Java para proteger contra desbordamientos de búfer. Estos lenguajes imponen prácticas estrictas de gestión de memoria y previenen el acceso directo a la memoria, reduciendo el riesgo de vulnerabilidades de desbordamiento.
- Actualizaciones Regulares de Software: Actualiza las bibliotecas y dependencias de software con prontitud al descubrir vulnerabilidades de desbordamiento de búfer y liberar parches. Mantenerse al día con los últimos parches de seguridad es crucial para protegerse contra vulnerabilidades conocidas.
Usar herramientas de prueba de fuzzing y técnicas de SAST, como el análisis de contaminación, puede ayudar a encontrar errores de desbordamiento de búfer mientras se desarrolla software.
Impacto en el Mundo Real de los Ataques de Desbordamiento de Búfer
A lo largo de la historia de la informática, numerosos incidentes de seguridad de alto perfil se han atribuido a vulnerabilidades de desbordamiento de búfer. El gusano Morris fue uno de los primeros gusanos de Internet. Se propagó explotando un fallo de seguridad en el programa Unix `finger` en 1988.
En 2017, el virus WannaCry aprovechó una debilidad en el protocolo del Bloque de Mensajes del Servidor de Windows para propagarse. En un solo día, WannaCry infectó a más de 230,000 computadoras en todo el mundo, causando pérdidas financieras significativas y disrupciones.
Conclusión
Los ataques de desbordamiento de búfer siguen siendo una preocupación crítica en el ámbito de la seguridad del software. Los desarrolladores pueden proteger su software de ataques aprendiendo sobre los desbordamientos de búfer y cómo los explotadores los usan.
Es importante usar prácticas de codificación segura. Estas prácticas incluyen la validación de entrada y las funciones seguras de memoria. Ayudan a prevenir vulnerabilidades de desbordamiento de búfer en los sistemas. Adoptar lenguajes de programación seguros para la memoria y mantener las dependencias del software actualizadas mejora aún más la resistencia de los sistemas de software.
Los desarrolladores de software pueden mejorar la seguridad de los sistemas digitales al prevenir desbordamientos de búfer y mantenerse al tanto de nuevas amenazas. Esto protege los datos y asegura que los sistemas informáticos sigan siendo confiables.