Ataque de texto plano elegido sobre AES-ECB
El modo Electronic Codebook (ECB) es un modo de cifrado por bloques que cifra cada bloque de texto plano de forma independiente con la misma clave. Esta independencia entre bloques elimina…
Definición
El modo Electronic Codebook (ECB) es un modo de cifrado por bloques que cifra cada bloque de texto plano de forma independiente con la misma clave. Esta independencia entre bloques elimina la difusión necesaria para cifrado seguro: bloques de plaintext idénticos producen bloques de ciphertext idénticos, dejando patrones en los datos cifrados y permitiendo un ataque de texto plano elegido (Chosen-Plaintext Attack, CPA) para recuperar el texto oculto byte a byte.
Contexto
Principios del cifrado simétrico seguro
AES (Advanced Encryption Standard, Rijndael) es matemáticamente robusto, pero requiere un modo de operación correcto para ser seguro en la práctica. Los tres principios que debe cumplir cualquier cifrado:
- Confusión: oscurecer la relación entre plaintext y ciphertext.
- Difusión: distribuir los bits del plaintext a lo largo de todo el ciphertext.
- Dependencia solo de la clave: la seguridad no puede depender de que el algoritmo sea secreto.
Por qué ECB viola estos principios
ECB cifra cada bloque de 16 bytes por separado. Si dos bloques de plaintext son iguales, sus correspondientes bloques de ciphertext son idénticos. Esto viola el principio de difusión: los patrones en los datos originales se conservan en el ciphertext.
El ECB Penguin: el caso más famoso de esta vulnerabilidad es cifrar la imagen del pingüino Tux de Linux con ECB. Aunque los datos están “cifrados”, la imagen sigue siendo reconocible porque los pixels de cada bloque repiten patrones que se mapean al mismo ciphertext.
# Solo en laboratorio — Demostración del ECB Penguinhead -c 54 test.bmp > test.header # Preservar cabeceratail -c +54 test.bmp > test.body # Separar datosopenssl enc -aes-128-ecb -nosalt \ -pass pass:"superpassword" \ -in test.body -out test.body.ecb # Cifrar con ECBcat test.header test.body.ecb > test.ecb.bmp # Reconstruir# Abrir test.ecb.bmp: ¡la imagen sigue siendo visible!Cómo funciona el cifrado ECB
Dado el mensaje CryptographyAndAESIsALotOfFun!!! (32 bytes, 2 bloques):
- Bloque 1:
CryptographyAndA→ Cifrado:92 FF C7 FE CD 0D 04 13 E8 B2 63 6D F4 38 BF 2A - Bloque 2:
ESIsALotOfFun!!!→ Cifrado:FE 7C 80 45 6D 0A 87 C7 7A 20 61 78 B9 7A 19 33
Cada bloque se cifra de forma totalmente independiente. Si el mensaje contuviera CryptographyAndA de nuevo, produciría exactamente 92 FF C7 FE CD 0D 04 13 E8 B2 63 6D F4 38 BF 2A.
Implementación en Python:
from Crypto.Cipher import AESimport binascii
key = b"secretpassword!!" # 16 bytesDEFAULT_BLOCK_SIZE = 16
def custom_pad(message): while len(message) % DEFAULT_BLOCK_SIZE != 0: message += "*" return message
plaintext_bytes = custom_pad("CryptographyAndABigSecret").encode()cipher = AES.new(key, AES.MODE_ECB)encrypted_bytes = cipher.encrypt(plaintext_bytes)encrypted_message = binascii.hexlify(encrypted_bytes).decode()Desarrollo
Ataque de texto plano elegido (CPA) contra ECB oracles
Un ECB oracle es cualquier endpoint que acepta input del usuario y lo incluye en el mensaje que cifra, devolviendo el ciphertext. Si la función es:
token = ECB("TryHackMe" + username + "TryHackMe")El atacante puede recuperar “TryHackMe” byte a byte sin conocer la clave.
Paso 1 — Determinar el tamaño de bloque
Enviar usernames de longitud creciente hasta que el ciphertext aumente de tamaño. El primer aumento ocurre al completar un bloque. El segundo aumento revela el tamaño de bloque:
def calculate_block_size(): username = "A" original_length = len(chat_to_oracle(username)) first_change_len = 1 while len(chat_to_oracle(username)) == original_length: username += "A" first_change_len += 1 new_length = len(chat_to_oracle(username)) second_change_len = first_change_len while len(chat_to_oracle(username)) == new_length: username += "A" second_change_len += 1 BLOCK_SIZE = second_change_len - first_change_len return BLOCK_SIZESi el primer crecimiento ocurre en username de longitud 5 y el segundo en longitud 21: BLOCK_SIZE = 21 - 5 = 16.
Paso 2 — Calcular el offset
El offset es el número de bytes que preceden al input del atacante en el primer bloque. Se determina enviando 2 × BLOCK_SIZE bytes de un carácter conocido (“A”) e incrementando con un carácter diferente (“B”) hasta que dos bloques del ciphertext sean idénticos. El número de “B” añadidos es el offset.
def calculate_offset(block_size): initial_text = "A" * block_size * 2 ciphertext = chat_to_oracle(initial_text) chunks = split_ciphertext(ciphertext, block_size) if len(chunks) != len(set(chunks)): return 0 # Sin offset offset = 0 while len(chunks) == len(set(chunks)): offset += 1 initial_text = "B" + initial_text ciphertext = chat_to_oracle(initial_text) chunks = split_ciphertext(ciphertext, block_size) return offsetPaso 3 — Extraer byte a byte
Con BLOCK_SIZE = 16 y offset = 4:
LengthToSend = BLOCK_SIZE + offset - 1 byte = 16 + 4 - 1 = 19 bytesSe envían 4 “B” (para el offset) + 15 “A” (para llenar el bloque menos un byte). El último byte del bloque controlado proviene del texto secreto. Este bloque es el chunk de referencia.
Luego se fuerza bruta el byte 0x00–0xFF: se envían los 19 bytes más cada candidato y se compara el chunk con el de referencia. La coincidencia revela el byte secreto.
def brute_forcer(reference_chunk, initial_text, block_size, offset): for byte_value in range(256): test_input = initial_text + chr(byte_value) ciphertext = chat_to_oracle(test_input) chunks = split_ciphertext(ciphertext, block_size) if chunks[1] == reference_chunk: return chr(byte_value) return NoneSe repite el proceso para todos los bytes, incrementando el conocimiento acumulado como parte del input de referencia para los bytes siguientes.
Complejidad del ataque
- Por byte: máximo 256 peticiones al oracle.
- Por bloque de 16 bytes: máximo 4.096 peticiones.
- Ataque online lento pero sistemático: no requiere la clave ni conocimiento previo del texto oculto.
Ejemplos
Detección de ECB en la práctica:
- Si el ciphertext contiene bloques repetidos con input repetido, es ECB.
- En imágenes: patrones visibles en el ciphertext indican ECB (ECB Penguin).
- Si al cambiar bloques del ciphertext, el descifrado no falla en otros bloques, es ECB (no hay encadenamiento).
Escenario real (2022): Microsoft Office 365 usó ECB en su sistema de cifrado de mensajes hasta que fue reportado como inseguro. Los patrones en mensajes cifrados eran recuperables.
Pitfalls
- “AES es seguro, así que mi implementación también”: la seguridad de AES como primitiva no garantiza la seguridad del modo de operación. ECB es un modo inseguro independientemente de la fortaleza de AES.
- “No devuelvo el ciphertext al usuario, no hay oracle”: si el servidor tiene cualquier comportamiento diferente (respuesta, tamaño, tiempo) según el ciphertext, puede usarse como oracle indirecto.
- “Cifrar bloques independientemente es más eficiente”: la eficiencia no justifica la inseguridad. AES-GCM ofrece autenticación + cifrado con overhead mínimo.
- “El relleno con
*es suficiente”: el padding personalizado simplifica el lab pero en producción se debe usar PKCS#7 correctamente. El padding incorrecto puede introducir vulnerabilidades adicionales (ver Padding Oracle Attack). - “Solo es un problema con imágenes”: ECB es inseguro para cualquier dato con patrones repetidos: tokens de sesión, cookies, datos de formulario, etc.
Diagrama
flowchart TD
A[Atacante envía input controlado] --> B[Oracle: ECB-username-secreto]
B --> C[Ciphertext devuelto]
C --> D{¿Determinar block size?}
D -->|Incrementar username| D
D -->|2 crecimientos detectados| E[Block size = diff entre 1º y 2º crecimiento]
E --> F{¿Calcular offset?}
F -->|Añadir Bs hasta bloques idénticos| G[Offset = número de Bs añadidos]
G --> H[Byte-by-byte brute force]
H --> I[Enviar BLOCK_SIZE + offset - 1 bytes]
I --> J[Capturar chunk de referencia]
J --> K[Iterar 0x00–0xFF]
K --> L{¿Coincide con referencia?}
L -->|No| K
L -->|Sí| M[Byte secreto recuperado]
M --> N{¿Más bytes?}
N -->|Sí| H
N -->|No| O[Plaintext completo recuperado]
Referencias
- TryHackMe — AES ECB: https://tryhackme.com/room/aesecb
- ECB Penguin (Robert Graham): https://github.com/robertdavidgraham/ecb-penguin
- Microsoft Office 365 ECB vulnerability (2022): https://www.theregister.com/2022/10/14/microsoft_office_365_message_encryption/
- NIST SP 800-38A — Recommendation for Block Cipher Modes: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf
- OWASP A02 — Cryptographic Failures: https://owasp.org/Top10/A02_2021-Cryptographic_Failures/
Ver también: Cryptographic Failures, Padding Oracle Attack, Hash Length Extension Attack