Ataques
Comprender la Prevención de Ejecución de Datos (DEP): protejamos la pila


VP of Hacking
5 min
En entradas anteriores de nuestro blog, hemos publicado varios artículos sobre el desarrollo de exploits para Windows; la mayoría de ellos se centraban en atacar Vulnserver, un servidor diseñado específicamente para ser vulnerable —tal como su nombre sugiere— con el fin de facilitar este tipo de ejercicios. También redactamos un par de artículos en los que creamos exploits para QuickZIP y MiTec Net Scanner. Todos estos exploits se basaban en la posibilidad de ejecutar instrucciones escritas en la pila (stack) del proceso.
Sin embargo, los procesadores modernos cuentan con un mecanismo que permite al sistema operativo evitar esto.
En este artículo, presentaremos dicha medida de protección y, en futuras publicaciones, analizaremos una técnica para eludirla, conocida como ROP (Return-Oriented Programming).
Bit «No-Execute» (No ejecución)
La protección presente en las CPU se conoce como bit NX («No-Execute» o «No ejecución»). El sistema operativo utiliza esta capacidad para marcar ciertas áreas de la memoria —en particular, la pila (stack)— como no ejecutables, evitando así ataques comunes basados en el desbordamiento de búfer (buffer overflow), como los que hemos empleado hasta ahora. Aclaremos esto.
En la arquitectura x86, cuando se invoca una función, se crea un marco de función (function frame) en la pila. A continuación, se muestra una distribución típica de un marco de función en la memoria:
Marco de función típico en la pila.
En un desbordamiento de búfer sencillo, al escribir más allá del tamaño de vuln_var, es posible sobrescribir cualquier elemento situado más abajo en la pila, incluidos el Saved EBP y el Saved EIP. Cuando la función vulnerable finaliza, recupera el valor de Saved EIP de la pila y lo utiliza como puntero a la siguiente instrucción. Por este motivo, solemos sobrescribir el Saved EIP con un puntero a una instrucción JMP ESP, lo que nos permite redirigir la ejecución de nuevo hacia la pila, concretamente al lugar donde hemos colocado el shellcode.
Ejemplo de vuln_var desbordada.
Por ejemplo, echemos un vistazo a este exploit:
Se trata de un exploit sencillo que aprovecha una vulnerabilidad de desbordamiento de búfer (buffer overflow) en el comando TRUN de Vulnserver. Aquí puedes consultar el análisis completo sobre cómo detectar dicha vulnerabilidad mediante fuzzing y, aquí, mediante ingeniería inversa.
Esta versión del exploit provoca un desbordamiento en la variable vulnerable de la siguiente manera:
Ejemplo de vuln_var desbordada.
Donde:
Se añaden 2.006 caracteres «A» para provocar el desbordamiento.
0x625011AF es un puntero a una instrucción
JMP ESPque se colocará en elSaved EIP(EIP guardado).Cuando la función vulnerable retorna, ejecutará la instrucción a la que apunta el
Saved EIP, la cual corresponde a la instrucciónJMP ESP.De este modo, el flujo de ejecución se redirige a la pila (stack), donde se ha colocado el shellcode.
En este caso, el shellcode ejecutará tres instrucciones arbitrarias:
xor eax, eax→ Establece el registro EAX a cero.add al, 0x8→ Hace que EAX = 0x00000008.nop→ No realiza ninguna operación.
Veámoslo en acción:

Como puede observarse, logramos ejecutar las instrucciones de nuestro shellcode —ubicado en la pila (stack)—, tal como se esperaba.
Habilitación de DEP
En las versiones modernas de Windows, es posible aprovechar el bit NX de la CPU mediante una funcionalidad denominada Data Execution Prevention (DEP, o Prevención de Ejecución de Datos). Una aplicación puede compilarse con el indicador /NXCOMPAT para habilitar DEP en ella. Asimismo, es posible utilizar editbin.exe /NXCOMPAT sobre un archivo ejecutable (.exe) para activar esta protección en un archivo que ya ha sido compilado.
Mediante un depurador (debugger), podemos verificar si un ejecutable tiene dicho indicador habilitado:

También puedes habilitar la DEP a nivel de todo el sistema, lo que la aplicará a todas las aplicaciones, incluidas aquellas que fueron compiladas sin /NXCOMPAT. Para ello, puedes seguir estas instrucciones:
Presiona la tecla Windows y busca «Ver configuración avanzada del sistema».
En la ventana que aparece, haz clic en la pestaña «Opciones avanzadas»:

Luego, en Rendimiento, haz clic en Configuración.
Ve a la pestaña Prevención de ejecución de datos:

La configuración predeterminada es «Activar DEP para los programas esenciales de Windows...», pero para activarlo en todas las aplicaciones, debe seleccionar «Activar DEP para todos los programas...».
Aplique los cambios y reinicie el equipo.
ADVERTENCIA: Si modifica este valor y tiene BitLocker habilitado, el sistema le solicitará la clave de recuperación de BitLocker tras el reinicio. Si no dispone de dicha información, no modifique el valor de DEP; de lo contrario, el sistema quedará inutilizable.
Una vez realizado esto, podemos volver a probar nuestro exploit para comprobar si DEP impide realmente la ejecución de las instrucciones de nuestro shellcode.
NOTA: Nos referiremos a la DEP basada en hardware, la cual utiliza el bit NX de la CPU para marcar regiones de memoria como no ejecutables. La DEP basada en software solo evita los desbordamientos (overflows) basados en SEH, lo cual queda fuera del alcance de este artículo. Puede obtener más información sobre los exploits basados en SEH aquí.
Ejecución de shellcode con DEP habilitado
Ahora, tras haber habilitado DEP en todo el sistema, procedamos a ejecutar nuestro exploit de nuevo:

Han ocurrido varias cosas:
Se produce el desbordamiento.
El valor guardado del EIP se sobrescribe correctamente con el puntero a
JMP ESP.Se ejecuta la instrucción
JMP ESPy el flujo de ejecución se redirige a la pila, donde se encuentra nuestro shellcode.Sin embargo, al intentar ejecutar la primera instrucción del shellcode (
xor eax, eax), se dispara una excepción de «violación de acceso»; esto significa que se intentó ejecutar código en una región de memoria marcada como no ejecutable. DEP ha funcionado.
Cómo eludir DEP
Aunque no podemos ejecutar instrucciones situadas en la pila (stack), sí controlamos el flujo de ejecución de la aplicación. Dado que la pila es un espacio donde la aplicación —y, por tanto, el exploit— puede leer y escribir datos, el control tanto del flujo de ejecución como de la pila nos permite realizar operaciones muy potentes.
En el ejemplo anterior, no podíamos ejecutar las instrucciones contenidas en el shellcode, pero sí logramos ejecutar una instrucción específica: JMP ESP. Para ello, colocamos el puntero a dicha instrucción en la ubicación adecuada.
Podemos aprovechar esto para ejecutar código arbitrario sin necesidad de ejecutar ni una sola instrucción directamente en la pila. Aquí es donde entra en juego la técnica conocida como Return-Oriented Programming (ROP).
Conclusiones
Este artículo presenta un mecanismo diseñado para evitar la explotación de vulnerabilidades de desbordamiento de búfer (buffer overflow). DEP logra, efectivamente, neutralizar los exploits convencionales. No obstante, en el próximo artículo veremos cómo eludir DEP mediante Return-Oriented Programming y, posteriormente, cómo crear un exploit funcional capaz de establecer una reverse TCP shell en una aplicación protegida por DEP.
En Fluid Attacks, ponemos a prueba las medidas de seguridad para identificar vulnerabilidades. Llevamos a cabo esta labor tanto en el ámbito de la investigación como mediante operaciones de red teaming para empresas de desarrollo de software. Actualmente, ofrecemos una prueba gratuita de 21 días de nuestro servicio de escaneo de vulnerabilidades. En cualquier momento, puedes ampliar el servicio para incluir pruebas de seguridad manuales, como pruebas de penetración (pentesting), realizadas por nuestros expertos.
Empieza ya con el PTaaS de Fluid Attacks
Etiquetas:
formacion
exploit
Suscríbete a nuestro boletín
Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.
Otros posts























