ROP - Return Oriented Programing
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE) Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Información Básica
Programación Orientada a Retornos (ROP) es una técnica de explotación avanzada utilizada para eludir medidas de seguridad como No-Execute (NX) o Prevención de Ejecución de Datos (DEP). En lugar de inyectar y ejecutar shellcode, un atacante aprovecha fragmentos de código ya presentes en el binario o en bibliotecas cargadas, conocidos como "gadgets". Cada gadget generalmente termina con una instrucción ret
y realiza una pequeña operación, como mover datos entre registros o realizar operaciones aritméticas. Al encadenar estos gadgets, un atacante puede construir una carga útil para realizar operaciones arbitrarias, eludiendo efectivamente las protecciones NX/DEP.
Cómo Funciona ROP
Secuestro del Flujo de Control: Primero, un atacante necesita secuestrar el flujo de control de un programa, típicamente explotando un desbordamiento de búfer para sobrescribir una dirección de retorno guardada en la pila.
Encadenamiento de Gadgets: El atacante luego selecciona y encadena cuidadosamente gadgets para realizar las acciones deseadas. Esto podría implicar configurar argumentos para una llamada a función, llamar a la función (por ejemplo,
system("/bin/sh")
), y manejar cualquier limpieza o operaciones adicionales necesarias.Ejecución de la Carga Útil: Cuando la función vulnerable retorna, en lugar de regresar a una ubicación legítima, comienza a ejecutar la cadena de gadgets.
Herramientas
Típicamente, los gadgets se pueden encontrar usando ROPgadget, ropper o directamente desde pwntools (ROP).
Ejemplo de Cadena ROP en x86
Convenciones de Llamada x86 (32-bit)
cdecl: El llamador limpia la pila. Los argumentos de la función se empujan en la pila en orden inverso (de derecha a izquierda). Los argumentos se empujan en la pila de derecha a izquierda.
stdcall: Similar a cdecl, pero el llamado es responsable de limpiar la pila.
Encontrando Gadgets
Primero, supongamos que hemos identificado los gadgets necesarios dentro del binario o sus bibliotecas cargadas. Los gadgets que nos interesan son:
pop eax; ret
: Este gadget saca el valor superior de la pila en el registroEAX
y luego retorna, permitiéndonos controlarEAX
.pop ebx; ret
: Similar al anterior, pero para el registroEBX
, habilitando el control sobreEBX
.mov [ebx], eax; ret
: Mueve el valor enEAX
a la ubicación de memoria apuntada porEBX
y luego retorna. Esto se llama a menudo un gadget de escritura-qué-dónde.Además, tenemos la dirección de la función
system()
disponible.
Cadena ROP
Usando pwntools, preparamos la pila para la ejecución de la cadena ROP de la siguiente manera, con el objetivo de ejecutar system('/bin/sh')
, nota cómo la cadena comienza con:
Una instrucción
ret
para propósitos de alineación (opcional)Dirección de la función
system
(suponiendo ASLR desactivado y libc conocida, más información en Ret2lib)Marcador de posición para la dirección de retorno de
system()
Dirección de la cadena
"/bin/sh"
(parámetro para la función system)
Cadena ROP en x64 Ejemplo
x64 (64-bit) Convenciones de llamada
Utiliza la convención de llamada System V AMD64 ABI en sistemas similares a Unix, donde los primeros seis argumentos enteros o punteros se pasan en los registros
RDI
,RSI
,RDX
,RCX
,R8
yR9
. Los argumentos adicionales se pasan en la pila. El valor de retorno se coloca enRAX
.La convención de llamada Windows x64 utiliza
RCX
,RDX
,R8
yR9
para los primeros cuatro argumentos enteros o punteros, con argumentos adicionales pasados en la pila. El valor de retorno se coloca enRAX
.Registros: Los registros de 64 bits incluyen
RAX
,RBX
,RCX
,RDX
,RSI
,RDI
,RBP
,RSP
yR8
aR15
.
Encontrando Gadgets
Para nuestro propósito, centrémonos en gadgets que nos permitan establecer el registro RDI (para pasar la cadena "/bin/sh" como argumento a system()) y luego llamar a la función system(). Supondremos que hemos identificado los siguientes gadgets:
pop rdi; ret: Extrae el valor superior de la pila en RDI y luego retorna. Esencial para establecer nuestro argumento para system().
ret: Un retorno simple, útil para la alineación de la pila en algunos escenarios.
Y sabemos la dirección de la función system().
Cadena ROP
A continuación se muestra un ejemplo utilizando pwntools para configurar y ejecutar una cadena ROP con el objetivo de ejecutar system('/bin/sh') en x64:
En este ejemplo:
Utilizamos el gadget
pop rdi; ret
para establecerRDI
en la dirección de"/bin/sh"
.Saltamos directamente a
system()
después de establecerRDI
, con la dirección de system() en la cadena.Se utiliza
ret_gadget
para la alineación si el entorno objetivo lo requiere, lo cual es más común en x64 para asegurar una alineación adecuada de la pila antes de llamar a funciones.
Alineación de la Pila
El ABI x86-64 asegura que la pila esté alineada a 16 bytes cuando se ejecuta una instrucción de llamada. LIBC, para optimizar el rendimiento, utiliza instrucciones SSE (como movaps) que requieren esta alineación. Si la pila no está alineada correctamente (lo que significa que RSP no es un múltiplo de 16), las llamadas a funciones como system fallarán en una cadena ROP. Para solucionar esto, simplemente agrega un gadget ret antes de llamar a system en tu cadena ROP.
Diferencia principal entre x86 y x64
Dado que x64 utiliza registros para los primeros argumentos, a menudo requiere menos gadgets que x86 para llamadas a funciones simples, pero encontrar y encadenar los gadgets correctos puede ser más complejo debido al mayor número de registros y al espacio de direcciones más grande. El mayor número de registros y el espacio de direcciones más grande en la arquitectura x64 ofrecen tanto oportunidades como desafíos para el desarrollo de exploits, especialmente en el contexto de la Programación Orientada a Retornos (ROP).
Ejemplo de cadena ROP en ARM64
Conceptos básicos de ARM64 y convenciones de llamada
Consulta la siguiente página para esta información:
Introduction to ARM64v8Protecciones contra ROP
Canarios de Pila: En caso de un BOF, es necesario eludir el almacenamiento del canario de pila para sobrescribir punteros de retorno y abusar de una cadena ROP.
Falta de Gadgets: Si no hay suficientes gadgets, no será posible generar una cadena ROP.
Técnicas basadas en ROP
Ten en cuenta que ROP es solo una técnica para ejecutar código arbitrario. Basado en ROP se desarrollaron muchas técnicas Ret2XXX:
Ret2lib: Usa ROP para llamar a funciones arbitrarias de una biblioteca cargada con parámetros arbitrarios (generalmente algo como
system('/bin/sh')
.
Ret2Syscall: Usa ROP para preparar una llamada a una syscall, por ejemplo,
execve
, y hacer que ejecute comandos arbitrarios.
EBP2Ret y EBP Chaining: El primero abusará de EBP en lugar de EIP para controlar el flujo y el segundo es similar a Ret2lib, pero en este caso el flujo se controla principalmente con direcciones de EBP (aunque también es necesario controlar EIP).
Otros Ejemplos y Referencias
64 bits, Pie y nx habilitados, sin canario, sobrescribir RIP con una dirección
vsyscall
con el único propósito de regresar a la siguiente dirección en la pila que será una sobrescritura parcial de la dirección para obtener la parte de la función que filtra la bandera.arm64, sin ASLR, gadget ROP para hacer la pila ejecutable y saltar a shellcode en la pila.
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE) Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Last updated