Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
ds3-nrssr-rce — Documentación y código de prueba de concepto para CVE-2022-24125 y CVE-2022-24126. | Kitploit
Herramientas/GitHubGitHub/tremwil/ds3-nrssr-rce
Frameworks de ExploitsAnálisis de VulnerabilidadesExplotaciónIngeniería InversaShellcodePruebas de PenetraciónAprendizaje y EducaciónRed TeamingGeneración de ShellcodeDesarrollo de PayloadsExplotación de Binarios
1698hace 3 añosRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
GitHub
tremwil/ds3-nrssr-rce

ds3-nrssr-rce

Documentación y código de prueba de concepto para CVE-2022-24125 y CVE-2022-24126.

Ver Repositorio

Actualización: Dark Souls III 1.15.1

Se ha lanzado una nueva actualización del juego, la 1.15.1, para Dark Souls III el 25/08/2022, junto con la restauración de los servicios en línea. Esta actualización corrigió tanto CVE-2022-24125 como CVE-2022-24126, junto con una amplia variedad de otras posibles vulnerabilidades de seguridad presentes en la red P2P del juego (lecturas/escrituras fuera de límites). Además, se han corregido todos los exploits conocidos que permitían corromper la partida de otros jugadores. Muchos trucos menores comunes (p. ej., "cuchillo de maldición") que se podían encontrar a menudo durante el multijugador en línea también han sido parcheados.

ds3-nrssr-rce

Este repositorio contiene código de prueba de concepto y documentación para el exploit RCE más reciente que afecta a los juegos de FROM SOFTWARE, CVE-2022-24126. Aunque teóricamente es posible en otros juegos, la atención se centra en Dark Souls III, ya que es el juego en el que he realizado mi investigación. Por ahora solo existe código de prueba de concepto para Dark Souls III; se ha confirmado que la vulnerabilidad está presente en:

  • Dark Souls 1 PTDE (crédito: LukeYui)
  • Dark Souls Remastered (crédito: metal-crow)
  • Dark Souls 2 (incluido Scholar) (crédito: LukeYui)
  • Dark Souls 3 (hasta 1.15.0) (crédito: tremwil)

El código vulnerable también está presente en Sekiro (crédito: LukeYui), aunque no hay forma de activarlo. La presencia en Demon's Souls no se ha confirmado, pero es muy probable. Si bien la prueba de red cerrada se vio afectada por esto, la versión final de Elden Ring no lo está. De hecho, una enorme lista de fallos de red, lecturas/escrituras fuera de límites y exploits que permitían a los jugadores modificar los datos del juego de sus pares, presentes en Dark Souls III, han sido parcheados en Elden Ring. Felicitaciones a LukeYui por recopilar esta lista y a FROM SOFTWARE por actuar con rapidez. Me alegra decir que Elden Ring es, indiscutiblemente, el título de FROM SOFTWARE más seguro en cuanto al alcance del daño que los hackers pueden infligir.

Disipando conceptos erróneos

Contrariamente a la creencia popular, esto NO es un exploit de red peer-to-peer. Está relacionado con el servidor de emparejamiento y, por lo tanto, es mucho más grave, ya que no necesitas participar en ninguna actividad multijugador para ser vulnerable debido a otra vulnerabilidad del servidor de emparejamiento (CVE-2022-24125).

En Dark Souls III, un atacante malintencionado que abusara de esto habría podido ejecutar de forma fiable una carga útil de hasta 1.3MiB1 de shellcode en la máquina de cada jugador en línea en cuestión de segundos.

Con un promedio de unos 20.000 jugadores concurrentes en los meses anteriores al cierre de los servidores, era claramente un problema que necesitaba una solución inmediata, especialmente ante la posibilidad de que estuviera en Elden Ring. Dado que FROM SOFTWARE no había actuado después de más de 40 días desde mi informe inicial con vídeos de prueba de concepto y documentación detallada del exploit (en la que se basa gran parte de este readme), decidí demostrar la existencia del exploit públicamente de manera benigna, con la esperanza de atraer la atención para que los desarrolladores lo abordaran, y funcionó.

Tabla de contenidos

  • Resumen del exploit (CVE-2022-24126)
  • Vectores de distribución (CVE-2022-24125)
  • La táctica general de explotación para todos los juegos
    • Bug #1: Sin comprobación de límites en el analizador de listas de entradas
    • Bug #2: Desbordamiento de búfer en el analizador de NRSessionSearchResult
    • Preparando el terreno para la cadena ROP
  • Código de prueba de concepto para Dark Souls III
    • Ejecutar el código PoC
    • Vector de ataque
    • Cadena de redirección de llamadas virtuales
    • Información adicional

Resumen del exploit (CVE-2022-24126)

La comprobación inadecuada de límites en un búfer de pila y en el campo de tamaño de datos durante el análisis de los datos de emparejamiento de NRSessionSearchResult permite a un atacante ejecutar código arbitrario. El desbordamiento de pila permite sobrescribir los dos bytes inferiores de vftable_ptr del objeto DLMemoryInputStream utilizado internamente por el lector de flujo, redirigiendo la ejecución a código vecino cuidadosamente elegido. La explotación inteligente de la estructura del objeto DLMemoryInputStream y de su campo de tamaño de datos permite entonces lograr la redirección de código arbitrario, con RCX apuntando a la dirección de nuestro paquete. A partir de ahí, se puede usar una serie de redirecciones de código mediante llamadas virtuales con diferentes desplazamientos (que ahora saltarán a las direcciones que escribimos en el búfer del paquete) para lograr la ejecución de código arbitrario.

Vectores de distribución (CVE-2022-24125)

Los vectores de distribución son lo que hace que este RCE en particular sea especialmente grave (más allá de ser ya un RCE). El exploit se transmite a través de solicitudes push de emparejamiento que contienen información de NRSessionSearchResult. Esto significa que el atacante puede apuntar a cualquiera que se una a su sesión en línea. En particular, para DS3:

  • invocaciones (PushRequestSummonSign)
  • invasores de espíritu oscuro (PushRequestAllowBreakInTarget)
  • jugadores que se unen mediante pacto (PushRequestVisit)
  • combatientes de arena (PushRequestAcceptQuickMatch)

Esto ya es bastante malo, pero el verdadero potencial se desbloquea con la solicitud RequestSendMessageToPlayers:

root@kitploit:~
message RequestSendMessageToPlayers { 
    repeated uint32 player_ids = 1; 
    required bytes push_message = 2;
}

El anfitrión usa esta solicitud para enviar directamente el mensaje push PushRequestAllowBreakInTarget a los invasores, de modo que puedan obtener las coordenadas de aparición y unirse a su sesión P2P. Eso es todo. Esa es la única forma en que el juego usa esta solicitud.

La táctica general de explotación para todos los juegos

Si bien el RCE no se traslada exactamente a todos los juegos, la idea central del exploit, que le da al atacante redirección de código arbitrario, es la misma. Si esto se puede lograr, es muy probable que se pueda encontrar una cadena de llamadas virtuales o una cadena ROP específica del juego. Este "primer paso" utiliza las siguientes vulnerabilidades:

Bug #1: Sin comprobación de límites en el analizador de listas de entradas

Las solicitudes push de emparejamiento que contienen información de unión a sesión almacenan dicha información en un formato binario personalizado que consiste en una cadena de entradas de datos delimitadas por longitud. Cada entrada tiene el siguiente formato:

root@kitploit:~
struct Entry
{
    uint32_t type_or_id; // not sure, but probably a type (fixed length = 2, variable length = 1 ?)
    uint32_t size;
    uint8_t data[size];
}

La función del juego responsable de copiar los datos de estas entradas confía ciegamente en el campo de tamaño, lo que crea una lectura fuera de límites. Un cliente malintencionado puede abusar de esto estableciendo el campo de tamaño a valores como 0x7FFFFFFF, lo que hace que la asignación de memoria falle y el juego de la víctima se bloquee. Más tarde, este tamaño también se pasa al constructor de un DLMemoryInputSteam, que es una parte instrumental del exploit.

Bug #2: Desbordamiento de búfer en el analizador de NRSessionSearchResult

Una de las entradas en la estructura de datos descrita anteriormente es un objeto NRSessionSearchResult serializado. El analizador de estos datos primero analiza una lista de propiedades. Estas propiedades pueden ser enteros de 4 bytes, enteros de 8 bytes o cadenas anchas terminadas en nulo. A esta lista de propiedades le sigue el nombre de persona de Steam del anfitrión como una cadena ancha terminada en nulo y algunos datos adicionales que no son importantes para el exploit. Tanto esta función como el analizador de la lista de propiedades usan un búfer de pila de tamaño fijo para leer cadenas, y en ambos casos no se realiza ninguna comprobación de límites en el búfer. Aquí está el código del juego responsable de copiar el nombre del anfitrión (producido con el descompilador de Ghidra y luego limpiado):

root@kitploit:~
size_t idx = 0;
wchar_t wchr = 0;
do {
  // read_wchar() function at vftable index 17 of DLEndianStreamReader
  wchr = stream_reader->read_wchar();
  player_name_buff[idx] = wchr;
  idx++;
} while (wchr != 0);

Esto conduce a un exploit de desbordamiento de búfer, que permite al atacante corromper la pila.

Preparando el terreno para la cadena de llamadas virtuales / ROP

Para lograr la redirección de código arbitrario, usamos esto y el diseño de memoria de un objeto DLMemoryInputStream instanciado en la pila por la función que llama al analizador, y que es utilizado internamente por el lector de flujo:

root@kitploit:~
struct DLMemoryInputStream {
    uintptr_t* vftable_ptr; // Offset 0
    size_t data_size;       // Offset 4 (32bit) / 8 (64bit)
    uint8_t* data_buffer;   // Offset 8 (32bit) / 16 (64bit)
    // Entries after the buffer are not important for the exploit
}

Dado que controlamos el campo data_size (Bug #1), se puede establecer en la dirección de memoria de pila del campo data_buffer. Esto tendrá éxito siempre que la dirección sea constante y no demasiado grande (DS3 cumple esos requisitos). Dado que el compilador coloca el búfer de pila en la parte superior del marco, el atacante puede usar Bug #2 para sobrescribir los dos bytes inferiores de vftable_ptr del DLMemoryInputStream. Por lo tanto, cuando el DLEndianStreamReader lea el siguiente carácter, llamará internamente al y el código será redirigido. Los 2 bytes dan suficiente margen para saltar a la función número 22 en la vftable de , que llama al sexto método virtual del objeto apuntado por su primer campo. En un proceso de 64 bits (es decir, Dark Souls III), se ejecutarían las siguientes instrucciones:

Consulta aquí para más detalles sobre estos 3 gadgets. Si para algún otro juego este método de llamada virtual no es un enfoque factible, la redirección de código arbitrario aún puede usarse para preparar un exploit ROP más tradicional.

Código de prueba de concepto para Dark Souls III

Ejecutar el código PoC

Para ejecutar el código de prueba de concepto, primero debes tener un servidor al que conectarte. Si bien los servidores oficiales se han deshabilitado debido al exploit, puedes configurar uno privado usando ds3os. ds3os está diseñado para imitar el comportamiento del servidor comercial lo más fielmente posible, pero ya se han implementado parches de seguridad en este proyecto para corregir este exploit. Sin embargo, aún puedes configurar un entorno de pruebas compilando el proyecto tú mismo con las constantes SEND_MESSAGE_TO_PLAYERS_SANITY_CHECKS y NRSSR_SANITY_CHECKS establecidas en false en BuildConfig.h. Esto imita el comportamiento inseguro del servidor comercial. Sigue las instrucciones proporcionadas por ds3os para iniciar el juego y conectarte a tu servidor.

Una vez hecho esto y con tu juego conectado a los servidores, compila el código PoC e inicia el ejecutable Injector.exe. Inyectará una DLL que contiene el código del exploit en el proceso de Dark Souls III. Luego, esta DLL usará la función del juego que envía mensajes FRPG al servidor para entregar el exploit a tu propio cliente.

Vector de ataque

Para la prueba de concepto decidí usar un mensaje PushRequestVisit enviado mediante RequestSendMessageToPlayers. Esta es la versión más potente del exploit, en el sentido de que el juego del objetivo analizará inmediatamente los datos vulnerables después de recibirlos en todas las situaciones (incluso en el menú principal).

Cadena de redirección de llamadas virtuales

Offset 0x18: 140e97700

root@kitploit:~
LEA       RAX,[DAT_144786150]
RET

Este gadget se usa en el de 0x68. Necesitamos poner en RAX una dirección inferior pero bastante cercana a 144786998; esta es la más cercana.

Offset 0x40: 1422be020

root@kitploit:~
MOV       RDX,RAX
MOV       R8,qword ptr [RCX]
CALL      qword ptr [R8 + 0x68]

Para poder usar el gadget en el offset 0x68, necesitamos que la dirección del búfer de datos esté almacenada en RDX y que RCX permanezca igual. Esto logra precisamente eso.

Offset 0x68: 140e40f15

Información adicional

Recomiendo revisar el código fuente del código de prueba de concepto, ya que tiene muchos comentarios que detallan la estructura del paquete. Si quieres ver lo que sucede en cada paso en tiempo real (¡deberías, es bastante genial!), puedes inyectar la DLL de la prueba de concepto mientras ejecutas el juego bajo un depurador con puntos de interrupción en las siguientes direcciones de interés:

140ca5960

Esencialmente donde comienza el exploit. Esta función es responsable de analizar los datos de la lista de entradas delimitadas por tamaño del mensaje PushRequestVisit. Primero extrae cada entrada de la lista en diferentes vectores:

root@kitploit:~
0x140ca59f8:
    player_data_cpy_ptr = (std_vector *)VectorCopy2_140ca4ef0(&player_data_cpy,player_data);
    FUN_140ca5010(player_data_cpy_ptr,&spawn_data,0x1c);
    FUN_140ca5010(player_data_cpy_ptr,&unk,4);
    FUN_140ca4fa0(player_data_cpy_ptr,&nrssr_data);

La función 140ca5010 comprueba los tamaños de las entradas, pero 140ca4fa0 es para entradas de tamaño variable y no realiza comprobaciones de validez en el campo de tamaño (Bug #1). Para lograr el exploit de redirección de código arbitrario descrito anteriormente, necesitamos establecerlo en 14F3B0. Esto causará una lectura fuera de límites de aproximadamente 1.3MiB, pero la página de memoria debería ser lo suficientemente grande para evitar violaciones de acceso.

140ca56b0

Esta función es llamada por la anterior con nrssr_data como argumento. Crea el objeto DLMemoryInputStream en la pila, que luego se pasa como argumento al analizador NRSSR.

141955f50: ParseNRSessionSeachResult

El analizador de NRSessionSearchResult. Verifica la firma y los números de versión de NRSSR (14196a0f0), analiza la lista de propiedades (14196a260), el nombre del anfitrión (14195603a) y algo más de información (consulta rce.h)

14195603a

Bucle en la función anterior que copia de forma insegura el nombre del anfitrión (Bug #2). Aquí hay algunas direcciones que pueden ayudar a seguir lo que está sucediendo durante el desbordamiento del búfer:

  • Dirección del búfer de pila del analizador: 14F128
  • Dirección de pila de DLMemoryInputStream: 14F3A0
  • Puntero vtable de DLMemoryInputStream después de la sobrescritura: 1439e8b30
  • Offset de la función virtual en el DLMemoryInputStream utilizado por el DLInputStreamReader: 0x18

1439e8b48

root@kitploit:~
MOV       RCX,qword ptr [RCX + 0x8]
MOV       RAX,qword ptr [RCX]
JMP       qword ptr [RAX + 0x40]

Donde terminamos después de la primera redirección de código causada por la vftable del flujo de memoria sobrescrita. Aquí es donde comienza la cadena de redirecciones de llamadas virtuales.

Footnotes

  1. Para Dark Souls III Ver. 1.15. El tamaño máximo teórico de la carga útil depende del diseño de la pila y, por lo tanto, variará según el juego y la versión. ↩

Descargar herramienta

Sin embargo, permite que cualquier cliente envíe mensajes push arbitrarios a cientos de miles de jugadores específicos.

No puedo enfatizar lo horriblemente inseguro que es esto. Cualquier jugador puede básicamente hacerse pasar por el servidor de emparejamiento. Al usar esta solicitud para enviar el exploit a través de un PushRequestVisit, cualquier jugador en línea puede ser atacado de forma remota siempre que se conozca su ID de jugador. El atacante también puede enviar el exploit a toda la base de jugadores en línea muy rápidamente enviando múltiples solicitudes, cada una conteniendo una gran porción de posibles IDs de jugador.

DLMemoryInputStream
DLEndianStreamReader
root@kitploit:~
MOV       RCX,qword ptr [RCX + 0x8]
MOV       RAX,qword ptr [RCX]
JMP       qword ptr [RAX + 0x40]

Dado que RCX es un puntero al objeto DLMemoryInputStream, la primera instrucción escribe el campo data_size, que el atacante ha establecido mediante Bug #1 a una dirección de pila que apunta al campo data_buffer, en RCX. Las dos siguientes instrucciones redirigirán así la ejecución a la dirección de memoria que el atacante ha escrito en el desplazamiento 0x40 del búfer de datos. ¡Se ha logrado la redirección de código arbitrario! A partir de ahí, el atacante puede establecer una cadena de redirección de código que copie su carga útil en una región de memoria adecuada y la ejecute eligiendo código cercano a llamadas virtuales con diferentes desplazamientos, ya que el búfer ahora actúa como una tabla de métodos virtuales. Para la prueba de concepto de Dark Souls III encontré una configuración que solo requiere 3 gadgets para lograr RCE:

  • 0x18: 140e97700
  • 0x40: 1422be020
  • 0x68: 140e40f15
root@kitploit:~
; Jumping here from the gadget at offset 0x40
MOV       RBX,RDX
CMP       R9,R8

 ; Never jumps, R9 != R8
JZ        LAB_140e40f7a
MOV       RAX,qword ptr [RCX]
MOV       R8,qword ptr [RSP + 0x50]
MOV       RDX,R9
MOV       qword ptr [RSP + 0x30],RSI

; Call gadget at offset 18 (140e97700). Loads 144786150 into RAX
CALL      qword ptr [RAX + 0x18]
MOV       RSI,RAX
TEST      RAX,RAX

; Never jumps, RAX is the data buffer addr.
JZ        LAB_140e40f4d 
CMP       RBP,RDI
MOV       RDX,RBX
MOV       RCX,RAX
CMOVC     RDI,RBP
MOV       R8,RDI ; RDI is a stack address close to 14F3B0, so the memcpy succeeds
CALL      memcpy

LAB_140e40f4d:
TEST      RBX,RBX
; Never jumps as RBX == RDX == data buffer addr, nonzero. 
JZ        LAB_140e40f62 

; We have our now fully control this RWE memory region due to the memcpy at 144786150. RCE has been achieved!
MOV       RCX,qword ptr [DAT_144786998]
MOV       RDX,RBX
MOV       RAX,qword ptr [RCX]
CALL      qword ptr [RAX + 0x68]

Este gadget hace casi todo por nosotros. Llama al offset 0x18 para obtener un puntero de destino para memcpy, copia nuestro paquete allí y luego llama a la función virtual en el offset 0x68 sobre el objeto estático en 144786998, que ahora controlamos por completo gracias a la llamada a memcpy. Dado que la cantidad de memoria corrompida por memcpy es grande y algunas regiones están siendo escritas constantemente por otros hilos del juego, el exploit primero carga una carga útil de «configuración» que se copia en una ubicación segura, suspende todos los demás hilos y vuelve a copiar nuestra carga útil real antes de saltar a ella. Consulta rce.h para más información.