Hackeando Ikariam: Cross Site Scripting (xss) persistente y 'weaponización'
Introducción ⚠
Este es un recorrido sobre como descubrí una vulnerabilidad seria en el popular juego web y móvil Ikariam
Análisis del juego 🔍
Ikariam es un juego desarrollado con tecnologías web que fue lanzado en 2009, cuando los juegos de navegador se encontraban en un punto álgido de popularidad. Más tarde, el juego dio el salto a los teléfonos móviles para Android e iOS.
En este juego controlamos una Isla y nuestro objetivo es crecer mediante la gestión eficiente de los recursos disponibles. El modelo de negocio del juego se basa en las microtransacciones mediante un recurso “premium” especial.
Una de las peculiaridades del juego es nuestro ratio de crecimiento, que aumenta si suscribimos una alianza o hacemos amigos. Para ello, el juego dispone de un sistema de mensajería en el cual podemos contactar a usuario concretos o con aliados, o incluso con usuarios a los que aun no conocemos (enemigos, posibles futuros aliados, etc).
Decidí testear este sistema para encontrar vulnerabilidades.
Normalmente, el uso de la suite Burp es lo adecuado, pero el juego no presentaba ni siquiera la más mínima protección a la hora de ofuscar o proteger el código en el lado del cliente y en las peticiones, así que decidí simplemente echar un vistazo rápido utilizando Firefox y el monitor de red.
Comenzé enviando un simple mensaje diciendo “Hola” a una persona, y analizando la petición:
{"action":"Messages",
"function":"send",
"receiverId":"13279",
"msgType":"50",
"content":"Hola\n",
"isMission":"0",
"closeView":"0",
"allyId":"0",
"backgroundView":"city",
"currentCityId":"13755",
"templateView":"sendIKMessage",
"actionRequest":"310725177c5713e52f6c217c09f7ac86",
"ajax":"1"}
Rapidamente se deduce que:
- action: define el tipo de acción que estamos haciendo (enviar un mensaje, construir un edificio, atacar a alguien, etc).
- function: provee más información respecto a la acción.
- receiverId: receptor del mensaje.
- content: texto de nuestro mensaje.
- actionRequest: token único que cambia por cada sesión y por cada acción.
El resto de valores no son importantes.
Dado la simplez de la petición decidí manipular manualmente los valores. Normalmente, para llevar a cabo un proceso de pentesting o análisis sin causar complicaciones a otros usuario, crearíamos dos cuentas bajo nuestro control, pero antes de eso descubrí que cambiando el campo ‘receiverId’ por mi propio ID de usuario, el sistema me enviaba mi mensaje…¡a mi mismo!. Así no tendremos que interferir con otros usuarios ni crear más cuentas.
Curiosamente, el juego se confunde y piensa que ¿Ikariam? es quien me envia el mensaje. Manos a la obra.
Persistent Cross Site Scripting 🔥
Teniendo una forma fácil y cómoda de atacar el sistema de mensajería procedo a buscar vulnerabilidades. El objetivo más obvio es el campo content
que contiene el mensaje que se muestra en pantalla.
Antes de eso, debemos obtener el nuevo valor de actionRequest
…¿no?. Pues resulta que esto es así para el 99% de todas las peticiones POST y GET pero… por alguna extraña razón, no se aplica a las peticiones cuya acción es enviar un mensaje. La razón no es tan extraña, pensemos en la comodidad del usuario, lo dejo como tarea para el lector.
Probemos el más sencillo de los tests para ver si hay alguna protección frente a xss:
<script> alert(1) </script>
Utilizando el fragmento superior como valor para content
observamos que el tag <script>
y cualquier variación de este están siendo filtrados.
Sin problema, continuemos y probemos un payload ligeramente más complejo:
<img src="" onload=alert(1)>
Y voilá! Nuestra pequeña pieza de javascript se ejecuta al abrir el mensaje:
Petición completa:
action=Messages&function=send&receiverId=10760&msgType=50&content=First,Part<img src="" onerror=alert(1)>SecondPart&isMission=0&closeView=0&allyId=0&backgroundView=city¤tCityId=23155&templateView=sendIKMessage&actionRequest=310725177c5713e52f6c217c09f7ac86&ajax=1
Estamos ante un payload muy útil y extremadamente sencillo. Utiliza el tag <img>
para intentar cargar una imagen, pero al estar el campo src=""
vacío, se producirá un error. Al ocurrir un error, se ejecutará el código especificado en el evento DOM onerror=
.
Es posible ejecutar casi cualquier acción de javascript de forma inline lo que es suficiente para cualquier tipo de ataque. Podemos ejecutar diferentes llamadas encadenándolas con el caracter ;
:
<img src="" onload=alert(1);alert('Hacked')>
También existe una serie de trucos adicionales que encontraréis más adelante.
Después de analizar el comportamiento de la página llegué a la conclusión de que el fallo es más serio de lo que parece.
- Primero: el XSS se ejecuta incluso antes de que el usuario abra el mensaje, simplemente abriendo la lista de mensajes (dado que la web pre-carga aproximadamente los primeros 10 mensajes en la página).
- Segundo: Sin ninguna modificación adicional, ¡funciona en la versión para teléfonos!
El código se ejecutará cada vez que el usuario abra la lista de mensajes o el mensaje mismo. Además el código inyectado es completamente invisible al usuario si asi lo queremos, el cual puede recibir un mensaje que aparente totalmente verídico.
‘Weaponizando’ el exploit 💣
La vulnerabilidad permitiría a un atacante malicioso:
- Robar Cookies
- Redirigir al usuario a un sitio malicioso
- Cargar scripts adicionales (publicidad, crypto miners,…)
- Interactuar con la sesión del juego
Los tres primeros ataques son bien conocidos y encontrareis cientos y cientos de ejemplos en la web. Por lo tanto, deicdí explorar la ultima posibilidad. Un escenario diferente. Interactuar con la sesión del juego y obtener ventaja dentro del juego.
Para ello desarrollé mi propia herramienta para controlar y explotar la vulnerabilidad de forma rápida y automática. La versión web fue trivial, conocemos el tipo de peticiones. Para la versión móvil dedica un par de horas a leer el código fuente, tras lo cual descubrí que utiliza una API diferente con peticiones ligeramente diferentes también. Esto será importante más adelante.
Necesitamos un par usuario:contraseña
para que la herramienta funcione. Esta utiliza la API de la versión web que tiene peticiones como las mostradas anteriormente.
DarkRagweed es el nombre de la herramienta escrita en Python para automatizar la explotación de esta vulnerabilidad. Utiliza los módulos requests
y BeautifulSoup
de Python3.
Después de obtener una sesión, podemos buscar el id de usuarios, enviarles mensajes con código a ejecutar, etc. Existen cierto detalles técnicos que no explicaré en profundidad. Por ejemplo, cada vez que lanzamos una petición debemos obtener el nuevo valor del token. Para ello se escrapea la página después de cada petición para obtener el valor y construir la siguiente. La herramienta hace esto de forma automática.
Sin embargo, si queremos que nuestro código funciona en la versión móvil que utiliza un WebView, el navegador y el contexto son diferentes y habrá que adaptar los payloads. Debemos comenzar utilizando javascript genérico para detectar si nos encontramos en la versión de navegador web o en un WebView y para ello he utilizado algunos que otros trucos sintácticos de javascript. Los ejemplos se encuentran en el repositorio y muestran como, por ejemplo, el valor requestAction
se obtiene de una forma diferente en cada motor web.
Nota: A fecha de noviembre de 2019, Gameforge está rediseñando su sistema de login y API, por lo que el login en la herramienta no funciona. El exploit sigue funcionando.
Conclusiones
En esta ocasión hemos visto como a veces encontrar una vulnerabilidad xss es extremadamente sencillo. Aun más importante, como podemos customizar el uso de esta vulnerabilidad en casos particular. Puede ser sencillo encontrar fallos y agujeros en sistemas, pero no todo el mundo es capaz de llevar a cabo la investigación o el desarrollo posterior para personalizar el ataque. Lo cual es por cierto, un buen truco para programas de ‘bug bounty’, donde la recompensa puede ser mayor si mostramos que la vulnerabilidad puede afectar al sistema pero también fijar su objetivo en usuarios concretos, o en el modelo de negocio. En el caso de Ikariam, nos permite obtener una ventaja descomunal sobre otros jugadores, ya que simplemente enviando un mensaje podemos forzarle a donarnos todos sus recursos, a que derruyan sus propios edificios, o a que abandonen su propia alianza, y todo ello sin recurrir a las microtransacciones.