stringtranslate.com

Protección contra desbordamiento de búfer

La protección contra desbordamiento de búfer es una de las diversas técnicas que se utilizan durante el desarrollo de software para mejorar la seguridad de los programas ejecutables detectando desbordamientos de búfer en las variables asignadas a la pila y evitando que provoquen un mal comportamiento del programa o se conviertan en vulnerabilidades de seguridad graves . Un desbordamiento de búfer de pila se produce cuando un programa escribe en una dirección de memoria en la pila de llamadas del programa fuera de la estructura de datos prevista, que suele ser un búfer de longitud fija. Los errores de desbordamiento de búfer de pila se producen cuando un programa escribe más datos en un búfer ubicado en la pila de los que realmente están asignados para ese búfer. Esto casi siempre da como resultado la corrupción de los datos adyacentes en la pila, lo que podría provocar fallas del programa, un funcionamiento incorrecto o problemas de seguridad.

Por lo general, la protección contra desbordamiento de búfer modifica la organización de los datos asignados a la pila para que incluya un valor canario que, cuando se destruye por un desbordamiento de búfer de pila, muestra que se ha desbordado un búfer que lo precede en la memoria. Al verificar el valor canario, se puede terminar la ejecución del programa afectado, lo que evita que se comporte mal o que permita que un atacante tome el control sobre él. Otras técnicas de protección contra desbordamiento de búfer incluyen la comprobación de límites , que verifica los accesos a cada bloque de memoria asignado para que no puedan ir más allá del espacio realmente asignado, y el etiquetado , que garantiza que la memoria asignada para almacenar datos no pueda contener código ejecutable.

Es más probable que el llenado excesivo de un búfer asignado en la pila influya en la ejecución del programa que el llenado excesivo de un búfer en el montón, porque la pila contiene las direcciones de retorno de todas las llamadas de función activas. Sin embargo, también existen protecciones específicas de la implementación similares contra los desbordamientos basados ​​en el montón.

Existen varias implementaciones de protección contra desbordamiento de búfer, incluidas aquellas para GNU Compiler Collection , LLVM , Microsoft Visual Studio y otros compiladores.

Descripción general

Un desbordamiento del búfer de pila ocurre cuando un programa escribe en una dirección de memoria en la pila de llamadas del programa fuera de la estructura de datos prevista, que suele ser un búfer de longitud fija. Los errores de desbordamiento del búfer de pila se producen cuando un programa escribe más datos en un búfer ubicado en la pila de los que realmente están asignados para ese búfer. Esto casi siempre da como resultado la corrupción de los datos adyacentes en la pila y, en los casos en que el desbordamiento se desencadenó por error, a menudo provocará que el programa se bloquee o funcione incorrectamente. El desbordamiento del búfer de pila es un tipo de mal funcionamiento de programación más general conocido como desbordamiento de búfer (o desbordamiento de búfer). Es más probable que llenar en exceso un búfer en la pila descarrile la ejecución del programa que llenar en exceso un búfer en el montón porque la pila contiene las direcciones de retorno de todas las llamadas de función activas. [1]

El desbordamiento del búfer de pila puede ser provocado deliberadamente como parte de un ataque conocido como stack smashing . Si el programa afectado se ejecuta con privilegios especiales o si acepta datos de hosts de red que no son de confianza (por ejemplo, un servidor web público ), entonces el error es una vulnerabilidad de seguridad potencial que permite a un atacante inyectar código ejecutable en el programa en ejecución y tomar el control del proceso. Este es uno de los métodos más antiguos y confiables para que los atacantes obtengan acceso no autorizado a una computadora. [2]

Por lo general, la protección contra desbordamiento de búfer modifica la organización de los datos en el marco de pila de una llamada de función para incluir un valor "canario" que, cuando se destruye, muestra que se ha desbordado un búfer que lo precede en la memoria. Esto proporciona el beneficio de prevenir toda una clase de ataques. Según algunos investigadores, [3] el impacto en el rendimiento de estas técnicas es insignificante.

La protección contra la destrucción de la pila no puede proteger contra ciertas formas de ataque. Por ejemplo, no puede proteger contra desbordamientos de búfer en el montón. No existe una forma sensata de alterar la disposición de los datos dentro de una estructura ; se espera que las estructuras sean las mismas entre módulos, especialmente con bibliotecas compartidas. Es imposible proteger con canarios cualquier dato en una estructura después de un búfer; por lo tanto, los programadores deben ser muy cuidadosos con la forma en que organizan sus variables y utilizan sus estructuras.

canarios

Los canarios , las palabras canarias o las cookies de pila son valores conocidos que se colocan entre un búfer y los datos de control en la pila para supervisar los desbordamientos del búfer. Cuando el búfer se desborda, los primeros datos que se corrompen normalmente serán los canarios y, por tanto, una verificación fallida de los datos canarios alertará de un desbordamiento, que puede gestionarse, por ejemplo, invalidando los datos dañados. Un valor canario no debe confundirse con un valor centinela .

La terminología es una referencia a la práctica histórica de utilizar canarios en las minas de carbón , ya que se veían afectados por los gases tóxicos antes que los mineros, lo que proporcionaba un sistema de alerta biológica. Los canarios también se conocen como galletas de pila , lo que pretende evocar la imagen de una "galleta rota" cuando el valor está alterado.

Hay tres tipos de canarios en uso: terminator , random y random XOR . Las versiones actuales de StackGuard admiten los tres, mientras que ProPolice admite canarios terminator y random .

Canarios terminadores

Los canarios de terminación utilizan la observación de que la mayoría de los ataques de desbordamiento de búfer se basan en ciertas operaciones de cadena que terminan en terminadores de cadena. La reacción a esta observación es que los canarios se construyen con terminadores nulos , CR , LF y FF . Como resultado, el atacante debe escribir un carácter nulo antes de escribir la dirección de retorno para evitar alterar el canario. Esto evita ataques que utilizan strcpy()y otros métodos que regresan al copiar un carácter nulo, mientras que el resultado indeseable es que el canario es conocido. Incluso con la protección, un atacante podría sobrescribir potencialmente el canario con su valor conocido y la información de control con valores no coincidentes, pasando así el código de verificación del canario, que se ejecuta poco antes de la instrucción de retorno de llamada del procesador específico.

canarios al azar

Los canarios aleatorios se generan aleatoriamente, generalmente a partir de un demonio que recolecta entropía , para evitar que un atacante conozca su valor. Por lo general, no es lógicamente posible ni plausible leer el canario para explotarlo; el canario es un valor seguro que solo conocen quienes necesitan conocerlo (en este caso, el código de protección contra desbordamiento de búfer).

Normalmente, se genera un canario aleatorio al inicializar el programa y se almacena en una variable global. Esta variable suele estar rellena con páginas no asignadas, de modo que intentar leerla mediante cualquier tipo de trucos que aprovechen errores para leer la RAM provoca un error de segmentación y termina el programa. Aún puede ser posible leer el canario si el atacante sabe dónde está o puede hacer que el programa lo lea desde la pila.

Canarios XOR aleatorios

Los canarios XOR aleatorios son canarios aleatorios que se mezclan mediante XOR utilizando todos o parte de los datos de control. De esta manera, una vez que se modifica el canario o los datos de control, el valor del canario es incorrecto.

Los canarios XOR aleatorios tienen las mismas vulnerabilidades que los canarios aleatorios, excepto que el método de "leer desde la pila" para obtener el canario es un poco más complicado. El atacante debe obtener el canario, el algoritmo y los datos de control para poder volver a generar el canario original necesario para burlar la protección.

Además, los canarios XOR aleatorios pueden brindar protección contra un cierto tipo de ataque que implica desbordar un búfer en una estructura en un puntero para cambiar el puntero para que apunte a un fragmento de datos de control. Debido a la codificación XOR, el canario será incorrecto si se modifican los datos de control o el valor de retorno. Debido al puntero, los datos de control o el valor de retorno se pueden cambiar sin desbordar el canario.

Aunque estos canarios protegen los datos de control de ser alterados por punteros modificados, no protegen ningún otro dato ni los punteros mismos. Los punteros de función en particular son un problema aquí, ya que pueden desbordarse y ejecutar código shell cuando se los llama.

Comprobación de límites

La comprobación de límites es una técnica basada en compiladores que agrega información de límites en tiempo de ejecución para cada bloque de memoria asignado y verifica todos los punteros con respecto a los de tiempo de ejecución. Para C y C++, la comprobación de límites se puede realizar en el momento del cálculo del puntero [4] o en el momento de la desreferencia. [5] [6] [7]

Las implementaciones de este enfoque utilizan un repositorio central, que describe cada bloque de memoria asignado, [4] [5] [6] o punteros fat , [7] que contienen tanto el puntero como datos adicionales, que describen la región a la que apuntan.

Etiquetado

El etiquetado [8] es una técnica basada en compilador o en hardware (que requiere una arquitectura etiquetada ) para etiquetar el tipo de un fragmento de datos en la memoria, que se utiliza principalmente para la comprobación de tipos. Al marcar ciertas áreas de la memoria como no ejecutables, se evita de forma eficaz que la memoria asignada para almacenar datos contenga código ejecutable. Además, ciertas áreas de la memoria se pueden marcar como no asignadas, lo que evita desbordamientos del búfer.

Históricamente, el etiquetado se ha utilizado para implementar lenguajes de programación de alto nivel; [9] con el soporte adecuado del sistema operativo , el etiquetado también se puede utilizar para detectar desbordamientos de búfer. [10] Un ejemplo es la característica de hardware de bit NX , compatible con los procesadores Intel , AMD y ARM .

Implementaciones

Colección de compiladores GNU (GCC)

La protección contra la destrucción de pilas fue implementada por primera vez por StackGuard en 1997 y publicada en el Simposio de Seguridad USENIX de 1998. [11] StackGuard fue presentado como un conjunto de parches para el backend x86 de Intel de GCC 2.7 . StackGuard se mantuvo para la distribución Linux Immunix desde 1998 hasta 2003 y se amplió con implementaciones para terminator, random y random XOR canaries. Se sugirió la inclusión de StackGuard en GCC 3.x en las Actas de la Cumbre GCC 2003, [12] pero esto nunca se logró.

Entre 2001 y 2005, IBM desarrolló parches GCC para la protección contra ataques de pila, conocidos como ProPolice . [13] Mejoró la idea de StackGuard al colocar buffers después de los punteros locales y los argumentos de función en el marco de la pila. Esto ayudó a evitar la corrupción de punteros, lo que impidió el acceso a ubicaciones de memoria arbitrarias.

Sin embargo, los ingenieros de Red Hat identificaron problemas con ProPolice y en 2005 volvieron a implementar la protección contra destrucción de pila para incluirla en GCC 4.1. [14] [15] Este trabajo introdujo la -fstack-protectorbandera, que protege solo algunas funciones vulnerables, y la -fstack-protector-allbandera, que protege todas las funciones, ya sea que la necesiten o no. [16]

En 2012, los ingenieros de Google implementaron la -fstack-protector-strongbandera para lograr un mejor equilibrio entre seguridad y rendimiento. [17] Esta bandera protege más tipos de funciones vulnerables que -fstack-protector, pero no todas las funciones, lo que proporciona un mejor rendimiento que -fstack-protector-all. Está disponible en GCC desde su versión 4.9. [18]

Todos los paquetes de Fedora se compilan con -fstack-protectordesde Fedora Core 5 y -fstack-protector-strongdesde Fedora 20. [19] [20] La mayoría de los paquetes en Ubuntu se compilan con -fstack-protectordesde 6.10. [21] Cada paquete de Arch Linux se compila con -fstack-protectordesde 2011. [22] Todos los paquetes de Arch Linux creados desde el 4 de mayo de 2014 usan -fstack-protector-strong. [23] La protección de pila solo se usa para algunos paquetes en Debian , [24] y solo para el sistema base FreeBSD desde 8.0. [25] La protección de pila es estándar en ciertos sistemas operativos, incluidos OpenBSD , [26] Hardened Gentoo [27] y DragonFly BSD . [ cita requerida ]

StackGuard y ProPolice no pueden brindar protección contra desbordamientos en estructuras asignadas automáticamente que se desbordan en punteros de función. ProPolice al menos reorganizará el orden de asignación para que dichas estructuras se asignen antes que los punteros de función. En PointGuard [28] se propuso un mecanismo independiente para la protección de punteros y está disponible en Microsoft Windows. [29]

Microsoft Visual Studio

La suite de compiladores de Microsoft implementa protección contra desbordamiento de búfer desde la versión 2003 a través del parámetro de línea de comandos /GS , que está habilitado de manera predeterminada desde la versión 2005. [30] El uso de /GS- deshabilita la protección.

Compilador de IBM

La protección contra la destrucción de pila se puede activar mediante el indicador del compilador -qstackprotect. [31]

Sonido metálico/LLVM

Clang admite las mismas -fstack-protectoropciones que GCC [32] y un sistema de "pila segura" más fuerte ( -fsanitize=safe-stack ) con un impacto de rendimiento igualmente bajo. [33] Clang también tiene tres detectores de desbordamiento de búfer, a saber, AddressSanitizer ( ), [6] UBSan ( ), [34] y el SafeCode no oficial (última actualización para LLVM 3.0). [35]-fsanitize=address-fsanitize=bounds

Estos sistemas tienen diferentes ventajas y desventajas en términos de pérdida de rendimiento, sobrecarga de memoria y clases de errores detectados. La protección de la pila es estándar en ciertos sistemas operativos, incluido OpenBSD . [36]

Compilador Intel

El compilador C y C++ de Intel admite protección contra destrucción de pila con opciones similares a las proporcionadas por GCC y Microsoft Visual Studio. [37]

C a prueba de fallos

Fail-Safe C [7] es un compilador ANSI C de código abierto y seguro para memoria que realiza comprobaciones de límites basadas en punteros fat y acceso a memoria orientado a objetos. [38]

StackGhost (basado en hardware)

Inventado por Mike Frantzen, StackGhost es un simple ajuste a las rutinas de desbordamiento/relleno de la ventana de registro que hace que los desbordamientos de búfer sean mucho más difíciles de explotar. Utiliza una característica de hardware única de la arquitectura SPARC de Sun Microsystems (que es: desbordamiento/relleno diferido de la ventana de registro en la pila) para detectar modificaciones de punteros de retorno (una forma común para que un exploit secuestre rutas de ejecución) de forma transparente, protegiendo automáticamente todas las aplicaciones sin requerir modificaciones binarias o de código fuente. El impacto en el rendimiento es insignificante, menos del uno por ciento. Los problemas de gdb resultantes fueron resueltos por Mark Kettenis dos años después, lo que permitió habilitar la característica. Después de este evento, el código de StackGhost se integró (y optimizó) en OpenBSD /SPARC.

Véase también

Referencias

  1. ^ Fithen, William L.; Seacord, Robert (27 de marzo de 2007). "VT-MB. Violación de los límites de la memoria". CERT de EE. UU .
  2. ^ Levy, Elias (8 de noviembre de 1996). "Destruir la pila por diversión y beneficio". Phrack . 7 (49): 14.
  3. ^ "Desbordamientos de búfer: ataques y defensas para la vulnerabilidad de la década*" (PDF) . Archivado desde el original (PDF) el 2013-03-09.
  4. ^ ab "Comprobación de límites para C". Doc.ic.ac.uk. Archivado desde el original el 26 de marzo de 2016. Consultado el 27 de abril de 2014 .
  5. ^ ab "SAFECode: Arquitectura virtual segura". Sva.cs.illinois.edu. 12 de agosto de 2009. Consultado el 27 de abril de 2014 .
  6. ^ abc "google/sanitizers". 19 de junio de 2021.
  7. ^ abc "Fail-Safe C: página principal". Staff.aist.go.jp. 7 de mayo de 2013. Archivado desde el original el 7 de julio de 2016. Consultado el 27 de abril de 2014 .
  8. ^ "Martes, 5 de abril de 2005" (PDF) . Feustel.us . Archivado desde el original (PDF) el 23 de junio de 2016. Consultado el 17 de septiembre de 2016 .
  9. ^ Steenkiste, Peter; Hennessy, John (1987). "Etiquetas y comprobación de tipos en LISP: enfoques de hardware y software". ACM Sigops Operating Systems Review . 21 (4). ACM: 50–59. doi : 10.1145/36204.36183 .
  10. ^ "Descripción general de la seguridad de ClearPath Enterprise Servers MCP" (PDF) . Public.support.unisys.com. Archivado desde el original (PDF) el 24 de enero de 2013. Consultado el 27 de abril de 2014 .
  11. ^ "Artículos - 7º Simposio de Seguridad USENIX, 1998". Usenix.org. 12 de abril de 2002. Consultado el 27 de abril de 2014 .
  12. ^ "Actas de la Cumbre de Desarrolladores del CCG" (PDF) . Mayo de 2003. Archivado desde el original el 15 de julio de 2004. Consultado el 17 de septiembre de 2016 .{{cite web}}: CS1 maint: bot: estado de URL original desconocido ( enlace )
  13. ^ "Extensión GCC para proteger aplicaciones de ataques de destrucción de pila". Research.ibm.com . Consultado el 27 de abril de 2014 .
  14. ^ "Serie de lanzamientos de GCC 4.1: cambios, nuevas características y correcciones - Proyecto GNU - Free Software Foundation (FSF)". Gcc.gnu.org . Consultado el 27 de abril de 2014 .
  15. ^ "Richard Henderson - [rfc] reimplementación del protector de destrucción de pila de IBM". Gcc.gnu.org . Consultado el 27 de abril de 2014 .
  16. ^ "Optimizar opciones - Uso de la colección de compiladores GNU (GCC)". Gcc.gnu.org . Consultado el 27 de abril de 2014 .
  17. ^ "Han Shen(ææ) - [PARCHE] Agregar una nueva opción "-fstack-protector-strong" (parche/documentación dentro)". Gcc.gnu.org. 2012-06-14 . Consultado el 2014-04-27 .
  18. ^ Edge, Jake (5 de febrero de 2014). "Protección de pila "fuerte" para GCC". Linux Weekly News . Consultado el 28 de noviembre de 2014 . Se ha abierto camino en GCC 4.9
  19. ^ "Características de seguridad". FedoraProject. 2013-12-11 . Consultado el 2014-04-27 .
  20. ^ "#1128 (cambio de "-fstack-protector" a "-fstack-protector-strong" en Fedora 20) – FESCo". Fedorahosted.org . Consultado el 27 de abril de 2014 .
  21. ^ "Seguridad/Características - Wiki de Ubuntu". Wiki.ubuntu.com . Consultado el 27 de abril de 2014 .
  22. ^ "FS#18864: Considere habilitar la protección contra destrucción de pila de GCC (ProPolice, SSP) para todos los paquetes". Bugs.archlinux.org . Consultado el 27 de abril de 2014 .
  23. ^ "svntogit/packages.git - Clon Git del repositorio 'packages'". Archivado desde el original el 18 de julio de 2014.
  24. ^ "Estadísticas de endurecimiento de la seguridad de Debian". Outflux.net. Archivado desde el original el 28 de abril de 2014. Consultado el 27 de abril de 2014 .
  25. ^ "Notas de la versión de FreeBSD 8.0-RELEASE". Freebsd.org. 13 de noviembre de 2013. Consultado el 27 de abril de 2014 .
  26. ^ "Página del manual gcc-local(1) de OpenBSD". gcc viene con la extensión de protección de pila ProPolice , que está habilitada de forma predeterminada.
  27. ^ "Hardened/Toolchain - Gentoo Wiki". 31 de julio de 2016. El GCC endurecido de Gentoo activa el protector de pila de forma predeterminada a menos que se solicite explícitamente lo contrario.
  28. ^ "12º Simposio de Seguridad USENIX — Documento técnico".
  29. ^ "Blogs de MSDN: obtenga la información, los conocimientos, los anuncios y las noticias más recientes de los expertos y desarrolladores de Microsoft en los blogs de MSDN". 6 de agosto de 2021.
  30. ^ "/GS (Buffer Security Check) (C++)". msdn.microsoft.com . Consultado el 27 de abril de 2014 .
  31. ^ "qstackprotect". Publib.boulder.ibm.com . Consultado el 27 de abril de 2014 .
  32. ^ "Lista de correo de Clang". Clang.llvm.org. 28 de abril de 2017. Consultado el 16 de noviembre de 2022 .
  33. ^ "SafeStack — Documentación de Clang 17.0.0git". clang.llvm.org .
  34. ^ "Manual del usuario del compilador de Clang — Documentación de Clang 3.5". Clang.llvm.org . Consultado el 27 de abril de 2014 .
  35. ^ "SAFECode". Safecode.cs.illinois.edu . Consultado el 27 de abril de 2014 .
  36. ^ "Página del manual clang-local(1) de OpenBSD". clang viene con la protección de pila habilitada de forma predeterminada, equivalente a la opción -fstack-protector-strong en otros sistemas.
  37. ^ "Guía de referencia y del usuario para el compilador Intel C++ 15.0: fstack-security-check, GS". software.intel.com . Consultado el 13 de febrero de 2015 .
  38. ^ "thesis.dvi" (PDF) . Staff.aist.go.jp . Consultado el 17 de septiembre de 2016 .

Enlaces externos