As atualizações de segurança lançadas pela Microsoft em 11 de abril de 2023 corrigiram mais de 90 vulnerabilidades individuais. Entre elas, destacou-se a CVE-2023-21554, conhecida como QueueJumper, uma vulnerabilidade de execução remota de código que afeta o serviço Microsoft Message Queueing (MSMQ). O MSMQ é um componente opcional do Windows que permite que aplicações troquem mensagens por meio de filas acessíveis tanto localmente quanto remotamente. Esta análise foi realizada em colaboração com as equipes Randori e X-Force Adversary Services, por Valentina Palmiotti, Fabius Watson e Aaron Portnoy.
As seguintes características da CVE-2023-21554 chamaram atenção inicial:
Mantenha-se atualizado sobre as tendências mais importantes (e intrigantes) do setor em IA, automação, dados e muito mais com o boletim informativo Think. Consulte a Declaração de privacidade da IBM.
Para determinar como detectar a presença do MSMQ, um sistema de teste foi configurado com o componente em um ambiente de laboratório. Ao se conectar ao endpoint MSMQ na porta 1801, o servidor não respondeu com nenhum dado. Pode-se presumir que o servidor não responderá sem receber dados razoavelmente formados. Para entender como conduzir uma conversa com o endpoint, foi necessária uma inspeção mais aprofundada do código subjacente.
No aviso, não está imediatamente claro qual binário foi corrigido. O primeiro passo para identificar o código alterado foi baixar o arquivo de atualização. Isso foi alcançado pesquisando por KB5025224 no catálogo do Microsoft Update.
Resultados da pesquisa do Microsoft Update Catalog para KB5025224
Especificamente, o arquivo de atualização cumulativa
windows10.0-kb5025224-x64_3522b1b562ed44bc949541dd1c7d08e1e967d925.msu
para Windows 11 para sistemas baseados em x86 foi recuperado.
O extrator 7-Zip foi usado para descompactar o conteúdo do arquivo MSU e os arquivos CAB que ele continha. A pesquisa pela string "msmq" em todos os arquivos nas pastas resultantes resultou no arquivo de índice do contêiner,
Windows11.0-KB5025239-x64/express.psf.cix.xml,
que incluía a seguinte entrada:
<File id="21296" name="amd64_microsoft-windows-msmq-queuemanager-core_31bf3856ad364e35_10.0.22621.1555_none_5f975f8fdb349517\f\mqqm.dll" length="34811" time="133245266510000000" attr="128">
Esta entrada faz referência a
Para identificar as alterações feitas entre as versões corrigida e não corrigida do arquivo MQQM.dll1.00
Resultados do BinDiff entre arquivos MQQM.data corrigidos e não corrigidos
Uma análise dessas funções usando o Hex-Rays Decompiler revelou símbolos para a versão corrigida que contém nomes de funções detalhados que parecem fazer referência a identificadores de vulnerabilidade do Microsoft Security Research Center (MSRC):
Símbolos de funcionalidade dentro do MQQM.dll
Após uma inspeção mais detalhada, observou-se que os símbolos contendo números de casos do MSRC correspondiam a sinalizadores de funcionalidade usados para aplicar correções de vulnerabilidades. Os sinalizadores de funcionalidade são um componente do Windows que permite ativar ou desativar determinados comportamentos, funcionalidades ou experimentos. Neste caso, a “funcionalidade” identificada é, na verdade, um patch para uma vulnerabilidade.
Trecho de código descompilado mostrando o sinalizador de funcionalidade para correções de vulnerabilidade
Um mecanismo de verificação por sinalizador de funcionalidade estava presente para cada uma das vulnerabilidades que compunham a CVE-2023-21554, tanto nos componentes de usuário quanto nos componentes de kernel. A análise dos demais binários incluídos no pacote de atualização de segurança revelou que sinalizadores de funcionalidade também foram introduzidos para a maioria, ou possivelmente todas, as vulnerabilidades corrigidas no Patch Tuesday de abril de 2023.
Ao cruzar esses sinalizadores, conseguimos identificar rapidamente os pontos do código que foram corrigidos. Em particular, as alterações no relacionadas ao sinalizadorMSRC76146_MSMQ_OOBRWFixes pareceram ser a de maior interesse.
A verificação desse sinalizador de funcionalidade ocorre oito vezes dentro da função. Uma dessas verificações é mostrada abaixo:
Trecho de código descompilado mostrando a correção de vulnerabilidade
No trecho de código descompilado mostrado acima, o caminho de código corrigido obtém o ponteiro para a próxima seção da mensagem MSMQ via uma chamada para
Trecho de descompilação da função GetNextSectionPtrSafe
Depois de examinar todas as ocorrências desse sinalizador de funcionalidade e de outros, foi determinado que o CVE-2023-21554 consiste em vários bugs que verificam de forma insuficiente o tamanho de vários tipos de seção de mensagens MSMQ. Essas vulnerabilidades permitem que o ponteiro para o final da mensagem MSMQ seja incrementado por um deslocamento arbitrário de 32 bits para fora dos limites do buffer originalmente alocado.
Após o processamento do pacote, a etapa final em
Verificação de acesso a memória fora dos limites para o fim da mensagem
Durante a avaliação da explorabilidade das vulnerabilidades, foram identificadas vulnerabilidades adicionais no kernel no driver correspondente ao serviço MSMQ, As vulnerabilidades foram novamente indicadas pelos símbolos do sinalizador de funcionalidade, que também fazem referência ao caso MSRC 76146, conforme observado em .
Sinalizadores de funcionalidade dentro de mqac.sys
A análise do sinalizador de funcionalidade
Para executar essas operações, o componente de kernel analisa diferentes seções do buffer de pacotes MSMQ. Duas dessas instâncias são retratadas abaixo:
Trecho de código descompilado mostrando o sinalizador de funcionalidade para a correção da vulnerabilidade no driver de kernel mqac.sys
Como pode ser visto no trecho de código descompilado acima, se o sinalizador de funcionalidade estiver habilitado, as funções “seguras”
A maior parte do código relacionado à análise de protocolo está localizada dentro da biblioteca , que é carregada pelo processomqsvc.exe, uma solicitação MSMQ válida deve ser enviada pelo cliente. A especificação e o protocolo de mensagens do MSMQ são vastos e complexos. Inicialmente, foi encontrado um código público para escanear servidores MSMQ em um gist do GitHub, o que forneceu a base para uma exploração mais aprofundada do processamento de pacotes. Além disso, a documentação da Microsoft foi útil para compreender o formato das mensagens do MSMQ.
O código dentro da DLL recebe dados pela porta 1801 por meio de uma chamada à função WSARecv, como pode ser visto na sessão de depuração abaixo:
0:020> bp ws2_32!wsarecv ; g
Breakpoint 0 hit
WS2_32!WSARecv:
00007ffd`651115c0 48895c2408 mov qword ptr [rsp+8],rbx ss:000000aa`f07fef30=0000000000000006
0:019> k
# Child-SP RetAddr Call Site
00 000000aa`f07fef28 00007ffd`57ba9cb0 WS2_32!WSARecv
01 000000aa`f07fef30 00007ffd`57b9683d
MQQM!NoReceivePartialBuffer+0x38
02 000000aa`f07fefb0 00007ffd`57b2078e MQQM!CWinsockConnection::ReceivePartialBuffer+0x7d
03 000000aa`f07ff010 00007ffd`57b25b28
MQQM!CSockTransport::BeginReceive+0xe6
04 000000aa`f07ff060 00007ffd`57b2d40b
MQQM!CSockTransport::NewSession+0x194
05 000000aa`f07ff3f0 00007ffd`57b2d367 MQQM!CSessionMgr::AcceptSockSession+0x6f
06 000000aa`f07ff430 00007ffd`645e26bd MQQM!AcceptIPThread+0x6a7
07 000000aa`f07ff7d0 00007ffd`6650a9f8
KERNEL32!BaseThreadInitThunk+0x1d
08 000000aa`f07ff800 00000000`00000000 ntdll!RtlUserThreadStart+0x28
0:019> r @$t0=@rdx
0:019> pt
WS2_32!WSARecv+0x19f:
00007ffd`6511175f c3 ret
0:019> dc poi(@$t0+8) LC
000001b5`880ff490 62f06110 524f494c 00000070 5a5a5a5a .a.bLIORp…ZZZZ
000001b5`880ff4a0 0073006e 00610074 0063006e 00200065 n.s.t.a.n.c.e. .
000001b5`880ff4b0 00440049 00000000 00000000 00000000 I.D………….
0:019> ba r1 poi(@$t0+8) ; g
Breakpoint 1 hit
MQQM!CBaseHeader::SectionIsValid+0x154:
00007ffd`57b53450 7462 je
MQQM!CBaseHeader::SectionIsValid+0x1b8 (00007ffd`57b534b4) [br=1]
A função é responsável por analisar o início de uma nova mensagem, que começa com uma estrutura BaseHeader no formato:
Estrutura do objeto BaseHeader
Os campos de mensagem recebida são validados por
Código de disassembly para SectionIsValid within MQQM.dll
Após a validação inicial, a maior parte da análise do protocolo ocorre dentro da função construtora
Descompilação mostrando vulnerabilidade no manuseio do EodHeader
A descompilação acima mostra o caminho corrigido chamando a função denominada
A estrutura de um
Disassembly calculando o tamanho total da seção EodHeader
Na desmontagem acima, o ponteiro para o início do
WinDbg session showing an access violation when writing to the end of a malformed MSMQ message
Para avaliar a robustez da escrita OOB obtida pelas vulnerabilidades, primeiro investigamos a alocação e o gerenciamento da memória subjacente.
Antes de um pacote de mensagemMSMQ. Essa função chama o driver do kernel viaNtDeviceIoControl.
Descompilação da função de alocação do buffer de pacote
O componente do kernel retorna um buffer que é particionado a partir de uma visão mapeada de um arquivo no disco, armazenado no diretório
Layout de memória do processo MQSVC no ProcessHacker mostrando que o buffer de pacote é um mapeamento de arquivo
O endereço do buffer de pacote, juntamente com um handle de pacote randomizado, é retornado ao espaço de usuário após a solicitação de alocação via IOCTL. O driver utiliza esse identificador para calcular o endereço do buffer de pacote ao lidar com requisições vindas do modo usuário.
A memória subjacente aos buffers vulneráveis está dentro de um arquivo mapeado, não em um heap do processo. Além disso, a memória afetada não armazena quaisquer objetos. O único conteúdo presente no mapeamento de arquivo são as mensagens MSMQ e os metadados armazenados em
Além disso, sem conhecimento prévio do layout de memória, não é possível saber a posição relativa desse mapeamento em relação ao heap do processo para que seja possível sobrescrever ponteiros de objetos. Portanto, para que a execução remota de código seja viável, é necessário algum método para moldar a memória em uma posição previsível.
Após experimentar o envio de mensagens MSMQ legítimas para o destino, descobriu-se que é possível forçar uma nova alocação de heap adjacente a um mapeamento de arquivo
Layout de memória do processo MQSVC mostrando um novo heap alocado adjacente a um mapeamento de arquivo MQ
No entanto, isso exige que o invasor tenha acesso para enviar mensagens para pelo menos uma fila no destino; caso contrário, as mensagens serão descartadas e substituídas e, portanto, não ocuparão a memória dos pacotes de forma persistente.
Na experimentação de pesquisa realizada, descobriu-se que é possível para um usuário sem privilégios criar uma fila aberta para qualquer pessoa enviar mensagens remotamente, por padrão. A fila persiste no sistema indefinidamente. Embora a maioria dos usos modernos do MSMQ exista como componentes e adaptadores opcionais, fornecedores como Oracle, Veritas e Xerox oferecem softwares corporativos que dependem do MSMQ como uma dependência central. Portanto, o cenário de ataque não é irrealista.
A vulnerabilidade de acesso à memória fora dos limites introduz uma primitiva de exploração que permite que um
cliente malicioso para screva umOnDiskExtensionHeader
alocados para o pacote.
Ao avaliar uma vulnerabilidade que permite escrever valores em um deslocamento, algumas propriedades
devem ser consideradas:
No caso das vulnerabilidades do QueueJumper, o deslocamento do buffer alocado para a escrita OOB é controlável porque é calculado usando dados controlados pelo invasor. O conteúdo da escrita OOB, no entanto, tem controle limitado.
Aqui,Address será gravado. A sequência de operações que ocorrem é a seguinte:
0x000000000000000C
é gravadoAddress0x00000000Address+0x2
0x0000000000000000Address+0x12
0x0000000000000000
é gravado em Address+0x1A
0x00000094
é gravado em Address+0xE
0x0000Address+0x220x0000
é gravado em Address+0x62
memcpy(Address+0xA6, Source, Source->AddressLength+0x08)
OSourcememcpy define um único endereço de transporte de um tipo específico (por exemplo, NetBIOS). A definição é a seguinte:
typedef struct _TA_ADDRESS {
USHORT AddressLength;;
USHORT AddressType; Info;
UCHAR Address[1];;
} TA_ADDRESS, *PTA_ADDRESS;
Especifica o número de bytes em um endereço do AddressType especificado.
Especifica o tipo de endereço de transporte.
Especifica uma matriz de tamanho variável contendo o endereço de transporte.
Em uma sessão de depuração abaixo,TA_ADDRESS
rax=0000014e1d110180 rbx=0000014e1d110180 rcx=0000014e1d110184
rdx=0000014e1ce86b10 rsi=0000014e1d110001 rdi=0000000545d7fa50
rip=00007ff843208074 rsp=0000000545d7f940 rbp=0000000545d7f980
r8=000000000000000c r9=0000000000000002 r10=0000000000000000
r11=0000000545d7f938 r12=0000000000000002 r13=0000000000001000
r14=0000014e1ce86b10 r15=0000000000000000
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
MQQM!CQmPacket::CQmPacket+0x89c:
00007ff8`43208074 e8a5780a00 call MQQM!memcpy (00007ff8`432af91e)
0:017> dc rcx
0000014e`1d110184 00010004 00000000 8620a8c0 00000000 ………. …..
Acima, o objetoTA_ADDRESS, um tipo de1. A conversão de cada byte em um octeto IPv4 resulta no endereço , que é o endereço de origem IPv4 do cliente.
Esse cenário permite a escrita de dados controlados pelo atacante por meio de um endereço de origem para um deslocamento controlado a partir da alocação do pacote na memória. Embora extremamente restritivo e possivelmente inviável na prática, isso poderia, teoricamente, ser usado para controlar com precisão os valores escritos na memória ao fazer múltiplas requisições a partir de endereços de origem variados, usando deslocamentos de pacote decrementais.
Devido ao número de limitações encontradas para alcançar a execução remota de código, cenários de ataque alternativos, como vazamentos de informações, foram investigados.
Um ataque que foi considerado estava corrompendo o ponteiro para uma seção de mensagem específica para apontar para a memória fora dos limites e, mais tarde, recuperar a mensagem para ler a memória adjacente.
No entanto, o início de cada análise de seção faz uma verificação da posição da seção atual, portanto, não podemos enganar a lógica de análise para analisar uma seção de dados que está fora dos limites.
Outro cenário de ataque possível é substituir o OnDiskExtensionHeader de outra mensagem para enganar o destinatário da mensagem sobre as origens da mensagem recebida. Da mesma forma, é possível substituir o conteúdo das mensagens destinadas a filas às quais o invasor não tem acesso. Sobrescrever conteúdos específicos de uma mensagem exigiria que o atacante tivesse informações sobre a mensagem, como seu tamanho.
Há uma seção opcional chamada SoapHeader que é suscetível ao acesso a dados fora dos limites, com o seguinte formato:
Formato do pacote SoapHeader
Observou-se que a memória usada para armazenar dados de pacotes MSMQ não é devidamente limpa quando o buffer é liberado. Como resultado, o envio de um pacote MSMQ que é menor em tamanho do que um pacote MSMQ anterior resultará em um layout de memória contendo os dados do pacote atual seguidos por dados de pacotes MSMQ processados anteriormente.
Descobriu-se que o
O controle limitado no conteúdo de escrita, com escritas não controladas adicionais corrompendo a memória adjacente, juntamente com o conhecimento limitado no layout da mensagem, provaram ser um grande obstáculo na invasão.
No momento da análise, apenas as vulnerabilidades do espaço do usuário envolvidas no processamento inicial de um pacote de mensagem MSMQ foram avaliadas quanto à sua explorabilidade. A análise das vulnerabilidades da variante do kernel pode descobrir caminhos adicionais de invasão. Por exemplo, é possível que a inclusão de um cabeçalho malformado ganhe uma primitiva de gravação no mapeamento de memória do kernel do arquivo . Em alguns casos, as verificações no processamento do espaço do usuário impedem que o kernel execute algumas operações de pós-processamento.
Além disso, no caso das variantes do kernel, o invasor tem menos controle direto sobre o conteúdo das mensagens e também está operando no mesmo mapeamento de arquivos que o espaço do usuário, não em um pool de kernel (heap). Mitigações, como KASLR, também apresentam obstáculos que são difíceis de superar para invasão remota.
Devido à implementação do código corrigido, um cliente remoto pode determinar com segurança se um sistema aplicou a atualização. Isso pode ser feito enviando uma mensagem MSMQ que aciona a vulnerabilidade, mas que não causa de fato um acesso fora dos limites, evitando assim a falha do processo do serviço. Se o sinalizador HTTP estiver definido no UserHeader da mensagem, espera-se um SRMPEnvelopeHeader . Na correção, é realizada uma verificação de estouro de inteiro no campo multiplicado por 2. Esse comprimento pode ser definido de forma que o resultado da multiplicação ultrapasse um número pequeno, igual ao tamanho real dos dados.
Trecho de código decompilado mostrando a verificação de estouro de número inteiro em SRMPEnvelocaHeader DataLength
Se o destino for corrigido, a vulnerabilidade fará com que uma exceção seja lançada e o servidor interromperá o processamento da mensagem, portanto, nenhuma resposta será enviada de volta ao cliente. Se não estiver corrigido, o servidor processará a mensagem normalmente e enviará uma resposta.
Devido à localização da memória que dão suporte aos buffers vulneráveis, a execução remota de código parece mais viável para um invasor com a capacidade de enviar mensagens para qualquer fila no alvo. Nesse cenário, é possível realizar o grooming de forma que um heap adjacente seja alocado. O controle extremamente limitado sobre o conteúdo de gravação apresenta desafios para a invasão remota. Devido a medidas de mitigação como o ASLR, também é necessário obter um vazamento de informações. É possível que uma única escrita de uma constante (0xC, 0x0
Em última análise, concluímos que, embora a exploração remota do QueueJumper não seja necessariamente impossível, os requisitos mencionados tornam sua realização extremamente difícil. Este artigo apenas arranha a superfície do que constitui uma especificação de mensagem, um serviço e um protocolo altamente complexos. Pesquisas adicionais de vulnerabilidades, tanto no componente de user space quanto no componente de kernel, são justificadas.