
Informações e PoC sobre a vulnerabilidade ENLBufferPwn
| CVE: | CVE-2022-47949 |
| CVSS v3.1: | 9.8/10 (Crítico) |
| Autores: | PabloMK7, Rambo6Glaz, Fishguy6564 |
| Reportado: | 8 de agosto de 2021 (Mario Kart 7) 14 de abril de 2022 (para os jogos de WiiU e Switch afetados) |
| Divulgado: | 22 de dezembro de 2022 |
ENLBufferPwn é uma vulnerabilidade no código de rede comum de vários jogos first-party da Nintendo desde o Nintendo 3DS que permite a um atacante executar código remotamente no console da vítima apenas ao jogar uma partida online com ela (execução remota de código). Ela foi descoberta por várias pessoas de forma independente durante 2021 e reportada à Nintendo durante 2021 e 2022. Desde o relato inicial, a Nintendo corrigiu a vulnerabilidade em muitos jogos afetados. As informações neste repositório foram divulgadas com segurança após obter permissão da Nintendo.
A vulnerabilidade obteve uma pontuação de 9.8/10 (Crítico) na calculadora CVSS 3.1.
Aqui está uma lista de jogos que se sabe terem tido a vulnerabilidade em algum momento (todos os jogos de Switch e 3DS listados receberam atualizações que corrigem a vulnerabilidade, portanto não são mais afetados):
A vulnerabilidade ENLBufferPwn explora um estouro de buffer na classe C++ NetworkBuffer presente na biblioteca de rede enl (Net no Mario Kart 7) usada por muitos jogos first-party da Nintendo. Essa classe contém dois métodos, Add e Set, que preenchem um buffer de rede com dados provenientes de outros jogadores. No entanto, nenhum desses métodos verifica se os dados de entrada realmente cabem no buffer de rede. Como os dados de entrada são controláveis, um estouro de buffer pode ser acionado em um console remoto apenas ao participar de uma sessão de jogo online com o atacante. Se feito corretamente, o usuário vítima pode nem notar que uma vulnerabilidade foi acionada em seu console. As consequências desse estouro de buffer variam conforme o jogo, desde simples modificações inofensivas na memória do jogo (como abrir e fechar repetidamente o menu inicial no 3DS) até ações mais graves, como assumir o controle total do console, conforme mostrado abaixo.
O restante do relatório se concentrará no Mario Kart 7, pois, devido à falta de medidas de segurança (ASLR, por exemplo) do 3DS, é o caso mais severo da vulnerabilidade. No entanto, pode ser possível contornar a ASLR em jogos de Switch sequestrando o NetworkBuffer usado para enviar dados de volta ao atacante (e fazendo-o vazar ponteiros para o heap e para o código).
O vídeo a seguir demonstra um caso severo da vulnerabilidade no Mario Kart 7, onde um console controlado por um atacante (lado esquerdo) assume totalmente um console não modificado (lado direito). A única interação feita pelo usuário é entrar em uma sessão de jogo online com o atacante, neste caso, usando o recurso de "comunidades" (observe que uma "comunidade" foi usada para testar a vulnerabilidade com segurança, de forma isolada, e não afetar outros usuários jogando em salas públicas). A tomada de controle é feita copiando um payload de ROP para o console remoto e, em seguida, executando-o. Esse payload de ROP usa então outras vulnerabilidades no sistema operacional para elevar privilégios e obter controle total. No vídeo, o console remoto é forçado a executar um instalador de CFW (SafeB9SInstaller). Usando as mesmas técnicas, seria teoricamente possível roubar informações de conta/cartão de crédito ou fazer gravações não autorizadas de áudio/vídeo usando o microfone/câmeras integrados do console.
Como mencionado anteriormente, a vulnerabilidade vem de uma falha em dois métodos da classe NetworkBuffer. Aqui está uma implementação da classe e dos métodos em C++:
class NetworkBuffer
{
public:
u8 bufferType;
u8* dataPtr;
u32 dataSize;
u32 currentSize;
void Set(u8* newData, u32 newDataSize);
void Add(u8* newData, u32 newDataSize);
}
void NetworkBuffer::Set(u8* newData, u32 newDataSize)
{
memcpy(this->dataPtr, newData, newDataSize);
this->currentSize = newDataSize;
}
void NetworkBuffer::Add(u8* newData, u32 newDataSize)
{
memcpy(this->dataPtr + this->currentSize, newData, newDataSize);
this->currentSize += newDataSize;
}
Como você pode ver, nem Set nem Add verificam se o newDataSize recebido cabe no tamanho do buffer (dataSize). Como essa classe NetworkBuffer é usada como um contêiner genérico para trocar dados entre jogadores online, o conteúdo de newData e o valor de newDataSize podem ser controlados por um atacante, e um estouro de buffer pode ser realizado.
Outro detalhe da biblioteca de rede é que ela é assíncrona, para que os jogos possam realizar outras operações enquanto os dados estão sendo preenchidos no buffer. Para conseguir isso, uma técnica de buffer duplo é usada. Dessa forma, o jogo pode acessar dados já recebidos em um NetworkBuffer ao mesmo tempo em que novos dados estão sendo recebidos. Depois que o segundo NetworkBuffer é preenchido, os buffers são trocados e o ciclo se repete.
Para este PoC, o NetworkBuffer duplo usado para receber dados de Mii (bufferType = 9) será explorado. Devido à ordem de alocação no heap, acontece justamente que o objeto NetworkBuffer e seu conteúdo são colocados lado a lado. Além disso, ambos os buffers do buffer duplo são contíguos na memória. O diagrama a seguir representa como fica a memória após a alocação do buffer:
