BROP - Blind Return Oriented Programming
Reading time: 7 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
L'objectif de cette attaque est de pouvoir abuser d'un ROP via un débordement de tampon sans aucune information sur le binaire vulnérable.
Cette attaque est basée sur le scénario suivant :
- Une vulnérabilité de pile et la connaissance de la façon de la déclencher.
- Une application serveur qui redémarre après un crash.
Attaque
1. Trouver l'offset vulnérable en envoyant un caractère de plus jusqu'à ce qu'un dysfonctionnement du serveur soit détecté
2. Brute-force canary pour le divulguer
3. Brute-force des adresses RBP et RIP stockées dans la pile pour les divulguer
Vous pouvez trouver plus d'informations sur ces processus ici (BF Forked & Threaded Stack Canaries) et ici (BF Addresses in the Stack).
4. Trouver le gadget d'arrêt
Ce gadget permet essentiellement de confirmer que quelque chose d'intéressant a été exécuté par le gadget ROP car l'exécution n'a pas planté. En général, ce gadget va être quelque chose qui arrête l'exécution et il est positionné à la fin de la chaîne ROP lors de la recherche de gadgets ROP pour confirmer qu'un gadget ROP spécifique a été exécuté.
5. Trouver le gadget BROP
Cette technique utilise le gadget ret2csu. Et cela est dû au fait que si vous accédez à ce gadget au milieu de certaines instructions, vous obtenez des gadgets pour contrôler rsi
et rdi
:
 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png)
Ce seraient les gadgets :
pop rsi; pop r15; ret
pop rdi; ret
Remarquez comment avec ces gadgets, il est possible de contrôler 2 arguments d'une fonction à appeler.
De plus, notez que le gadget ret2csu a une signature très unique car il va poper 6 registres de la pile. Donc, en envoyant une chaîne comme :
'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP
Si le STOP est exécuté, cela signifie essentiellement qu'une adresse qui pop 6 registres de la pile a été utilisée. Ou que l'adresse utilisée était également une adresse STOP.
Pour éliminer cette dernière option, une nouvelle chaîne comme suit est exécutée et elle ne doit pas exécuter le gadget STOP pour confirmer que le précédent a bien popé 6 registres :
'A' * offset + canary + rbp + ADDR
Sachant l'adresse du gadget ret2csu, il est possible d'inférer l'adresse des gadgets pour contrôler rsi
et rdi
.
6. Trouver le PLT
La table PLT peut être recherchée à partir de 0x400000 ou de l'adresse RIP divulguée de la pile (si PIE est utilisé). Les entrées de la table sont séparées par 16B (0x10B), et lorsqu'une fonction est appelée, le serveur ne plante pas même si les arguments ne sont pas corrects. De plus, vérifier l'adresse d'une entrée dans le PLT + 6B ne plante également pas car c'est le premier code exécuté.
Par conséquent, il est possible de trouver la table PLT en vérifiant les comportements suivants :
'A' * offset + canary + rbp + ADDR + STOP
-> pas de crash'A' * offset + canary + rbp + (ADDR + 0x6) + STOP
-> pas de crash'A' * offset + canary + rbp + (ADDR + 0x10) + STOP
-> pas de crash
7. Trouver strcmp
La fonction strcmp
définit le registre rdx
à la longueur de la chaîne comparée. Notez que rdx
est le troisième argument et nous avons besoin qu'il soit plus grand que 0 afin de pouvoir utiliser write
pour divulguer le programme par la suite.
Il est possible de trouver l'emplacement de strcmp
dans le PLT en fonction de son comportement en utilisant le fait que nous pouvons maintenant contrôler les 2 premiers arguments des fonctions :
- strcmp(<adresse non lue>, <adresse non lue>) -> crash
- strcmp(<adresse non lue>, <adresse lue>) -> crash
- strcmp(<adresse lue>, <adresse non lue>) -> crash
- strcmp(<adresse lue>, <adresse lue>) -> pas de crash
Il est possible de vérifier cela en appelant chaque entrée de la table PLT ou en utilisant le chemin lent du PLT qui consiste essentiellement à appeler une entrée dans la table PLT + 0xb (ce qui appelle dlresolve
) suivi dans la pile par le numéro d'entrée que l'on souhaite sonder (commençant à zéro) pour scanner toutes les entrées PLT à partir de la première :
- strcmp(<adresse non lue>, <adresse lue>) -> crash
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
-> Va planter- strcmp(<adresse lue>, <adresse non lue>) -> crash
b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
- strcmp(<adresse lue>, <adresse lue>) -> pas de crash
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
Rappelez-vous que :
- BROP + 0x7 pointe vers
pop RSI; pop R15; ret;
- BROP + 0x9 pointe vers
pop RDI; ret;
- PLT + 0xb pointe vers un appel à dl_resolve.
Ayant trouvé strcmp
, il est possible de définir rdx
à une valeur supérieure à 0.
tip
Notez qu'en général, rdx
contiendra déjà une valeur supérieure à 0, donc cette étape pourrait ne pas être nécessaire.
8. Trouver Write ou équivalent
Enfin, il faut un gadget qui exfiltre des données afin d'exfiltrer le binaire. Et à ce moment, il est possible de contrôler 2 arguments et de définir rdx
supérieur à 0.
Il existe 3 fonctions courantes qui pourraient être abusées pour cela :
puts(data)
dprintf(fd, data)
write(fd, data, len(data)
Cependant, le document original ne mentionne que la fonction write
, alors parlons-en :
Le problème actuel est que nous ne savons pas où se trouve la fonction write dans le PLT et nous ne savons pas un numéro de fd pour envoyer les données à notre socket.
Cependant, nous savons où se trouve la table PLT et il est possible de trouver write en fonction de son comportement. Et nous pouvons créer plusieurs connexions avec le serveur et utiliser un FD élevé en espérant qu'il corresponde à certaines de nos connexions.
Signatures de comportement pour trouver ces fonctions :
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> S'il y a des données imprimées, alors puts a été trouvé'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> S'il y a des données imprimées, alors dprintf a été trouvé'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> S'il y a des données imprimées, alors write a été trouvé
Exploitation automatique
Références
- Document original : https://www.scs.stanford.edu/brop/bittau-brop.pdf
- https://www.ctfrecipes.com/pwn/stack-exploitation/arbitrary-code-execution/code-reuse-attack/blind-return-oriented-programming-brop
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.