Você passou dos limites: perturbação de um host
Resumo executivo
- O pesquisador da Akamai, Ben Barnea, encontrou duas vulnerabilidades importantes em um serviço RPC do Microsoft Windows que foram atribuídas ao CVE-2022-37998 e CVE-2022-37973 com uma pontuação básica de 7,7.
- As vulnerabilidades tiram vantagem de vários bugs na interface RPC do Gerenciador de sessão local.
- As vulnerabilidades levam a ataques de negação de serviço que impedem o funcionamento de contêineres e serviços de sessão (como o Microsoft Defender Application Guard, Sandbox, Docker e Windows Terminal Server).
- A vulnerabilidade existe em máquinas com Windows 10, Windows 11 e Windows Server 2022 não corrigidas.
- As vulnerabilidades foram divulgadas com responsabilidade à Microsoft e corrigidas na Patch Tuesday de outubro de 2022.
- Fornecemos uma prova de conceito da vulnerabilidade em nosso repositório de pesquisa.
Introdução
O Grupo de inteligência sobre a segurança da Akamai se aprofundou na pesquisa do MS-RPC no ano passado. Apesar de ser um protocolo bastante comum, o MS-RPC é pouco pesquisado e pode ter efeitos no mundo real. Um desses efeitos é que as vulnerabilidades em uma interface RPC ficam expostas. É nisso que estamos focando nesta publicação do blog: vulnerabilidades na interface RPC do Gerenciador de sessão local (LSM).
O LSM é um serviço que faz parte do Subsistema do Gerenciador de sessões. Ele é responsável pelo gerenciamento de sessões locais relacionadas a sessões de servidores de terminal em uma máquina Windows. Ele se comunica com outros componentes relacionados do Windows, como Winlogon e CSRSS.
O LSM é implementado em lsm.dll e contém lógica de cliente e servidor. O LSM expõe várias interfaces RPC, com uma interface interessante relacionada ao gerenciamento de sessões de contêineres que são executadas dentro de uma máquina virtual Hyper-V. As vulnerabilidades estão dentro desta interface.
O que é essa interface?
A nova interface RPC recebe o UUID c938b419-5092-4385-8360-7cdc9625976a. Essa interface expõe exatamente duas funções: ContainerCom_AskForSession e ContainerCom_SessionLoggedOff. A interface também é registrada com um retorno de chamada de segurança que sempre retorna RPC_S_OK, permitindo, assim, o acesso de todos. O servidor LSM registra um ponto de extremidade do soquete do Hyper-V (hvsocket) que é acessível somente aos contêineres do Hyper-V.
Fig. 1: Cliente configurando conexão RPC por hvsocket vs servidor configurando um ponto de extremidade hvsocket
Quando uma sessão é criada dentro do contêiner (por exemplo, devido a uma conexão RDP), o cliente LSM primeiro chama RpcGetRequestForWinlogon no LSM do contêiner. Essa função arbitra a criação da sessão e, quando ela está sendo executada dentro de um contêiner, ela primeiro pede permissão do host. Isso é feito invocando a chamada RPC ContainerCom_AskForSession para o host usando um hvsocket para o principal. A interface RPC limita o número de sessões aos contêineres. Isso é feito acompanhando as sessões recém-criadas.
Como o LSM acompanha as sessões?
A resposta é simples. Há um objeto global chamado ContainerSessionServer que contém duas variáveis para acompanhar as sessões:
- contador do total de sessões criadas. Isso é limitado a um, o que significa que apenas uma sessão é permitida em um determinado momento.
- Um mapa entre o GUID de um contêiner e a contagem de sessões do contêiner. Isso é limitado a dois para cada recipiente.
Cada vez que um contêiner solicita uma sessão, ContainerSessionServer::AskForSession primeiro verifica se o contador total de sessões é menor que um. Nesse caso, aumenta a contagem total de sessões e também aumenta o contador de sessões do contêiner no mapa.
Quando ContainerSessionServer::OnSessionLoggedOff é chamado (na saída do contêiner ou diretamente como uma chamada RPC), a função diminui a contagem total de sessões e a contagem de sessões do contêiner em um.
Auditoria de funções RPC por vulnerabilidades
Embora essa interface pareça simples e fácil de implementar, encontramos quatro bugs que conseguimos encadear em duas vulnerabilidades.
Cadeia nº 1: DoS através de uma seção crítica, CVE-2022-37998
Bug nº 1: falha ao sair de uma seção crítica
ContainerSessionServer::AskForSession usa uma seção crítica para sincronizar o acesso ao objeto global ContainerSessionServer.
Fig. 2: Descompilação do código da vulnerabilidade. A função é encerrada sem liberar a seção crítica
Conforme visto acima, a seção crítica é inserida na linha 112. Mais tarde, nas linhas 114 a 116, há uma verificação para ver se o contador de sessões do contêiner está no seu limite (dois). Em caso afirmativo, o LSM não acompanhará essa sessão e sairá imediatamente da função (linha 125). Infelizmente, o código não sai da seção crítica inserida. Portanto, outras chamadas para essa interface ficarão presas aguardando a liberação dessa seção crítica.
Conforme dito anteriormente, entretanto, há um limite de um para o contador total de sessões, então como podemos chegar a um ponto em que o contador de sessões de um contêiner é dois? Bem, logicamente, não é possível. É aí que entra o segundo bug!
Bug nº 2: rastreamento incorreto do contador
Quando uma sessão para o contêiner é desconectada, uma chamada RPC é feita para ContainerSessionServer::OnSessionLoggedOff no host. Esta função primeiro diminui o contador total de sessões chamando DecreaseTotalSessionCount. Ele faz isso independentemente de o contêiner estar sendo rastreado. Se descobrir que o recipiente não está sendo rastreado, ele sai sem incrementar novamente o contador total de sessões.
Isso pode levar a um contador de sessões totais com o valor de um número negativo (já que é um número inteiro assinado). Podemos simplesmente enviar muitas solicitações OnSessionLoggedOff antes de enviar qualquer solicitação AskForSession e, assim, continuar diminuindo o contador total da sessão para um número negativo arbitrário.
Encadeamento do primeiro e do segundo bug
Podemos usar o bug nº 2 para diminuir o contador total de sessões algumas vezes até que seja um número negativo. Então, podemos explorar o bug nº 1 enviando duas solicitações para AskForSession. Na segunda vez que essa função for chamada, ela verificará se o contador total da sessão é menor que 1, e por causa do segundo bug, é. Em seguida, ela verá que o contador de sessão do contêiner é 2 e retornará sem sair da seção crítica.
Fig. 3: Uma descrição do processo de exploração
O DoS depende se as novas chamadas RPC recebidas são despachadas para o mesmo thread que criou o impasse da seção crítica. Se o tempo de execução do RPC despachasse a nova chamada para o mesmo thread, o DoS não aconteceria, pois EnterCriticalSection permite propriedade aninhada; ou seja, o mesmo thread pode chamar EnterCriticalSection duas vezes. Se a chamada RPC fosse despachada para qualquer thread diferente daquela que contém a seção crítica, ela esperaria para sempre.
Cadeia nº 2: DoS através de vazamento de memória, CVE-2022-37973
Bug nº 3: vazamento de memória
ContainerSessionServer::AskForSession também rastreia os eventos dos contêineres, como saída/pausa/retomada do contêiner. Isso é feito chamando HcsOpenComputeSystem com o GUID do contêiner e, em seguida, registrando um retorno de chamada com HcsRegistrarComputerSystemCallback.
O retorno de chamada registrado recebe um objeto de contexto. O contexto é alocado em ContainerSessionServer::AskForSession. Infelizmente, em muitos casos em que ocorre um erro, a função sai sem liberar a memória alocada para o contexto. Isso leva a um vazamento de memória que um invasor pode acionar várias vezes. Depois de chamadas suficientes, há esgotamento de memória para o processo LSM e o processo trava.
Em nossos testes, o envio de solicitações RPC em um loop infinito produziu aproximadamente 3 MB de alocação a cada segundo. Em nosso caso, após a alocação de 24 GB de memória, o serviço LSM travou. O tempo necessário para esgotar 24 GB é de cerca de duas horas. O serviço não é automaticamente gerado novamente.
Bug nº 4: acesso remoto
Os pontos de extremidade no MS-RPC são multiplexados. Se um servidor registrar várias interfaces e vários pontos de extremidade, cada interface estará acessível por meio de cada ponto de extremidade. Pontos de extremidade e interfaces não estão vinculados uns aos outros.
Essa interface deve ser acessível somente através do hvsocket para contêineres. Em nosso caso, o LSM registra um ponto de extremidade de pipe nomeado "\pipe\LSM_API_Service", que é acessível remotamente. Devido à multiplexação de ponto de extremidade, um invasor remoto pode se conectar ao ponto de extremidade de pipe nomeado e enviar uma solicitação à interface do contêiner. A correção é simples: o retorno de chamada de segurança deve verificar qual ponto de extremidade o cliente usou e, se não for hvsocket, negar acesso.
Encadeamento do terceiro e do quarto bug
A funcionalidade de rastreamento de contêiner é baseada na propriedade do identificador do cliente. Isso significa que, para um hvsocket, o identificador do cliente será o GUID do contêiner. Para um pipe nomeado, esse será o nome da máquina do cliente.
Para acionar a primeira exploração, o cliente precisa ter um identificador de cliente que seja um GUID real de um contêiner em execução. Como tal, um cliente remoto não seria capaz de acionar esses bugs, a menos que verificasse com sucesso o GUID de um contêiner em execução e mudasse o nome de sua máquina, um cenário improvável.
Infelizmente, o terceiro bug (vazamento de memória) aloca os objetos antes que qualquer verificação seja feita no contêiner solicitado. Isso significa que um invasor remoto (usando o bug nº 4) pode acionar remotamente o vazamento de memória. Ao chamá-lo várias vezes, um invasor pode esgotar a memória e travar o processo.
Impacto
Embora essas vulnerabilidades sejam categorizadas como vulnerabilidades de negação de serviço (DoS), elas têm um impacto na segurança, pois permitem que um invasor ignore os recursos de segurança.
Com a primeira exploração (seção crítica), há um dos para a nova interface específica. Esse problema impediria a criação de novas instâncias de sandbox.
Com a segunda exploração, que pode ser acionada remotamente e a partir de um contêiner, todo o processo trava. Assim, todas as dependências do LSM não funcionariam. Recursos de segurança, como o Microsoft Defender Application Guard e o Sandbox, também não funcionariam. Além disso, RDP e Docker não funcionariam.
Fig. 4: Erro de MDAG conforme mostrado no Microsoft Edge
Resumo
Essas vulnerabilidades são um ótimo exemplo de como algo que parece simples ou insignificante pode ter impactos verdadeiramente negativos. Essa interface pode parecer secundária: ele nos apresentou alguns bugs que são fáceis de acionar e têm um impacto interessante.
A realidade da situação é que os agentes mal-intencionados estão utilizando essas cadeias de ataque livremente. Quanto mais insignificante algo parece, mais fácil é de ser ignorado, e isso é o principal fator a ser explorado.
Em conjunto com o trabalho contínuo que estamos fazendo sobre RPC, incentivamos outros pesquisadores a procurar bugs semelhantes em outras interfaces RPC.
Se você estiver interessado em tópicos de pesquisa RPC, como este, confira nosso kit de ferramentas RPC para obter mais anotações e ferramentas. Você também pode nos seguir no Twitter para atualizações em tempo real sobre isso e outras pesquisas que estamos fazendo aqui na Akamai.