ROP - Return Oriented Programing
Last updated
Last updated
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)
La Programación Orientada a Retornos (ROP) es una técnica avanzada de explotación utilizada para evadir medidas de seguridad como No Ejecutar (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, evitando efectivamente las protecciones NX/DEP.
Secuestro de 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: Luego, el atacante selecciona cuidadosamente y encadena gadgets para realizar las acciones deseadas. Esto podría implicar configurar argumentos para una llamada de función, llamar a la función (por ejemplo, system("/bin/sh")
), y manejar cualquier limpieza necesaria u operaciones adicionales.
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.
Normalmente, los gadgets se pueden encontrar utilizando ROPgadget, ropper o directamente desde pwntools (ROP).
cdecl: El llamador limpia la pila. Los argumentos de la función se empujan a la pila en orden inverso (de derecha a izquierda). Los argumentos se empujan a la pila de derecha a izquierda.
stdcall: Similar a cdecl, pero el llamado es responsable de limpiar la pila.
Primero, asumamos que hemos identificado los gadgets necesarios dentro del binario o sus bibliotecas cargadas. Los gadgets de interés son:
pop eax; ret
: Este gadget saca el valor superior de la pila al registro EAX
y luego retorna, permitiéndonos controlar EAX
.
pop ebx; ret
: Similar al anterior, pero para el registro EBX
, permitiendo control sobre EBX
.
mov [ebx], eax; ret
: Mueve el valor en EAX
a la ubicación de memoria apuntada por EBX
y luego retorna. Esto se llama comúnmente un gadget de escribir-dónde.
Adicionalmente, tenemos disponible la dirección de la función system()
.
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 deshabilitado 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)
Utiliza la convención de llamada System V AMD64 ABI en sistemas tipo Unix, donde los primeros seis argumentos enteros o de puntero se pasan en los registros RDI
, RSI
, RDX
, RCX
, R8
y R9
. Los argumentos adicionales se pasan en la pila. El valor de retorno se coloca en RAX
.
La convención de llamada de Windows x64 utiliza RCX
, RDX
, R8
y R9
para los primeros cuatro argumentos enteros o de puntero, con argumentos adicionales pasados en la pila. El valor de retorno se coloca en RAX
.
Registros: Los registros de 64 bits incluyen RAX
, RBX
, RCX
, RDX
, RSI
, RDI
, RBP
, RSP
y R8
a R15
.
Para nuestro propósito, nos enfocaremos en gadgets que nos permitirán 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: Saca 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 conocemos la dirección de la función system().
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 establecer RDI
en la dirección de "/bin/sh"
.
Saltamos directamente a system()
después de establecer RDI
, con la dirección de system() en la cadena.
Se utiliza el gadget ret_gadget
para alineación si el entorno objetivo lo requiere, lo cual es más común en x64 para garantizar una alineación adecuada del stack antes de llamar a funciones.
El ABI x86-64 asegura que el stack esté alineado en 16 bytes cuando se ejecuta una instrucción call. LIBC, para optimizar el rendimiento, utiliza instrucciones SSE (como movaps) que requieren esta alineación. Si el stack no está alineado 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.
Dado que x64 utiliza registros para los primeros argumentos, a menudo requiere menos gadgets que x86 para llamadas de función 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).
Consulta la siguiente página para obtener esta información:
Introduction to ARM64v8Canarios de Stack: En caso de un desbordamiento de búfer, es necesario evitar los canarios de stack para sobrescribir los punteros de retorno y abusar de una cadena ROP.
Falta de Gadgets: Si no hay suficientes gadgets, no será posible generar una cadena ROP.
Ten en cuenta que ROP es solo una técnica para ejecutar código arbitrario. Basándose en ROP se desarrollaron muchas técnicas Ret2XXX:
Ret2lib: Usa ROP para llamar 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 llamada al sistema, por ejemplo execve
, y hacer que ejecute comandos arbitrarios.
EBP2Ret & 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).
64 bits, Pie y nx habilitado, sin canario, sobrescribe RIP con una dirección vsyscall
con el único propósito de regresar a la siguiente dirección en el stack 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 el stack ejecutable y saltar al shellcode en el stack
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)