Programación Orientada al Retorno

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 28 de julio de 2015; las comprobaciones requieren 7 ediciones .

La programación orientada al retorno ( ROP ) es un  método de explotación de vulnerabilidades en el software , mediante el cual un atacante puede ejecutar el código que necesita si existen tecnologías de protección en el sistema, por ejemplo, una tecnología que prohíbe la ejecución de código desde ciertas páginas de memoria [ 1] . El método radica en que el atacante puede obtener control sobre la pila de llamadas , encontrar secuencias de instrucciones en el código que realizan las acciones necesarias y se denominan "gadgets", ejecutar "gadgets" en la secuencia deseada [2] . Un "dispositivo" generalmente termina con una instrucción de retorno y reside en la memoria principal en el código existente (en código de programa o código de biblioteca compartida ). El atacante logra la ejecución secuencial de gadgets utilizando instrucciones de devolución, organiza una secuencia de gadgets de tal manera que realice las operaciones deseadas. El ataque es factible incluso en sistemas que tienen mecanismos para prevenir ataques más simples.

Historia

Ataque de desbordamiento de búfer

La Programación Orientada a Retorno es una versión avanzada del ataque de desbordamiento de búfer . En este ataque, el atacante usa un error en el programa cuando la función no verifica (o verifica incorrectamente) los límites al escribir en el búfer de datos recibidos del usuario. Si el usuario envía más datos que el tamaño del búfer, los datos adicionales ingresan al área de memoria destinada a otras variables locales y también pueden sobrescribir la dirección de retorno. Si se sobrescribe la dirección de retorno, cuando la función regrese, el control se transferirá a la dirección recién escrita.

En la versión más simple de un ataque de desbordamiento de búfer, el atacante inserta el código ("carga útil") en la pila y luego sobrescribe la dirección de retorno con la dirección de las instrucciones que acaba de escribir. Hasta finales de la década de 1990, la mayoría de los sistemas operativos no brindaban protección contra estos ataques. Los sistemas Windows no tenían protección contra ataques de desbordamiento de búfer hasta 2004. [3] Eventualmente, los sistemas operativos comenzaron a lidiar con la explotación de vulnerabilidades de desbordamiento de búfer al marcar ciertas páginas de memoria como no ejecutables (una técnica llamada "Prevención de ejecución de datos"). Con la prevención de ejecución de datos habilitada, la máquina se negará a ejecutar código en las páginas de memoria marcadas como "solo datos", incluidas las páginas que contienen una pila. Esto le impide empujar la carga útil a la pila y luego saltar a ella, sobrescribiendo la dirección de retorno. Más tarde, apareció el soporte de hardware para la prevención de ejecución de datos para mejorar la protección .

Ataque de retorno de biblioteca

La prevención de ejecución de datos evita el ataque mediante el método descrito anteriormente. El atacante se limita al código que ya se encuentra en el programa atacado y en las bibliotecas compartidas. Sin embargo, las bibliotecas compartidas, como libc , a menudo contienen funciones para realizar llamadas al sistema y otras funciones útiles para un atacante, lo que permite que estas funciones se utilicen en un ataque.

El ataque de retorno de la biblioteca también explota un desbordamiento de búfer. La dirección de retorno se sobrescribe con el punto de entrada de la función de biblioteca deseada. Las celdas sobre la dirección de retorno también se sobrescriben para pasar parámetros a la función o encadenar varias llamadas. Esta técnica fue introducida por primera vez por Alexander Peslyak (conocido como Solar Designer) en 1997, [4] y desde entonces se ha ampliado para permitir una cadena ilimitada de llamadas a funciones. [5]

Préstamo de fragmentos de código

Con la difusión del hardware y los sistemas operativos de 64 bits, se ha vuelto más difícil realizar un ataque de retorno de biblioteca: en las convenciones de llamada utilizadas en los sistemas de 64 bits, los primeros parámetros se pasan a la función no en la pila, sino en registros Esto complica la preparación de los parámetros que se llamarán durante el ataque. Además, los desarrolladores de bibliotecas compartidas comenzaron a eliminar o restringir funciones "peligrosas", como envoltorios de llamadas al sistema, de las bibliotecas.

La siguiente ronda de desarrollo del ataque fue el uso de partes de funciones de biblioteca en lugar de funciones completas. [6] Esta técnica busca partes de funciones que envían datos de la pila a los registros. La selección cuidadosa de estas partes le permite preparar los parámetros necesarios en los registros para llamar a la función de acuerdo con la nueva convención. Además, el ataque se lleva a cabo de la misma forma que el ataque de devolución de biblioteca.

Ataque por programación orientada al retorno

La programación orientada al retorno amplía el enfoque de préstamo de código al proporcionar al atacante una funcionalidad completa de Turing , incluidos bucles y bifurcaciones . [7] En otras palabras, la programación orientada al retorno proporciona al atacante la capacidad de realizar cualquier operación. Hovav Shaham publicó una descripción del método en 2007 [8] y lo demostró en un programa que usa la biblioteca C estándar y contiene una vulnerabilidad de desbordamiento de búfer. La programación orientada al retorno es superior a los otros tipos de ataques descritos anteriormente tanto en el poder expresivo como en la resistencia a las medidas defensivas. Ninguno de los métodos anteriores para contrarrestar los ataques, incluida la eliminación de funciones peligrosas de las bibliotecas compartidas, es efectivo contra la programación orientada al retorno.

A diferencia del ataque de regreso a la biblioteca, que usa funciones completas, la programación orientada al regreso usa pequeñas secuencias de instrucciones que terminan con una instrucción de regreso, los llamados "gadgets". Los gadgets son, por ejemplo, terminaciones de funciones existentes. Sin embargo, en algunas plataformas, especialmente x86 , los gadgets pueden aparecer "entre líneas", es decir, al decodificar desde la mitad de una instrucción existente. Por ejemplo, la siguiente secuencia de instrucciones: [8]

prueba edi , 7 ; f7 c7 07 00 00 00 setnz byte [ ebp-61 ] ; 0f 95 45 c3

cuando la decodificación comienza un byte más tarde, da

mov dword [ edi ], 0 f000000h ; c7 07 00 00 00 0f xchg ebp , eax ; 95 inc ebp ; 45 ent ; c3

Los gadgets también pueden estar en los datos, por una u otra razón ubicados en la sección de códigos. Esto se debe a que el conjunto de instrucciones x86 es bastante denso, lo que significa que existe una alta probabilidad de que un flujo arbitrario de bytes se interprete como un flujo de instrucciones reales. Por otro lado, en la arquitectura MIPS , todas las instrucciones tienen una longitud de 4 bytes y solo se pueden ejecutar instrucciones alineadas en direcciones que son múltiplos de 4 bytes. Por lo tanto, no hay forma de obtener una nueva secuencia "leyendo entre líneas".

El ataque aprovecha una vulnerabilidad de desbordamiento de búfer. La dirección de retorno de la función actual se sobrescribe con la dirección del primer gadget. Las posiciones subsiguientes en la pila contienen las direcciones de los siguientes dispositivos y los datos utilizados por los dispositivos.

Ejemplos

Extensiones

En su versión original para la plataforma x86, los gadgets son cadenas de instrucciones ordenadas secuencialmente sin saltos, que terminan con una instrucción de retorno cercano. En las versiones extendidas del ataque, las instrucciones en cadena pueden no ser necesariamente secuenciales, pero están conectadas por instrucciones de salto directo. Además, el papel de la instrucción final puede ser realizado por otra instrucción de retorno (en x86 también hay una instrucción de retorno lejano, instrucciones de retorno cercano y lejano con limpieza de pila), una instrucción de salto indirecto o incluso una instrucción de llamada indirecta. Esto complica la lucha contra este método de ataque.

Generación automática

Existen herramientas para encontrar dispositivos automáticamente y diseñar un ataque. Un ejemplo de tal herramienta es ROPgadget. [9]

Protección contra la programación orientada al retorno

Hay varios métodos para protegerse contra la programación orientada al retorno. [10] La mayoría se basa en la ubicación del código del programa y las bibliotecas en una dirección relativamente arbitraria, por lo que un atacante no puede predecir con precisión la ubicación de las instrucciones que podrían ser útiles en los dispositivos y, por lo tanto, no puede construir una cadena de dispositivos para atacar. Una implementación de este método, ASLR , carga bibliotecas compartidas en una dirección diferente cada vez que se ejecuta el programa. Sin embargo, aunque esta tecnología se usa ampliamente en los sistemas operativos modernos, es vulnerable a ataques de fuga de información y otros ataques que permiten determinar la posición de una función de biblioteca conocida. Si un atacante puede determinar la ubicación de una función, puede determinar la ubicación de todas las instrucciones en la biblioteca y realizar un ataque de programación orientado al retorno.

Puede reorganizar no solo bibliotecas completas, sino también instrucciones individuales de programas y bibliotecas. [11] Sin embargo, esto requiere un amplio soporte en tiempo de ejecución, como la traducción dinámica, para volver a colocar las instrucciones permutadas en el orden correcto para su ejecución. Este método hace que encontrar y usar dispositivos sea más difícil, pero tiene una gran sobrecarga.

El enfoque de kBouncer [12] consiste en comprobar que la instrucción de retorno transfiere el control a la instrucción que sigue inmediatamente a la instrucción de llamada. Esto reduce en gran medida el conjunto de posibles dispositivos, pero también provoca un impacto significativo en el rendimiento. [12] Además, en una versión extendida de la programación orientada al retorno, los dispositivos se pueden conectar no solo con una instrucción de retorno, sino también con una instrucción indirecta de salto o llamada. Contra un ataque tan extenso, kBouncer será ineficaz.

Notas

  1. Shacham, Hovav; Buchanan, Eric; Romer, Ryan; Savage, Stefan Programación orientada al retorno: exploits sin inyección de código . Consultado el 12 de agosto de 2009. Archivado desde el original el 1 de junio de 2010.
  2. Erik Buchanan, Ryan Roemer, Hovav Shacham y Stefan Savage; When Good Instructions Go Bad: Generalizing Return-Oriented Programming to RISC Archivado el 11 de agosto de 2017 en Wayback Machine , en Proceedings of CCS 2008 , ACM Press, octubre de 2008.
  3. Prevención de ejecución de datos de Microsoft Windows XP SP2 . Consultado el 10 de abril de 2014. Archivado desde el original el 14 de abril de 2018.
  4. Solar Designer, Return-into-lib(c) exploits Archivado el 23 de febrero de 2018 en Wayback Machine , Bugtraq
  5. Nergal, Phrack 58 Artículo 4, exploits return-into-lib(c) Archivado el 25 de abril de 2013 en Wayback Machine .
  6. Sebastian Krahmer, Exploits de desbordamiento de búfer x86-64 y la técnica de explotación de fragmentos de código prestado Archivado el 22 de diciembre de 2018 en Wayback Machine , 28 de septiembre de 2005
  7. Martín Abadi, Mihai Budiu, Úlfar Erlingsson y Jay Ligatti. Integridad del flujo de control: principios, implementaciones y aplicaciones . En Catherine Meadows y Ari Juels, editores, Actas de CCS 2005 . ACM. octubre de 2005
  8. 1 2 Hovav Shacham. La geometría de la carne inocente en el hueso: return-into-libc sin llamadas a funciones (en el x86) . En Actas de la 14.ª conferencia ACM sobre seguridad informática y de las comunicaciones (CCS '07) . MCA 2007
  9. Jonathan Salwan y Allan Wirth, ROPgadget: buscador de gadgets y autorroper. Archivado el 18 de abril de 2014 en Wayback Machine .
  10. Richard Skowyra, Kelly Casteel, Hamed Okhravi y William Streilein; Análisis sistemático de las defensas contra la programación orientada al retorno Archivado el 22 de febrero de 2014 en Wayback Machine , en Proceedings of RAID 2013 , Lecture Notes in Computer Science (LNCS), vol. 8145, págs. 82-102, 2013.
  11. Jason Hiser, Anh Nguyen-Tuong, Michele Co, Matthew Hall, JackW. Davidson, ILR: ¿Adónde se fueron mis dispositivos? Actas del Simposio IEEE sobre seguridad y privacidad, mayo de 2012, San Francisco, CA
  12. 1 2 Vasilis Pappas. kBouncer: Mitigación de ROP eficiente y transparente Archivado el 30 de agosto de 2017 en Wayback Machine . abril de 2012.