En la entrega anterior vimos algunas de las vulnerabilidades presentadas por la aplicación Acierta las preguntas, además de posibles soluciones que se podrían haber implementado para evitar su explotación.
Nos quedó por analizar el apartado multi-jugador, así que manos a la obra.
El modo online
El juego dispone también de un modo multi-jugador donde retar a otros usuarios. Cuando accedemos capturaremos la siguiente petición/respuesta:
Como en mi caso no tenía iniciada ninguna partida, el resultado es una estructura JSON vacía. Si ahora buscamos un reto a través del modo online capturaremos el siguiente tráfico:
Aquí podemos ver el ID asociado a nuestra partida, que cuando termine generará la siguiente petición:
Una vez más esta petición puede ser fácilmente alterada de modo que podemos indicar de manera libre nuestra puntuación.
Además al carecer de cualquier tipo de control de acceso nos permite enumerar las partidas de otros jugadores, esto se puede lograr si modificamos en la petición de recuperación de partidas la URL para indicar otro usuario:
De modo que si al final sumamos todas estas ausencias de comprobaciones, a través de la pantalla de ranking se podría obtener los nombre de los mejores jugadores, iniciarles un reto online sin su consentimiento y enviarles una puntuación de 0. Esto en el sistema de juego online supondría una bajada de nivel para su perfil.
Para protegernos ante estos problemas podríamos optar por soluciones que ya comentábamos en el artículo anterior:
Como ya hemos visto a lo largo de las peticiones capturadas se hace notar la falta de una gestión correcta de la sesión y prácticamente todo el sistema se basa en enviar el nombre del usuario como parámetro, ya sea en la URL porque es una petición de tipo GET o en el cuerpo de la petición porque es de tipo POST, pero, ¿de donde sale este nombre de usuario?
La respuesta está en el formulario de login que rellenamos en la primera ejecución. Este formulario intercambió la siguiente petición/respuesta:
A partir de este intercambio de información la aplicación ya da por supuesto que somos el usuario nodoraiz, que al ser un dato persistente nos induce a pensar que se encuentra en la base de datos. Lo comprobamos:
De este comportamiento podemos deducir que lo que ocurre es que:
- Realizar una gestión correcta de la sesión. De este modo no haría falta intercambiar el usuario como un parámetro de la petición y evitaríamos la alteración de este dato.
- Delegando la responsabilidad al servidor de determinar la puntuación. Si cada acierto/error se envía al servidor, cuando haya que calcular la puntuación no será necesario confiar en una petición que ha podido ser alterada por la parte cliente del juego.
Suplantación de identidad
Como ya hemos visto a lo largo de las peticiones capturadas se hace notar la falta de una gestión correcta de la sesión y prácticamente todo el sistema se basa en enviar el nombre del usuario como parámetro, ya sea en la URL porque es una petición de tipo GET o en el cuerpo de la petición porque es de tipo POST, pero, ¿de donde sale este nombre de usuario?
La respuesta está en el formulario de login que rellenamos en la primera ejecución. Este formulario intercambió la siguiente petición/respuesta:
A partir de este intercambio de información la aplicación ya da por supuesto que somos el usuario nodoraiz, que al ser un dato persistente nos induce a pensar que se encuentra en la base de datos. Lo comprobamos:
De este comportamiento podemos deducir que lo que ocurre es que:
- Rellenamos el formulario de inicio de sesión
- Se envía el nombre de usuario y el MD5(clave)
- Se recibe una respuesta que varía entre si es un alta, si es un inicio de sesión válido (es la captura que he puesto arriba de Respuesta) o si es un inicio de sesión inválido
- Si es un alta o inicio de sesión válido, se guarda en la base de datos y a partir de entonces se trabaja con ese valor en todas las peticiones
Una vez analizado el comportamiento, sólo queda hacer una explotación como prueba de concepto.
- Desde el menú de Android nos vamos a Ajustes > Aplicaciones > Acierta las preguntas y borramos los datos. Esto lo hacemos para limpiar la base de datos del juego y así eliminar nuestro usuario.
- Ponemos a interceptar el tráfico de nuestro dispositivo Android con un Web proxy
- Iniciamos sesión en el juego con otro usuario inventándonos la clave:
- Alteramos la respuesta y cambiamos el mensaje recibido de error:
- {"resultado":[{"msg":"El usuario ya existe","code":2,"nivel":1}]}
- Por el mensaje recibido cuando todo es correcto:
- {"resultado":[{"msg":"Autentificaci\u00f3n correcta","code":1,"nivel":"1"}]}
A partir de este momento el juego enviará en todas las peticiones el nombre de usuario suplantado y el servidor se lo creerá ciegamente al no existir ningún otro mecanismo de validación.
Una vez más, para evitar este problema hubiera bastado con introducir un mecanismo de gestión de sesión y que cada vez que se abra la aplicación se envíen los credenciales al servidor para que este los valide. También y por pasarlo por alto, se debería evitar el uso de MD5 para realizar el hash de la clave y utilizar alguna función más robusta como SHA-2 con sal.
Conclusiones finales
Aunque sea a través del ejemplo de un juego, hemos visto como prácticamente toda la aplicación cae por tres motivos clave:
- Ausencia de gestión de sesión: Al arrancar la aplicación se hace una gestión incorrecta, y a partir de ese momento hay una ausencia total de control de la sesión.
- Referencias indirectas inseguras a objetos: Los parámetros y los valores enviados en ellos están completamente expuestos y no son controlados en el servidor.
- Ausencia de control de acceso a funciones: El servidor se cree cualquier llamada a función (subir de nivel, iniciar partida, indicar puntuación...) sin realizar ningún tipo de validación.
No olvidemos que aunque se trate de un juego se deben cuidar estos aspectos, de otro modo se favorece la aparición de cheaters y con ello se produce la caída de la experiencia jugable.
Artículo cortesía de Miguel Ángel García
Twitter: @nodoraiz
0 comments :
Publicar un comentario