27 votes

Comment invoquer un dépassement de tampon ?

J'ai reçu un devoir me demandant d'invoquer une fonction sans l'appeler explicitement, en utilisant le dépassement de tampon. Le code est essentiellement le suivant :

#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;
}

Bien que je ne sois pas sûr de la façon de procéder. J'ai pensé à changer l'adresse de retour du compteur de programme pour qu'il aille directement à l'adresse de g(), mais je ne sais pas comment y accéder. Quoi qu'il en soit, des conseils seraient les bienvenus.

13voto

codaddict Points 154968

L'idée de base est de modifier l'adresse de retour de la fonction de sorte que lorsque la fonction revient, elle continue à s'exécuter à une nouvelle adresse piratée. Comme l'a fait Nils dans l'une des réponses, vous pouvez déclarer une partie de la mémoire (généralement un tableau) et la déborder de telle sorte que l'adresse de retour soit également écrasée.

Je vous suggère de ne pas suivre aveuglément les programmes proposés ici sans en comprendre le fonctionnement. Cet article est très bien écrit et vous le trouverez très utile :

Une étape par étape sur la vulnérabilité de dépassement de tampon

11voto

Nils Pipenbrinck Points 41006

Cela dépend du compilateur, il n'est donc pas possible de donner une réponse unique.

Le code suivant fera ce que vous voulez pour gcc 4.4.1. Compiler avec les optimisations désactivées (important !)

#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;
}

Sortie :

nils@doofnase:~$ gcc overflow.c
nils@doofnase:~$ ./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

7voto

jschmier Points 8088

Puisqu'il s'agit d'un devoir, je voudrais faire écho à suggestion de codeaddict de comprendre comment un dépassement de tampon fonctionne réellement.

J'ai appris la technique en lisant l'excellent (bien qu'un peu daté) article/tutoriel sur l'exploitation des vulnérabilités par dépassement de tampon Démolir la pile pour le plaisir et le profit .

3voto

ammoQ Points 17866

Essaie celle-là :

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;
}

ou celui-ci :

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;
}

2voto

jschmier Points 8088

Bien que cette solution n'utilise pas de technique de débordement pour écraser l'adresse de retour de la fonction sur la pile, elle provoque tout de même g() pour être appelé depuis f() sur son chemin de retour vers main() en modifiant seulement f() et ne pas appeler g() directement.

Épilogue de la fonction -L'assemblage en ligne est ajouté à f() pour modifier la valeur de l'adresse de retour sur la pile de façon à ce que f() reviendra par 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;
}

Comprendre le fonctionnement de ce code peut permettre de mieux comprendre comment le cadre de pile d'une fonction est configuré pour une architecture particulière, ce qui constitue la base des techniques de dépassement de tampon.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X