- Randar es un exploit en Minecraft Beta 1.8~1.12.2 que reconstruye el estado de
java.util.Random del servidor usando las coordenadas de drop de ítems generadas al romper bloques, y luego rastrea hacia atrás la ubicación de otros jugadores
- La causa principal es la reutilización del RNG, donde la generación de terreno/estructuras y eventos de minería comparten el mismo generador aleatorio, y las posiciones observables de los drops se vuelven pistas del estado interno
- Los offsets X/Y/Z del drop de ítems son tres salidas consecutivas de
World.rand.nextFloat(), así que se exponen los 24 bits superiores de una semilla de 48 bits y se puede reconstruir la semilla rápidamente con reducción de retículas LLL
- La semilla reconstruida se rebobina para verificar si proviene de una comprobación de Woodland Mansion y, si coincide, identificar la región Woodland de 1280×1280 bloques cargada más recientemente
- Si datos de drops quedaron guardados en registros de paquetes antiguos como ReplayMod, la ubicación de actividad de la era Beta 1.8~1.12.2 puede quedar expuesta incluso después de que el servidor haya sido parchado
Qué información busca Randar
- El objetivo es encontrar las coordenadas dentro del juego de otros jugadores en el mismo mundo
- 2b2t se usa como caso principal
- 2b2t es un servidor “anarchy” de Minecraft sin reglas
- Su mapa del servidor tiene una escala de 3.6 quadrillion square tiles, por lo que mantener la ubicación en secreto es la condición clave para proteger ítems
- Antes de Randar, 2b2t ya había tenido el exploit de coordenadas Nocom, usado entre 2018 y 2021
Versiones afectadas y el error en el código
- El problema existió desde el lanzamiento de Minecraft Beta 1.8 en 2011 hasta el lanzamiento de 1.12.2 en 2017
- 2b2t se mantuvo en 1.12.2 hasta el 14 de agosto de 2023
- El error central fue reutilizar sin cuidado una instancia de
java.util.Random en varias rutas del código
- El RNG se comparte entre acciones del juego como la generación de terreno y la minería
java.util.Random tampoco es un RNG para uso de seguridad
- Minecraft usa generación determinista para que con la misma seed del mundo y la misma posición se genere el mismo terreno
- Para ese objetivo, usar
java.util.Random es natural
- El problema es que manipular el RNG de generación del mundo, que debe ser predecible, también afectó eventos que deberían ser impredecibles
Comprobación de Woodland Mansion y World.rand global
- Durante el proceso de comprobar dónde se genera un Woodland Mansion,
World.rand se reinicia
- La región Woodland se calcula en unidades de 80×80 chunks
- Para elegir el chunk donde aparecería una Mansion dentro de esa región, se llama cuatro veces a
random.nextInt(60)
- El flujo vulnerable es que
World.setRandomSeed(seedX, seedY, seedZ) establece una nueva semilla en el this.rand global y devuelve ese mismo objeto
- La fórmula específica para 2b2t es la siguiente
seed = x * 341873128712 + z * 132897987541 - 4172144997891902323 mod 2^48
setRandomSeed se llama no solo cuando realmente estás cerca de un Woodland Mansion, sino en cada carga de chunk con fines de comprobación
- El impacto varía según la dimensión
- Overworld es la dimensión principalmente afectada
- Nether es seguro porque la generación de estructuras siempre usa un RNG seguro
- The End se ve afectado en la generación inicial por las end cities, pero como no vuelve a afectarse cada vez que se recarga el mismo chunk, es relativamente más seguro
Cómo las coordenadas de drop de ítems revelan el RNG
- Al minar un bloque, el ítem cae en una posición aleatoria dentro del bloque
- Por ejemplo, si la coordenada del bloque es
(10, 20, 30), el ítem aparece entre (10.25, 20.25, 30.25) y (10.75, 20.75, 30.75)
- Esa posición define los offsets X, Y y Z con tres llamadas a
world.rand.nextFloat()
- A partir de las coordenadas del drop se puede volver al valor original de
nextFloat()
- En el proceso de multiplicar el float por 0.5 solo se reduce el exponente, así que no hay pérdida de información
- Luego se convierte a double y se suma la coordenada del bloque, y se transmite por red con precisión completa
java.util.Random.nextFloat() actualiza la semilla de 48 bits, luego toma los 24 bits superiores como entero y los divide por 2^24
- La fórmula del LCG es
newSeed = oldSeed * 25214903917 + 11 mod 2^48
- Tres floats consecutivos entregan los 24 bits superiores de tres semillas consecutivas
Reconstrucción de la semilla con reducción de retículas LLL
- Un enfoque simple es probar los
2^24 candidatos posibles de bits inferiores que coincidan con la primera medición
- Ese método también funciona, pero es lento, así que se usa el enfoque de retículas
- Las tres mediciones dan el rango de estos tres valores
seed
nextSeed(seed)
nextSeed(nextSeed(seed))
- Si se consideran esos tres valores como el punto 3D
(seed, nextSeed(seed), nextSeed(nextSeed(seed))), todas las semillas posibles forman una estructura de retícula
- Los vectores base se toman así
(1, a, a^2)
(0, c, 0)
(0, 0, c)
- donde
a = 25214903917, c = 2^48
- LLL basis reduction encuentra una base más corta y casi ortogonal que genera la misma retícula
- El ejemplo en Mathematica es
LatticeReduce[{{1, a, a^2}, {0, c, 0}, {0, 0, c}}]
- La base resultante es
(1270789291, -2446815537, 2154219555), (-2355713969, 1026597795, 4110294631), (-3756485696, -2345310016, -2015749696)
- Si se convierte el centro del cubo de medición al espacio de la base reducida y luego se redondea cada coeficiente al entero más cercano, se obtiene un punto válido de la retícula, y su primera coordenada es la semilla interna reconstruida
- Un ejemplo de código Java optimizado puede reconstruir la semilla desde tres mediciones en alrededor de 10 ns
Rebobinar la semilla para encontrar la ubicación
- El LCG de
java.util.Random puede avanzarse y también rebobinarse
- Hacia adelante:
newSeed = oldSeed * 25214903917 + 11 mod 2^48
- Hacia atrás:
oldSeed = (newSeed - 11) * 246154705703781 mod 2^48
- Se rebobina la semilla reconstruida y en cada paso se comprueba si podría ser una semilla salida de una comprobación de Woodland Mansion
- El rango del mundo de Minecraft va de -30 millones a +30 millones de bloques
- La implementación fija el rango de Woodland region por eje entre -23440 y +23440
- El número de regiones Woodland posibles es
(23440*2+1)^2, o sea 2,197,828,161
- Comparar los 2.2 mil millones de candidatos es lento, y un
HashSet enorme también consume mucha memoria
- Como el coeficiente Z
132897987541 es impar, tiene inverso en mod 2^48
- El inverso es
211541297333629
- Con eso solo hace falta recorrer los 46,881 candidatos de X y calcular Z para hallar la región candidata
Optimizaciones con GPU y tablas de consulta
- Recorrer solo X era razonable para una semilla individual, pero cuando muchos bots minaban varios bloques por segundo y había que revisar miles de pasos de RNG por cada medición, el procesamiento en tiempo real era difícil en un VPS de baja gama
- Después, la implementación cambió a trabajos por lotes en CUDA y tablas de consulta
- La clave de la tabla es los 32 bits inferiores de la mansion seed
- El valor es la coordenada X de la Woodland region
- Se indica que no hubo colisiones en la clave de 32 bits inferiores, aunque no se entendía por qué
- La tabla usa
2^32 entradas y 2 bytes por entrada, así que requiere alrededor de 9 GB de VRAM
- En una RTX 3090 se podían crackear unas 10 millones de seeds por segundo
- El resultado reconstruido indica la Woodland region de 1280×1280 bloques donde ocurrió la carga de chunk más reciente, y eso basta para ubicar la posición tras unos minutos de búsqueda
Distribución de steps observada en servidores reales
- En teoría, el intervalo promedio entre Woodland seeds es de unos 128,000 RNG steps
- En 2b2t, la mayoría de las veces se encontraba una Woodland seed en unas pocas decenas de steps
- La medición ocurre muy temprano dentro del tick por el momento en que se procesan los paquetes
- Normalmente, el chunk se había cargado en el tick anterior
- Las mediciones confiables empiezan como mínimo desde 4 RNG steps
- Esto se debe a que el código de Woodland Mansion llama a
rand.nextInt cuatro veces antes de la observación
- Los picos grandes aparecían en múltiplos de 1354 steps
- Se sugieren explosiones de end crystal o wither skulls como posible causa
- En el caso de las explosiones de end crystal, el cálculo de daño a bloques
16^3-14^3=1352 más dos efectos de sonido da 1354 steps
ReplayMod y el riesgo de exposición posterior
- Incluso si el servidor se actualizó a una versión moderna o parchó la manipulación del RNG, el riesgo de Randar sigue si quedan datos antiguos
- Algunos jugadores de Minecraft graban paquetes con mods como ReplayMod
- Si el archivo grabado incluye drops de ítems, se puede reconstruir el estado del RNG del servidor de ese momento
- Romper bloques es una acción muy común, así que es probable que esté incluido en la grabación
- Toda ubicación que haya estado activa en Beta 1.8~1.12.2 debe considerarse expuesta, incluso si el servidor se actualizó hace mucho
- Se ofrece una herramienta web del lado del cliente para probar Randar directamente
- En hobune.stream/randar se puede arrastrar un archivo ReplayMod de 1.12.2 para ver las coordenadas
- El archivo de grabación no sale del navegador
Operación de Randar y mapas de calor
- SpawnMasons ya tenía cuentas minando stone/cobblestone 24/7 por otros proyectos, y empezó a registrar las coordenadas de drop de ítems desde esas cuentas
- Se reutilizó el sistema headless de Minecraft usado en Nocom y se agregó una base de datos Postgres para guardar mediciones
- El software para crackear las mediciones de RNG se mejoró varias veces y al final se estableció como un trabajo por lotes asíncrono en CUDA
- Cuando una medición crackeada se agrega a la base de datos, también se actualiza una tabla de análisis para heatmaps
- Guarda el número de hits total, diario y por hora
- Permite verlos en el navegador con una UI de Plotly Dash para elegir rango temporal y granularidad
- El spam de carga de chunks generado por la búsqueda de stashes con Elytra se elimina considerando solo coordenadas cargadas a lo largo de varias horas distintas
- Para dar seguimiento a hotspots detectados, también se agregó un sistema simple de anotaciones compartidas
- Con un bot de Baritone traído de Nocom se automatizó en AFK el proceso de robar y vaciar stashes de ítems
Regiones Woodland señuelo para protección
- Randar no siempre encuentra con exactitud el chunk más reciente
- Si al rebobinar el RNG aparece antes una Woodland region señuelo más reciente, un exploit que devuelve la primera coincidencia puede generar un falso positivo
- En general, una Woodland seed aparece aproximadamente una vez cada 130,000 RNG seeds, pero la distribución tiene outliers
- En 2b2t, se dice que alrededor de 1 de cada 20 mil Woodland regions tiene una propiedad especial de ocultamiento donde existe otra Woodland region dentro de los siguientes 4 RNG steps
- SpawnMasons construyó stashes en ese tipo de regiones
- Para evitar que se cargaran chunks fuera de la región protegida por la distancia de render, construían estructuras compactas
- En la ubicación señuelo colocaban una cuenta AFK y una base pequeña para que otros usuarios de Randar vieran la ubicación falsa
- Según sus propios logs de Randar, esos stashes se mantuvieron “limpios” durante todo el periodo, sin cargar por error Woodland regions vecinas
- Al momento de la publicación, dicen que esos stashes ya habían sido movidos
Ejemplo completo y limitaciones por versión
- Se ofrece un ejemplo en Java que detecta en
SPacketSpawnObject ítems que parecen drops de minería, revierte sus coordenadas a tres mediciones float, luego ejecuta el crack basado en LLL y el rastreo inverso de Woodland region
- Un ejemplo con mediciones reales de 2b2t produce el siguiente resultado
- item drop:
0.41882818937301636, 0.6833633482456207, 0.46088552474975586
- RNG measurements:
5664934 14541261 7076144
- semilla interna:
95041827771683
- Woodland region:
-12008 0
- rango de ubicación:
-15370368,-128 a -15369089,1151
- El ejemplo del diagrama encuentra la Woodland Region
123,456
- El rango final de ubicación es
157312,583552 a 158591,584831
- Incluye las coordenadas de entrada originales
x=157440 z=583680
- En versiones anteriores a 1.11, la estructura explotable no es Woodland Mansion sino otra estructura, así que hace falta otro código
- Antes de 1.9, la posición de los ítems se transmitía no como double sino como parte fraccional fixed-point de 5 bits, así que crackear el estado del RNG con un solo ítem es poco realista y se necesita otra estrategia de medición
Cómo parchearlo
- La forma fácil es buscar un parche o configuración que desactive la manipulación del RNG
- La implementación vulnerable hace que
World.setRandomSeed establezca la seed en el this.rand global y lo devuelva
public Random setRandomSeed(int seedX, int seedY, int seedZ) {
this.rand.setSeed(seedX * 341873128712L + seedY * 132897987541L + seedZ + this.getWorldInfo().getSeed());
return this.rand;
}
- Si se quiere protección completa, se puede cambiar para que devuelva un
Random nuevo en cada llamada
public Random setRandomSeed(int seedX, int seedY, int seedZ) {
return new Random(seedX * 341873128712L + seedY * 132897987541L + seedZ + this.getWorldInfo().getSeed());
}
- Si preocupa el rendimiento, se puede crear un campo RNG separado solo para generación del mundo,
separateRandOnlyForWorldGen, y evitar que se comparta con otros usos
- Hay un parche para PaperMC 1.12.2 disponible en PaperWithRandarPatched commit y un archivo patch alternativo
Apéndice de n0pf0x: otras formas de buscar coordenadas y The End
- n0pf0x usa una búsqueda de coordenadas basada en caché en lugar de la gran tabla de consulta en GPU del lado de Mason
- Cuando ocurre un hit, mete esa coordenada y las coordenadas dentro de un radio cercano en un
HashMap
- El primer pass rebobina el RNG y comprueba rápido si hay hit en caché o duplicado con la seed procesada justo antes
- El segundo pass solo corre si falla el primero, y usa el algoritmo costoso de búsqueda de coordenadas descrito antes
- Este enfoque con caché puede ayudar a reducir falsos positivos porque salta ubicaciones válidas menos plausibles
- En The End, el RNG solo se ve afectado cuando un chunk se genera por primera vez, así que no es fácil observar repetidamente la carga de chunks de una base como en Overworld
- En The End hay dos escenarios aprovechables
- Cuando un jugador en una base se mueve y genera chunks no generados antes
- Cuando un jugador que va hacia una base crea una trail de chunks nuevos en su ruta
- Esa trail permitiría construir un sistema de identificación automática, pero n0pf0x no lo implementó y la seguía manualmente de forma visual
- Para identificar jugadores se usó la idea de End Occupancy Tracker (EOT)
- Se basa en la suposición de que la cantidad de llamadas al RNG por tick se correlaciona hasta cierto punto con la cantidad de chunks cargados, y eso a su vez se relaciona con la cantidad de jugadores en esa dimensión
- Mirando si el número de llamadas al RNG sube o baja bruscamente justo después de que un jugador entra o sale, se intenta estimar cuántos jugadores hay en The End
- EOT solo se probó en 9b9t y puede no funcionar en otras condiciones de servidor como 2b2t
- Debe ser posible muestrear el RNG de forma estable en cada tick
- Si la actividad de jugadores en The End es mucho mayor, puede volverse más difícil
1 comentarios
Comentarios de Hacker News
En 1999~2000 hubo una International RoShamBo Programming Competition donde competían bots de piedra, papel o tijera [1]
El bot de referencia jugaba con selección aleatoria, una estrategia teóricamente imposible de vencer, pero una participación en broma fue diseñada para reconstruir el estado del generador de números aleatorios y predecir con 100% de precisión qué jugada haría el jugador aleatorio
Corrección: ese bot era “Nostradamus” de Tim Dierks, y fue declarado ganador de la categoría “supermodified” en la primera competencia [2]
[1] https://web.archive.org/web/20180719050311/http://webdocs.cs...
[2] https://groups.google.com/g/comp.ai.games/c/qvJqOLOg-oc
En realidad me fui al campo de la seguridad, escribí el RFC de TLS y terminé siendo principal engineer de seguridad en Google. Gracias por traer de vuelta los recuerdos
https://web.archive.org/web/20180719050236/http://webdocs.cs...
Nostradamus fue escrito por Tim Dierks, VP of Engineering en Certicom y con mucha experiencia en criptografía, y vencía al jugador óptimo mediante ingeniería inversa del estado interno del generador
random(). Según él, “fue más fácil y más difícil de lo que esperaba”. Aun así, por deportividad, jugaba de forma óptima contra todos los demás rivalesFork Bot surgió de una idea que se le ocurrió a Dan Egnor unos minutos después de oír hablar del concurso, y aprovechaba la regla de “se permiten rutinas de biblioteca” para lanzar tres procesos con
fork(), hacer que cada uno jugara una opción distinta y matar a los dos que perdieran. Andreas Junghanns lo implementó en unas 10 líneas de código, pero después del primer turno las tres opciones perdieron contra Psychic Friends Network, así que el programa terminó y el resto de las partidas se registraron como abandonoPsychic Friends Network era un código C ofuscado realmente gracioso, escrito por Michael Schatz y gente de RST Corporation, que incluía funciones auxiliares para buscar buen karma, consultar el horóscopo, cocinar espagueti y pizza mística, usar
#definepara convertir demócratas en comunistas y hacerundefde Dios. Aún siguen tratando de averiguar exactamente qué hace con los stack frames, pero salvo contra un meta-meta-tramposo, nunca anota menos de +998 en una partidaThe Matrix fue escrito por Darse Billings, quien llevaba el prestigioso título de “Student for Life”, y vencía a todos los oponentes con puntuación perfecta siguiendo el simple principio de que “no hay cuchara”
Como The Matrix también era el programa del torneo, tenía acceso total a todos los demás algoritmos, estructuras de datos y rutinas de salida, así que era poco probable que alguien lo superara. Por eso se declaró resuelta esta categoría y fue eliminada de competencias posteriores
Al final, esa transparencia sí ayudó a mejorar la seguridad
La reducción de retículas LLL es justo el mismo algoritmo que hace unos días también se usó en un CVE para romper claves de PuTTY con nonces sesgados
tptacekexplicó un poco el ataque y también dejó enlazado un problema de cryptopals con el que, si entrecierras los ojos, casi puedes fingir que lo entiendes https://news.ycombinator.com/item?id=40045377De forma similar, en el servidor de Minecraft SciCraft había una especie de dispositivo de magia negra que manipulaba de forma determinista el estado del generador de números aleatorios, haciendo que cayera un rayo “aleatorio” sobre un bloque específico en cada frame para mejorar los drops de creepers en una granja de creepers https://youtu.be/TM7SutJyDCk
En los tres casos, lo único necesario es álgebra lineal básica, y ni siquiera demasiada. Kelby espera que entiendas Gram-Schmidt, que es algo que suele aparecer justo antes del examen parcial en una introducción universitaria al álgebra lineal
No tengo palabras para expresar lo bueno que es este texto. Me alegró la semana
Una explicación muy breve del mismo proceso que luego puedes seguir en Python:
https://crypto.stackexchange.com/questions/37836/problem-wit...
https://youtu.be/ZcdN1wCJPqM?t=390
Te levantas por la mañana y resulta que hay bloques flotando en el cielo que no estaban la noche anterior; al principio parecen una niebla fantasmal, pero pronto se distinguen como redstone, observadores y bloques de slime, y se ve una caída infinita de TNT
Todo eso pasa solo porque el servidor filtró tu ubicación. Aun así, todavía podrías escapar, y quizá tengas unos segundos para sacar cosas de los cofres y huir o para construir un refugio de obsidiana. Pero eso es todo
No hay tiempo para construir un cañón de precisión, y de todos modos no podrías ajustar la altitud. Si tienes élitros y cohetes, quizá podrías ir a estorbar, pero hay un agujero gigante de world eater justo a 16 chunks de distancia. ¿Habrán puesto trampas de lava en cada portal del Nether cercano?
He visto muchos problemas de generadores de números aleatorios interesantes y graciosos, pero este es uno de los exploits más sofisticados en relación con lo que se obtiene. Parece una obra de arte
Es un exploit bastante genial
La idea de un servidor donde se permite abusar de bugs también está buena, y parece casi una etapa completamente distinta del juego
Si el metaverso llegara a hacerse realidad, da la impresión de que “pelear de verdad”, en vez de limitarse a las mecánicas de combate del juego, se vería algo así
Como desde hace mucho hay un montón de ítems valiosos duplicados, el PvP terminó consistiendo en spamear end crystals que, al romperse, causan un daño enorme, y la defensa pasa a depender de cuántos “tótems de la inmortalidad” tengas para absorber daño letal
Naturalmente, los clientes hack automatizan la colocación de end crystals, la recarga de tótems y la identificación de posiciones débiles y fuertes, y el jugador sigue esas indicaciones para seguir metiendo daño
Un poco antes de eso, también hubo espadas hackeadas con +32,767 de daño que mataban al instante, hasta que el servidor las parcheó
ARK: Survival Evolved y Eve Online son infames porque clanes gigantes de miles de jugadores llevan al extremo el metajuego y el abuso de bugs
No siempre es algo romántico. En ARK había mecanismos que permitían doxxear a jugadores y sus múltiples cuentas de Steam, y parece que durante la Great War algunas relaciones dentro del juego también se desbordaron hacia la vida real
A veces incluso se usan tácticas muy básicas. Por ejemplo, durante un asalto, si construías una torre enorme y la derrumbabas para provocar una denegación de servicio en el servidor y hacerlo crashear, el servidor volvía a un respaldo de 10 a 20 minutos antes, así que las bases con jugadores activos se volvían muy difíciles de asaltar. Era una táctica antiquísima y se arregló hace años
Rust también tuvo una política que alentaba a difundir bugs y exploits públicamente en YouTube, aunque con otro objetivo: hacer que los desarrolladores los detectaran y parchearan más rápido. Como resultado, terminó siendo un juego bastante sólido, muy difícil de explotar si no usas hacks externos reales
Eso sí, solo de los que se pueden explotar manualmente con un control normal; no es hacking real. Un exploit llamado Wobbling fue prohibido en 2019, y este es un juego de 2001
Acabo de ver un video sobre este tema. Es un caso de advertencia clarísimo sobre los riesgos de cuando las fuentes de aleatoriedad interactúan entre sí, y aplica mucho también a sistemas importantes
Por rendimiento, muchas veces se comparte un generador de números aleatorios en el código, y escuchar historias así definitivamente te hace pensarlo dos veces
Porque parecería más difícil observar suficientes puntos donde se actualiza. Pero este caso demuestra de forma bastante impactante y entretenida que esa intuición estaba equivocada
Este video es impresionante: https://www.youtube.com/watch?v=maMpMOnIJDE
No sabía que la comunidad fuera tan sofisticada
Si esto te sorprendió, mira esto también. Te va a volar la cabeza
https://www.youtube.com/watch?v=ea6py9q46QU
y luego
https://www.youtube.com/watch?v=GaRurhiK-Lk
Yendo más allá, este tipo de crackeo de generadores aleatorios incluso llegó a implementarse dentro del propio juego
https://youtu.be/FPmQ0rnJjNc?si=tTFObcfZ-ILanL_A
Sorprendentemente, existe una máquina construida dentro del propio Minecraft llamada Mess Detector. En vez de usar block drops, utiliza la posición de TNT encendido para predecir el estado interno del generador aleatorio
https://www.youtube.com/watch?v=FPmQ0rnJjNc
Esto parece un ataque de extensión por compromiso de estado (https://en.wikipedia.org/wiki/Random_number_generator_attack)
Es un ataque al que puede ser vulnerable un PRNG que no sea un generador seudoaleatorio criptográficamente seguro (CSPRNG)
A estas alturas, hasta se siente poco seguro que una biblioteca ofrezca un PRNG como opción predeterminada. Se parece a permitir TLSv1.0 o blowfish por defecto en 2024