19 febrero 2014

Jugando con Cuadraditos

Hace poco llegó a mi otro juego de Google Play, en este caso se trata de Cuadraditos, juego en el que en turnos de 3 en 3 movimientos nos enfrentaremos a un contrincante seleccionando bordes y apuntándonos un punto cada vez que completemos el último borde de un cuadrado.

El giro de tuerca que propone el juego consiste en que para apuntarnos el borde tendremos que responder correctamente a una pregunta..., ¿o tal vez no?. Vamos a verlo.


Reuniendo información 

Empecemos analizando las peticiones que se intercambian cuando seleccionamos un borde:


En la petición ya vemos algunos campos a destacar:
  • access_token: Es un token de acceso que varía con cada inicio de sesión en la aplicación
  • match_id: Es el identificador de la partida en curso
  • version: Es el turno por el que nos vamos en el juego

Si vemos la respuesta que nos devuelve el servidor a la petición anterior encontraremos lo siguiente:


En la estructura JSON que el servidor nos devuelve al seleccionar un borde recibimos los siguientes campos a destacar:
  • question: Es la pregunta que nos realiza el juego
  • answers: Son las posibles respuestas
  • correct_answer: Es el hash con la respuesta correcta
  • ticket: Un token asociado a la pregunta que hemos recibido en este turno

Buceando en el código

Llegados a este punto lo primero que intentaremos será analizar el hash recibido enviándolo a la herramienta "hash-identifier":


Identifica que posiblemente se trate de un hash MD5 así que intentamos generar algunos resúmenes en busca del literal o literales a combinar sobre los que aplicar el hash:


Además de las cadenas más evidentes, se prueban distintas combinaciones en las que se incluye el turno, el ID de la pregunta, la propia pregunta, la posición de la respuesta correcta dentro del array de respuestas.., todo ello sin éxito. 

Vista la resistencia optamos por descargar y analizar el APK como ya lo hicimos en el caso del juego Pixel Dungeon con la diferencia de que esta vez no vamos a tener tanta suerte para analizar el código, se encuentra ofuscado:


Si os sentís fuertes para desenredar el código veréis que la palabra "correct_answer" que sería la que más nos aproximaría al resultado que buscamos no nos devolverá ningún resultado.
Buscar por otras palabras encontradas en la estructura JSON como "answers" y "question" sí nos devolverá resultados, sin embargo explorar esta vía será tedioso debido a la ofuscación, de modo que sin querer disuadir a nadie de resolver el misterio de este hash, aplicando pensamiento divergente voy a buscar otras vías de resolución.

Reuniendo más información

Continuemos capturando tráfico generado por la aplicación, veamos qué ocurre cuando acertamos una pregunta:


He resaltado en la petición los detalles más relevantes, pero comento los principales campos:
  • match_id: Identificador de la partida en curso
  • version: Turno por el que va la partida. Se envía un 1 porque estamos contestando a la pregunta anterior del primer turno
  • access_token: Token de sesión de juego. Coincide con el enviado al seleccionar el borde
  • message: Campo codificado que incluye el ticket asociado a la pregunta que nos han hecho en el turno 1 y el borde que queremos desbloquear. Podemos ver más claramente su contenido en la siguiente captura:


Sacamos entonces las siguientes conclusiones:
  • No se observa que en la dinámica de acierto se envíe de ningún modo la respuesta seleccionada al servidor, de modo que deducimos que la validación se realiza en nuestra aplicación
  • Hay que incluir en el campo version el mismo valor que obtuvimos al solicitar la pregunta, que será el número de turno de la partida 
  • En el campo message se incluye:
    • Un literal "take_border" que indica al servidor que se quiere capturar un borde
    • El valor del ticket que se había recibido al seleccionar un borde
    • Un número que indica el borde que se quiere desbloquear

Veamos ahora lo que ocurre cuando fallamos una pregunta:



Si observamos los datos enviados identificamos los siguientes campos importantes:
  • match_id: Identificador de la partida que estamos jugando
  • version: Turno que estamos jugando. En este caso es 7 porque yo he jugado 3 turnos y después mi contrincante otros 3
  • access_token. Token de sesión de juego
  • message. Campo codificado que indica que hemos fallado y el ticket asociado a la pregunta que nos han hecho en el turno 7. Se pueden ver sus valores más claramente en la siguiente captura:



Se confirman nuestras sospechas, en el caso de fallo vemos como sólo varían los campos de version (porque son turnos diferentes) y message (que incluye un literal "question_fail" y el ticket correspondiente a la pregunta fallida).


Conclusión final


Hemos visto a través del análisis entre acierto y fallo que la información que se envía al servidor se asume válida, de modo que si queremos falsear un acierto no necesitaremos conocer la respuesta correcta, nos valdrá con saber el turno en el que nos encontramos, el ticket asociado a la pregunta y el borde que queremos desbloquear.

Respecto a los bordes a desbloquear, realizar algunas pruebas refleja que los valores asignados parten de 0 y crecen en orden izquierdo, arriba y derecha. Os dejo en la siguiente imagen algunos valores:


Si tenemos que resumir el problema podemos indicar que comete un error habitual de algunas aplicaciones Web, un sistema no puede basar su confianza en validar en cliente sin realizar validación en servidor, de hacerlo así se permite a los usuarios malintencionados la posibilidad de alterar y falsificar las entradas.

Finalmente, os dejo un ejemplo en video de la explotación de esta "vulnerabilidad" del juego y un enlace de descarga:



Artículo cortesía de Miguel Ángel García.
Twitter: @nodoraiz

2 comments :

Roberto_Serrano_Diaz_Grande dijo...

Excelente artículo XD.
Cuando termine con Pixel Dungeon pruebo este.
Gracias por tus aportes.
Saludos

BeRniTo dijo...

Excelente artículo! :)


Me gustaría que analizaras el juego Super Stickman Golf 2. ¿Puede ser?