Dota 2 bajo ataque: ¿Cómo se explotó un error V8 en el juego?

Los exploits de V8 suelen asociarse a sofisticadas cadenas de exploits de día cero para navegadores. Aunque el navegador puede ser el vector de ataque más interesante de V8, no debemos olvidar que V8, el motor JavaScript de código abierto de Google, también está integrado en innumerables proyectos distintos del navegador. Y cuando un motor JavaScript se utiliza a través de un límite de seguridad para ejecutar código potencialmente no fiable, pueden surgir problemas de seguridad.

Uno de estos problemas afectó al masivamente popular videojuego Dota 2. Dota utilizaba una compilación obsoleta de "v8.dll" compilada en diciembre de 2018. No es de extrañar que esta build fuera vulnerable a una serie de CVE, muchas de ellas incluso vulnerabilidades explotadas conocidas con exploits de prueba de concepto (PoC) públicos. Descubrimos que una de estas vulnerabilidades,CVE-2021-38003, se explotaba in the wild en cuatro modos de juego personalizados publicados dentro del juego. Dado que V8 no estaba aislado en Dota, el exploit por sí solo permitía la ejecución remota de código contra otros jugadores de Dota.

Revelamos nuestros hallazgos al desarrollador de Dota 2, Valve. En respuesta, Valve lanzó una actualización para Dota el 12 de enero, actualizando la versión antigua y vulnerable de V8. Esta actualización entró en vigor inmediatamente, ya que Dota tiene que estar al día para que los jugadores puedan participar en partidas en línea. Valve también tomó medidas adicionales, retirando los modos de juego personalizados ofensivos, notificando a los jugadores afectados e introduciendo nuevas mitigaciones para reducir la superficie de ataque del juego.


Lista de cambios de Dota para la actualización del 12 de enero

Background

Dota 2 es un juego MOBA que se lanzó inicialmente el 9 de julio de 2013. A pesar de tener casi 10 años (o quizá 20 si contamos el Dota 1 original), sigue atrayendo a una gran base de jugadores, de unos 15 millones de jugadores activos mensuales. Al igual que otros juegos populares, Dota es una pieza compleja de software bajo el capó, ensamblado a partir de múltiples componentes separados. Un componente que nos interesa especialmente es el marco Panorama. Se trata de un marco diseñado por la propia Valve para permitir el desarrollo de interfaces de usuario utilizando la conocida tríada web de HTML, CSS y JavaScript. La parte JavaScript aquí era problemática porque era ejecutada por la versión vulnerable de V8. Así, un JavaScript malicioso podría explotar una vulnerabilidad de V8 y obtener el control de la máquina de la víctima. Esto no sería un problema para el juego sin modificar, porque por defecto, sólo los scripts legítimos de Valve deben ser ejecutados. Sin embargo, Dota está muy abierto a la personalización por parte de la comunidad de jugadores, lo que abre las puertas a los actores de amenazas para intentar colar piezas maliciosas de JavaScript a sus víctimas desprevenidas.

La personalización de Dota puede adoptar muchas formas: Hay objetos del juego que se pueden llevar puestos, paquetes de anuncios, pantallas de carga, emoticonos del chat y mucho más. También hay modos de juego personalizados desarrollados por la comunidad. En esencia, se trata de juegos completamente nuevos que aprovechan el potente motor de Dota para permitir que cualquiera con un poco de experiencia en programación implemente sus ideas para un juego. Los modos de juego personalizados juegan un papel importante en Dota, y Valve es muy consciente de los beneficios de permitir a los jugadores expresar su creatividad mediante el desarrollo de modos de juego personalizados. Después de todo, el propio Dota comenzó como un modo de juego para "Warcraft III: The Frozen Throne". Quizá por eso se pueden instalar modos de juego con un solo clic desde el juego. Como resultado, hay miles de modos de juego disponibles, algunos de los cuales son extremadamente populares. Por ejemplo, DOTA AUTO CHESS fue jugado por más de 10 millones de jugadores.

Antes de que un modo de juego pueda ser jugado por jugadores normales, debe ser publicado en la tienda de Steam. El proceso de publicación incluye una verificación realizada por Valve. Aunque esto podría eliminar algunos modos de juego maliciosos, ningún proceso de verificación es perfecto. Como mostraremos más adelante, al menos cuatro modos de juego maliciosos consiguieron colarse. Creemos que el proceso de verificación existe principalmente por razones de moderación para evitar que se publique contenido inapropiado. Hay muchas formas de ocultar una puerta trasera en un modo de juego, y llevaría mucho tiempo intentar detectarlas todas durante la verificación.

La lógica principal de los modos de juego personalizados está codificada en Lua. Se ejecuta en el servidor del juego, por lo que no puede utilizarse directamente para atacar a jugadores individuales. Para las secuencias de comandos del lado del cliente, existe JavaScript del marco Panorama. Se utiliza principalmente para representar los elementos de la interfaz de usuario, como los marcadores o las barras de estado de las misiones. JavaScript se ejecuta mediante el motor V8 y es totalmente compatible con muchas funciones avanzadas, como la ejecución de WebAssembly. Además, también hay una API específica de Dota que expone funcionalidades adicionales. Particularmente interesante fue la función "$.AsyncWebRequest", que, en combinación con "eval", podría haber sido utilizada para backdoor un modo de juego para que pudiera ejecutar código JavaScript adicional arbitrario descargado de Internet. Quizás fue esta preocupación la que hizo que la función "$.AsyncWebRequest" fuera obsoleta y finalmente eliminada por completo. Sin embargo, hay formas de evitarlo. Por ejemplo, la petición web puede ser hecha por el código Lua del lado del servidor, con la respuesta pasada al JavaScript del lado del cliente usando APIs de mensajería de eventos del juego.


Sólo probando

Hemos descubierto cuatro modos de juego personalizados maliciosos publicados en la tienda de Steam, todos desarrollados por el mismo autor. El primer modo de juego (id 1556548695) es particularmente interesante, ya que parece que es donde el atacante sólo probó el exploit, a juzgar por la falta de una carga útil real adjunta. Curiosamente, el atacante también utilizó este modo de juego para probar otras técnicas, dejando código comentado o funciones no utilizadas. Esto nos ofreció una gran oportunidad para entender el proceso de pensamiento del atacante.

La página de Steam del modo de juego personalizado donde el atacante probó el exploit.


Como se puede ver en la captura de pantalla anterior, el atacante fue muy transparente sobre la naturaleza de este modo de juego, nombrándolo "test addon plz ignore" e incluso llegando a utilizar la descripción para instar a otros jugadores a no descargar este modo de juego. Aunque esto podría parecer una expresión de buena fe, en breve demostraremos que en los otros tres modos de juego maliciosos, el mismo atacante adoptó el enfoque exactamente opuesto e intentó que el código malicioso fuera lo más sigiloso posible. 

El exploit JavaScript de este modo de juego personalizado se encuentra dentro de "overthrow_scoreboard.vjs_c". Solía ser un archivo JavaScript legítimo que implementaba la funcionalidad del marcador, pero el atacante sustituyó su contenido por un exploit para CVE-2021-38003. Esta vulnerabilidad fue descubierta originalmente como un día cero por los investigadores de Google Clément Lecigne y Samuel Groß, cuando se utilizó in the wild en una cadena de exploits contra un teléfono Samsung totalmente parcheado.

Ahora hay PoCs públicos y escritos para esta CVE. Sin embargo, no estaban disponibles en marzo de 2022, cuando el atacante actualizó por última vez el modo de juego. Esto significa que tuvieron que desarrollar una gran parte del exploit ellos mismos (incluso si hubiera un PoC público en ese momento, el atacante todavía necesitaría poseer algunas habilidades técnicas para retroportarlo a la anticuada compilación V8 que Dota estaba utilizando). Aún así, el núcleo del exploit fue proporcionado en la entrada del bug tracker de Chromium para el CVE. Hay un fragmento de código que puede activar la vulnerabilidad para filtrar el objeto supuestamente inaccesible "TheHole" y luego utilizar este objeto filtrado para corromper el tamaño de un mapa. El atacante tomó este fragmento y lo pegó en su exploit, construyendo el resto del exploit sobre este mapa corrupto


El núcleo del exploit que activa CVE-2021-38003 para filtrar el objeto "TheHole". Fíjate en el "¡yay!" del final: es simplemente una expresión de alegría y no es en absoluto necesario para que el exploit funcione.


Curiosamente, el exploit contiene una gran cantidad de código comentado e impresiones de depuración. Esto sugiere además que el atacante tuvo que esforzarse mucho para convertir la vulnerabilidad en un arma. La parte del exploit desarrollada por el atacante comienza utilizando el mapa corrupto para corromper la longitud de un array, consiguiendo una primitiva lectura/escritura relativa. A continuación, corrompe un puntero de almacenamiento de respaldo "ArrayBuffer" para obtener una primitiva de lectura/escritura arbitraria. No hay función "addrof", ya que las direcciones se filtran colocando el objeto de destino en un desplazamiento conocido del array corrompido y utilizando después la primitiva de lectura relativa. Finalmente, con la lectura/escritura arbitraria en su lugar, el exploit utiliza un conocido truco de WebAssembly para ejecutar shellcode personalizado. Hemos probado todo el exploit localmente contra Dota y podemos confirmar que funcionó.


Un fragmento de JavaScript extraído del exploit. Fíjate en las impresiones de depuración, los comentarios y el código comentado.


Aparte de este exploit JavaScript, el modo de juego personalizado también contiene otro archivo interesante, que lleva el inquietante nombre de "evil.lua". Aquí es donde el atacante probó las capacidades de ejecución de Lua del lado del servidor. Vea el fragmento de Lua a continuación donde el atacante probó lo siguiente en particular:

  • Registro

  • Compilación dinámica de código Lua adicional ("loadstring")

  • Determinación de la versión exacta del intérprete de Lua

  • Ejecución de comandos arbitrarios del sistema ("whoami")

  • Creación de coroutines

  • Conectividad de red (peticiones HTTP GET


Un fragmento de Lua extraído de "evil.lua".


Por desgracia, no tenemos acceso al historial completo de actualizaciones de este modo de juego en particular. Por lo tanto, es posible que algún código interesante de versiones anteriores ya no esté presente en la versión que analizamos. Al menos podemos ver en el registro de cambios que hubo nueve actualizaciones de este modo de juego, todas ellas ocurrieron en noviembre de 2018 o en marzo de 2022. Dado que la vulnerabilidad JavaScript explotada no se descubrió hasta 2021, suponemos que el modo de juego comenzó inicialmente como un juego legítimo y que la funcionalidad maliciosa solo se añadió en las actualizaciones de marzo de 2022.


La puerta de atrás

Tras descubrir este primer modo de juego malicioso, nos preguntamos por supuesto si hay más exploits de este tipo por ahí. Dado que el atacante no se molestó en informar de la vulnerabilidad a Valve, nos pareció probable que tuviera intenciones maliciosas e intentará explotar a mayor escala. Como resultado, desarrollamos un script que descargaba todos los archivos JavaScript de todos los modos de juego personalizados publicados en la tienda de Steam. Así obtuvimos gigabytes de JavaScript que pudimos consultar en busca de patrones de código sospechosos. 

No tardamos en descubrir otros tres modos de juego maliciosos, todos del mismo autor (que también resultó ser el autor del modo de juego "test addon plz ignore" analizado anteriormente). Estos modos de juego se llamaban "Overdog no annoying heroes" (id 2776998052), "Custom Hero Brawl" (id 2780728794) y "Overthrow RTZ Edition X10 XP" (id 2780559339). Curiosamente, el mismo autor también publicó un quinto modo de juego llamado "Brawl in Petah Tiqwa" (id 1590547173), que no incluía ningún código malicioso (para nuestra gran sorpresa).


La página de Steam de uno de los modos de juego personalizados.

El código malicioso de estos tres nuevos modos de juego es mucho más sutil. No hay ningún archivo llamado "evil.lua" ni ningún exploit JavaScript directamente visible en el código fuente. En su lugar, sólo hay una simple puerta trasera que consiste en sólo unas veinte líneas de código. Esta puerta trasera puede ejecutar JavaScript arbitrario descargado a través de HTTP, dando al atacante no sólo la capacidad de ocultar el código del exploit, sino también la capacidad de actualizarlo a su discreción sin tener que actualizar todo el modo de juego personalizado (y pasar por el arriesgado proceso de verificación del modo de juego).

La puerta trasera comienza con el código JavaScript enviando un evento personalizado "ClientReady" al servidor. Esto es para indicar al servidor que hay un nuevo cliente del juego víctima, esperando recibir la carga útil de JavaScript. El código Lua en el servidor registró un oyente para el evento "ClientReady". Cuando recibe este evento, realiza una petición HTTP GET a su servidor C&C para obtener la carga JavaScript. Este payload es esperado en el cuerpo de la respuesta, y es reenviado al JavaScript del lado del cliente en un evento personalizado llamado "test".


La parte Lua del backdoor, que se ejecuta en el servidor del juego.


Cuando el JavaScript del lado del cliente recibe este evento de "prueba", desenvuelve la carga útil, crea dinámicamente una nueva función a partir de ella y la ejecuta inmediatamente. A un alto nivel, esto es claramente sólo un simple descargador capaz de ejecutar JavaScript arbitrario descargado del servidor C&C. La cooperación de JavaScript del lado del cliente y código Lua del lado del servidor sólo era necesaria porque JavaScript ya no podía acceder directamente a Internet.


La parte JavaScript del backdoor, que se ejecuta en los clientes del juego.


En el momento en que descubrimos esta puerta trasera, el servidor de C&C ya no respondía. Aún así, podemos asumir con seguridad que esta puerta trasera tenía como objetivo descargar el exploit JavaScript para CVE-2021-38003. Esto se debe a que los tres modos de juego con puerta trasera fueron actualizados por el mismo autor en un plazo de 10 días después de que dicho autor introdujera el exploit de JavaScript en su primer modo de juego malicioso. Sin embargo, seguimos sin estar seguros de si había algún shellcode malicioso adjunto al exploit. Después de todo, el uso de "ngrok" para C&C es poco convencional y podría sugerir que el atacante sólo probó la funcionalidad de puerta trasera. De una forma u otra, podemos decir que este ataque no fue muy grande en escala. Según Valve, menos de 200 jugadores se vieron afectados.


Reflexiones finales

Después de descubrir los cuatro modos de juego maliciosos, intentamos cazar más - desafortunadamente, nuestro rastro se perdió. Por lo tanto, no está claro cuáles eran las intenciones últimas del atacante. Sin embargo, creemos que no eran exactamente puras intenciones de investigación, por dos razones principales. En primer lugar, el atacante no informó de la vulnerabilidad a Valve (lo que generalmente se consideraría una buena acción). En segundo lugar, el atacante intentó ocultar el exploit en una puerta trasera sigilosa. En cualquier caso, también es posible que el atacante no tuviera intenciones puramente maliciosas, ya que podría abusar de esta vulnerabilidad con un impacto mucho mayor.

Por ejemplo, un atacante malicioso podría intentar apoderarse de un popular modo de juego personalizado. Hay muchos modos de juego que son descuidados por sus desarrolladores originales, por lo que el atacante podría intentar algo tan simple como prometer corregir errores y continuar el desarrollo de forma gratuita. Tras un cierto número de actualizaciones legítimas, el atacante podría intentar colar la puerta trasera JavaScript. Dado que los modos de juego se actualizan automáticamente en segundo plano, los desprevenidos jugadores víctimas no tendrían muchas oportunidades de defenderse.

Alternativamente, el atacante podría buscar otras formas de explotar la vulnerabilidad sin involucrar ningún modo de juego personalizado. Por ejemplo, el atacante podría intentar buscar una vulnerabilidad XSS independiente para encadenar con el exploit V8. Tal vulnerabilidad XSS podría permitir al atacante ejecutar JavaScript arbitrario dentro de la instancia Panorama de la víctima remota. El exploit V8 podría utilizarse entonces para salir del marco de trabajo de Panorama. Tenga en cuenta que Panorama también se utiliza en gran medida en el menú principal del juego, por lo que dependiendo de la naturaleza de la vulnerabilidad XSS, esto podría tener un radio de explosión tan grande como los 15 millones de jugadores mensuales.

Antes de despedirnos, nos gustaría dar las gracias a Valve por atender rápidamente nuestros informes. Esperamos que sigan actualizando V8 en el futuro y reduzcan la brecha de parches tanto como sea posible. Valve también compartió con nosotros algunos planes sobre mitigaciones adicionales, y estaremos muy emocionados de verlos implementados en la práctica. Debido al impacto potencial, también recomendamos que se revisen cuidadosamente las actualizaciones de los juegos personalizados más populares.

También podemos apreciar que Valve ha tomado la decisión de publicar modos de juego personalizados en Steam a pesar de que esto podría poner más responsabilidad sobre sus hombros. En última instancia, esto es positivo para la seguridad general de los jugadores, ya que Valve puede moderar los modos de juego publicados y eliminar los maliciosos. Muchos otros juegos no tienen un soporte tan integrado para las partidas personalizadas, por lo que los jugadores recurren a descargar mods de sitios de terceros al azar, que a menudo son conocidos por incluir malware.


No hay comentarios.

Imágenes del tema de enot-poloskun. Con tecnología de Blogger.