Segurança do DHCP no Windows 10: Explorando a vulnerabilidade crítica CVE-2019-0726



Imagem: Pexels

Com o lançamento das atualizações de janeiro para o Windows, as notícias da vulnerabilidade criticamente perigosa CVE-2019-0547 nos clientes DHCP despertaram o público. A alta classificação CVSS e o fato de a Microsoft não publicar imediatamente a avaliação de desempenho, o que dificultava a decisão dos usuários sobre uma atualização urgente do sistema, despertou o interesse. Algumas publicações até sugeriram que a falta de um índice pode ser interpretada como evidência de que uma exploração em funcionamento aparecerá em um futuro próximo.

Soluções como o MaxPatrol 8 podem identificar computadores na rede vulneráveis ​​a ataques específicos. Outras soluções, como o PT NAD, detectam esses ataques eles mesmos. Para tornar isso possível, é necessário descrever as regras para detectar vulnerabilidades em produtos e as regras para detectar ataques a esses produtos. Por sua vez, para tornar isso possível, é necessário que cada vulnerabilidade individual descubra o vetor, o método e as condições de sua operação, ou seja, literalmente todos os detalhes e nuances associados à operação. É necessário um entendimento muito mais completo e profundo do que o que geralmente pode ser compilado a partir de descrições em sites de fornecedores ou no CVE, como:

A vulnerabilidade se manifesta porque o sistema operacional processa incorretamente objetos na memória.

Portanto, para adicionar aos produtos da empresa as regras para detectar ataques a uma vulnerabilidade recém-criada no DHCP, bem como as regras para identificar dispositivos afetados por ela, você deve entender os detalhes. No caso de vulnerabilidades binárias, o patch-diff é frequentemente usado para obter informações sobre os erros subjacentes, ou seja, uma comparação das alterações feitas no código binário de um aplicativo, biblioteca ou kernel do sistema operacional com um patch específico, uma atualização que corrige esse erro. Mas o primeiro estágio é sempre o reconhecimento.

Nota : Para ir diretamente para a descrição da vulnerabilidade, ignorando os conceitos subjacentes do DHCP, você pode pular as primeiras páginas e ir diretamente para a seção "Função DecodeDomainSearchListData".

Reconhecimento


Voltamos ao mecanismo de pesquisa e visualizamos todos os detalhes de vulnerabilidade atualmente conhecidos. Desta vez, há um mínimo de detalhes e todos eles são procissões gratuitas das informações coletadas da publicação original no site do MSRC. Essa situação é bastante típica para erros descobertos pela Microsoft durante uma auditoria interna.

Descobrimos pela publicação que estamos diante de uma vulnerabilidade do tipo corrupção de memória contida nos sistemas cliente e servidor do Windows 10 versão 1803 e que se manifesta no momento em que um invasor envia respostas especialmente criadas para um cliente DHCP. Após alguns dias a partir desse momento na página, os índices de desempenho também aparecerão:



Como você pode ver, o MSRC classificou "2 - Exploração como menos provável". Isso significa que um erro com alta probabilidade ou não é operacional, ou a operação está repleta de tais dificuldades, cuja superação exigirá custos trabalhistas muito altos. É certo que a Microsoft não costuma subestimar essas estimativas. Isso é parcialmente influenciado pelo risco de perda de reputação e parcialmente por alguma independência do centro de resposta dentro da empresa. Portanto, suponha: como a ameaça de exploração é indicada no relatório como improvável, certamente é. Na verdade, isso poderia ter concluído a análise, mas não seria supérfluo verificar novamente e pelo menos descobrir qual era a vulnerabilidade. Por fim, apesar de toda a personalidade inegável, os erros tendem a se repetir e a se manifestar em outros lugares.

Na mesma página, baixamos o patch (atualização de segurança) fornecido na forma de um arquivo .msu, descompacte-o e procure por arquivos provavelmente relacionados ao processamento de respostas DHCP no lado do cliente. Recentemente, tornou-se muito mais difícil fazer isso, pois as atualizações começaram a ser entregues não na forma de pacotes separados que corrigem erros específicos, mas como um único pacote cumulativo que inclui todas as correções mensais. Isso aumentou bastante o excesso de ruído, ou seja, alterações não relacionadas à nossa tarefa.

Entre todo o conjunto de arquivos, a pesquisa encontra várias bibliotecas adequadas para o filtro, que comparamos com suas versões em um sistema sem patch. A biblioteca dhcpcore.dll parece a mais promissora. Nesse caso, o BinDiff produz alterações mínimas:



Na verdade, além das alterações cosméticas feitas em uma única função - DecodeDomainSearchListData. Se você conhece bem o protocolo DHCP e suas opções que não são usadas com muita frequência, já pode assumir que esta função processa a lista. Caso contrário, passe para o segundo estágio - o estudo do protocolo.

DHCP e suas opções


O DHCP ( RFC 2131 | wiki ) é um protocolo extensível cujos recursos de reposição são fornecidos pelo campo de opções. Cada opção é descrita por uma tag exclusiva (número, identificador), o tamanho ocupado pelos dados contidos na opção e os próprios dados. Essa prática é típica dos protocolos de rede e uma das opções "implantadas" no protocolo é a opção de pesquisa de domínio descrita na RFC 3397 . Ele permite que o servidor DHCP defina as terminações de nomes de domínio padrão nos clientes, que serão usadas como sufixos DNS para a conexão configurada dessa maneira.

Vamos, por exemplo, definir os seguintes finais de nome em nosso cliente:

.microsoft.com .wikipedia.org 



Em seguida, em qualquer tentativa de determinar o endereço pelo nome de domínio, as consultas DNS substituirão os sufixos dessa lista até que uma exibição bem-sucedida seja encontrada. Por exemplo, se o usuário digitou ru na barra de endereços do navegador, as consultas DNS serão geradas primeiro para ru.microsoft.com e depois para ru.wikipedia.org:



De fato, os navegadores modernos são muito inteligentes e, portanto, respondem aos redirecionamentos para o mecanismo de pesquisa para nomes que não são semelhantes ao FQDN. Portanto, abaixo, apresentamos a conclusão de utilidades menos deterioradas:



Pode parecer ao leitor que essa é a vulnerabilidade, porque a mera possibilidade de substituir sufixos DNS por um servidor DHCP, com o qual qualquer dispositivo da rede pode se identificar, representa uma ameaça para os clientes que solicitam parâmetros de rede via DHCP . Mas não: como segue a RFC, isso é considerado um comportamento documentado e bastante legítimo. Na verdade, o servidor DHCP é inerentemente um daqueles componentes confiáveis ​​que podem ter um forte impacto nos dispositivos que os acessam.

Opção de pesquisa de domínio


A opção de pesquisa de domínio tem o número 0x77 (119). Como todas as opções, ele é codificado com uma tag de byte único com o número da opção. Como a maioria das outras opções, imediatamente após a tag há um tamanho de byte único dos dados após o tamanho. Instâncias de opção podem estar presentes na mensagem DHCP mais de uma vez. Nesse caso, os dados de todas essas seções são concatenados na sequência em que aparecem na mensagem.



No exemplo apresentado, extraído da RFC 3397 , os dados são divididos em três seções, cada uma com 9 bytes. Como você pode ver na figura, os nomes de subdomínios no nome de domínio totalmente qualificado são codificados com um comprimento de byte único, seguido imediatamente pelo próprio nome. A codificação do nome de domínio totalmente qualificado termina com um byte nulo (ou seja, um nome de subdomínio de tamanho nulo).

Além disso, a opção usa o método mais simples de compactação de dados, ou melhor, apenas pontos de nova análise. Em vez do tamanho do nome do domínio, o campo pode conter o valor 0xc0. O próximo byte define o deslocamento em relação ao início dos dados da opção, que deve ser usado para procurar o final do nome do domínio.

Assim, neste exemplo, uma lista de dois sufixos de domínio é codificada:

 .eng.apple.com .marketing.apple.com 

Função DecodeDomainSearchListData


Portanto, a opção de número de DHCP 0x77 (119) permite que o servidor configure sufixos DNS nos clientes. Mas não em máquinas com sistemas operacionais Windows. Os sistemas Microsoft tradicionalmente ignoraram essa opção; portanto, historicamente, o final dos nomes DNS, se necessário, era revertido pelas políticas de grupo. Isso continuou até recentemente, quando a próxima versão do Windows 10, versão 1803, adicionou processamento à opção de pesquisa de domínio. A julgar pelo nome da função no dhcpcore.dll, no qual as alterações foram feitas, é no manipulador adicionado que está o erro em questão.

Começando a trabalhar. Combinamos um pouco o código e descobrimos o seguinte. O procedimento DecodeDomainSearchListData, de acordo com o nome, decodifica os dados da opção de pesquisa de domínio da mensagem recebida do servidor. Na entrada, ele recebe uma matriz de dados compactada da maneira descrita no parágrafo anterior e, na saída, gera uma sequência terminada em nulo contendo uma lista de terminações de nomes de domínio, separadas por vírgulas. Por exemplo, esta função converte os dados do exemplo acima em uma sequência:

  eng.apple.com,marketing.apple.com 

DecodeDomainSearchListData é chamado no procedimento UpdateDomainSearchOption, que define a lista retornada para o valor "DhcpDomainSearchList" da chave do registro:
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{INTERFACE_GUID}\
armazenando os principais parâmetros de uma interface de rede específica.



A função DecodeDomainSearchListData cumpre em duas passagens. Na primeira passagem, ele executa todas as ações, exceto a gravação no buffer de saída. Assim, a primeira passagem é dedicada ao cálculo do tamanho da memória necessária para acomodar os dados retornados. Na segunda passagem, a memória já está alocada para esses dados e a memória alocada é preenchida. A função é bastante pequena, com cerca de 250 instruções, e sua principal tarefa é processar cada uma das três opções possíveis para o caractere representado no fluxo de entrada: 1) 0x00, 2) 0xc0 ou 3) todos os outros valores. A correção hipotética do erro relacionado ao DHCP basicamente se resume em adicionar uma verificação do tamanho do buffer resultante no início da segunda passagem. Se esse tamanho for zero, a memória não será alocada para o buffer e a função imediatamente encerrará a execução e retornará um erro:



Acontece que a vulnerabilidade se manifesta nos casos em que o tamanho do buffer de destino é zero. Ao mesmo tempo, no início da execução, a função verifica os dados de entrada, cujo tamanho não pode ser inferior a dois bytes. Portanto, para a operação, é necessário selecionar uma opção não vazia de sufixos de domínio de forma que o tamanho do buffer de saída seja zero.

Operação


A primeira coisa que vem à mente é que você pode usar os pontos de nova análise descritos anteriormente para que dados de entrada não vazios gerem uma linha de saída vazia:



Um servidor configurado para enviar uma opção com esse conteúdo na resposta realmente causará violação de acesso em clientes não atualizados. Isso acontece pelo seguinte motivo. A cada etapa, quando a função analisa parte do nome de domínio totalmente qualificado, ela a copia para o buffer de destino e coloca um ponto após ele. No exemplo do RFC, os dados serão copiados para o buffer na seguinte ordem:

 1). eng. 2). eng.apple. 3). eng.apple.com. 

Em seguida, quando o domínio contiver tamanho zero, a função substituirá o caractere anterior do buffer de destino por uma vírgula:

 4). eng.apple.com, 

e continua analisando:

 5). eng.apple.com,marketing. 6). eng.apple.com,marketing.apple. 7). eng.apple.com,marketing.apple.com. 8). eng.apple.com,marketing.apple.com, 

No final da entrada, resta apenas substituir a última vírgula por um caractere zero e você prepara uma linha para gravar no registro:

 9). eng.apple.com,marketing.apple.com 

O que acontece quando um invasor envia um buffer formado da maneira descrita? Se você observar o exemplo, poderá ver que a lista contida nele consiste em um elemento - uma sequência vazia. Na primeira passagem, a função calcula o tamanho dos dados de saída. Como os dados não contêm um único nome de domínio diferente de zero, o tamanho é zero.

Na segunda passagem, um bloco de memória dinâmica é alocado para colocar dados nele e copiar os dados em si. Mas a função de análise encontra imediatamente um caractere nulo, significando o final do nome de domínio e, portanto, como foi dito, substitui o caractere anterior de um ponto para uma vírgula. E aqui estamos diante de um problema. O iterador de buffer de destino está na posição zero. Não existe um personagem anterior. O caractere anterior pertence ao cabeçalho do bloco de memória dinâmica. E esse mesmo caractere será substituído por 0x2c, ou seja, por uma vírgula.

No entanto, isso acontece apenas em sistemas de 32 bits. O uso de unsigned int para armazenar a posição atual do iterador de buffer de destino introduz ajustes no processamento em sistemas x64. Vamos prestar mais atenção ao trecho de código responsável por escrever uma vírgula no buffer:



A unidade é subtraída da posição atual usando o registro eax de 32 bits, enquanto ao endereçar o buffer, o código acessa o registro rax completo de 64 bits. Na arquitetura AMD64, qualquer operação com registradores de 32 bits anula a parte superior do registrador. Isso significa que no registro rax, que anteriormente continha zero, após a subtração, não o valor –1, mas 0xffffffff será armazenado. Portanto, em sistemas de 64 bits, o valor 0x2c será gravado no endereço buf [0xffffffff], ou seja, muito além dos limites da memória alocada para o buffer.

Os dados obtidos estão de acordo com a avaliação de desempenho da Microsoft, pois, para explorar essa vulnerabilidade, um invasor precisa aprender a executar remotamente a pulverização de heap em um cliente DHCP e, ao mesmo tempo, ter controle suficiente sobre a alocação de memória dinâmica para registrar valores predefinidos, como vírgula e zero byte, produzido no endereço preparado e levou a consequências negativas controladas. Caso contrário, a gravação de dados em um endereço não verificado resultará em uma queda no processo svchost.exe, junto com todos os serviços atualmente hospedados nele e em uma reinicialização adicional desses serviços pelo sistema operacional. Um fato que atacantes em determinadas condições também podem usar para seu próprio benefício.

Isso parece ser tudo o que pode ser dito sobre o erro sob investigação. Resta apenas a sensação de que isso está longe do fim. Como se não considerássemos todas as opções. Deve haver algo mais que está oculto nessas linhas.

CVE-2019-0726


Provavelmente do jeito que está. Se você observar atentamente o tipo de dados que está causando o erro e compará-lo com a forma exata com que esse erro ocorre, notará que a lista de nomes de domínio pode ser alterada de forma que o buffer resultante seja de tamanho diferente de zero, mas uma tentativa de gravar fora dele é o mesmo será feito. Para fazer isso, o primeiro elemento da lista deve ser uma sequência vazia e todos os outros podem conter terminações de domínio normais. Por exemplo:



A opção apresentada inclui dois elementos. O sufixo do primeiro domínio está vazio, termina imediatamente com um byte zero. O segundo sufixo é .ru. O tamanho da linha calculada na saída será igual a três bytes, o que permitirá superar a verificação imposta pela atualização de janeiro sobre o vazio do buffer de destino. Ao mesmo tempo, zero no início dos dados forçará a função a escrever uma vírgula com o caractere anterior na sequência resultante, mas como a posição atual do iterador na sequência é zero, como no caso considerado acima, a gravação ocorrerá novamente fora do buffer alocado.

Agora é necessário confirmar os resultados teóricos obtidos na prática. Simulamos uma situação na qual o servidor DHCP envia uma mensagem com a opção apresentada em resposta a uma solicitação do cliente e captamos imediatamente uma exceção ao tentar escrever uma vírgula na posição 0xffffffff alocada na linha de buffer resultante:



Aqui, o registro r8 contém um ponteiro para as opções recebidas, rdi é o endereço do buffer de destino selecionado e rax é a posição nesse buffer em que o caractere deve ser gravado. Obtivemos esses resultados em um sistema completamente atualizado (a partir de janeiro de 2019).

Escrevemos sobre o problema descoberto na Microsoft e ... eles perdem a carta. Sim, isso às vezes acontece mesmo com fornecedores respeitáveis. Nenhum sistema é perfeito e, nesse caso, você deve procurar outras formas de comunicação. Portanto, uma semana depois, mesmo sem receber uma resposta automática nesse período, contatamos o gerente diretamente via Twitter e, de acordo com os resultados de vários dias de análise do aplicativo, descobrimos que os detalhes enviados não têm nada a ver com o CVE-2019-0547 e representam uma vulnerabilidade independente pela qual novo identificador CVE. Um mês depois, em março, a correção correspondente é lançada e o erro recebe o número CVE-2019-0726 .

É assim que às vezes você pode tentar descobrir os detalhes da vulnerabilidade de 1 dia para descobrir acidentalmente o dia 0 simplesmente confiando na sua intuição.

Postado por Mikhail Tsvetkov, especialista em análise de aplicativos da Positive Technologies.

Source: https://habr.com/ru/post/pt448378/


All Articles