Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
ds3-nrssr-rce — Documentation et code de preuve de concept pour CVE-2022-24125 et CVE-2022-24126. | Kitploit
Outils/GitHubGitHub/tremwil/ds3-nrssr-rce
Frameworks d'ExploitationAnalyse des VulnérabilitésExploitationRétro-ingénierieShellcodeTests d'IntrusionApprentissage et ÉducationRed TeamingGénération de ShellcodeDéveloppement de Charges UtilesExploitation de Binaires
16985il y a 4 ansVérifié par Kitploit
GitHub
tremwil/ds3-nrssr-rce

ds3-nrssr-rce

Documentation et code de preuve de concept pour CVE-2022-24125 et CVE-2022-24126.

Voir le dépôt

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Mise à jour : Dark Souls III 1.15.1

Une nouvelle mise à jour du jeu, la 1.15.1, est sortie pour Dark Souls III le 25/08/2022, en même temps que la restauration des services en ligne. Cette mise à jour a corrigé à la fois CVE-2022-24125 et CVE-2022-24126, ainsi qu'une grande variété d'autres vulnérabilités de sécurité potentielles présentes dans le réseau P2P du jeu (lectures/écritures hors limites). De plus, tous les exploits connus permettant de corrompre la sauvegarde d'autres joueurs ont été corrigés. De nombreuses tricheries mineures courantes (par exemple « curse knife ») souvent rencontrées lors du multijoueur en ligne ont également été corrigées.

ds3-nrssr-rce

Ce dépôt contient le code de preuve de concept et la documentation pour l'exploit RCE le plus récent affectant les jeux de FROM SOFTWARE, CVE-2022-24126. Bien que théoriquement possible dans d'autres jeux, l'accent est mis sur Dark Souls III, car c'est le jeu sur lequel mes recherches ont été menées. À l'heure actuelle, le code de preuve de concept n'existe que pour Dark Souls III. La vulnérabilité a été confirmée comme présente dans :

  • Dark Souls 1 PTDE (crédit : LukeYui)
  • Dark Souls Remastered (crédit : metal-crow)
  • Dark Souls 2 (y compris Scholar) (crédit : LukeYui)
  • Dark Souls 3 (jusqu'à la version 1.15.0) (crédit : tremwil)

Le code vulnérable est également présent dans Sekiro (crédit : LukeYui), bien qu'il n'existe aucun moyen de le déclencher. Sa présence dans Demon's Souls n'a pas été confirmée mais est très probable. Bien que le test réseau fermé ait été affecté, la version finale d'Elden Ring ne l'est pas. En réalité, une énorme liste de crashs réseau, de lectures/écritures hors limites et d'exploits permettant aux joueurs de modifier les données de jeu de leurs pairs, présents dans Dark Souls III, a été corrigée dans Elden Ring. Merci à pour avoir compilé cette liste et à FROM SOFTWARE pour avoir agi rapidement ! Je suis heureux de dire qu'

LukeYui
Elden Ring est sans conteste le titre de FROM SOFTWARE le plus sûr en ce qui concerne l'étendue des dégâts que les pirates peuvent infliger.

Dissiper les idées reçues

Contrairement à la croyance populaire, il ne s'agit PAS d'un exploit de réseau pair-à-pair. Il est lié au serveur de matchmaking et est donc bien plus grave, car vous n'avez pas besoin de participer à une quelconque activité multijoueur pour être vulnérable, en raison d'une autre vulnérabilité du serveur de matchmaking (CVE-2022-24125).

Dans Dark Souls III, un attaquant malveillant abusant de cette faille aurait pu exécuter de manière fiable une charge utile allant jusqu'à 1,3 Mio1 de shellcode sur la machine de chaque joueur en ligne en quelques secondes.

Le jeu ayant une base de joueurs simultanés moyenne d'environ 20 000 joueurs dans les mois précédant la fermeture des serveurs, il était clairement nécessaire de corriger ce problème immédiatement, surtout avec la possibilité qu'il soit présent dans Elden Ring. Comme FROM SOFTWARE n'avait toujours pas agi plus de 40 jours après mon rapport initial accompagné de vidéos de preuve de concept et d'une documentation détaillée sur l'exploit (sur laquelle une grande partie de ce readme est basée), j'ai décidé de démontrer publiquement l'existence de l'exploit de manière bénigne, dans l'espoir d'attirer l'attention pour que les développeurs s'en occupent — et cela a fonctionné.

Table des matières

  • Résumé de l'exploit (CVE-2022-24126)
  • Vecteurs de distribution (CVE-2022-24125)
  • La tactique d'exploitation générale pour tous les jeux
    • Bug n° 1 : absence de vérification des limites dans l'analyseur de liste d'entrées
    • Bug n° 2 : dépassement de tampon dans l'analyseur NRSessionSearchResult
    • Ouvrir la voie à la chaîne ROP
  • Code de preuve de concept pour Dark Souls III
    • Exécution du code PoC
    • Vecteur d'attaque
    • Chaîne de redirection d'appels virtuels
    • Informations supplémentaires

Résumé de l'exploit (CVE-2022-24126)

Une vérification des limites inappropriée sur un tampon de pile et le champ de taille des données lors de l'analyse des données de matchmaking NRSessionSearchResult permet à un attaquant d'exécuter du code arbitraire. Le débordement de pile permet d'écraser les deux octets inférieurs de vftable_ptr de l'objet DLMemoryInputStream utilisé en interne par le lecteur de flux, redirigeant l'exécution vers du code voisin soigneusement choisi. Une exploitation astucieuse de la structure de l'objet DLMemoryInputStream et du champ de taille des données permet ensuite d'obtenir une redirection de code arbitraire, avec RCX pointant vers l'adresse de notre paquet. À partir de là, une série de redirections de code via des appels virtuels avec différents décalages (qui sauteront désormais vers les adresses que nous avons écrites dans le tampon du paquet) peut être utilisée pour parvenir à l'exécution de code arbitraire.

Vecteurs de distribution (CVE-2022-24125)

Les vecteurs de distribution sont ce qui rend cet RCE particulièrement grave (au-delà du fait qu'il s'agisse déjà d'un RCE). L'exploit est transmis via des requêtes push de matchmaking contenant des informations NRSessionSearchResult. Cela signifie que l'attaquant peut cibler toute personne qui rejoint sa session en ligne. En particulier, pour DS3 :

  • les invocations (PushRequestSummonSign)
  • les envahisseurs esprit sombre (PushRequestAllowBreakInTarget)
  • les joueurs rejoignant via une alliance (PushRequestVisit)
  • les combattants de l'arène (PushRequestAcceptQuickMatch)

C'est déjà assez grave, mais le véritable potentiel est débloqué par la requête RequestSendMessageToPlayers :

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

L'hôte utilise cette requête pour envoyer directement le message push PushRequestAllowBreakInTarget aux envahisseurs afin qu'ils puissent obtenir les coordonnées d'apparition et rejoindre sa session P2P. C'est tout. C'est la seule façon dont cette requête est utilisée par le jeu.

Pourtant, elle permet à n'importe quel client d'envoyer des messages push arbitraires à des centaines de milliers de joueurs spécifiques.

Je ne saurais trop insister sur le danger extrême que cela représente. N'importe quel joueur peut essentiellement se faire passer pour le serveur de matchmaking. En utilisant cette requête pour envoyer l'exploit via un PushRequestVisit, tout joueur en ligne peut être ciblé à distance par l'attaquant, à condition que son identifiant de joueur soit connu. L'attaquant peut également envoyer l'exploit à l'ensemble de la base de joueurs en ligne très rapidement en envoyant plusieurs requêtes, chacune contenant une grande tranche d'identifiants de joueurs possibles.

La tactique d'exploitation générale pour tous les jeux

Bien que le RCE ne se transpose pas exactement à chaque jeu, l'idée centrale de l'exploit, qui donne à l'attaquant une redirection de code arbitraire, est la même. Si cela peut être réalisé, il est très probable qu'une chaîne d'appels virtuels ou une chaîne ROP spécifique au jeu puisse ensuite être trouvée. Cette « première étape » utilise les vulnérabilités suivantes :

Bug n° 1 : absence de vérification des limites dans l'analyseur de liste d'entrées

Les requêtes push de matchmaking contenant des informations de connexion à une session stockent ces informations dans un format binaire personnalisé composé d'une chaîne d'entrées de données délimitées par leur longueur. Chaque entrée a le format suivant :

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 fonction du jeu chargée de copier les données de ces entrées fait aveuglément confiance au champ de taille, ce qui crée une lecture hors limites. Un client malveillant peut en abuser en définissant le champ de taille sur des valeurs comme 0x7FFFFFFF, provoquant l'échec de l'allocation mémoire et le crash du jeu de la victime. Plus tard, cette taille est également transmise au constructeur d'un DLMemoryInputSteam, qui est un élément essentiel de l'exploit.

Bug n° 2 : dépassement de tampon dans l'analyseur NRSessionSearchResult

L'une des entrées de la structure de données décrite ci-dessus est un objet NRSessionSearchResult sérialisé. L'analyseur de ces données commence par analyser une liste de propriétés. Ces propriétés peuvent être des entiers de 4 octets, des entiers de 8 octets ou des chaînes larges terminées par un caractère nul. Cette liste de propriétés est suivie du nom de personnage Steam de l'hôte sous forme de chaîne large terminée par un caractère nul, puis de quelques données supplémentaires sans importance pour l'exploit. Cette fonction ainsi que l'analyseur de liste de propriétés utilisent un tampon de pile de taille fixe pour lire les chaînes, et dans les deux cas, aucune vérification des limites n'est effectuée sur le tampon. Voici le code du jeu responsable de la copie du nom de l'hôte (produit avec le décompilateur Ghidra puis nettoyé) :

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);

Cela conduit à un exploit de dépassement de tampon, permettant à l'attaquant de corrompre la pile.

Ouvrir la voie à la chaîne d'appels virtuels / ROP

Pour obtenir une redirection de code arbitraire, nous utilisons cette vulnérabilité ainsi que la disposition mémoire d'un objet DLMemoryInputStream instancié sur la pile par la fonction appelant l'analyseur, et utilisé en interne par le lecteur de flux :

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
}

Puisque nous contrôlons le champ data_size (Bug n° 1), il peut être défini sur l'adresse mémoire de pile du champ data_buffer. Cela fonctionnera à condition que l'adresse soit constante et pas trop grande (DS3 satisfait ces exigences). Comme le compilateur place le tampon de pile en haut de la frame, l'attaquant peut ensuite utiliser le Bug n° 2 pour écraser les deux octets inférieurs de vftable_ptr du DLMemoryInputStream. Ainsi, lorsque le caractère suivant est lu par DLEndianStreamReader, il appellera le DLMemoryInputStream en interne et le code sera redirigé. Les 2 octets laissent assez de marge pour sauter vers la 22e fonction de la vftable de DLEndianStreamReader, qui appelle la 6e méthode virtuelle de l'objet pointé par son premier champ. Dans un processus 64 bits (c'est-à-dire Dark Souls III), les instructions suivantes seraient exécutées :

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

Comme RCX est un pointeur vers l'objet DLMemoryInputStream, la première instruction écrit le champ data_size, qui a été défini par l'attaquant à une adresse de pile pointant vers le champ data_buffer via le Bug n° 1, dans RCX. Les deux instructions suivantes redirigeront donc l'exécution vers l'adresse mémoire que l'attaquant a écrite à l'offset 0x40 dans le tampon de données. La redirection de code arbitraire est désormais accomplie ! À partir de là, l'attaquant peut mettre en place une chaîne de redirection de code qui copie sa charge utile dans une région mémoire appropriée et l'exécute en choisissant du code proche d'appels virtuels avec différents décalages, puisque le tampon agit désormais comme une table de méthodes virtuelles. Pour la preuve de concept Dark Souls III, j'ai trouvé une configuration qui ne nécessite que 3 gadgets pour parvenir au RCE :

  • 0x18 : 140e97700
  • 0x40 : 1422be020
  • 0x68 : 140e40f15

Voir ici pour plus de détails sur ces 3 gadgets. Si pour un autre jeu cette méthode d'appel virtuel n'est pas une approche réalisable, la redirection de code arbitraire peut toujours être utilisée pour mettre en place un exploit ROP plus traditionnel.

Code de preuve de concept pour Dark Souls III

Exécution du code PoC

Pour exécuter le code de preuve de concept, vous devez d'abord disposer d'un serveur auquel vous connecter. Les serveurs officiels ayant été désactivés à cause de l'exploit, vous pouvez configurer un serveur privé en utilisant ds3os. ds3os est conçu pour imiter le comportement du serveur commercial aussi fidèlement que possible, mais des correctifs de sécurité ont déjà été déployés sur ce projet pour corriger cet exploit. Vous pouvez toutefois configurer un environnement de test en compilant le projet vous-même avec les constantes SEND_MESSAGE_TO_PLAYERS_SANITY_CHECKS et NRSSR_SANITY_CHECKS définies sur false dans BuildConfig.h. Cela imite le comportement non sécurisé du serveur commercial. Suivez les instructions fournies par ds3os pour démarrer le jeu et vous connecter à votre serveur.

Une fois cela fait et votre jeu connecté aux serveurs, compilez le code PoC et lancez l'exécutable Injector.exe. Il injectera une DLL contenant le code de l'exploit dans le processus de Dark Souls III. Cette DLL utilisera ensuite la fonction du jeu qui envoie les messages FRPG au serveur afin de livrer l'exploit à votre propre client.

Vecteur d'attaque

Pour la preuve de concept, j'ai décidé d'utiliser un message PushRequestVisit envoyé via RequestSendMessageToPlayers. C'est la version la plus puissante de l'exploit, dans le sens où le jeu de la cible analysera immédiatement les données vulnérables après les avoir reçues, dans toutes les situations (même dans le menu principal).

Chaîne de redirection d'appels virtuels

Offset 0x18 : 140e97700

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

Ce gadget est utilisé dans celui à l'offset 0x68. Nous devons placer dans RAX une adresse inférieure mais assez proche de 144786998 ; c'est la plus proche.

Offset 0x40 : 1422be020

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

Pour pouvoir utiliser le gadget à l'offset 0x68, nous avons besoin que l'adresse du tampon de données soit stockée dans RDX et que RCX reste le même. C'est exactement ce que cela permet.

Offset 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]

Ce gadget fait presque tout pour nous. Il appelle l'offset 0x18 pour obtenir un pointeur de destination pour memcpy, y copie notre paquet, puis appelle la fonction virtuelle à l'offset 0x68 sur l'objet statique à 144786998, que nous contrôlons désormais entièrement grâce à l'appel memcpy. Comme la quantité de mémoire corrompue par memcpy est importante et que certaines régions sont constamment écrites par d'autres threads du jeu, l'exploit charge d'abord une charge utile de « configuration » copiée dans un emplacement sûr, suspend tous les autres threads, puis recopie notre charge utile réelle avant d'y sauter. Voir rce.h pour plus d'informations.

Informations supplémentaires

Je recommande d'examiner le code source du code de preuve de concept, car il contient de nombreux commentaires détaillant la structure du paquet. Si vous voulez voir ce qui se passe à chaque étape en temps réel (vous devriez, c'est plutôt cool !), vous pouvez injecter la DLL de preuve de concept tout en exécutant le jeu sous un débogueur, avec des points d'arrêt aux adresses intéressantes suivantes :

140ca5960

C'est essentiellement là que l'exploit commence. Cette fonction est chargée d'analyser les données de la liste d'entrées délimitées par leur taille du message PushRequestVisit. Elle extrait d'abord chaque entrée de la liste dans différents vecteurs :

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 fonction 140ca5010 vérifie les tailles des entrées, mais 140ca4fa0 est destinée aux entrées de taille variable et n'effectue pas de vérifications de cohérence sur le champ de taille (Bug n° 1). Pour réaliser l'exploit de redirection de code arbitraire décrit ci-dessus, nous devons la définir sur 14F3B0. Cela provoquera une lecture hors limites d'environ 1,3 Mio, mais la page mémoire devrait être assez grande pour éviter les violations d'accès.

140ca56b0

Cette fonction est appelée par la précédente avec nrssr_data comme argument. Elle crée l'objet DLMemoryInputStream sur la pile, qui est ensuite passé en argument à l'analyseur NRSSR.

141955f50 : ParseNRSessionSeachResult

L'analyseur NRSessionSearchResult. Vérifie la signature NRSSR et les numéros de version (14196a0f0), analyse la liste de propriétés (14196a260), le nom de l'hôte (14195603a) et quelques informations supplémentaires (voir rce.h)

14195603a

Boucle dans la fonction ci-dessus qui copie de manière non sécurisée le nom de l'hôte (Bug n° 2). Voici quelques adresses qui peuvent aider à suivre ce qui se passe pendant le débordement de tampon :

  • Adresse du tampon de pile de l'analyseur : 14F128
  • Adresse de pile du DLMemoryInputStream : 14F3A0
  • Pointeur de vtable du DLMemoryInputStream après l'écrasement : 1439e8b30
  • Offset de la fonction virtuelle du DLMemoryInputStream utilisée par le DLInputStreamReader : 0x18

1439e8b48

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

C'est là que l'on aboutit après la première redirection de code provoquée par la vftable de flux mémoire écrasée. C'est ici que commence la chaîne de redirections d'appels virtuels.

Footnotes

  1. Pour Dark Souls III version 1.15. La taille théorique maximale de la charge utile dépend de la disposition de la pile et varie donc selon le jeu et la version. ↩

Télécharger l’outil