Seguridad de red-----¿Cuáles son los métodos de protección para el desbordamiento del búfer?
Actualmente existen cuatro métodos básicos para proteger los buffers de ataques y efectos de desbordamiento del buffer.
Escribir código correcto, verificación de límites de matriz de búfer no ejecutable, verificación de integridad del puntero del programa
1. Escribir código correcto Arriba
Escribir código correcto es muy significativo. Pero es una tarea que requiere mucho tiempo, especialmente cuando se escriben programas en lenguaje C que son propensos a errores (como la terminación en cero de cadenas). Este estilo se debe a la tradición de buscar el rendimiento por encima de la corrección. Aunque a la gente le llevó mucho tiempo saber cómo escribir programas seguros, todavía aparecían programas con vulnerabilidades de seguridad. Por lo tanto, se han desarrollado herramientas y técnicas para ayudar a los programadores menos experimentados a escribir programas seguros y correctos.
La forma más sencilla es utilizar grep para buscar llamadas a bibliotecas vulnerables en el código fuente, como llamadas a strcpy y sprintf. Ninguna de estas dos funciones comprueba la longitud de los parámetros de entrada. De hecho, todas las versiones de la biblioteca estándar C tienen este tipo de problemas. Para encontrar vulnerabilidades comunes, como desbordamientos de búfer y condiciones de carrera del sistema operativo, algunos equipos de inspección de código inspeccionan grandes cantidades de código. Sin embargo, todavía hay peces que se escapan de la red. Aunque se utilizan funciones alternativas como strcpy y sprintf para evitar desbordamientos del búfer, esta situación todavía ocurre debido a problemas al escribir código. Por ejemplo, el programa lprm es el mejor ejemplo. Aunque pasó la verificación de seguridad del código, todavía hay un problema de desbordamiento del búfer.
Para abordar estos problemas, la gente ha desarrollado algunas herramientas avanzadas de verificación de errores, como la inyección de fallas. El propósito de estas herramientas es encontrar vulnerabilidades de seguridad en el código generando de forma artificial y aleatoria algunos desbordamientos del búfer. También existen herramientas de análisis estático que se utilizan para detectar la presencia de desbordamientos del búfer. Aunque estas herramientas pueden ayudar a los programadores a desarrollar programas más seguros, debido a las características del lenguaje C, estas herramientas no pueden encontrar todas las vulnerabilidades de desbordamiento del búfer. Por lo tanto, la tecnología de depuración sólo se puede utilizar para reducir la posibilidad de desbordamiento del búfer, pero no puede eliminar por completo su existencia, a menos que el programador pueda garantizar que su programa se perderá.
2. Búfer no ejecutable Top
Al hacer que el espacio de direcciones del segmento de datos del programa atacado no sea ejecutable, es imposible para el atacante ejecutar el búfer de entrada implantado en el programa atacado. Esta técnica se denomina técnica de búfer no ejecutable. De hecho, muchos sistemas Unix antiguos están diseñados de esta manera, pero los sistemas Unix y MS Windows recientes a menudo colocan dinámicamente código ejecutable en el segmento de datos para lograr un mejor rendimiento y funcionalidad. Por lo tanto, para mantener la compatibilidad del programa, es imposible hacer que los segmentos de datos de todos los programas no sean ejecutables. Pero podemos configurar el segmento de datos de la pila para que no sea ejecutable, a fin de garantizar la compatibilidad del programa en la mayor medida posible. Tanto Linux como Solaris han lanzado parches para el kernel a este respecto. Debido a que casi ningún programa compatible almacena código en la pila, este enfoque rara vez crea problemas de compatibilidad, excepto en dos casos especiales en Linux, donde el código ejecutable debe colocarse en la pila:
1. Entrega de señales
Linux implementa el envío de señales Unix al proceso liberando código a la pila de procesos y luego activando una interrupción para ejecutar el código en la pila. El parche para el búfer de no ejecución permite que el búfer sea. disponible al enviar señales. Ejecutado.
2. Reutilización en línea de GCC
La investigación encontró que gcc coloca el código ejecutable en el área de la pila para su reutilización en línea. Sin embargo, desactivar esta función no causa ningún problema. Sólo algunas funciones parecen no estar disponibles. La protección de pila sin ejecución puede hacer frente eficazmente a los ataques de desbordamiento del búfer que incrustan código en variables automáticas, pero no tiene ningún efecto sobre otras formas de ataques. Esta protección se puede eludir haciendo referencia a un puntero a un programa que reside
. Otros ataques pueden eludir la protección insertando código en el montón o segmentos de datos estáticos.
3. Verificación de límites de matriz Arriba
Inyectar código para provocar un desbordamiento del búfer es un aspecto, interrumpir el flujo de ejecución del programa es otro aspecto. A diferencia de la protección del búfer no ejecutable, la verificación de los límites de la matriz elimina por completo la posibilidad de ataques y desbordamientos del búfer. De esta manera, mientras la matriz no pueda desbordarse, los ataques de desbordamiento están fuera de discusión. Para implementar la verificación de los límites de la matriz, se deben verificar todas las operaciones de lectura y escritura en la matriz para garantizar que las operaciones en la matriz estén dentro del rango correcto. El enfoque más sencillo es verificar todas las operaciones de la matriz, pero generalmente se pueden utilizar algunas técnicas de optimización para reducir la cantidad de comprobaciones. Actualmente existen varios métodos de verificación:
1. Compilador C de Compaq
El compilador C desarrollado por Compaq para CPU Alpha admite la verificación de límites limitados (use el parámetro -check_bounds). Estas restricciones son: solo se verifican las referencias explícitas a matrices; por ejemplo, se verificará "a[3]", pero no "*(a
3)". Dado que todas las matrices C se pasan por puntero, la matriz pasada a la función no se verificará. Las funciones de biblioteca peligrosas como strcpy no realizarán la verificación de límites en el momento de la compilación, incluso si se especifica la verificación de límites. El uso de punteros para operaciones y transferencias de matrices es muy frecuente en lenguaje C, por lo que esta limitación es muy grave. Por lo general, este tipo de verificación de límites se utiliza para verificar errores del programa y no puede garantizar que no se produzcan vulnerabilidades de desbordamiento del búfer.
2. Jones & Kelly: verificación de límites de matriz C
Richard Jones y Paul Kelly desarrollaron un parche gcc para implementar la verificación completa de límites de matriz para programas C. Dado que el significado del puntero no cambia, el programa compilado tiene buena compatibilidad con otros módulos de gcc. Además, derivaron un puntero "base" a partir de la expresión sin puntero y luego verificaron el puntero base para detectar si el resultado de la expresión estaba dentro del rango permitido. Por supuesto, el costo de rendimiento de esto es enorme: para un programa que usa punteros con frecuencia, como la multiplicación de vectores, será 30 veces más lento debido al uso frecuente de punteros. Este compilador aún es muy inmaduro y algunos programas complejos (como elm) no se pueden compilar ni ejecutar en él. Sin embargo, con una versión actualizada, al menos puede compilar y ejecutar el paquete de software de cifrado del software ssh, pero su rendimiento se reduce 12 veces.
3. Purify: verificación de acceso a la memoria
Purify es una herramienta para verificar el uso de la memoria al depurar programas C, en lugar de una herramienta de seguridad dedicada. Purify utiliza tecnología de "inserción de código objeto" para verificar todos los accesos a la memoria. Al conectarse con la herramienta de conexión Purify, la pérdida de rendimiento causada por la ejecución del código ejecutable se reducirá entre 3 y 5 veces.
4. Lenguaje con seguridad de tipos
Todas las vulnerabilidades de desbordamiento del búfer se originan en la seguridad de tipos del lenguaje C. Si sólo se permitieran operaciones con seguridad de tipos, la coerción de variables sería imposible. Si es un principiante, se recomienda utilizar lenguajes con seguridad de tipos como JAVA y ML.
Pero la máquina virtual Java como plataforma de ejecución de Java es un programa en C. Por lo tanto, una forma de atacar la JVM es desbordar el búfer de la JVM. Por lo tanto, el uso de tecnología de prevención de desbordamiento de búfer y lenguaje obligatorio con seguridad de tipos en el sistema puede tener efectos inesperados.
4. Verificación de integridad del puntero del programa Arriba
La verificación de integridad del puntero del programa y la verificación de límites son ligeramente diferentes. En lugar de evitar que se cambie el puntero del programa, la verificación de integridad del puntero del programa detecta cambios en el puntero del programa antes de que se haga referencia a él. Por lo tanto, incluso si un atacante cambia con éxito el puntero del programa, el puntero no se utilizará porque el sistema ha detectado el cambio de puntero de antemano. A diferencia de la comprobación de límites de matriz, este método no resuelve todos los problemas de desbordamiento del búfer; otros métodos de desbordamiento del búfer pueden evitar dicha detección.
Pero este método tiene grandes ventajas en cuanto a rendimiento y buena compatibilidad.
l.Monitoreo de pila escrito a mano
Snarskii desarrolló una libc personalizada para FreeBSD que puede detectar desbordamientos de búfer monitoreando la pila de la CPU. Esta aplicación está escrita completamente en ensamblaje manual y solo protege las funciones de registro actualmente válidas en libc. Esta aplicación cumple con los requisitos de diseño y tiene una buena defensa contra ataques basados en funciones de la biblioteca libc, pero no puede defenderse contra ataques de otras maneras. p >
2. Protección de pila
La protección de pila es una tecnología de compilación que proporciona verificación de la integridad del puntero del programa. Esto se hace comprobando la dirección del remitente en el registro de actividad de la función. Como un pequeño parche de gcc, la protección de pila agrega código para la creación y destrucción de funciones en cada función. El código de creación de función agregado en realidad agrega algunos bytes adicionales en la pila después de la dirección de retorno de la función. Cuando la función regresa, primero verifica si los bytes adicionales se han modificado. Si se produce un ataque de desbordamiento del búfer, este ataque se puede detectar fácilmente antes de que la función regrese. Sin embargo, ¿qué pasa si un atacante prevé la existencia de estos bytes adicionales y puede crearlos de manera similar durante el proceso de desbordamiento? Entonces podrá omitir con éxito la detección de protección de pila. generalmente. Tenemos las siguientes dos soluciones para lidiar con este tipo de engaño:
1. Símbolo de terminación
Utilice el símbolo de terminación en lenguaje C como o(null, CR, LF, - 1 (Eof) y otros símbolos no se pueden usar en funciones de cadena de uso común, porque una vez que estas funciones encuentren estos símbolos de terminación, el proceso de la función finalizará
2. a. Se genera un número aleatorio de 32 bits cuando se llama a la función para lograr la confidencialidad, lo que hace imposible que un atacante adivine el contenido de los bytes adicionales. Además, el contenido de los bytes adicionales cambia con cada llamada y no se puede predecir. inspeccionando la pila. El método de protección de la pila de integridad evolucionó a partir del método Synthetix. El método Synthetix garantiza la exactitud de variables específicas mediante el uso de cuasi-invariantes que pueden ser predichos por la implementación del programa y que solo pueden cumplir ciertas condiciones. Estas variables se denominan cuasiinvariantes. Synthetix ha desarrollado algunas herramientas para proteger estas variables. Los cambios realizados por un atacante mediante el desbordamiento del búfer pueden considerarse acciones ilegales por parte del sistema en algunos casos extremos. Se modifica ilegalmente y se necesita protección de pila para proporcionar una protección más completa. Los datos experimentales muestran que la protección de pila tiene un buen efecto protector contra ataques de desbordamiento de búfer en varios sistemas. El análisis muestra que la protección de pila puede resistir eficazmente la corriente. y futuros ataques basados en pila La versión de protección de pila de Red Hat Linux 5.1 se ha estado ejecutando en varios sistemas durante muchos años, incluidos portátiles personales y servidores de archivos de grupos de trabajo
3. p>Cuando se diseña la protección de la pila, impactar la pila constituye una forma común de ataque de desbordamiento del búfer. Se especuló que existía una plantilla para estos ataques (en 1996, se han descubierto, implementado y parcheado muchas vulnerabilidades simples). , y muchos atacantes han comenzado a usar métodos más generales para implementar ataques de desbordamiento de búfer. Una generalización de la protección de pila para esta situación, colocando bytes adicionales después de todos los punteros de código para verificar la validez de los punteros antes de ser llamados. Se emitirá una señal de alarma y se saldrá de la ejecución del programa, como en El comportamiento en la protección de la pila es el mismo. Hay dos puntos a tener en cuenta en este esquema:
(1) Posicionamiento de. bytes adicionales
El espacio para bytes adicionales es donde está la variable protegida. Se asigna cuando se asigna y se inicializa durante la inicialización de los bytes protegidos. Esto genera un problema: para mantener la compatibilidad. No queremos cambiar el tamaño de la variable protegida, por lo que no podemos simplemente definirla en la estructura de la variable. Además, hay diferentes números de bytes adicionales para cada tipo.
(2) Verificar los bytes adicionales
Cada vez que se hace referencia al puntero del programa, se debe verificar la integridad de los bytes adicionales. También hay un problema con esto porque "leer desde el accesor" no tiene semántica en el compilador. El compilador está más preocupado por el uso de punteros y varios algoritmos de optimización tienden a leer variables de la memoria. Los métodos de lectura también son diferentes. Hasta ahora, sólo unos pocos ataques que utilizan variables que no son de puntero han podido escapar a la detección mediante la protección de puntero. Sin embargo, la detección se puede lograr obligando al compilador a agregar bytes adicionales a una determinada variable. En este caso, el programador debe agregar manualmente la protección correspondiente.