Skip to main content

Pasty – Nullcon CTF 2026

nullcon CTF 2026

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

  1. Frontend: Crear paste en / (POST a create.php) → el servidor devuelve una URL de la forma http://.../view.php?id=<HEX_ID>&sig=<HEX_SIG>.
  2. Verificación: Para ver un paste se requieren id y sig; el servidor comprueba la firma antes de mostrar el contenido.
  3. 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:

  1. h = SHA256(d) en binario → 32 bytes.
  2. m = primeros 24 bytes de SHA256(k) → 3 bloques de 8 bytes: m0, m1, m2.
  3. Para cada bloque i = 0..3: b_i = h[8*i : 8*i+8], p = (h[8*i] % 3) * 8 → se elige c_i = uno de m0, m1, m2. Salida: o_0 = b_0 XOR c_0; para i > 0: o_i = (b_i XOR c_i) XOR o_{i-1} (encadenamiento tipo CBC con XOR).
  4. 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:

  1. Recolección de pares (id, sig): POST a create.php; en el header Location (o parámetro url) vienen id y sig en hex.
  2. Recuperación de m0, m1, m2: Para cada (id, sig), calcular h = SHA256(id), partir sig en 4 bloques de 8 bytes, calcular cada c_i y asignar m_blocks[h[8*i] % 3] = c_i.
  3. Falsificación de firma para "flag": Calcular h = SHA256("flag") y aplicar el mismo bucle de compute_sig con los m_blocks recuperados; obtener la firma en hex.
  4. 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.