Hacer que la carga útil inyectada "no sea ejecutable" es de lo que se trata Prevención de ejecución de datos . Existen varias técnicas para lograrlo, dependiendo de lo que pueda hacer el hardware subyacente. En la mayoría de las arquitecturas, esto se hará a través de la MMU : páginas que se supone que contienen "datos" (por ejemplo, la pila) están marcados como no ejecutables.
La vieja CPU x86 hace que el DEP sea un poco difícil porque la MMU original del 80386 no distingue entre los accesos de "lectura" y "ejecución"; por lo tanto, no permite que la memoria se marque como no ejecutable mientras aún se puede leer. El DEP todavía se puede hacer hasta cierto punto con la ayuda de los registros de segmento , aunque con menos flexibilidad (básicamente, puede hacer el conjunto). pila no ejecutable, pero es difícil o imposible reutilizar dinámicamente los fragmentos de memoria). Otro método llamado PaX permite separar los derechos de acceso de "lectura" y "ejecución" en una base por página, pero esto requiere un poco de malabarismo con el TLB, y tiene una sobrecarga de tiempo de ejecución (el TLB pierde las excepciones de la CPU). Consulte esta respuesta para obtener más información.
La x86 más nueva, y en particular todas las CPU x86 que pueden ejecutarse en modo de 64 bits, tienen una MMU que sabe de forma nativa cómo distinguir entre "leer" y "ejecutar" (esto se denomina NX bit ) haciendo que estos segmentos o juegos PaX sean obsoletos.
La llamada " política de W ^ X " (léala como "grabable exclusive-o eXecutable" ) indica que el sistema operativo nunca debe permitir que un fragmento de memoria pueda escribirse y ejecutarse al mismo tiempo, por lo que si la carga útil se puede inyectar (es decir, se escribe en un trozo de RAM), entonces no se puede ejecutado hasta que se realice algún cambio de derechos de acceso explícito en la página (y, presumiblemente, el código de destino no tiene ninguna razón para realizar dicho cambio).
DEP no es una panacea; los atacantes ahora han aprendido a usar piezas de código existentes, que ya están en la RAM y marcados como ejecutables, para servir como carga útil. Consulte Programación orientada al retorno para obtener más información.
No permitir el punto 3 sería un método más completo para prevenir ataques exitosos; desafortunadamente, algunos lenguajes de programación tradicionales generalizados (C y C ++, a saber) son deficientes en esa tarea. El desvío del flujo de control se produce como consecuencia de un acceso a la memoria no controlado (desbordamiento de búfer, uso después de la liberación, doble libertad ...) que el idioma permitió que ocurriera porque no verifica tales ocurrencias. El desarrollador debe agregar todos los controles necesarios. Da la casualidad de que incluso los mejores desarrolladores con las prácticas de desarrollo más exhaustivas a veces no lo hacen.