Una estrategia típica para derrotar a ASLR es encontrar un error de desbordamiento de búfer y un error de divulgación de información. Pero cuando se atacan los servidores que se reinician automáticamente cada vez que se cuelgan o mueren, ¿es suficiente un error de desbordamiento del búfer? ¿Podemos usar ese error de desbordamiento de búfer para también proporcionarnos la capacidad de divulgación de información?
Déjame desarrollar el escenario. Supongamos que tengo un programa de servidor que procesa una solicitud de la red y se reiniciará automáticamente en un bloqueo, y supongo que he encontrado una vulnerabilidad de desbordamiento de búfer (de un búfer asignado B
) en el servidor que puedo aprovechar de manera confiable enviando una solicitud apropiadamente diseñada al servidor. Supongamos también que puedo detectar cuándo se bloquea el servidor (por ejemplo, podría fallar porque le envié una solicitud que dañó su memoria y provocó un fallo de seguridad; en cualquier caso, suponga que puedo enviar una solicitud de mi elección y determinar si se estrelló o no al tratar de manejar esa solicitud). El servidor usa ASLR, y quiero desaleatorizar ASLR para poder montar un ataque de inyección de código, pero no conozco un error de divulgación de información por separado: la vulnerabilidad de saturación de búfer es todo con lo que tengo que trabajar. / p>
¿Puedo usar esta vulnerabilidad de saturación de búfer para la divulgación de información, para conocer el contenido de la memoria después de B
?
Este es un ejemplo del tipo de ataque que estoy imaginando:
Suponga que el búfer desbordable
B
tiene una longitud de 512 bytes, y suponga que hay un puntero secreto de 8 bytesP
almacenado inmediatamente después deB
. Supongamos que el campo F de la solicitud se copia byte por byte sobreB
sin ninguna verificación de longitud.Si envío una solicitud con un valor de 513 bytes para F, se copiará sobre
B
. Si el 513º byte de mi valor difiere del primer byte deP
, entonces el valor deP
se corromperá, y (asumiendo que el programa hace poco las referenciasP
), el programa probablemente se bloqueará durante el procesamiento de esta solicitud. Por otro lado, si el 513 byte de mi valor coincide con el primer byte deP
, entoncesP
permanecerá sin cambios, y el programa probablemente no se bloqueará.Entonces, puedo imaginar el envío de 256 solicitudes, cada una con un valor diferente para el 513º byte del campo F; si 255 de ellos causan que el servidor se bloquee y uno no lo hace, entonces inmediatamente sé el valor del primer byte de
P
. Ahora puedo continuar hasta que aprenda cada uno de los bytes deP
. Esto podría ser útil si el programa está usando ASLR: al aprender el valor del punteroP
, puedo eliminar al azar parte de la memoria.
Esto es solo un ejemplo simple. En la práctica, me imagino que podría haber espacio no utilizado después del final de B
y antes del siguiente objeto almacenado en el montón, pero puede imaginar formas de adaptar estas técnicas para tratar esa situación también (por ejemplo, si el byte después de que B
no esté en uso, puede sobrescribirlo con cualquier cosa y el servidor no se bloqueará, por lo que es fácil detectar ubicaciones que no están en uso y continuar el ataque hasta que encuentre el siguiente objeto después de B
).
¿Este ataque funciona en la práctica? ¿Proporciona una forma efectiva de vencer a ASLR, cuando tiene un desbordamiento de pila en un servidor que se reinicia automáticamente y cuando tiene una manera de detectar bloqueos?
¿Hay algún obstáculo que haya pasado por alto que evite que esto funcione? Por ejemplo, puedo imaginar que si la asignación de memoria para los objetos en el montón no fuera determinista y aleatoria, el ataque fallaría; ¿Pero las plataformas hacen eso? ¿Son las compensaciones relativas entre objetos en el montón deterministas en la práctica, si ejecuta el mismo programa dos veces en las mismas entradas?
Supongo que el desbordamiento del búfer permite sobrescribir B
con datos binarios arbitrarios que están totalmente bajo el control del atacante. (El ataque no funcionará con un strcpy()
o un desbordamiento relacionado con la cadena, ya que entonces los datos se verán forzados a terminar en cero). Además, supongamos que el servidor se reinicia con fork (), o para por alguna otra razón, parte del diseño de la memoria es la misma cada vez que se reinicia el servidor. (Por ejemplo, esto se mantiene automáticamente en Windows y Mac, ya que las bibliotecas están en la misma dirección base cada vez que reinicia el servidor, y se mantiene para procesos que no son PIE en Linux).
Crédito: Estoy inspirado en un método que leí recientemente para explotar un error de desbordamiento de búfer de un búfer asignado a la pila para fines de divulgación de información. Esto se describió en The Blind ROP paper recientemente publicado en IEEE Security & Privacidad 2014. Muestran cómo hacerlo cuando el búfer B
está asignado en la pila. En esta pregunta, estoy preguntando si su técnica se puede generalizar en el caso donde el búfer B
está en el montón.