Movimento lateral sem arquivos com objetos COM armadilhados

Close nas mãos de um homem digitando no notebook e segurando um tablet em um escritório escuro com iluminação azul

O Component Object Model (COM) tem sido um pilar do desenvolvimento do Microsoft Windows desde o início dos anos 1990 e continua muito presente nos sistemas operacionais e aplicações modernos da Microsoft. A dependência de componentes COM e o extenso desenvolvimento de funcionalidades ao longo dos anos criou uma superfície de ataque generosa. Em fevereiro de 2025, James Forshaw (@tiraniddo) do Google Project Zero lançou um post de blog detalhando uma nova abordagem para abusar da tecnologia remota Distributed COM (DCOM), onde objetos COM presos podem ser usados para executar código gerenciado .NET no contexto de um processo DCOM de servidor. Forshaw destaca vários casos de uso para escalonamento de privilégios e contornamento do Protected Process Light (PPL).

Com base na pesquisa de Forshaw, Mohamed Fakroud (@T3nb3w) publicou uma implementação da técnica para contornar as proteções de PPL no início de março de 2025. Jimmy Bayne (@boops) e eu realizamos uma pesquisa semelhante em fevereiro de 2025, o que nos levou a desenvolver uma técnica de movimento lateral sem arquivos de prova de conceito, abusando de objetos COM presos.

Contexto

O COM é um padrão de interface binária e uma camada de serviço de middleware que permite a exposição de componentes modulares distintos para interagir entre si e com aplicações, independentemente da linguagem de programação subjacente. Por exemplo, objetos COM desenvolvidos em C++ podem interagir facilmente com uma aplicação .NET, permitindo que os desenvolvedores integrem diversos módulos de software de forma eficaz. DCOM é uma tecnologia remota que permite que clientes COM se comuniquem com servidores COM via comunicação entre processos (IPC) ou chamadas de procedimento remoto (RPC). Muitos serviços do Windows implementam componentes DCOM que podem ser acessados local ou remotamente.

As classes COM normalmente são registradas e contidas no Registro do Windows. Um programa cliente interage com um servidor COM criando uma instância da classe COM, conhecida como objeto COM. Esse objeto fornece um ponteiro para uma interface padronizada. O cliente utiliza esse ponteiro para acessar os métodos e propriedades do objeto, facilitando a comunicação e a funcionalidade entre o cliente e o servidor.

Objetos COM são frequentemente alvos de pesquisa para avaliar a exposição a vulnerabilidades e descobrir funcionalidades abusáveis. Um objeto COM preso é uma classe de bug na qual um cliente COM instancia uma classe COM em um servidor DCOM fora de processo, onde o cliente controla o objeto COM por meio de um ponteiro de objeto empacotado por referência. Dependendo da condição, esse vetor de controle pode apresentar falhas lógicas relacionadas à segurança.

O blog de Forshaw descreve um caso de uso de desvio de PPL em que a interface IDispatch, conforme exposto na classe COM WaaSRemediation, é manipulada para abuso de objetos COM presos e execução de código .NET. OWaaSRemediation é implementado no serviço WaaSMedicSvc, que é executado como um processo svchost.exe protegido no contexto de NT AUTHORITY\SYSTEM. O excelente passo a passo de Forshaw foi a base para nossa pesquisa aplicada e desenvolvimento de uma técnica de movimento lateral sem arquivos como prova de conceito.

Homem olhando para computador

Fortaleça sua inteligência de segurança 


Fique à frente das ameaças com notícias e insights sobre segurança, IA e outros semanalmente no boletim informativo do Think. 


Visão geral da pesquisa

Nossa jornada de pesquisa começou explorando a classe COM do WaaSRemediation, compatível com a interface co IDispatch. Essa interface permite que os clientes realizem a vinculação tardia. Normalmente, os clientes COM têm as definições de interface e tipo para os objetos que estão usando definidas no tempo de compilação. Em vez disso, a vinculação tardia permite que o cliente descubra e chame métodos no objeto em tempo de execução. IDispatch inclui o método GetTypeInfo, que retorna uma interface ITypeInfo. ITypeInfo tem métodos que podem ser usados para descobrir informações de tipo para o objeto que a implementa.

Se uma classe COM usar uma biblioteca de tipos, ela poderá ser consultada pelo cliente via ITypeLib (obtido de ITypeInfo-> GetContainingTypeLib) para recuperar informações de tipo. Além disso, bibliotecas de tipos também podem fazer referência a outras bibliotecas de tipos para obter informações adicionais de tipos.

De acordo com o post de blog de Forshaw, o WaaSRemediation faz referência à biblioteca de tipos WaaSRemediationLib, que, por sua vez, faz referência ao stdole (automação de OLE). O WaaSRemediationLib utiliza duas classes COM dessa biblioteca, StdFont e StdPicture. Ao executar o sequestro de COM no objeto StdFont modificando sua chave de registro TreatAs, a classe apontará para outra classe COM de nossa escolha, como System.Object na.NET Framework. Digno de nota, Forshaw destaca que StdPicture não é viável, pois esse objeto executa uma verificação de instanciação fora de processo, então mantivemos nosso foco no uso de StdFont.

Objetos .NET são interessantes para nós por causa do método GetType do System.Object. Por meio do GetType, podemos realizar a reflexão do .NET para acessar o Assembly.Load eventualmente. Embora System.Object tenha sido escolhido, esse tipo passa a ser a raiz da hierarquia de tipos em.NET. Portanto, qualquer objeto COM .NET pode ser usado.

Com o estágio inicial definido, havia dois outros valores DWORD sob a chave HKLM\Software\Microsoft\.NetFramework necessários para tornar nosso caso de uso percebido em realidade:

  • AllowDCOMReflection: conforme observado por Forshaw, esse valor habilitado nos permite realizar reflexão arbitrária para chamar qualquer método.NET. Normalmente, o .NET Reflection over DCOM é impedido devido às mitigações abordadas no MS14-009.
  • OnlyUseLatestCLR: usando o Procmon,descobrimos que esse valor deve ser habilitado para carregar a versão mais recente do.NET CLR (versão 4), caso contrário, a versão 2 será carregada por padrão.

Ao confirmar que a versão mais recente do CLR e do .NET poderia ser carregada em nossos esforços iniciais de teste, sabíamos que estávamos no caminho certo.

Do processo local para o computador remoto

Mudando nossa atenção para focar em aspectos programáticos remotos, primeiro usamos o Remote Registry para manipular os arquivos. valores de chave de registro do NetFramework e sequestra o objeto StdFont na máquina de destino. Em seguida, trocamos CoCreateInstance por CoCreateInstanceEx para instanciar o objeto COM WaaSRemediation no destino remoto e obter um ponteiro para a interface IDispatch.

Com um ponteiro para IDispatch, chamamos o método do membro GetTypeInfo para obter um ponteiro para a interface ITypeInfo, que está presa no servidor. Os métodos-membros chamados a partir daí ocorrem no lado do servidor. Depois de identificar a referência de interesse da biblioteca de tipos contida (stdole) e derivar a referência de objeto de classe subsequente de interesse (StdFont), acabamos usando o método CreateInstance "remotable" na interface ITypeInfo para redirecionar o fluxo de link do objeto StdFont (via manipulação TreatAs ) anterior para instanciar System.Object.

Com o AllowDCOMReflection está definido corretamente, podemos executar a reflexão .NET sobre DCOM para acessar Assembly.Load para carregar um assembly .NET no servidor COM. Como estamos usando Assembly.Load em vez de DCOM, essa técnica de movimento lateral é completamente sem arquivos, pois a transferência de bytes de assembly é tratada pela magia remota DCOM. Para uma explicação detalhada desse fluxo técnico da instanciação do objeto até a reflexão, consulte o diagrama a seguir:

fluxograma mostrando instanciação da Classe System.Object
Fluxo de Instanciação da Classe System.Object

Dores de desenvolvimento

Nosso primeiro e principal problema era chamar Assembly.Load_3, via IDispatch->Invoke. Invoke passa uma matriz de argumentos de objeto para a função de destino e Load_3 é a sobrecarga de Assembly.Load que utiliza uma matriz de byte único. Assim, precisávamos agrupar o SAFEARRAY de bytes dentro de outro SAFEARRAY de VARIANTs – inicialmente, continuamos tentando passar um único SAFEARRAY de bytes.

código mostrando como criar um equivalente não gerenciado do Object Byte
Criação de um equivalente não gerenciado do Object Byte

Outro problema era encontrar a sobrecarga adequada do Assembly.Load. As funções auxiliares foram extraídas do código CVE-2014-0257 da Forshaw, que incluía a função GetStaticMethod . Essa função utilizou a reflexão do .NET sobre DCOM para encontrar um método estático dado um ponteiro de tipo, o nome do método e sua contagem de parâmetros. Assembly.Load tem duas sobrecargas estáticas que recebem um único argumento; como tal, acabamos usando uma solução hacky. Percebemos que a terceira instância de Load com um único argumento foi a nossa escolha certa.

usado para buscar a sobrecarga adequada de Assembly.Load
Buscando a sobrecarga adequada do Assembly.Load

Dificuldades operacionais

Uma das maiores desvantagens que observamos com essa técnica foi que o beacon gerado teria sua vida útil limitada ao cliente COM; neste caso, o tempo de vida da aplicação do nosso binário de armamento "ForsHops.exe" (com um nome elegante, é claro). Portanto, se o ForsHops.exe limpasse suas referências COM ou saísse, o mesmo faria o beacon que estava sendo executado sob o svchost.exe da máquina remota. Tentamos soluções diferentes, como fazer com que nosso assembly .NET travasse indefinidamente seu thread principal, executar shellcode em outro thread e fazer com que o ForsHops.exe deixasse o thread de exploração travado, mas nada era elegante.

O thread principal do carregador .NET trava enquanto o shellcode é executado em um thread separado
O thread principal do carregador .NET trava enquanto o shellcode é executado em um thread separado

Em seu estado atual, ForsHops.exe é executado até que o beacon termine, quando então remove suas operações de registro. Há oportunidades de melhoria, mas deixaremos isso como um exercício para o leitor.

demonstração da execução do ForShops.exe
Execução do ForShops.exe
Beacon bem-sucedido no Windows 2019 Server
Beacon bem-sucedido no Windows 2019 Server
captura de tela do Beacon sendo executado em um processo svchost do PPL
O Beacon é executado em um processo svchost do PPL
exemplo de ForShops.exe removendo alterações após o beacon sair
ForShops.exe removendo alterações após o beacon sair

Recomendações defensivas

A orientação de detecção proposta por Samir Bousseaden (@SBousseaden) após Mohamed Fakroud publicar sua implementação também se aplica a esta técnica de movimento lateral:

  • Detecção de eventos de carga CLR no processo svchost.exe do WaaSMedicSvc
  • Detecção da manipulação do Registry (ou criação) da seguinte chave: HKLM\SOFTWARE\Classes\CLSID\{0BE35203-8F91-11CE-9DE3-00AA004BB851}\TreatAs ( chave TreatAs do StandardFont CLSID)

Além disso, recomendamos implementar os seguintes controles adicionais:

  • Detectando manipulação DACL de HKLM\SOFTWARE\Classes\CLSID\{0BE35203-8F91-11CE-9DE3-00AA004BB851}
  • Buscando a presença de valores OnlyUseLatestCLR e AllowDCOMReflection habilitados em HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework
  • Permitir que o firewall baseado em host restrinja o acesso à porta efêmera DCOM sempre que possível

Além disso, aproveite a seguinte regra de YARA de prova de conceito para detectar o executável ForsHops.exe padrão:

regra Detect_Standard_ForsHops_PE_By_Hash

{
    meta:   
        description = "Detects the standard ForShops PE file by strings"
        reference = "GitHub Project: https://github.com/xforcered/ForsHops/"
    strings:
        $s1 = "System.Reflection.Assembly, mscorlib" wide
        $s2 = "{72566E27-1ABB-4EB3-B4F0-EB431CB1CB32}" wide
        $s3 = "{34050212-8AEB-416D-AB76-1E45521DB615}" wide
        $s4 = "GetType" wide
        $s5 = "Load" wide

    condition:
        all of them
}

Conclusão

Nossa implementação estende ligeiramente o abuso de COM explicado no blog de Forshaw, aproveitando objetos COM presos para movimento lateral em vez de execução local para desvio de PPL. Portanto, ainda está suscetível às mesmas detecções que as implementações que realizam a execução local.

Você pode encontrar o código de movimento lateral de prova de conceito do ForsHops.exe aqui.

Reconhecimento

Agradecemos a Dwight Hohnstein (@djhohnstein) e Sanjiv Kawa (@sanjivkawa) pelo feedback sobre esta pesquisa e pela revisão do conteúdo do post de blog.

Recursos

Mixture of Experts | 12 de dezembro, episódio 85

Decodificando a IA: resumo semanal das notícias

Participe do nosso renomado painel de engenheiros, pesquisadores, líderes de produtos e outros enquanto filtram as informações sobre IA para trazerem a você as mais recentes notícias e insights sobre IA.

Soluções relacionadas
Soluções de segurança corporativa

Transforme seu programa de segurança com soluções do maior provedor de segurança corporativa.

Explore as soluções de cibersegurança
Serviços de cibersegurança

Transforme sua empresa e gerencie riscos com consultoria em cibersegurança, nuvem e serviços de segurança gerenciados.

    Conheça os serviços de segurança cibernética
    Cibersegurança de inteligência artificial (IA)

    Melhore a velocidade, a precisão e a produtividade das equipes de segurança com soluções de cibersegurança impulsionadas por IA.

    Explore a cibersegurança da IA
    Dê o próximo passo

    Quer você necessite de soluções de segurança de dados, gerenciamento de endpoints ou gerenciamento de acesso e identidade (IAM), nossos especialistas estão prontos para trabalhar com você para alcançar uma postura de segurança forte. Transforme sua empresa e gerencie os riscos com um líder mundial em consultoria de cibersegurança, nuvem e serviços de segurança gerenciados.

    Explore as soluções de cibersegurança Descubra os serviços de cibersegurança