ROP - Return Oriented Programing
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 toute opération de nettoyage ou supplémentaire nécessaire.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.
Chaîne ROP dans un Exemple 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 nettoie la pile.
Recherche de Gadgets
Tout d'abord, supposons que nous avons 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.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 (optionnel)Adresse de la fonction
system
(en supposant que 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)
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') sur x64 :
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 (c'est-à-dire si 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
Étant donné que 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).
Protections
Autres exemples et références
Techniques basées sur ROP
Remarquez que ROP est juste une technique pour exécuter du code arbitraire. Basé sur ROP, de nombreuses techniques Ret2XXX ont été développées :
Ret2lib : Utilise 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')
.
Ret2Syscall : Utilise ROP pour préparer un appel à un syscall, par exemple
execve
, et lui faire exécuter des commandes arbitraires.
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).
Last updated