
Exploit remoto do Sistema de Arquivos de Rede do Windows para CVE-2022-30136
autor: Ricardo Narvaja
Apenas para fins de demonstração. O exploit completo funciona em sistemas Windows Server vulneráveis.
Consulte o artigo Análise da CVE-2022-30136 “Vulnerabilidade do Windows Network File System”.
Eu queria escrever este artigo para demonstrar a análise que fiz durante o desenvolvimento do exploit “Windows Network File System Remote” do Core Impact, que abusa da vulnerabilidade CVE-2022-30136.
A vulnerabilidade de execução remota de código no Windows Network File System é um erro de cálculo de tamanho que ocorre ao criar a resposta do servidor em uma COMPOUND REQUEST usando a versão 4.1 do NFS.
O servidor calcula um tamanho menor do que o necessário para alocar o pool e, em seguida, ao copiar os dados para gerar a resposta, estoura o buffer.
A função Nfs4SvrXdrpGetEncodeOperationResultByteCount em nfssvr.sys é chamada para cada operação e retorna um tamanho menor do que o necessário (4 bytes a menos para cada operação).
Foi feito um patch para Nfs4SvrXdrpGetEncodeOperationResultByteCount.
Essa função é chamada durante cada OPERATION de uma COMPOSE REQUEST para que retorne os bytes necessários para cada uma delas com base no OPCODE. O valor retornado é então adicionado ao cabeçalho e a outras partes da resposta. Depois, calcula o tamanho final de toda a resposta a ser alocada e então copia para ela a resposta.
Em cada caso, podemos ver que o valor do tamanho retornado para cada operação é quatro bytes menor na versão vulnerável do que na versão corrigida.
Eu criei o POC para Windows Server 2019.
Abaixo está a versão vulnerável do nfssvr.sys usada para este POC, seguida pela versão corrigida para o Windows Server 2019:

A próxima imagem mostra o CASE 26 no diff:

No exemplo do CASE 26, podemos ver que a constante adicionada ao valor calculado é 0x2c na versão vulnerável e 0x30 na versão corrigida.
O mesmo pode ser visto em cada caso correspondente a cada OPCODE. A versão vulnerável sempre retorna um tamanho quatro bytes menor do que a corrigida.
Não vamos mostrar todos os casos porque o patch é semelhante para todos os OPCODES.
O pai de Nfs4SvrXdrpGetEncodeOperationResultByteCount é Nfs4SvrXdrEncodeCompoundResults. Ele lê o número de operações enviadas na COMPOUND REQUEST.
Neste POC, o valor é 0x34 (52d). Quando meu POC se conecta ao servidor na porta 2049 (a porta padrão do NFS), preciso definir um breakpoint condicional para parar.


Neste caso, ele para quando number_of_operations=0x34.

O pool com a tag ARGS é alocado aqui.

Em seguida, criarei uma estrutura chamada TAG_ARGS_0x10e0 para reverter os campos.

Ele copia o number_of_operations para r13 e faz um loop na função vulnerável uma vez por operação, até que o contador alcance o valor de r13.

Ele mostra que o primeiro package_OPCODE = 0x35, que corresponde a SEQUENCE na primeira operação obrigatória em uma COMPOUND REQUEST. Na imagem abaixo, a seta aponta para esse OPCODE no meu pacote.

Aqui podemos ver os argumentos da função vulnerável.


Dentro da função vulnerável, ela lê o OPCODE e vai para o CASE correspondente.

Três é subtraído do valor original do OPCODE (53).

E salta para o CASE 50, retornando 0x28 como o tamanho necessário para esta operação.


Podemos ver no diff como a versão corrigida retorna 0x2c.

Esse valor retornado é adicionado ao valor anterior de outros campos da resposta para calcular o tamanho das operações. Neste caso, esse valor é 0X40c.

Abaixo podemos ver os valores sendo adicionados:

Quando sai do loop, o tamanho total é calculado. Neste caso, o tamanho total é 0x1310.

Podemos deduzir a diferença entre a versão vulnerável e a versão corrigida calculando o tamanho, usando a fórmula: number_of_operations * 4.
Neste caso, a alocação na versão corrigida será 0x34 * 4 = 0x68 maior do que na versão vulnerável.

Depois disso, ele adiciona 0x24. Esse valor é calculado de forma semelhante nas versões vulnerável e corrigida.

Em seguida, adiciona a constante 0xf em ambos os casos.

Até este ponto, o tamanho neste exemplo foi 0x1340.

Em seguida, chega a rpcxdr_OncRpcBufMgrpAllocate.

Em seguida, passa para r15.


Ele subtrai um e adiciona quatro. Em seguida, compara com 0x800.
Esse tamanho calculado incorretamente só é usado se for maior que 0x800. Por esse motivo, apenas uma COMPOUND REQUEST acionará o bug.

Primeiro, ele aloca um pool com o tamanho = 0x80 e a tag XdBD.

Finalmente, ele aloca o pool para a resposta aqui com o tamanho 0x1398, que adiciona alguns valores constantes.

Em seguida, aloca 0x13a0 (incluindo a tag XdBP e o cabeçalho).

A partir daí, ele armazena o endereço do novo pool alocado no campo: tag_XdBD_0x80.p_TAG_XDBP_0x13a0.

Isso aponta para o endereço da resposta para a qual ele está sempre copiando.

Então ele começará a construir o cabeçalho da resposta.

O seguinte é um exemplo de como ele salva os dados no conteúdo de um ponteiro temporário e adiciona quatro a ele.


Abaixo podemos ver como ele copia para o conteúdo do endereço da resposta.

Isso escreve o primeiro dword e aumenta o ponteiro em quatro.

Em seguida, escreve o segundo dword e adiciona quatro.


Após sair da função, todo o cabeçalho é escrito.

Depois disso, ele retorna a nfssvr.sys para continuar escrevendo a resposta.

Ele continuará decodificando e escrevendo na resposta, adicionando quatro ao ponteiro temporário.

Quando conclui o cabeçalho, ele chega a este loop para escrever todas as operações. Começa com o primeiro OPCODE 0x35.


Podemos ver que ele escreve 0x428 a partir do início do pool.

Agora ele aponta depois da tag.


Ao colocar um breakpoint aqui, podemos ver como todas as operações foram escritas.


Após sair do loop, todas as operações são copiadas.

Vamos verificar o final do pool.

Lá podemos ver a escrita após o limite.

A alocação é menor do que os dados copiados, produzindo um estouro de pool.
Isso produz um BSOD na máquina alvo. No entanto, a questão é: podemos alcançar uma execução remota de código, ou um Write what where?
Tentei várias combinações de opcodes para obter uma resposta com dados controlados nos bytes estourados. Infelizmente, não tive sorte.
A tag máxima (controlada por mim) só pode ser colocada no início e tem um tamanho máximo de 0x400.

Todos os outros opcodes que tentei não respondem com dados controlados. Consequentemente, não acho que seja possível ou, no mínimo, é incrivelmente difícil obter um RCE ou elevar privilégios com esse bug. Dito isso, ainda pode ser possível, pois não tentei todas as combinações entre o grande número de possibilidades que existem.
Para a construção do POC, tentei com um cliente chamado “NFS CLIENT”. Ele suporta NFS 4.1 e pude testar diferentes opcodes copiando arquivos, editando, criando pastas etc.

Nesta construção, pude criar um pacote de exemplo COMPOUND e ajustar o tamanho, o client id, o session id etc.

Em seguida, enviei um EXCHANGE_ID para obter o client id, usando-o para enviar um CREATE_SESSION e, finalmente, a grande COMPOUND REQUEST.

Neste ponto, temos o bug explorado, o que leva a uma execução remota de código que permite um ataque de DoS.
Esperamos que seja útil; se tiver alguma dúvida, pode nos contatar em [email protected].
Aproveite!