2010-02-25 16 views
29

Obtuve una tarea que me pedía invocar una función sin llamarla explícitamente, usando el desbordamiento del búfer. El código es básicamente esto:¿Cómo puedo invocar el desbordamiento del búfer?

#include <stdio.h> 
#include <stdlib.h> 

void g() 
{ 
    printf("now inside g()!\n"); 
} 


void f() 
{ 
    printf("now inside f()!\n"); 
    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 
} 

int main (int argc, char *argv[]) 
{ 
    f(); 
    return 0; 
} 

Aunque no estoy seguro de cómo proceder. Pensé en cambiar la dirección de retorno del contador del programa para que proceda directamente a la dirección de g(), pero no estoy seguro de cómo acceder a ella. De todos modos, los consejos serán geniales.

+9

4 upvotes para una pregunta de tarea! El OP ni siquiera se le ocurrió la pregunta ... wow, algunas personas se impresionan fácilmente. – Lazarus

+0

@Lazarus, He subido tu comentario. ¡UH oh!:-) –

+15

@Lazarus el hecho de que sea una pregunta de tareas no tiene nada que ver con el hecho de que me parece interesante. También lo voté porque quiero alentar preguntas interesantes sobre tareas en lugar de la simple "Cerré el búfer de archivos y ahora, cuando intento leer el archivo, no funciona. ¿Por qué?" (En otras palabras, renuncié a las preguntas que no sé la respuesta, pero quiero) – Yacoby

Respuesta

14

La idea básica consiste en alterar la dirección de retorno de la función de modo que cuando la función devuelve es continúa para ejecutar en una nueva dirección hackeado. Como lo hizo Nils en una de las respuestas, puede declarar una porción de memoria (normalmente una matriz) y desbordarla de tal forma que la dirección de retorno también se sobrescriba.

Le sugiero que no tome a ciegas ninguno de los programas que se ofrecen aquí sin realmente entender cómo funcionan. Este artículo está muy bien escrito y lo encontrará muy útil:

A step-by-step on the buffer overflow vulnerablity

3

Prueba con esto:

void f() 
{ 
    void *x[1]; 
    printf("now inside f()!\n"); 
    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 
    x[-1]=&g; 
} 

o esta otra:

void f() 
{ 
    void *x[1]; 
    printf("now inside f()!\n"); 
    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 
    x[1]=&g; 
} 
+3

Una explicación sería genial, porque es una tarea. –

+2

x es una variable local, por lo que se encuentra en la pila. Como x es una matriz de tamaño 1, solo x [0] es válido. Al escribir la dirección de g en x [-1] o x [1], existe la posibilidad de que sobrescribamos la dirección de retorno. Depende de la organización de la pila que funcione la versión. –

11

Eso depende del compilador, por lo que hay una única respuesta se puede dar.

El siguiente código hará lo que quieras para gcc 4.4.1. Compilar con optimizaciones desactivadas

#include <stdio.h> 
#include <stdlib.h> 

void g() 
{ 
    printf("now inside g()!\n"); 
} 


void f() 
{ 
    int i; 
    void * buffer[1]; 
    printf("now inside f()!\n"); 

    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 

    // place the address of g all over the stack: 
    for (i=0; i<10; i++) 
    buffer[i] = (void*) g; 

    // and goodbye.. 
} 

int main (int argc, char *argv[]) 
{ 
    f(); 
    return 0; 
} 

salida (¡importante!):

[email protected]:~$ gcc overflow.c 
[email protected]:~$ ./a.out 
now inside f()! 
now inside g()! 
now inside g()! 
now inside g()! 
now inside g()! 
now inside g()! 
now inside g()! 
Segmentation fault 
+0

Estoy usando gcc 4.4.1, y no estoy seguro de cómo desactivar la optimización: intenté gcc -O0 -o buff buff.c (eso es oh-cero) y también gcc -O1 -fno-defer-pop - fno-thread-jumps -fno-branch-probabilities -fno-cprop-registers -fno-guess-branch-probability -fno-omit-frame-pointer -o buff buff.c Ninguno funcionó. – sa125

+1

Haga que la aplicación salga dentro de la función 'g()' para evitar la falla de segmentación =) – Kieveli

+0

sa125, tal vez gcc intente optimizar a una arquitectura de CPU diferente. Por lo que yo sé, su valor predeterminado es la CPU del sistema que está ejecutando. Eso puede cambiar la apariencia del marco de pila de f() y evitar que ocurra el desbordamiento. –

7

Dado que esta es la tarea, me gustaría hacer eco codeaddict's suggestion de entender cómo un desbordamiento de búfer en realidad funciona.

Aprendí la técnica leyendo el artículo/tutorial excelente (aunque un poco anticuado) sobre la explotación de las vulnerabilidades de desbordamiento del búfer Smashing The Stack For Fun And Profit.

+2

+1 para vincular a ese artículo. –

3

Si bien esta solución no utiliza una técnica de desbordamiento para sobreescribir la dirección de retorno de la función en la pila, todavía provoca g() a ser llamado de f() en su camino de regreso a main() sólo modificando f() y no llamar directamente g().

Function epilogue -como ensamblado en línea se añade a f() para modificar el valor de la dirección de retorno en la pila de modo que f() volverá a través de g().

#include <stdio.h> 

void g() 
{ 
    printf("now inside g()!\n"); 
} 

void f() 
{ 
    printf("now inside f()!\n"); 
    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 

    /* x86 function epilogue-like inline assembly */ 
    /* Causes f() to return to g() on its way back to main() */ 
    asm(
     "mov %%ebp,%%esp;" 
     "pop %%ebp;" 
     "push %0;" 
     "ret" 
     : /* no output registers */ 
     : "r" (&g) 
     : "%ebp", "%esp" 
     ); 
} 

int main (int argc, char *argv[]) 
{ 
    f(); 
    return 0; 
}

La comprensión de cómo funciona este código puede conducir a una mejor comprensión de cómo el marco de pila de una función es la configuración para una arquitectura particular que constituye la base de las técnicas de desbordamiento de búfer.

Cuestiones relacionadas