Datos del reto
| Campo | Valor |
|---|---|
| CTF | Nullcon CTF 2026 |
| Reto | Pasty |
| Categoría | Web / Crypto |
| Flag | ENO{cr3at1v3_cr7pt0_c0nstruct5_cr4sh_c4rd5} |
Descripción del reto
Servicio de Pastebin "seguro" con firmas criptográficas propias. El objetivo es acceder al paste con id flag.
Reconocimiento
- Frontend: Crear paste en
/(POST acreate.php) → el servidor devuelve una URL de la formahttp://.../view.php?id=<HEX_ID>&sig=<HEX_SIG>. - Verificación: Para ver un paste se requieren
idysig; el servidor comprueba la firma antes de mostrar el contenido. - Archivo
sig.php: Contiene la lógica de firma "custom" que debemos analizar.
Análisis de la vulnerabilidad
Esquema de firma en sig.php:
- Entrada:
$d= dato a firmar (el id del paste, p. ej."flag"),$k= clave secreta. - Salida: 32 bytes (firma en bruto; en la URL se muestra en hex).
Algoritmo resumido:
h = SHA256(d)en binario → 32 bytes.m = primeros 24 bytes de SHA256(k)→ 3 bloques de 8 bytes: m0, m1, m2.- Para cada bloque
i = 0..3:b_i = h[8*i : 8*i+8],p = (h[8*i] % 3) * 8→ se eligec_i= uno de m0, m1, m2. Salida:o_0 = b_0 XOR c_0; parai > 0:o_i = (b_i XOR c_i) XOR o_{i-1}(encadenamiento tipo CBC con XOR). - Firma final:
sig = o_0 || o_1 || o_2 || o_3(32 bytes).
La vulnerabilidad: el material de clave efectivo son solo 24 bytes (3×8) y cada bloque de la firma revela uno de esos subbloques cuando tenemos el par (d, sig) y podemos calcular h = SHA256(d).
Explotación
Idea del ataque: Para cada par (id, sig) al crear pastes, conocemos id y h = SHA256(id), y la firma nos da o_0..o_3. Despejando: c_0 = b_0 XOR o_0; c_i = b_i XOR o_i XOR o_{i-1}. El índice del subbloque usado en el bloque i es h[8*i] % 3, así que vamos rellenando m0, m1, m2. Creando bastantes pastes, estadísticamente recuperamos m0, m1 y m2 completos y podemos calcular la firma para cualquier d, en particular d = "flag".
Implementación:
- Recolección de pares (id, sig): POST a
create.php; en el headerLocation(o parámetrourl) vienenidysigen hex. - Recuperación de m0, m1, m2: Para cada
(id, sig), calcularh = SHA256(id), partirsigen 4 bloques de 8 bytes, calcular cadac_iy asignarm_blocks[h[8*i] % 3] = c_i. - Falsificación de firma para
"flag": Calcularh = SHA256("flag")y aplicar el mismo bucle decompute_sigcon losm_blocksrecuperados; obtener la firma en hex. - Petición:
GET view.php?id=flag&sig=<firma_calculada>.
El script exploit.py automatiza estos pasos; con ~80 pastes suele ser suficiente para recuperar los tres bloques y obtener la flag.
Flag
ENO{cr3at1v3_cr7pt0_c0nstruct5_cr4sh_c4rd5}
Lecciones aprendidas
Los diseños criptográficos propios son riesgosos: aunque se use SHA256, un esquema que deriva solo 24 bytes de clave y los reutiliza en pocos bloques, y expone la salida por bloques en una estructura lineal (XOR + encadenamiento), permite recuperar todo el material de clave efectivo a partir de mensajes firmados legítimos y luego firmar cualquier mensaje. Usar primitivas estándar (HMAC, Ed25519, etc.) en lugar de construcciones ad hoc.