¿Cuándo es determinista la asignación / disposición de memoria?

7

Estoy tratando de aprender sobre los ataques de desbordamiento de búfer, tanto en la pila como en el montón. Sin embargo, estoy confundido acerca de cuándo es posible determinar la dirección del búfer.

El clásico "Smashing the Stack for Fun and Profit" menciona la necesidad de usar un considerable sled NOP en un ataque de desbordamiento de búfer basado en stack para saltar de manera confiable al shellcode colocado en el amortiguador Esto parece deberse al hecho de que el búfer termina en una ubicación de memoria diferente en cada ejecución del programa. Sin embargo, no es completamente aleatorio, ya que es posible adivinar la dirección del búfer aumentando las posibilidades de usar un trineo NOP .

En contraste, las variables de entorno pasadas al programa se colocan en las mismas direcciones cada vez, lo que permite un ataque local mucho más fácil en el que el atacante coloca el código de shell en una variable de entorno, y luego puede saltar exactamente a la ubicación de esa variable de entorno.

Todavía estoy en la oscuridad sobre cómo se desarrolla esto para los búferes basados en almacenamiento dinámico (por ejemplo, los asignados por malloc ) y en otras situaciones. Por lo tanto:

  1. ¿En qué circunstancias es determinista el diseño de la memoria?
  2. ¿Qué tan predecible es incluso cuando no es completamente determinista? Finalmente, ¿cómo afectan esto las mitigaciones de explotación como ASLR y PIE?
pregunta ikdc 04.02.2015 - 03:11
fuente

2 respuestas

3

Parte del problema tiene que ver con cómo se ve el diseño de la memoria en la inicialización del programa y cómo se ve el diseño de la memoria después de que se haya estado ejecutando durante un tiempo y logre que el búfer se desborde. También tiene que lidiar con el problema 'funciona en mi máquina'.

En la inicialización, todas las variables de entorno se cargarán en el mismo lugar porque se trata de un proceso razonable y temprano. Cuando se trata de desbordar el búfer, no siempre puede asegurarse de que lo está activando a la misma hora cada vez. por ejemplo, si la aplicación recibe paquetes de red, sería muy difícil enviar exactamente los mismos paquetes todo el tiempo. Entonces, como resultado, cuando llegue el momento de activar el desbordamiento, es posible que el diseño de la memoria no sea exactamente el mismo. De ahí la necesidad de que el trineo NOP sea un lugar para 'aterrizar'.

El problema de 'funciona en mi máquina' es que, realmente, quieres que tu exploit funcione en las máquinas de otras personas, y es poco probable que estén configuradas exactamente como lo está la tuya. Puede haber diferentes versiones de bibliotecas, diferentes configuraciones de hardware, lo que significa que es poco probable que la dirección de desbordamiento de destino sea la misma que la suya.

Estos problemas pueden hacer que sea difícil garantizar que el diseño de la memoria sea el mismo todo el tiempo, por lo que, en cambio, trabajas en un rango de valores probables y, por lo tanto, la necesidad de un sled NOP.

    
respondido por el Colin Cassidy 20.01.2017 - 11:35
fuente
0

Determinismo desde dentro de los programas

Aunque todas las operaciones deterministas en datos conocidos producen resultados deterministas, el diseño de la memoria no debe considerarse como determinista para nadie más que para los desarrolladores de kernel. Aunque el diseño es determinista en el sentido matemático 1 , la naturaleza cambiante y diversificada de la industria del software hace que la visión de una aplicación de las estructuras del sistema sea efectivamente estocástica.

Funciones de seguridad portátiles

Ni el código portátil ni el confiable pueden basarse en suposiciones sobre la memoria fuera de los modelos de programación estandarizados provistos. Los encabezados de memoria estática, asignados dinámicamente o asignados pueden cambiar, y el compilador puede cambiar en sincronización para que un programa que cumpla con los estándares se ejecute sin problemas, pero que explote el conocimiento especial del sistema operativo pueda romperse y crear más riesgos de seguridad de los que pretende eliminar. .

Un programa no puede depender de la consistencia de las estructuras del kernel a lo largo del tiempo. Las arquitecturas de la CPU, sus conjuntos de instrucciones, los modelos de ejecución, el manejo de la pila y la mecánica de Malloc y Free pueden cambiar en cualquier momento. Esta es la razón para POSIX y otros estándares. Solo las fachadas proporcionadas a través de las llamadas de sistemas estandarizados pueden (generalmente) ser confiables para exhibir un comportamiento predecible.

Evitar las explotaciones de variables de entorno

Uno puede, si lo desea, recorrer las variables de entorno, copiar los valores necesarios y luego escribir ceros en el rango exacto que ocupa cada uno. Pero esa no es la mejor estrategia.

Práctica de fuente y sistema generalmente útil

La fruta de baja pendiente que ha sido utilizada por muchos equipos de seguridad es bloquear la ejecución de todo el software ejecutable de todos los usuarios, excepto las combinaciones de software de usuario que tienen sentido. Aquellos que están permitidos se analizan para detectar posibles agujeros de seguridad y se aplican los parches apropiados para minimizarlos.

La protección adecuada específica de la explotación contra el desbordamiento y el desbordamiento del búfer es una validación cuidadosa del índice del búfer en lenguajes como C o el uso adecuado de objetos diseñados para evitar estas manipulaciones de modelos de memoria explotables.

Métodos de inyección de entropía

La aleatorización del diseño del espacio de direcciones (ASLR) es una característica del sistema operativo que proporciona resistencia adicional al ataque. Usted no agrega ASLR a su programa. Cada sistema operativo que proporciona el modelo tiene un mecanismo en el que se pueden ejecutar programas específicos sin proporcionar una sugerencia a través de la inspección del proceso desde donde se cargan en la CPU los bytes de instrucciones ejecutables.

El ejecutable independiente de la posición (PIE) es un mecanismo que admite la aleatorización al garantizar que todas las direcciones en el ejecutable sean relativas o se comporten (en grupos de instrucciones) como si lo fueran.

Necesitará ver cómo se puede agregar la entropía al posicionamiento del modelo de memoria en su sistema operativo de destino en particular. Puede haber restricciones asociadas en su código, compilación o marcas de enlace para usar, y la ejecución del contenedor que realiza la aleatorización necesaria. Puede haber un impacto en la velocidad y los recursos, por lo que es posible que desee identificar sus niveles de riesgo ejecutables y ser selectivo.

¿Cómo funcionan? La introducción de la entropía en la determinación de la ubicación de la memoria hace que sea difícil para los atacantes adivinar dónde residen las instrucciones de la CPU en la memoria, por lo que la fuente de salto y el destino deseado son más difíciles de determinar en tiempo real desde otra porción de tiempo en un escenario de multitarea.

Estrategias de capas para la seguridad acumulativa

Recuerda que estos son guardias, no garantías de seguridad. Pocas estrategias rentables son tan efectivas como la combinación tradicional de administración de sistemas de clase mundial, acceso cuidadoso a la memoria dentro de los programas y excelente saneamiento de los datos entrantes. Las adiciones más recientes a esta lista son agregar capas a estas estrategias efectivas para aumentar la resistencia del sistema al ataque.

[1] El diseño de la memoria es determinista a menos que el sistema operativo capture la entropía real del movimiento del mouse, la entrada de audio o algún otro transductor y luego se emplee para posicionar la memoria de forma aleatoria (en oposición a pseudo-aleatoria). p>     

respondido por el Douglas Daseeco 20.01.2017 - 06:14
fuente

Lea otras preguntas en las etiquetas