Me dê um E, me dê um T, me dê um W. O que a gente tem? RPC!
Índice
O conteúdo deste blog foi apresentado originalmente no BlackHat USA 2023.
MS-RPC: Desgastar o protocolo para detectar ataques
Estamos dando continuidade ao nosso mergulho no MS-RPC, com mais uma postagem de blog. Mas, agora, em vez de nos concentrarmos no lado ofensivo e procurarmos vulnerabilidades, discutiremos alguns dos recursos defensivos integrados do Windows. Veremos como utilizá-los para ter uma ideia do que está acontecendo sob o domínio do RPC, com esperança de detectar atividades nefastas ao longo do caminho.
O MS-RPC é parte integrante do sistema operacional Windows, o que, consequentemente, significa que muitos caminhos de movimento lateral passam por ele. Ataques como PSExec, Tarefa programada remota, DCSynce PetitPotam são todos realizados por meio de protocolos MS-RPC e pode ser difícil discernir o tráfego de rede benigno do mal-intencionado apenas com metadados de rede tradicionais (porta de origem e destino e endereço IP; às vezes também informações de processo).
Reforçaremos nossa visibilidade com o Event Tracing for Windows (ETW), um mecanismo de rastreamento e monitoramento integrado no sistema operacional Windows. O ETW nos fornece um tesouro de informações, especificamente para RPC, especialmente quando comparado aos metadados de rede tradicionais.
O conceito de rastrear eventos de RPC usando seu provedor de ETW não é novo, e há muitos recursos e ferramentas bons já disponíveis para ele (nós vinculamos alguns em nossa sessão de Referências e informe-nos se tiver conhecimento de outras). A maioria das pesquisas existentes se concentra em eventos no lado do cliente, que os invasores podem adulterar ou modificar o programa para contornar o registro completamente. Em vez disso, focaremos na detecção de ataques usando eventos no lado do servidor, que estão fora do alcance dos invasores, e veremos as considerações exclusivas que precisamos analisar.
Nesta postagem, veremos como consumir e analisar eventos do provedor de RPC ETW e como verificar eventos para atividades potencialmente mal-intencionadas. Usando o provedor de ETW, também podemos ver a operação exata que está sendo solicitada, o que nos dá uma granularidade muito mais fina com a qual podemos detectar ataques. Veremos exatamente como isso funciona daqui a pouco.
Lembre-me: O que é RPC?
RPC significa chamada de procedimento remoto (remote procedure call) e é uma forma de comunicação entre processos (IPC). Especificamente, este protocolo foi projetado para permitir a invocação de função remota entre diferentes processos. No nosso caso, focaremos na implementação da Microsoft, MS-RPC.
O MS-RPC foi projetado como um modelo cliente-servidor. O servidor define a interface que irá expor usando a linguagem de definição de interface (IDL). Dentro da IDL, temos um identificador universal exclusivo (UUID) para a interface, bem como as definições de função para as funções que ela expõe (Figura 1).
[
uuid(12345678-4000-2006-0000-20000000001a)
]
interface Test
{
void Foo([in] int number, [in] char *message);
void Bar([out] int * result);
}
Fig. 1: Um exemplo de definição de interface IDL
A comunicação é transportada sobre certos protocolos de transporte, e cada transporte tem seu próprio tipo de endpoint (Figura 2). A melhor maneira de explicar isso é com um exemplo: A comunicação de RPC pode ser transportada por TCP, caso em que o transporte é TCP e o ponto de extremidade é o soquete TCP, identificado por um número de porta.
Transportes |
Endpoints |
---|---|
TCP Tubulação nomeada UDP ALPC Tipo Soquete Hyper-V |
<número da porta> <nome do tubo> <número da porta> <Porta ALPC> <nomes de host> <UUID> |
Fig. 2: Os protocolos de transporte comuns e seus respectivos tipos de endpoint
É importante observar que, embora as funções tenham um nome legível no arquivo IDL, elas são identificadas de forma diferente na rede. Em vez de um nome de cadeia de caracteres, são identificadas por um número inteiro, chamado opnum (abreviação de número de operação). Geralmente, é atribuído pela ordem de exibição das funções na definição da interface IDL (portanto, no exemplo da Figura 1, Foo será identificado usando opnum 0, enquanto Bar será opnum 1). Isso será importante mais tarde, quando precisaremos saber o número de funções relevantes para identificá-las nos dados que veremos.
Para obter uma visão geral mais longa e aprofundada do MS-RPC, consulte nossa postagem anteriorou uma de nossas apresentações de conferência em HexaCon ou CON DEF sobre o tópico.
#define ETW
O Event Tracing for Windows (ETW) é um mecanismo integrado de rastreamento e registro, implementado dentro do kernel do Windows. Ele funciona em um modelo provedor-consumidor; os provedores enviam eventos para o kernel, que os redireciona para qualquer programa do consumidor. Os provedores e consumidores precisam se registrar com o kernel antecipadamente (Figura 3).
Além disso, como o roteamento de eventos é tratado pelo kernel, se os eventos forem enviados por provedores, mas não houver consumidores para eles, os eventos serão simplesmente descartados e enviados para o vazio.
Microsoft-Windows-RPC
O Provedor RPC ETW é implementado dentro do tempo de execução RPC, rpcrt4.dll. Ele tem 13 eventos diferentes, mas estamos principalmente interessados em quatro deles: eventos 5 e 7 para início e parada de chamada do cliente (respectivamente) e eventos 6 e 8 para início e parada de chamada do servidor. Vamos nos concentrar nos eventos de início de chamada (Figura 4), pois eles fornecem mais informações. (Os eventos de interrupção de chamada simplesmente informam o status de retorno RPC.) Os eventos do cliente e do servidor compartilham o mesmo formato.
<template tid="RpcServerCallStartArgs_V1">
<data name="InterfaceUuid" inType="win:GUID"/>
<data name="ProcNum" inType="win:UInt32"/>
<data name="Protocol" inType="win:UInt32"/>
<data name="NetworkAddress" inType="win:UnicodeString"/>
<data name="Endpoint" inType="win:UnicodeString"/>
<data name="Options" inType="win:UnicodeString"/>
<data name="AuthenticationLevel" inType="win:UInt32"/>
<data name="AuthenticationService" inType="win:UInt32"/>
<data name="ImpersonationLevel" inType="win:UInt32"/>
</template>
Fig. 4: Esquema de evento de início de chamada RPC ETW
Teoricamente, os dados no evento devem nos fornecer todos os metadados necessários para obter percepções sobre o tráfego RPC. Agora sabemos o que é solicitado a partir da interface UUID e opnum, e também sabemos o endereço de origem ou destino (dependendo se estamos analisando para eventos de cliente ou servidor), através do campo NetworkAddress. Não pode ser tão simples assim, pode?
Mas não é. Acontece que o tempo de execução do RPC não preenche o campo NetworkAddress ao processar eventos de chamada do servidor, portanto, precisaremos encontrar outra maneira de descobrir esses dados se quisermos ter os metadados da conexão. Os eventos do cliente têm este campo preenchido.
Isso cria uma dúvida: Podemos ignorar eventos de servidor e confiar apenas no lado do cliente? A resposta é não. Como estamos tentando encontrar um comportamento mal-intencionado, que se originará da máquina controlada do invasor, não podemos ter certeza de que eles não violarão o provedor de ETW do lado do cliente (e o desativará ou bloquearão seus eventos), nem podemos ter certeza de que eles irão até mesmo passar pelo tempo de execução de RPC.
A popular biblioteca Python Impacket, que geralmente é usada em provas de conceito de ataque, ou PoCs (Proofs of Concept), e ferramentas (e contém implementações de ataques de rede, como o PSExec), implementa o tráfego RPC dentro dele, de modo que todos os invasores que o utilizam contornarão o tempo de execução RPC e não serão registrados no provedor ETW. Como os eventos de servidor estão fora do controle dos invasores, é mais prudente contar com eles, e é por isso que agora vamos nos concentrar em como obter os dados de rede de outro lugar e combiná-los com o evento de RPC.
Correspondência de eventos de RPC a fluxos de rede
Fazemos uma pausa do nosso foco no provedor de RPC ETW para analisar outros provedores de ETW, ou seja, o TCP e SMB fornecedores. Esses dois protocolos são os protocolos de transporte comuns para o tráfego RPC. Como dissemos que o tipo de endpoint RPC depende do protocolo de transporte, podemos combinar o endpoint (número da porta, nome do pipe, etc.) à medida que o recebemos do provedor RPC com o respetivo valor no provedor ETW de transporte.
Para TCP, é muito simples. Vejamos o ID do evento 1017, chamado TcpAcceptListenerComplete, que é acionado quando o handshake tridirecional TCP é concluído.
Ele tem dois campos (basicamente): Endereço local e Endereço remoto (no entanto, no nosso caso, estamos analisando eventos de servidor, local refere-se ao servidor, enquanto remoto refere-se ao cliente). Os valores dos campos de endereço estão em binário e contêm a família de endereços, o endereço IP e o número da porta (Figura 6).
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
---|---|---|---|---|---|---|---|
Família de endereços |
Número da porta |
Valor do endereço (IP se AF_INET) |
Fig. 6: O formato do campo binário do endereço
Tudo o que precisamos fazer é extrair o endereço IP (do cliente) do campo Endereço remoto e rastreá-lo com a porta (destino) no campo Endereço local . Agora, no momento em que obtemos um evento RPC em nosso servidor, pelo mesmo número de porta TCP, podemos dizer de onde ele veio com base na correspondência porta-ip que extraímos do provedor TCP (Figura 7).
Com a SMB, a situação é um pouco mais complicada, pois diferentes bits de informações aparecem em diferentes eventos ETW. O endpoint é um pipe nomeado, que é o mesmo que um arquivo, mas vários arquivos podem ser acessados pela mesma sessão SMB. Portanto, nesse caso, para corresponder o endpoint com sua fonte de rede, precisamos acompanhar dois eventos: um para a conexão real e outro para a solicitação de arquivo (Figura 8).
Para o evento de conexão, temos a ID de evento 500, Smb2ConnectionAcceptStart, que é acionada quando a conexão SMB é estabelecida. Nós obtemos o IP de origem e um UUID de conexão dele. Depois, procuramos o evento ID 8, Smb2RequestCreate_V2, que contém o nome de arquivo solicitado e o mesmo campo UUID de conexão. Agora, só precisamos cruzar os dois eventos por meio do UUID da conexão para corresponder o nome do pipe ao IP que o solicitou (e, posteriormente, precisaremos corresponder o nome do pipe ao endpoint da chamada RPC).
Para evitar o incômodo de implementar toda essa correspondência, implementamos esse algoritmo e você pode encontrar a ferramenta RPC Visibility em nosso Repositório do GitHub. A ferramenta é escrita em Python e salva o tráfego de rede RPC registrado em um banco de dados Neo4j para facilitar a visualização.
Detecção do ataque
Agora que temos todas as informações de que precisamos, podemos finalmente voltar nossa atenção para a detecção de fluxos de RPC mal-intencionados. O processo geral é simples: Encontramos um ataque que é transportado por RPC, executamos uma PoC dele e vemos qual interface RPC ele usa, bem como a operação solicitada. Em seguida, podemos simplesmente criar uma assinatura que corresponda a essa operação. Incluímos consultas de assinatura que funcionam com a implementação do banco de dados Neo4J em nosso lançamento, mas continue lendo para obter a lógica e as condições gerais.
PSExec
PsExec é um nome geral para uma técnica de ataque e uma ferramenta Sysinternals (a que deu o nome da técnica). Basicamente, a ferramenta copia um binário de serviço para o compartilhamento ADMIN$ do destino remoto (pasta de instalação do Windows) e, em seguida, comunica-se com o gerenciador de serviços por meio de sua interface RPC (MS-SCMR) para criar um serviço para o binário e executá-lo (Figura 9).
Há muitos motivos legítimos para entrar em contato com máquinas remotas via SCMR; watchdogs que consultam o status do serviço remoto, por exemplo. Há muito menos motivos para criar remotamente novos serviços. Portanto, não queremos acionar alertas em qualquer conexão pelo SCMR (que podemos detectar mesmo sem o RPC ETW, simplesmente fazendo a correspondência de uma conexão de rede de entrada com o processo do gerenciador de serviços services.exe), mas somente em conexões que criam serviços remotos.
Assim, nossa assinatura deve ser (em termos gerais, não específica para nossa implementação de visibilidade de RPC):
interface_uuid == “367ABB81-9844-35F1-AD32-98F038001003” AND (opnum == 0xC OR opnum == 0x18)
onde 0xC é o opnum para RCreateServiceW e 0x18 é para RCreateServiceA.
Agendador de Tarefas remoto
Semelhante ao PsExec, o Agendador de Tarefas pode ser usado para iniciar um binário remoto e obter movimento lateral dessa maneira. Ele nem precisa lançar um novo binário, pois também pode iniciar um console cmd ou PowerShell e baixar um binário hospedado online.
Também de forma semelhante ao PSExec, não queremos detectar qualquer acesso ao serviço Agendador de Tarefas, mas estamos interessados principalmente em chamadas RPC para SchRpcRegisterTask.
interface_uuid == “86D35949-83C9-4044-B424-DB363231FD0C” AND opnum == 0x1
DCSync
O DCSync é outro ataque baseado em RPC, mas direcionado a controladores de domínio. Nesse caso, o invasor se conecta ao controlador de domínio real, fingindo ser um novo controlador de domínio. Em seguida, ele pede para replicar o banco de dados de credenciais do controlador de domínio, obtendo acesso aos hashes de senha KRBTGT.
A solicitação de replicação ocorre na interface MS-DRSR RPC e usa a função específica DRSGetNCChanges (opnum 3).
interface_uuid == “e3514235-4b06-11d1-ab04-00c04fc2dcd2” AND opnum == 0x3
PetitPotam
PetitPotam é um ataque de coerção de autenticação no serviço EFS (Encrypted File System). Basicamente, os invasores podem se conectar à interface EFS RPC (MS-EFSR) e dizer para ela abrir um arquivo remoto especificado por um caminho UNC. Em seguida, ela acionaria uma conexão SMB de saída com a autenticação que o invasor pode transmitir.
Quando o ataque PoC foi liberado, ele usou EfsRpcOpenFileRaw (opnum 0), que foi corrigido desde então. Topotam, o pesquisador que encontrou a vulnerabilidade, também descobriu que EfsRpcEncryptFileSrv (opnum 4) tinha a mesma falha.
interface_uuid == “c681d488-d850-11d0-8c52-00c04fd90f7e” AND (opnum == 0x0 OR opnum == 0x4)
Desvantagens
Apesar de haver muitas informações a serem obtidas do provedor RPC ETW, ele não é um balcão único para todas as nossas necessidades de segurança. Embora saber qual operação é solicitada em cada fluxo de rede seja um dado valioso, as informações mais cruciais, quais dados foram passados em cada solicitação, não são registradas. Isso significa que a detecção de movimento lateral através do provedor de ETW ainda é apenas uma abordagem baseada na heurística, embora com muito mais contexto do que as quatro redes tradicionais.
Também é um método de detecção puro e não pode ser usado para parar ou responder a ataques. A Microsoft nos fornece outro mecanismo de defesa integrado para RPC, o filtro de RPC no firewall do Windows. Você pode ler mais sobre esse mecanismo de filtragem e aprender a usá-lo em nosso Guia definitivo para a publicação Filtro de chamada de procedimento remoto (RPC).
Resumo
O provedor RPC ETW não é uma adição nova ao Windows, mas tem sido negligenciado em termos de defesa de rede. Há algumas ferramentas que interagem e consomem eventos com ele, mas são principalmente destinadas a pesquisadores de segurança e se concentram menos no lado da rede das coisas.
Nesta publicação, discutimos como podemos usar o provedor de RPC ETW, juntamente com os provedores de TCP e SMB, para obter visibilidade das operações de RPC solicitadas que vêm da rede. Isso nos fornece uma abordagem baseada em heurística que podemos usar para detectar possíveis solicitações maliciosas que podem ser usadas para movimentação lateral.
Referências
Ferramentas de consumo do RPC ETW por outros pesquisadores de segurança
RpcMon da CyberArk
RpcInvestigator da Trail of bits
Utilizando telemetria RPC por Jonathan Johnson da SpecterOps
Nossos Repositório do GitHub para todas as coisas RPC
Componentes internos de IPC do Windows ofensivo 2: RPC por Carsten Sandker
- Como proteger um servidor RPC do Windows e como não protegê-lo por James Forshaw