2010-07-20 17 views
15

He leído muchos artículos sobre funciones inseguras como strcpy, memcpy, etc. que pueden provocar problemas de seguridad al procesar datos externos, como el contenido de un archivo o datos procedentes de sockets. Esto puede sonar estúpido, pero escribí un programa vulnerable pero no logré "piratearlo".Ejemplo de desbordamiento de búfer que provoca una fuga de seguridad

Entiendo el problema del desbordamiento del búfer. Tome este código de ejemplo:

int main() { 
    char buffer[1]; 
    int var = 0; 

    scan("%s", &buffer); 
    printf("var = 0x%x\n", var); 
    return 0; 
} 

Cuando ejecuto el programa y el tipo "ABCDE", los resultados de los programas, que es 0x65646362 "EDCB" en hexadecimal + ascendente hacia la izquierda. Sin embargo, leí que podría modificar el valor de eip que se apretó en la pila para hacer que el programa ejecute algún código no deseado (por ejemplo, justo antes de una llamada a la función del sistema()).

Sin embargo ensamblaje de la función comienza así:

push %ebp 
mov %ebp, %esp 
and $0xfffffff0, %esp 
sub $0x20, %esp 

Como el valor de% esp es al azar al comienzo de la función y debido a esto "y", no parece haber ninguna manera confiable para escribir un valor preciso en el valor de eip empujado.

Además, leí que era posible ejecutar el código que escribiste en el búfer (aquí el búfer tiene solo 1 byte de longitud, pero en realidad sería lo suficientemente grande como para almacenar algún código) pero ¿qué valor daría? eip para hacerlo (considerando que la ubicación del buffer es aleatoria)?

¿Por qué los desarrolladores están tan preocupados por los problemas de seguridad (excepto que el programa podría fallar)? ¿Tiene un ejemplo de un programa vulnerable y cómo "piratearlo" para ejecutar código no deseado? Intenté esto en Linux, ¿Windows es menos seguro?

Respuesta

15

Lea el excelente artículo de Aleph One: Smashing the Stack for Fun and Profit.

+0

Corrígeme si me equivoco pero, por muy seminal que fuera, no recuerdo este artículo que trate sobre cualquier mecanismo de protección de la pila. – torak

+0

@torak: No, no es así. Sin embargo, no creo que sea un problema de OP, y la pregunta parece más básica que tratar con las salvaguardas del sistema operativo/hardware. Este artículo es un muy buen punto de partida. –

+0

@Moron: Bueno, dado que el OP mencionó específicamente que las instrucciones 'y $ 0xfffffff0,% esp' representan el desplazamiento a la variable EIP, parecería que captan el mecanismo de desbordamiento básico. – torak

4

Bueno, por un lado, no subestime los riesgos asociados con la posibilidad de colocar un valor no confiable dentro de EIP. Si un exploit funciona uno en 16 veces, y el servicio que está atacando se reinicia automáticamente, como muchas aplicaciones web, entonces un atacante que falla al tratar de obtener acceso siempre puede intentarlo, intente de nuevo.

También en muchos casos, el valor de ESP es menos aleatorio de lo que crees. Para los principiantes en un sistema de 32 bits, casi siempre es un múltiplo de cuatro. Eso significa que el relleno adicional ofrecido por la instrucción and $0xfffffff0, %esp será de 0, 4, 8 o 12 bytes. Eso significa que es posible simplemente repetir el valor que se va a escribir en el EIP de retorno cuatro veces para cubrir todas las compensaciones posibles a la dirección de EIP de retorno.

En realidad, hay mecanismos mucho más agresivos stack protection/buffer overflow detection alrededor. Sin embargo, existen formas y medios incluso en estos casos.

También, para un ejemplo de donde este tipo de cosas pueden ser peligrosas, considere si el valor de var fue importante para su lógica como en el siguiente ejemplo de juguete.

int main() { 
    char buffer[1]; 
    int var = 0; 

    var = SecurityCheck(); 

    scan("%s", &buffer); 
    if (var != 0) 
    GrantAccess(); 
    else 
    DenyAccess() 
} 
+1

I También mencionaría los trineos nop. En resumen, puede colocar muchos NOP antes de su shellcode, así que adivinar dónde es más fácil el "inicio" de su shellcode. – ninjalj

+0

Estrechamente relacionado con este ejemplo está la técnica de corromper una estructura como ' struct user_account {char name [16]; int group; int permisos;}; 'proporcionando un' nombre' que es demasiado largo para sobrescribir los miembros 'group' y' permisos'. La clave es poder predecir qué está ubicado en la memoria en los bytes que siguen una matriz. – bta

1

Un ejemplo clásico de una verdadera hazaña basan en las saturaciones del búfer es el Morris Worm de 1988.

+0

+1: ¡Estaba pensando exactamente en este mismo ejemplo! –

+1

En realidad, el gusano Morris explotó varias vulnerabilidades, una de las cuales fue una desbordamiento de búfer en fingerd. – ninjalj

+0

@ninjalj: Sí, eso parece exacto. –

4

Además usted no tiene que sobrescribir EIP con un puntero a algo en su cadena. Por ejemplo, puede sobrescribirlo con un puntero a system() y sobrescribir la siguiente palabra con un puntero a /bin/sh en una ubicación fija en la imagen del programa.

Editar: Tenga en cuenta que system utiliza el PATH (en realidad ejecuta el comando a través de un shell), por lo que "sh" sería igual de bueno; por lo tanto, cualquier palabra en inglés que termine en "sh" al final de una cadena proporciona el argumento que necesita.

0

Aquí hay una versión de Windows y tutorial:

http://www.codeproject.com/KB/winsdk/CodeInject.aspx

El caso general siempre me habían advertido sobre fue:

printf(string); 

Debido a que el usuario puede proporcionar una "%n" allí, lo que le permite para insertar todo lo que quieras en la memoria. Todo lo que necesita hacer es encontrar la compensación de memoria para una llamada al sistema, pasar unos pocos "%n" y caracteres basura, y así insertar la dirección de la memoria en la pila donde normalmente estaría el vector de retorno. Voila: inserta cualquier código que te guste.

+2

Eso se llama una vulnerabilidad de cadena de formato, no un desbordamiento de búfer :) – ninjalj

+0

@ninjali: meh, son primos besos. éste desborda el buffer de la pila. :-) – eruciform

1

Como se menciona en otras respuestas, la fiabilidad absoluta no siempre es esencial para que el ataque tenga éxito. Las aplicaciones que se reinician automáticamente son un ejemplo. Los desbordamientos de búfer localmente explotables en programas suid serían otro. Y existe la técnica de trineo NOP para aumentar las posibilidades de una explotación exitosa, poner muchos NOP antes de su shellcode, por lo que tiene muchas más posibilidades de adivinar correctamente el "inicio" de su shellcode.

Existen muchas más técnicas para aumentar la fiabilidad de los ataques. En Windows, en el pasado, muchos exploits sobrescribieron la dirección de devolución con la dirección de un "jmp% esp" ubicado en algún lugar del programa (trampolín).

"Programación insegura por ejemplo" tenía un buen truco para Linux. Limpia tu entorno y pon tu shellcode en una variable de entorno. De vuelta en el día, esto llevaría a una dirección predecible cerca de la parte superior de la pila.

Y también hay variantes como return-in-libc y return-oriented programming.

Incluso hubo un artículo sobre cómo explotar desbordamientos de pila de 1 byte (lo que significa que el buffer fue invadido solo por un byte) (por cierto, los desbordamientos de 1 byte también son explotables en la gran mayoría de los casos, salvo protecciones).

En resumen, no es que los desarrolladores son paranoicos, hay un montón de maneras de explotar incluso en los casos más extraños, y recuerde:

  • Un programa es de buena calidad cuando se hace lo que se supone que debe hacer.
  • Un programa es seguro cuando hace lo que se supone que debe hacer, y nada más.
Cuestiones relacionadas