VaultBy ENSAMIENParrainage
Notebook

TP N° 3 : Analyse de la vulnérabilité Padding Oracle (CBC)

tp3-padding-oracle-cbc.ipynb

Nom : CHOUBIK

Prénom : HOUSSAM

Filière : CS2C-1

!pip install pycryptodome
Requirement already satisfied: pycryptodome in c:\users\houssamclap\anaconda3\lib\site-packages (3.23.0)
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad , unpad
# Configuration du serveur ( Ne pas modifier)
KEY = b"TP_CRYPT0_2026_#"
IV = b"INITIAL_VECTOR_1"
def oracle (ciphertext):
    try:
        iv_received = ciphertext [:16]
        data_received = ciphertext [16:]
        cipher = AES. new ( KEY , AES. MODE_CBC , iv= iv_received    )
        # unpad leve une Value Error si le padding est invalide 
        unpad ( cipher. decrypt( data_received ),16)
        return True
    except ( ValueError ,KeyError): return False
intercepted_ct = IV + b"\x0c\x82\x05\xed\x03\x13@\x92\xc7\x01\xe3\x02\x90\xec\x92\x04"
def padding_oracle_attack(ct):
    # Séparer le texte chiffré en blocs de 16 octets
    blocks = [ct[i:i+16] for i in range(0, len(ct), 16)]
    decrypted_message = b""

    # On itère sur chaque bloc de données (en ignorant l'IV au début pour C_i) 
    for b_idx in range(1, len(blocks)):
        prev_block = list(blocks[b_idx-1]) # C_{i-1} 
        curr_block = blocks[b_idx]         # C_i 
        
        decrypted_block = [0] * 16
        intermediate_state = [0] * 16 # D_k(Ci)

        # On remonte de l'octet 15 à 0 [cite: 55]
        for byte_idx in range(15, -1, -1):
            target_padding = 16 - byte_idx # Valeur de padding cible (ex: 0x01, 0x02...)
            
            # Préparer le bloc de test basé sur les octets déjà trouvés
            test_prev_block = prev_block[:]
            for k in range(byte_idx + 1, 16):
                test_prev_block[k] = intermediate_state[k] ^ target_padding

            # Itérer les 256 valeurs possibles pour l'octet n 
            found = False
            for val in range(256):
                test_prev_block[byte_idx] = val
                payload = bytes(test_prev_block) + curr_block
                
                if oracle(payload):
                    if byte_idx == 15:
                        check = bytearray(payload)
                        check[14] ^= 1
                        if not oracle(bytes(check)):
                            continue
                    # On a trouvé une valeur qui donne un padding valide ! 
                    found = True
                    # Calcul de l'état intermédiaire : D_k(Ci) = C_test ^ Padding
                    intermediate_state[byte_idx] = val ^ target_padding
                    # Déduction du texte clair [cite: 59, 60]
                    # P[n] = C_orig[n] ^ C_test[n] ^ Padding
                    decrypted_block[byte_idx] = prev_block[byte_idx] ^ val ^ target_padding
                    break
            
            if not found:
                print(f"Erreur : Aucune valeur trouvée pour l'octet {byte_idx}")

        decrypted_message += bytes(decrypted_block)
        print(f"Bloc {b_idx} déchiffré !")

    return decrypted_message

# Lancement de l'attaque
plaintext = padding_oracle_attack(intercepted_ct)
print(f"Texte clair retrouvé : {plaintext}")
Bloc 1 déchiffré !
Texte clair retrouvé : b'\x0e\xdc\xc6\x8f\xb5\x18\x9c\r\xec\x80x\xbf\xf4Tw\x07'

Remarque : le bloc donné dans l'énoncé ne se déchiffre pas avec un padding valide sous cette clé, donc le résultat ci-dessus n'est pas lisible. La cellule suivante vérifie l'attaque sur un message chiffré nous-mêmes avec la clé du serveur.

secret = AES.new(KEY, AES.MODE_CBC, iv=IV).encrypt(pad(b"Bravo CS2C !", 16))
retrouve = padding_oracle_attack(IV + secret)
print(unpad(retrouve, 16))
Bloc 1 déchiffré !
b'Bravo CS2C !'

Questions de réflexion 1. Pourquoi l’attaquant n’a-t-il pas besoin de connaître la KEY pour réussir ?

L'attaque exploite l'oracle pour retrouver l'état intermédiaire (le bloc juste après le déchiffrement AES mais avant le XOR avec le bloc précédent). Puisque l'on contrôle totalement le bloc précédent , on peut déduire le texte clair par simple opération XOR, sans jamais avoir à manipuler la clé secrète.

  1. Si le message comporte 3 blocs (IV + 2 blocs de données), comment adapter l’attaque ?
  • On commence par déchiffrer le dernier bloc ($C_2$) en utilisant le bloc $C_1$ comme vecteur de manipulation.
  • Une fois $C_2$ fini, on déchiffre $C_1$ en utilisant l'IV comme vecteur de manipulation.
  1. Quelle modification simple côté serveur permettrait de rendre cette attaque impossible tout en conservant le mode CBC ?
  • Uniformisation des erreurs : Le serveur doit renvoyer la même erreur.
  • Utiliser l'Encrypt-then-MAC : Calculer un code d'authentification sur le message chiffré et le vérifier avant toute tentative de déchiffrement.
  1. Pourquoi le mode AES-GCM n’est-il pas sensible à cette attaque ?

AES-GCM est un mode AEAD (Authenticated Encryption with Associated Data). Contrairement au CBC standard, il intègre une vérification d'intégrité. Si un seul bit du texte chiffré est modifié par l'attaquant(ce qui est nécessaire pour l'attaque par oracle), la vérification de l'étiquette d'authentification (Tag) échouera immédiatement, et le serveur rejettera le message avant même d'avoir testé le padding.

    Type at least 2 letters. ↑ ↓ to move, Enter to open.