Tiene curiosidad por la implementación del "Control de seguridad de búfer" de Microsoft

6

Un resumen muy rápido para ayudarte a responder mis preguntas:

  • Aprendizaje del desensamblador interactivo IDA, la antigua edición gratuita (demasiado cara para un aficionado)
  • Sysadmin Linux de 15 años & Experiencia DBA
  • codificador de aficionados (C / ASM / Fortran / ...)
  • No tiene mucho conocimiento sobre Windows & Microsoft en general
  • no es un experto en seguridad, pero lo suficiente como para ser un administrador de sistemas

Después de un montón de desmontaje aleatorio de cosas aquí y allá, rápidamente noté que tienen muchos códigos comunes. Y al comienzo de muchos ejecutables hay algo que se parece a esto:

lea     eax, [ebp+SystemTimeAsFileTime]
push    eax             ; lpSystemTimeAsFileTime
call    ds:GetSystemTimeAsFileTime
mov     eax, [ebp+SystemTimeAsFileTime.dwHighDateTime]
xor     eax, [ebp+SystemTimeAsFileTime.dwLowDateTime]

Está literalmente en casi todos los binarios y rápidamente se encontró que es algo llamado por microsoft: "Buffer Security Check". Y el compilador lo agrega automáticamente mediante el indicador / GS.

Es un pila canaria que se usa para detectar el desbordamiento del búfer de la pila:

  

Este método funciona colocando un entero pequeño, cuyo valor se elige aleatoriamente al inicio del programa, en la memoria justo antes del puntero de retorno de pila. La mayoría de los desbordamientos de búfer sobrescriben la memoria de las direcciones de memoria inferiores a las más altas, por lo que para sobrescribir el puntero de retorno (y, por lo tanto, tomar el control del proceso), el valor canario también debe sobrescribirse. Este valor se verifica para asegurarse de que no haya cambiado antes de que una rutina use el puntero de retorno en la pila

Y de la documentación de Microsoft :

  

En las funciones que el compilador reconoce como sujetas a problemas de saturación del búfer, el compilador asigna espacio en la pila antes de la dirección de retorno. En la entrada de la función, el espacio asignado se carga con una cookie de seguridad que se calcula una vez en la carga del módulo. Al salir de la función y durante el desenrollado de cuadros en sistemas operativos de 64 bits, se llama a una función auxiliar para asegurarse de que el valor de la cookie sigue siendo el mismo. Un valor diferente indica que puede haberse producido una sobrescritura de la pila. Si se detecta un valor diferente, el proceso finaliza.

Todo está bien y todos vivirán felices para siempre.

Sin embargo , estoy confundido acerca de la implementación en sí. Es un montón de XOR de: tiempo del sistema, processId, ThreadId, tiempo de actividad, más tiempo (contador de rendimiento) en la creación del proceso.

No estoy diciendo que sea una implementación terriblemente mala (pero a primera vista lo estoy pensando) ya que es una gran cantidad de XOR y un solo bit equivocado estropeará la cookie por completo (a menos que haya un ataque, estoy no se tiene en cuenta) y el forzado bruto se evita al 100% (o no se sabe) ya que el proceso finalizará cuando la suposición sea incorrecta y se genere una nueva cookie en el siguiente proceso.

Ahora tengo un montón de preguntas:

  • ¿Por qué una implementación de generación pseudoaleatoria tan extraña? ¿El sistema operativo no tiene una fuente de entropía confiable? ¿Está relacionada la portabilidad? ¿Es posible que alguna variante de Windows no tenga una fuente de entropía?
  • ¿no se conoce processId y threadId a todos en el sistema de todos modos? entonces, ¿por qué usarlo como fuente de entropía?
  • Todo el tiempo está relacionado y se inicia al inicio del proceso, no se garantiza que los servicios no se inicien (lo que necesita la mayor protección) tiene un tiempo de funcionamiento y amp; ¿Contador de resultados y una hora del sistema conocida y, por lo tanto, la entropía más débil?
  • ¿Por qué esta generación de cookies no es una llamada al sistema de todos modos? Ahora es irreparable sin recompilación.

Aquí está mi desmontaje comentado:

push    ebp             ; prolog
mov     ebp, esp        ; prolog
sub     esp, 14h        ; prolog
and     [ebp+SystemTimeAsFileTime.dwLowDateTime], 0
and     [ebp+SystemTimeAsFileTime.dwHighDateTime], 0
mov     eax, __security_cookie
push    esi
push    edi
mov     edi, 0BB40E64Eh ; a global constant thingy for security cookie
mov     esi, 0FFFF0000h
cmp     eax, edi        ; compare local security cookie
                    ; with global security cookie.
                    ; if security_cookie == 0BB40E64Eh then it's not initialized
                    ; and it need to be initialized
jz      short generate_security_cookie

La generación se realiza solo si se compara con una constante mundial conocida "0BB40E64Eh". ¿No hay un vector de ataque potencial? ¿No podría ser un valor local para el sistema?

  • ¿Por qué molestarse en condicionar la generación?
  • ¿Es incluso una característica de seguridad? o simplemente un medio de depuración para detectar que algo salió mal con la pila?

Son muchas preguntas, es mi primer paso en este tipo de cosas, pero esto es muy confuso. Pero principalmente: ¿por qué no llamar simplemente a una fuente de entropía del SO? Estoy tan confundido ...

Pregunta de bonificación: si uno tiene un medio para modificar el binario (¿un solo bit en la constante mundial conocida?), no se podría pasar por alto todo / GS? y si es un servicio de red abierta, ahora tiene un servicio con un potencial de desbordamiento de búfer abierto al mundo, ¿no?

    
pregunta ker2x 03.10.2017 - 13:34
fuente

1 respuesta

2

El objetivo del apilado de pila es proteger el programa de un desbordamiento de búfer. El canario se vuelve dinámico, se usa uno nuevo en cada ejecución y, un atacante solo tendrá un intento de adivinarlo, ya que si falla, abortará el programa.

La fuente del canario es bastante similar a la de CryptGenRandom , que es la forma oficial de obtener Números criptográficamente seguros en Windows (antes de la introducción de la API de CNG), por lo que no está mal.

  

¿no se conoce processId y threadId a todos en el sistema de todos modos? Entonces, ¿por qué usarlo como fuente de entropía?

No todos los ataques provendrán del sistema local. Un atacante de la red no tendrá idea de eso. Como es barato incluirlos en el cálculo, se agregan para una entropía adicional.

  

no se garantiza que los servicios se inicien en el arranque (las cosas que necesitan la mayor protección) tienen un tiempo de actividad y amp; perfcounter (…)?

No es suficiente saber que "se utiliza un valor bajo". Necesita el número exacto para evitar el canario y solo tiene un intento. No creo que puedas adivinar el canario de ninguno de ellos.

  

¿Por qué esta generación de cookies no es una llamada al sistema de todos modos?

Una llamada al sistema significaría que el canario solo se puede usar en una nueva versión de Windows. De esta manera, no hay dependencia en la versión del sistema operativo. Además, eso haría que la generación sea más lenta.

Los números aleatorios de Windows en general se beneficiarían al tener una llamada de sistema dedicada (u otra interfaz del sistema operativo para entropía, similar a unix / dev / random) para acceder a la entropía mantenida en todo el sistema, en lugar del conjunto por proceso creado por CryptGenRandom , pero sería de poco beneficio aquí.

  

¿Por qué molestarse en condicionar la generación?

Este es un enfoque clásico de verificación en caso de que sea inicializado. La variable global para la cookie inicialmente es 0BB40E64Eh. Luego, en el primer uso, ve que no se generó un canario adecuado y ejecuta el cálculo. De esta manera, no se pierden recursos calculando el canario a menos que sea necesario. (Un valor más habitual sería haber utilizado 00000000h, no sé por qué se seleccionó 0BB40E64Eh)

  

¿Es incluso una característica de seguridad? ¿O simplemente un medio de depuración para detectar que algo salió mal con la pila?

Sirve como una ayuda de depuración, pero es una función de seguridad.

  

Pregunta de bonificación: si uno tiene un medio para modificar el binario (¿un solo bit en la constante mundial conocida?), no se podría pasar por alto todo / GS?

Sí. El uso de un canario constante permitiría eludir los controles de canarios (¡aunque es más difícil que no tener ningún control de canarios!)

  

y si se trata de un servicio de red abierta, ahora tiene un servicio con un potencial de desbordamiento de búfer abierto al mundo, ¿no?

No. Usted tiene un servicio con la protección de la pila del compilador deshabilitada. ¡Pero este servicio no debería ser vulnerable a un desbordamiento de búfer para empezar!

Y probablemente no querrá que la denegación de servicio producida por el cheque canario fallido anule su servicio (es mejor que ejecución de código , pero sigue siendo mala. Esta verificación es la última línea de defensa).

Si pudieras modificar el binario, aunque solo sea un poco, probablemente encuentres objetivos más letales que deshabilitar el canario de pila, como cambiar el JE de la comparación de la contraseña a un JNE :)

    
respondido por el Ángel 15.07.2018 - 02:12
fuente

Lea otras preguntas en las etiquetas