Débordement hors par un
Reading time: 6 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépôts github.
Informations de base
Avoir juste accès à un débordement de 1B permet à un attaquant de modifier le champ size
du prochain chunk. Cela permet de manipuler quels chunks sont réellement libérés, générant potentiellement un chunk qui contient un autre chunk légitime. L'exploitation est similaire à double free ou des chunks qui se chevauchent.
Il existe 2 types de vulnérabilités hors par un :
- Octet arbitraire : Ce type permet d'écraser cet octet avec n'importe quelle valeur
- Octet nul (hors par nul) : Ce type permet d'écraser cet octet uniquement avec 0x00
- Un exemple courant de cette vulnérabilité peut être vu dans le code suivant où le comportement de
strlen
etstrcpy
est incohérent, ce qui permet de définir un octet 0x00 au début du prochain chunk. - Cela peut être exploité avec la Maison d'Einherjar.
- Si Tcache est utilisé, cela peut être exploité dans une situation de double free.
Hors par nul
// From https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/off_by_one/
int main(void)
{
char buffer[40]="";
void *chunk1;
chunk1 = malloc(24);
puts("Get Input");
gets(buffer);
if(strlen(buffer)==24)
{
strcpy(chunk1,buffer);
}
return 0;
}
Parmi d'autres vérifications, maintenant chaque fois qu'un morceau est libéré, la taille précédente est comparée à la taille configurée dans le morceau des métadonnées, rendant cette attaque assez complexe à partir de la version 2.28.
Exemple de code :
- https://github.com/DhavalKapil/heap-exploitation/blob/d778318b6a14edad18b20421f5a06fa1a6e6920e/assets/files/shrinking_free_chunks.c
- Cette attaque ne fonctionne plus en raison de l'utilisation de Tcaches.
- De plus, si vous essayez d'en abuser en utilisant des morceaux plus grands (donc les tcaches ne sont pas impliqués), vous obtiendrez l'erreur :
malloc(): invalid next size (unsorted)
Objectif
- Faire en sorte qu'un morceau soit contenu dans un autre morceau afin qu'un accès en écriture sur ce deuxième morceau permette d'écraser le contenu.
Exigences
- Débordement off by one pour modifier les informations de taille des métadonnées.
Attaque générale off-by-one
- Allouer trois morceaux
A
,B
etC
(disons de taille 0x20), et un autre pour empêcher la consolidation avec le top-chunk. - Libérer
C
(inséré dans la liste libre Tcache de 0x20). - Utiliser le morceau
A
pour déborder surB
. Abuser du débordement off-by-one pour modifier le champsize
deB
de 0x21 à 0x41. - Maintenant, nous avons
B
contenant le morceau libreC
. - Libérer
B
et allouer un morceau de 0x40 (il sera placé ici à nouveau). - Nous pouvons modifier le pointeur
fd
deC
, qui est toujours libre (empoisonnement Tcache).
Attaque off-by-null
- 3 morceaux de mémoire (a, b, c) sont réservés l'un après l'autre. Ensuite, le morceau du milieu est libéré. Le premier contient une vulnérabilité de débordement off by one et l'attaquant en abuse avec un 0x00 (si le byte précédent était 0x10, cela ferait indiquer au morceau du milieu qu'il est 0x10 plus petit qu'il ne l'est réellement).
- Ensuite, 2 morceaux plus petits sont alloués dans le morceau libéré du milieu (b), cependant, comme
b + b->size
ne met jamais à jour le morceau c parce que l'adresse pointée est plus petite qu'elle ne devrait l'être. - Ensuite, b1 et c sont libérés. Comme
c - c->prev_size
pointe toujours vers b (b1 maintenant), les deux sont consolidés en un seul morceau. Cependant, b2 est toujours à l'intérieur entre b1 et c. - Enfin, un nouveau malloc est effectué pour récupérer cette zone mémoire qui va en fait contenir b2, permettant au propriétaire du nouveau malloc de contrôler le contenu de b2.
Cette image explique parfaitement l'attaque :
.png)
https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks
Autres exemples et références
- https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks
- Bon-nie-appetit. HTB Cyber Apocalypse CTF 2022
- Off-by-one à cause de
strlen
considérant le champsize
du morceau suivant. - Tcache est utilisé, donc une attaque générale off-by-one fonctionne pour obtenir une primitive d'écriture arbitraire avec empoisonnement Tcache.
- Asis CTF 2016 b00ks
- Il est possible d'abuser d'un off by one pour divulguer une adresse du tas parce que le byte 0x00 à la fin d'une chaîne est écrasé par le champ suivant.
- L'écriture arbitraire est obtenue en abusant de l'écriture off by one pour faire pointer le pointeur vers un autre endroit où une structure factice avec de faux pointeurs sera construite. Ensuite, il est possible de suivre le pointeur de cette structure pour obtenir une écriture arbitraire.
- L'adresse libc est divulguée car si le tas est étendu en utilisant mmap, la mémoire allouée par mmap a un décalage fixe par rapport à libc.
- Enfin, l'écriture arbitraire est abusée pour écrire à l'adresse de __free_hook avec un one gadget.
- plaidctf 2015 plaiddb
- Il existe une vulnérabilité NULL off by one dans la fonction
getline
qui lit les lignes d'entrée utilisateur. Cette fonction est utilisée pour lire la "clé" du contenu et non le contenu. - Dans le rapport, 5 morceaux initiaux sont créés :
- chunk1 (0x200)
- chunk2 (0x50)
- chunk5 (0x68)
- chunk3 (0x1f8)
- chunk4 (0xf0)
- chunk defense (0x400) pour éviter la consolidation avec le top chunk
- Ensuite, les morceaux 1, 5 et 3 sont libérés, donc :
-
[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]
- Ensuite, en abusant de chunk3 (0x1f8), le null off-by-one est abusé en écrivant le prev_size à `0x4e0`.
- Notez comment les tailles des morceaux initialement alloués 1, 2, 5 et 3 plus les en-têtes de 4 de ces morceaux équivalent à `0x4e0` : `hex(0x1f8 + 0x10 + 0x68 + 0x10 + 0x50 + 0x10 + 0x200) = 0x4e0`
- Ensuite, le morceau 4 est libéré, générant un morceau qui consomme tous les morceaux jusqu'au début :
- ```python
[ 0x4e0 Chunk 1-2-5-3 (free) ] [ 0xf0 Chunk 4 (corrupted) ] [ 0x400 Chunk defense ]
[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]