2 puntos por GN⁺ 2024-01-15 | 1 comentarios | Compartir por WhatsApp
  • El entorno de addons de Lua de World of Warcraft facilitó modificar la UI, pero separaba el acceso a la información y los permisos de ejecución para evitar la automatización del comportamiento dentro del juego
  • El modelo de seguridad de Blizzard dividía entre un entorno no seguro que podía leer mucha información y un entorno seguro que podía ejecutar acciones, y ese límite era la clave para impedir la automatización
  • El random permitido en el entorno seguro era un envoltorio muy delgado del rand de C de MSVC, y como era un débil generador congruencial lineal con estado global compartido, podía convertirse en un canal para transmitir información
  • Con solo dos salidas del RNG se podían reducir los candidatos del estado interno a un máximo de 8, y con salidas adicionales elegir un único candidato; después era posible avanzar el RNG hasta que apareciera el índice de acción deseado
  • En apariencia parecía una selección aleatoria de acciones, pero en realidad podía forzarse la elección de una acción específica, como un hechizo; no está claro cómo lo corrigió Blizzard después

Los addons de World of Warcraft como puerta de entrada a la programación

  • Los addons de World of Warcraft funcionaban poniendo en una carpeta unos cuantos archivos fuente .lua escritos en Lua, y el juego los cargaba directamente
  • Bastaba con modificar y guardar los archivos, recargar la interfaz y ver el resultado de inmediato, así que la barrera de entrada era muy baja
  • Al principio se hacían varios addons copiando código de otras personas, refactorizándolo o recombinándolo para crear la funcionalidad deseada
  • Con el tiempo se terminó dedicando más tiempo a escribir addons que a jugar de verdad, y World of Warcraft se convirtió en el punto de partida para empezar a programar en serio

El modelo de seguridad de addons de Blizzard

  • Si los usuarios pudieran crear addons totalmente programables, surgiría el problema de los bots, así que Blizzard diseñó una estructura para limitar la automatización disponible para un jugador normal
  • La mayoría de los elementos de UI y las APIs de recopilación de información podían usarse sin restricciones
    • Por ejemplo, se podía crear una barra de vida que leyera la salud del personaje y ajustara el tamaño de un frame en primer plano
  • Las APIs que ejecutaban acciones reales del juego, como mover al personaje, lanzar hechizos o usar ítems, estaban protegidas y solo podían invocarse desde código oficial de Blizzard
  • Las APIs para obtener la posición exacta en el mundo y la dirección de la cámara también pasaron a estar protegidas en cierto momento
    • Fue una medida contra addons que dibujaban elementos 3D sobre el mundo del juego para facilitar peleas contra jefes

Separación entre entorno seguro y entorno no seguro

  • Como algunos elementos de UI sí necesitaban interactuar con acciones reales del juego, se podían crear botones especiales que ejecutaran código en un entorno seguro al hacer clic
  • Esos botones no podían crearse, borrarse ni moverse durante el combate
    • Así se evitaba automatizar acciones colocando condicionalmente un botón bajo el cursor durante la pelea
  • El entorno seguro permitía configurar programáticamente qué hechizo lanzar, pero bloqueaba el acceso a estado externo necesario para una automatización arbitraria
  • Solo se permitían algunas APIs de recopilación de información al nivel del sistema de macros, y no se proporcionaban datos necesarios para elegir el hechizo óptimo, como tiempos de reutilización o salud de unidades
  • Como resultado, ambos entornos tenían permisos distintos
    • Entorno no seguro: puede obtener mucha información, pero no puede actuar
    • Entorno seguro: puede actuar, pero no puede obtener la información necesaria para automatizar

random como canal trasero

  • El objetivo no era desactivar las restricciones de seguridad con software de terceros, sino comprobar si era posible sortear el entorno seguro usando únicamente herramientas técnicamente permitidas
  • Al revisar la lista de funciones permitidas en el entorno seguro, apareció random
  • Como la mayoría de los generadores de números aleatorios de una computadora son generadores seudoaleatorios con estado interno, se concluyó que, si ese estado podía manipularse, sería posible transmitir información externa al entorno seguro
  • En la práctica, random era un pequeño envoltorio sobre rand de C
  • Como World of Warcraft estaba compilado con MSVC, la implementación de rand era un generador basado en estado global
uint32_t state;

int rand() {
    state = state * 214013 + 2531011;
    return (state >> 16) & 0x7fff;
}
  • Esta implementación compartía un único estado global del RNG en todo el proceso, y además era un débil generador congruencial lineal

Recuperar el estado de rand en MSVC

  • La salida de rand expone directamente 15 bits que forman parte del estado
  • Como el espacio de estados es pequeño y parte de la salida es visible, se pueden conservar posibles candidatos de estado e ir eliminándolos con salidas adicionales
  • Un método más eficiente es observar dos salidas del RNG, r0 y r1, y calcular los candidatos para la parte oculta de 16 bits del estado
  • Las constantes de rand en MSVC son las siguientes
    • a = 214013
    • b = 2531011
  • Si se llama h0 y h1 a la parte oculta de 16 bits después de la primera y segunda llamada, respectivamente, se puede derivar el candidato de h0 a partir de la ecuación de transición de estado
  • El resultado muestra que basta revisar el rango -1 <= k < 7, así que los candidatos del estado oculto se reducen a un máximo de 8
  • Después se comparan los resultados de llamadas adicionales al RNG con la salida de los RNG candidatos para descartar los incorrectos
  • La implementación de ejemplo en Python usaba un bucle while, pero en la práctica parecía que bastaba con una tercera salida para reducirlo a un único candidato
  • En ese momento era difícil hacer personalmente la derivación de matemática discreta, así que se publicó una pregunta en crypto.SE con la excusa de “quiero mostrarle a un colega qué tan débil es este RNG”

Cómo elegir la acción deseada en el entorno seguro

  • Una vez que se podía recuperar el estado interno del RNG, era posible transmitir decisiones de automatización arbitrarias al entorno seguro
  • El proceso funcionaba así
    • Se registraba un hook no seguro que se ejecutaba justo antes de que corriera el código del entorno seguro
    • Dentro del hook se podía acceder a toda la información y decidir qué acción tomar
    • La acción elegida se buscaba en una lista de acciones codificada y se convertía en un índice
    • Luego se recuperaba el estado actual del RNG con el método anterior
    • Si la siguiente salida del RNG, tomada módulo el tamaño de la lista de acciones, no daba el índice deseado, se hacía avanzar el RNG y se volvía a comprobar
    • Cuando llegaba el turno de la salida deseada, el hook terminaba y el entorno seguro llamaba a random para ejecutar la acción correspondiente a ese índice
  • Desde el entorno seguro parecía que la lista de acciones se indexaba con un número aleatorio, pero en realidad se controlaba el momento de la siguiente llamada a random para forzar la acción deseada
  • Si la lista de acciones tenía tamaño n, en promedio había que avanzar el RNG n pasos para transmitir el valor deseado, pero en la práctica eso no causaba problemas

Qué pasó después

  • Años después, al volver a intentarlo, este método ya no funcionaba
  • No se sabe cuándo Blizzard corrigió el problema del RNG débil y compartido, ni siquiera si lo reconoció como problema
  • Entre los cambios posibles están haber cambiado a otro algoritmo o haber separado correctamente el estado del RNG dentro del entorno seguro
  • Aunque no era código que se pensara usar, fue un caso acotado de evasión de seguridad en un juego donde se logró manipular un comportamiento aparentemente aleatorio para producir un resultado deseado

1 comentarios

 
GN⁺ 2024-01-15
Comentarios en Hacker News
  • Hace mucho tiempo, cuando trabajaba como GM de WoW, una versión específica del popular addon Titan Bar tenía un bug y seguía llamando a la API que abría la UI de “escribir un ticket al GM”
    Aunque lo cancelaras, volvía a aparecer, y aparte de borrar el addon problemático, la única forma de cerrar la ventana era escribir algo en el cuadro de texto y enviarlo
    Desde la perspectiva de un GM, fueron uno o dos días realmente duros, y muchos jugadores ni siquiera sabían qué era un GM o que podían enviar un ticket
    Tampoco solía ser fácil explicarles que “probablemente enviaste un ticket por culpa de un addon y por eso yo, del equipo de soporte, te contacté”
    Como en la práctica destruyó los tiempos normales de respuesta, los desarrolladores sacaron un hotfix y pusieron esa llamada de API detrás de una protección para que solo pudiera hacerse desde el código LUA propio de Blizzard

    • Creo que se llamaba Titan Panel
      Era un addon que integraba varios elementos de la UI, como el oro total o el porcentaje de reparación, en una sola barra
  • Curiosamente, ya había respondido en StackOverflow una pregunta casi idéntica sobre “invertir un generador de números aleatorios” unos meses antes de que apareciera la pregunta en Crypto.SE: https://stackoverflow.com/a/15237585/1204143
    Solo que, en vez de atacar el generador aleatorio de MSVC, ataqué el generador aleatorio de Java
    Ambos tienen la estructura de un generador congruencial lineal (LCG) simple que produce valores truncados de su estado interno, así que la forma de atacarlos es muy parecida

    • No soy muy bueno en matemáticas, así que le pedí a Z3 que encontrara una seed conveniente, y el resultado fue este código
      import java.util.Random;
      public class Hayley {
      public static void main(String... args) {
      byte[] b1 = new byte[4], b2 = new byte[2];
      (new Random(0x2effe2140e00L)).nextBytes(b1);
      (new Random(0xc2f0097)).nextBytes(b2);
      System.out.println(new String(b1) + new String(b2));
      }
      }
  • Me pregunto si en el servidor también podrían haber usado el mismo generador aleatorio
    Si fuera así, en teoría parecería explotable cuando tienes un servidor de uso muy bajo, baja latencia, la capacidad de acotar con bastante precisión el estado actual del generador, y la capacidad de volver a disparar rápidamente eventos cuya variación económica depende mucho del azar

    • Los servidores de Vanilla WoW usaban Mersenne Twister y, aunque tenía varios problemas, no se rompía de una forma tan trivial
      Blizzard reveló bastantes detalles del funcionamiento del generador aleatorio del lado del servidor como parte de un intento, mayormente fallido, de acabar con los rumores de jugadores supersticiosos que creían firmemente que existían cosas como “seeds de loot”
  • La economía y los incentivos de los generadores aleatorios son interesantes
    Si en la práctica no hay nada en juego y la gente no se da cuenta con facilidad, un generador aleatorio imperfecto o incluso malo puede ser suficiente, como en un juego
    Pero cuando hay dinero o seguridad de por medio, como en billeteras de criptomonedas o casinos en línea, de pronto se vuelve extremadamente importante

  • Más seriamente, el generador aleatorio congruencial lineal presentado repite su secuencia después de producir solo 12,445 números distintos
    Habría sido mucho más razonable usar mejores parámetros para este algoritmo

    • La ingeniería es compromiso
      Para tareas simples como el movimiento de partículas en estelas de humo, necesitas un generador aleatorio rápido y simple
      En criptografía o juegos de casino, no solo usas un generador mejor con al menos 32 bytes de estado, sino que además haces avanzar el motor a intervalos aleatorios para dificultar aún más la predicción de los resultados
  • World of Warcraft fue lo que despertó mi interés por la programación
    Tenía 6 años cuando salió, y cuando empezaron a aparecer los servidores privados, quise aprender a crear NPCs por mi cuenta, así que aprendí Lua
    Todavía quedan muchos posts del foro MMOwned donde participaba entonces, y una de las personas que me ayudó a aprender en esa época todavía publica de vez en cuando
    Compilando yo mismo los cores de servidores privados aprendí sobre compilación, SVN y aplicación de parches, y creo que había un bug relacionado con Fireball del mago, aunque no lo recuerdo con precisión

    • Me da nostalgia cada vez que me acuerdo de los foros de antes
      Hoy se siente como si gran parte de lo bueno de internet hubiera sido reemplazado por apps de redes sociales que disparan dopamina y por foros cerrados como Reddit
      Supongo que una de las razones por las que sigo en HN es porque todavía me recuerda a esos foros viejos
  • “Cualquiera que considere métodos aritméticos para producir números aleatorios está, por supuesto, en estado de pecado. Como se ha señalado varias veces, no existe tal cosa como un número aleatorio. Solo existen métodos para producir números aleatorios, y un procedimiento aritmético estricto, por supuesto, no es uno de ellos.” — John von Neumann

    • Como criptógrafo, esta cita de verdad es difícil de soportar
      No tengo nada contra Neumann, pero la gente que la difunde como si fuera una máxima sabia normalmente solo está desinformando
      En contexto, se parece más a un chiste filosófico innecesario
      Ya tenemos desde hace décadas generadores aleatorios criptográficos rápidos y confiables
      Funcionan correctamente con seeds muy pequeñas y solo operaciones aritméticas, los números que producen son indistinguibles de cualquier otra fuente válida de aleatoriedad y pueden usarse para cualquier propósito
      Demasiada gente no entiende esto, e incluso han surgido empresas que venden la charlatanería de la “aleatoriedad real” y cosas por el estilo
  • Un muy buen ejemplo de un generador aleatorio realmente terrible está en Monster Hunter 3 Ultimate, y quizá también en otros títulos
    No solo se podía explotar completamente para conseguir buen loot, sino que también existían las “charm tables”
    Algunos ítems generan sus stats al azar, pero la seed inicial se guarda en el archivo de guardado, así que el rango de ítems que puedes obtener queda limitado
    Si tenías mala suerte, podías caer en una “tabla maldita”, que en la práctica es un generador aleatorio con un período muy corto, y nunca obtener los valores máximos
    No rompe el juego por completo, pero sí es malo para el juego de alto nivel

  • Escribí un script para la casa de subastas
    Escaneaba todas las subastas, compraba los ítems subvaluados y los volvía a listar
    Si alguien los publicaba más baratos, podía cancelar y volver a publicarlos apenas por debajo, y en la práctica era oro infinito

    • Después de que unos verdaderos borrachos que trabajaban en Goldman me explicaron cómo se mueve el dinero, se lo expliqué a mi hermano, y él respondió: “Entonces, ¿el IRS sería un gold sink?”
      “Mmm, en realidad es una muy buena analogía. La diferencia es… mmm… ¡es una muy buena analogía!”
    • Esto es básicamente lo mismo que el high-frequency trading del mundo real
    • ¿Tú hiciste Auctioneer? ¿O era algo parecido a Auctioneer, pero decidiste no usar la herramienta “estándar de la industria”?
    • Me pregunto cómo determinabas si algo estaba subvaluado
    • ¿Eres el autor del addon Auctioneer? Si sí, entonces probablemente fue uno de los addons de WoW más populares durante más de 10 años
  • “Cuando las variables no varían y las constantes no son constantes”

    • Interesante. Parece un meme con raíces profundas
      https://www.theregister.com/2006/07/26/constants_are_not/
      “La lamentación sobre el capricho de la programación en el título de este artículo, ‘Variables Won't Constants Aren't’, la leí por primera vez en Creative Computing varios años antes de que se creara C.”
      Si existe algún documento específico que se sienta como el texto canónico de este meme, me gustaría leerlo