Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
ds3-nrssr-rce — Documentação e código de prova de conceito para CVE-2022-24125 e CVE-2022-24126. | Kitploit
Ferramentas/GitHubGitHub/tremwil/ds3-nrssr-rce
Frameworks de ExploraçãoAnálise de VulnerabilidadesExploraçãoEngenharia ReversaShellcodeTestes de PenetraçãoAprendizado e EducaçãoRed TeamingGeração de ShellcodeDesenvolvimento de PayloadsExploração de Binários
16985há 4 anosRevisado pelo Kitploit
GitHub
tremwil/ds3-nrssr-rce

ds3-nrssr-rce

Documentação e código de prova de conceito para CVE-2022-24125 e CVE-2022-24126.

Ver Repositório

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Atualização: Dark Souls III 1.15.1

Uma nova atualização do jogo, 1.15.1, foi lançada para Dark Souls III em 25/08/2022, juntamente com a restauração dos serviços online. Esta atualização corrigiu tanto CVE-2022-24125 quanto CVE-2022-24126, além de uma vasta gama de outras potenciais vulnerabilidades de segurança presentes na rede P2P do jogo (leituras/escritas fora dos limites). Além disso, todos os exploits conhecidos que permitiam corromper o save de outros jogadores foram corrigidos. Muitos cheats menores comuns (ex.: "curse knife") que eram frequentemente encontrados durante o multijogador online também foram corrigidos.

ds3-nrssr-rce

Este repositório contém código de prova de conceito e documentação para o exploit RCE mais recente que afeta jogos da FROM SOFTWARE, CVE-2022-24126. Embora teoricamente possível em outros jogos, o foco está em Dark Souls III, pois é o jogo onde minha pesquisa foi conduzida. Atualmente, o código de prova de conceito existe apenas para Dark Souls III; a vulnerabilidade foi confirmada como presente em:

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

O código vulnerável também está presente em Sekiro (crédito: LukeYui), embora não haja como acioná-lo. A presença em Demon's Souls não foi confirmada, mas é muito provável. Embora o teste de rede fechado tenha sido afetado, a versão de lançamento de Elden Ring não foi. Na verdade, uma enorme lista de crashes de rede, leituras/escritas fora dos limites e exploits que permitiam aos jogadores modificar os dados de jogo de outros jogadores, que estavam presentes em Dark Souls III, foram corrigidos em Elden Ring. Parabéns a por compilar essa lista e à FROM SOFTWARE por agir rapidamente! Fico feliz em dizer que

LukeYui
Elden Ring é, indiscutivelmente, o título mais seguro da FROM SOFTWARE no que diz respeito à extensão dos danos que hackers podem causar.

Dissipando Equívocos

Ao contrário do que se acredita, isso NÃO é um exploit de rede peer-to-peer. Está relacionado ao servidor de matchmaking e, portanto, é muito mais grave, pois você não precisa participar de nenhuma atividade multijogador para estar vulnerável devido a outra vulnerabilidade do servidor de matchmaking (CVE-2022-24125).

Em Dark Souls III, um atacante malicioso que abusasse disso seria capaz de executar de forma confiável um payload de até 1,3 MiB1 de shellcode na máquina de cada jogador online em segundos.

Com o jogo tendo uma base média de jogadores simultâneos de cerca de 20.000 jogadores nos meses anteriores ao desligamento do servidor, era claramente um problema que precisava ser corrigido imediatamente, especialmente com a possibilidade de estar presente em Elden Ring. Como a FROM SOFTWARE não havia agido por mais de 40 dias após meu relatório inicial com vídeos de prova de conceito e documentação detalhada do exploit (na qual grande parte deste readme é baseada), decidi demonstrar a existência do exploit publicamente de forma benigna, na esperança de chamar a atenção para que fosse resolvido pelos desenvolvedores, e funcionou.

Índice

  • Resumo do Exploit (CVE-2022-24126)
  • Vetores de Distribuição (CVE-2022-24125)
  • A Tática Geral de Exploração para Todos os Jogos
    • Bug #1: Nenhuma Verificação de Limites no Analisador de Lista de Entradas
    • Bug #2: Estouro de Buffer no Analisador de NRSessionSearchResult
    • Preparando o Caminho para a Cadeia ROP
  • Código de Prova de Conceito para Dark Souls III
    • Executando o código PoC
    • Vetor de Ataque
    • Cadeia de Redirecionamento de Chamada Virtual
    • Informações Extras

Resumo do Exploit (CVE-2022-24126)

A verificação inadequada de limites em um buffer de pilha e no campo de tamanho de dados durante a análise dos dados de matchmaking NRSessionSearchResult permite que um atacante execute código arbitrário. O estouro de pilha permite sobrescrever os dois bytes inferiores do vftable_ptr do objeto DLMemoryInputStream usado internamente pelo leitor de stream, redirecionando a execução para código vizinho cuidadosamente escolhido. A exploração inteligente da estrutura do objeto DLMemoryInputStream e do campo de tamanho de dados permite então alcançar o redirecionamento arbitrário de código, com RCX apontando para o endereço do nosso pacote. A partir daí, uma série de redirecionamentos de código por meio de chamadas virtuais com diferentes deslocamentos (que agora saltarão para quaisquer endereços que escrevemos no buffer do pacote) pode ser usada para alcançar a execução arbitrária de código.

Vetores de Distribuição (CVE-2022-24125)

Os vetores de distribuição são o que tornam este RCE particularmente grave (além de já ser um RCE). O exploit é transmitido através de solicitações de push de matchmaking contendo informações de NRSessionSearchResult. Isso significa que o atacante pode atingir qualquer pessoa que entre em sua sessão online. Em particular, para DS3:

  • summons (PushRequestSummonSign)
  • dark spirit invaders (PushRequestAllowBreakInTarget)
  • players joining via covenant (PushRequestVisit)
  • arena combattants (PushRequestAcceptQuickMatch)

Isso já é bastante ruim, mas o verdadeiro potencial é desbloqueado pela solicitação RequestSendMessageToPlayers:

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

O host usa essa solicitação para enviar diretamente a mensagem push PushRequestAllowBreakInTarget para invasores, para que eles possam obter as coordenadas de spawn e entrar na sessão P2P. É só isso. Essa é a única maneira como essa solicitação é usada pelo jogo.

No entanto, ela permite que qualquer cliente envie mensagens push arbitrárias para centenas de milhares de jogadores específicos.

Não posso enfatizar o quão terrivelmente inseguro isso é. Qualquer jogador pode basicamente se passar pelo servidor de matchmaking. Ao usar essa solicitação para enviar o exploit através de um PushRequestVisit, qualquer jogador online pode ser alvo remoto do atacante, desde que seu ID de jogador seja conhecido. O atacante também pode enviar o exploit para toda a base de jogadores online muito rapidamente, enviando várias solicitações, cada uma contendo uma grande fatia de possíveis IDs de jogadores.

A Tática Geral de Exploração para Todos os Jogos

Embora o RCE não seja exatamente portável para todos os jogos, a ideia central do exploit que dá ao atacante o redirecionamento arbitrário de código é a mesma. Se isso puder ser alcançado, é muito provável que uma cadeia de chamadas virtuais ou cadeia ROP específica do jogo possa ser encontrada. Este "primeiro passo" usa as seguintes vulnerabilidades:

Bug #1: Nenhuma Verificação de Limites no Analisador de Lista de Entradas

As solicitações de push de matchmaking que contêm informações de entrada em sessão armazenam essas informações em um formato binário personalizado que consiste em uma cadeia de entradas de dados delimitadas por tamanho. Cada entrada tem o seguinte 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];
}

A função do jogo responsável por copiar os dados dessas entradas confia cegamente no campo de tamanho, o que cria uma leitura fora dos limites. Isso pode ser abusado por um cliente malicioso ao definir o campo de tamanho para valores como 0x7FFFFFFF, fazendo com que a alocação de memória falhe e o jogo da vítima trave. Posteriormente, esse tamanho também é passado para o construtor de um DLMemoryInputSteam, que é uma parte instrumental do exploit.

Bug #2: Estouro de Buffer no Analisador de NRSessionSearchResult

Uma das entradas na estrutura de dados descrita acima é um objeto NRSessionSearchResult serializado. O analisador para esses dados primeiro analisa uma lista de propriedades. Essas propriedades podem ser inteiros de 4 bytes, inteiros de 8 bytes ou strings largas terminadas em nulo. Essa lista de propriedades é seguida pelo nome de persona Steam do host como uma string larga terminada em nulo e alguns dados adicionais não importantes para o exploit. Tanto esta função quanto o analisador de lista de propriedades usam um buffer de pilha de tamanho fixo para ler strings, e em ambos os casos nenhuma verificação de limites é realizada no buffer. Aqui está o código do jogo responsável por copiar o nome do host (produzido usando o descompilador Ghidra e depois limpo):

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

Isso leva a um exploit de estouro de buffer, permitindo que o atacante corrompa a pilha.

Preparando o Caminho para a Cadeia de Chamada Virtual / ROP

Para alcançar o redirecionamento arbitrário de código, usamos isso e o layout de memória de um objeto DLMemoryInputStream instanciado na pilha pela função que chama o analisador, que é usado internamente pelo leitor de stream:

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
}

Como controlamos o campo data_size (Bug #1), ele pode ser definido como o endereço de memória da pilha do campo data_buffer. Isso terá sucesso desde que o endereço seja constante e não muito grande (DS3 satisfaz esses requisitos). Como o compilador coloca o buffer da pilha na parte superior do quadro, o atacante pode então usar o Bug #2 para sobrescrever os dois bytes inferiores do vftable_ptr do DLMemoryInputStream. Portanto, quando o próximo caractere for lido pelo DLEndianStreamReader, ele chamará o DLMemoryInputStream internamente e o código será redirecionado. Os 2 bytes dão margem suficiente para pular para a 22ª função na vtable do DLEndianStreamReader, que chama o 6º método virtual do objeto apontado por seu primeiro campo. Em um processo de 64 bits (ou seja, Dark Souls III), as seguintes instruções seriam executadas:

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

Como RCX é um ponteiro para o objeto DLMemoryInputStream, a primeira instrução escreve o campo data_size, que foi definido como um endereço de pilha apontando para o campo data_buffer pelo atacante usando o Bug #1, em RCX. As duas próximas instruções redirecionarão, portanto, a execução para o endereço de memória que o atacante escreveu no deslocamento 0x40 no buffer de dados. O redirecionamento arbitrário de código foi alcançado! A partir daí, o atacante pode configurar uma cadeia de redirecionamento de código que copia seu payload para uma região de memória adequada e o executa, escolhendo código próximo a chamadas virtuais com diferentes deslocamentos, já que o buffer agora atua como uma tabela de métodos virtuais. Para a prova de conceito de Dark Souls III, encontrei uma configuração que requer apenas 3 gadgets para alcançar RCE:

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

Veja aqui para mais detalhes sobre esses 3 gadgets. Se para algum outro jogo esse método de chamada virtual não for uma abordagem viável, o redirecionamento arbitrário de código ainda pode ser usado para configurar um exploit ROP mais tradicional.

Código de Prova de Conceito para Dark Souls III

Executando o código PoC

Para executar o código de prova de conceito, você deve primeiro ter um servidor para se conectar. Embora os servidores oficiais tenham sido desativados devido ao exploit, você pode configurar um servidor privado usando ds3os. O ds3os foi projetado para imitar o comportamento do servidor de varejo o mais próximo possível, mas patches de segurança já foram implantados neste projeto para corrigir este exploit. No entanto, você ainda pode configurar um ambiente de teste compilando o projeto com as constantes SEND_MESSAGE_TO_PLAYERS_SANITY_CHECKS e NRSSR_SANITY_CHECKS definidas como false em BuildConfig.h. Isso imita o comportamento inseguro do servidor de varejo. Siga as instruções fornecidas por ds3os para iniciar o jogo e conectar-se ao seu servidor.

Depois que isso for feito e seu jogo estiver conectado aos servidores, compile o código PoC e inicie o executável Injector.exe. Ele injetará uma DLL contendo o código do exploit no processo de Dark Souls III. Essa DLL usará a função do jogo que envia mensagens FRPG para o servidor para entregar o exploit ao seu próprio cliente.

Vetor de Ataque

Para a prova de conceito, decidi usar uma mensagem PushRequestVisit enviada usando RequestSendMessageToPlayers. Esta é a versão mais potente do exploit no sentido de que o jogo do alvo irá imediatamente analisar os dados vulneráveis após recebê-los em todas as situações (até mesmo no menu principal).

Cadeia de Redirecionamento de Chamada Virtual

Offset 0x18: 140e97700

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

Este gadget é usado no de offset 0x68. Precisamos colocar um endereço menor, mas bastante próximo de 144786998 em RAX; este é o mais próximo.

Offset 0x40: 1422be020

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

Para poder usar o gadget no offset 0x68, precisamos que o endereço do buffer de dados seja armazenado em RDX e que RCX permaneça o mesmo. Isso alcança exatamente isso.

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]

Este gadget faz quase tudo para nós. Ele chama o offset 0x18 para obter um ponteiro de destino para memcpy, copia nosso pacote para lá e então chama a função virtual no offset 0x68 no objeto estático em 144786998, que agora controlamos totalmente por causa da chamada a memcpy. Como a quantidade de memória corrompida pelo memcpy é grande e algumas regiões estão sendo constantemente escritas por outras threads do jogo, o exploit primeiro carrega um payload de "configuração" que é copiado para um local seguro, suspende todas as outras threads e recopia nosso payload real antes de pular para ele. Veja rce.h para mais informações.

Informações Extras

Recomendo conferir o código fonte do código de prova de conceito, pois ele tem muitos comentários detalhando a estrutura do pacote. Se você quiser ver o que acontece em cada etapa em tempo real (e deveria, é bem legal!), você pode injetar a DLL de prova de conceito enquanto executa o jogo sob um depurador com pontos de interrupção nos seguintes endereços de interesse:

140ca5960

Essencialmente onde o exploit começa. Esta função é responsável por analisar os dados da lista de entradas delimitadas por tamanho da mensagem PushRequestVisit. Ela primeiro extrai cada entrada da lista em diferentes vetores:

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

A função 140ca5010 verifica os tamanhos das entradas, mas 140ca4fa0 é para entradas de tamanho variável e não realiza verificações de sanidade no campo de tamanho (Bug #1). Para alcançar o exploit de redirecionamento arbitrário de código descrito acima, precisamos defini-lo como 14F3B0. Isso causará uma leitura fora dos limites de aproximadamente 1,3 MiB, mas a página de memória deve ser grande o suficiente para evitar violações de acesso.

140ca56b0

Esta função é chamada pela anterior com nrssr_data como argumento. Cria o objeto DLMemoryInputStream na pilha, que é então passado como argumento para o analisador NRSSR.

141955f50: ParseNRSessionSeachResult

O analisador de NRSessionSearchResult. Verifica a assinatura NRSSR e números de versão (14196a0f0), analisa a lista de propriedades (14196a260), nome do host (14195603a) e mais algumas informações (veja rce.h)

14195603a

Loop na função acima que copia o nome do host de forma insegura (Bug #2). Aqui estão alguns endereços que podem ajudar a acompanhar o que está acontecendo durante o estouro de buffer:

  • Endereço do buffer da pilha do analisador: 14F128
  • Endereço da pilha do DLMemoryInputStream: 14F3A0
  • Ponteiro da vtable do DLMemoryInputStream após a sobrescrita: 1439e8b30
  • Deslocamento da função virtual no DLMemoryInputStream usado pelo DLInputStreamReader: 0x18

1439e8b48

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

Onde acabamos após o primeiro redirecionamento de código causado pela vtable do stream de memória sobrescrita. É aqui que a cadeia de redirecionamentos de chamada virtual começa.

Footnotes

  1. Para Dark Souls III Ver. 1.15. O tamanho máximo teórico do payload depende do layout da pilha e, portanto, varia de acordo com o jogo e a versão. ↩

Baixar ferramenta