
Análise detalhada e implementação de exploração para o Windows PrintNightmare (CVE-2021-1675/34527) com escalonamento de privilégio baseado em RPC e execução remota de código via instalação de driver de impressora malicioso.
= Print Nightmare - Relatório de Análise :imagesdir: Figures :toc: :icons: font :figure-caption: Figura :xrefstyle: short :pdf-theme: basic-theme.yml
Em 29 de junho de 2021, uma vulnerabilidade muito grave no serviço de impressão do Windows foi exposta como uma vulnerabilidade de dia zero, com uma pontuação base de 8,8, publicada no GitHub (agora removida). Esta vulnerabilidade é a famosa PrintNightmare: CVE-2021-34527, que é ainda mais perigosa que o EternalBlue.
== Informações Básicas da Vulnerabilidade
A vulnerabilidade CVE-2021-34527 afeta quase todas as versões do Windows a partir do Windows 7 e Windows Server 2008. Consulte <> para mais detalhes.
Do ponto de vista do dano, um atacante pode usar autenticação de usuário comum e executar código arbitrário remotamente com privilégios de administrador. Em termos de dificuldade de exploração, esta vulnerabilidade é muito fácil de explorar, portanto extremamente prejudicial.
Quanto às características da vulnerabilidade, a CVE-2021-34527 baseia-se na CVE-2021-1675. A vulnerabilidade CVE-2021-1675 é uma vulnerabilidade de escalada local de privilégios e execução remota de código, e tem muitas semelhanças com a CVE-2021-34527.
Antes de entender o princípio de funcionamento da vulnerabilidade, devemos ter uma compreensão geral da arquitetura do spooler de impressão do Windows, para que possamos clarificar a relação entre os vários módulos envolvidos na vulnerabilidade.
== Fluxo de Chamada da CVE-2021-1675
=== Arquitetura do Spooler de Impressão do Windows
A arquitetura do spooler pode ser representada por <<spooler_arch>>:
[[spooler_arch]] .Arquitetura do Spooler de Impressão image::Print Spooler Architecture.png[]
Especificamente, o spooler de impressão é usado para gerenciar tarefas de impressão e é composto pelos seguintes componentes:
winspool.drv:: O arquivo de biblioteca de ligação dinâmica fornecido ao usuário. Este arquivo define as APIs Win32 relacionadas ao spooler para o usuário chamar. Todas as APIs dentro são obtidas por chamada de procedimento remoto.
spoolsv.exe:: spoolsv.exe atua como o servidor no sistema, sendo o primeiro programa a processar chamadas de API. Este design permite que o print spooler lide com trabalhos de impressão locais e, indistintamente, lide com trabalhos de impressão remotos.
spoolsv.dll:: O roteador. Ele encaminha os pedidos de impressão recebidos pelo spoolsv.exe para vários provedores de impressão e decide qual provedor finalmente processa o pedido. Sua função é distinguir se a tarefa de impressão é remota ou local. Em máquinas remotas, ele atribui a tarefa fixamente ao provedor de impressão local.
localspl.dll:: O provedor de impressão local. A principal tarefa do provedor de impressão é resolver as necessidades de gerenciamento de tarefas de impressão, e a grande maioria das APIs são implementadas neste módulo.
Com base na teoria acima, continuamos a estudar. Por exemplo, quando chamo a função AddPrinterDriverEx (CVE-2021-1675), o seguinte fluxo ocorre:
=== Seleção da Versão da Função
Primeiro, esta função é na verdade uma macro que seleciona a versão Unicode (W) ou Ansi (A) dependendo do ambiente de compilação local, como em <>:
[[AddPrinterDriverEx]] .AddPrinterDriverEx image::AddPrinterDriverEx.png[]
Mas quer seja na versão de caracteres largos ou estreitos, o resultado é o mesmo, porque as strings no kernel do Windows usam codificação Unicode, então, no final, a chamada da versão Ansi será convertida para a chamada da versão Unicode, como em <>:
[[AnsiToUnicode]] .AnsiToUnicode image::AnsiToUnicode.png[]
Depois que todos os parâmetros da função Ansi são convertidos para a versão Unicode, uma função é chamada (<>):
[[AnsiCallUnicode]] .AnsiCallUnicode image::AnsiCallUnicode.png[]
E essa função é na verdade a versão Unicode de AddPrinterDriverEx (<>):
[[GetUnicodeProcAddress]] .GetUnicodeProcAddress image::GetUnicodeProcAddress.png[]
=== A Função API Envia uma Solicitação RPC para o Servidor Spooler
Ao entrar na função da versão Unicode, primeiro é feita uma seleção do tipo de parâmetro da função com base no valor de Level:
image::pDriverInfo.png[]
Nesta vulnerabilidade, definimos Level como 2, ou seja, selecionamos o tipo do parâmetro pDriverInfo como a estrutura DRIVER_INFO_2.
Em seguida, o Windows processa os parâmetros da função. Após o processamento, a chamada de API é continuada através de chamada de procedimento remoto:
image::set arguments.png[]
image::NdrClientCall3.png[]
=== Mecanismo MSRPC
O mecanismo de chamada de procedimento remoto da Microsoft é construído com base no padrão DCE. Explicando de forma simples, chamada de procedimento remoto significa executar processos em sistemas remotos, processos que são pré-definidos pelo programador ou pelo sistema.
O método específico do RPC é serializar a função que se deseja chamar remotamente, transmiti-la através da rede para o sistema remoto, que a desserializa e a executa. No sistema construído pela Microsoft, TCP/IP e SMB são normalmente os protocolos escolhidos para transportar chamadas RPC.
Para usar MSRPC, primeiro é necessário definir a descrição da interface IDL da função a ser chamada e, em seguida, usar a ferramenta MIDL para gerar os stubs de serialização correspondentes do cliente e do servidor. Para algumas APIs Win32, o stub do servidor já está definido, e podemos apenas gerar e usar o stub do cliente.
MSRPC usa UUID para identificar um tipo de protocolo. Por exemplo, MS-RPRN é usado para descrever o protocolo de impressão remota. Todas as funções relacionadas à impressão remota fazem parte deste protocolo. O MSPRC usa o UUID 12345678-1234-ABCD-EF00-0123456789AB para identificar este protocolo (<<rprn_uuid>>):
[[rprn_uuid]] .UUID do MS-RPRN image::spoolss uuid.png[]
Em seguida, pode-se continuar usando o número de operação (opnum) para identificar funções dentro do protocolo nesta conexão, para chamá-las remotamente. Por exemplo, AddPrinterDriverEx usa 89 para se identificar (<<addPrinterDriverEx_opnum>>):
[[addPrinterDriverEx_opnum]] .Opnum do AddPrinterDriverEx image::AddPrinterDriverEx Opnum.png[]
Ao usar MSRPC, há dois pontos que merecem atenção:
"Como você pode ver na sua saída, os scripts estão tentando se conectar à porta 135 (endpoint mapper) para obter a porta TCP/IP onde o endpoint DCOM está escutando (que é uma porta dinâmica)." -- SecureAuthCorp/impacket issue #412
=== spoolsv.exe Processa a Solicitação da API
[[call_flow]] .Fluxo de Chamada do RpcAddPrinterDriverEx image::Function Calls.png[]
A partir de <<call_flow>>, pode-se ver que spoolsv.exe chama essas funções, mas a análise interna da função mostra que, além da inicialização, este módulo não realiza nenhuma operação.
Finalmente, este módulo chama a função apontada por pLocalProvidor, ou seja, a função LocalAddPrinterDriverEx no módulo localspl.dll.
localspl, como um provedor de impressão local, é de fato o módulo que implementa a funcionalidade da API.
=== Lógica de Implementação da Função do Provedor de Impressão Local
[[LocalAddPrinterDriverEx]] .LocalAddPrinterDriverEx image::LocalAddPrinterDriverEx.png[]
Primeiro, <> indica que este módulo verifica se o spooler está funcionando corretamente e, em seguida, salta para a função SplAddPrinterDriverEx.
[[SplAddPrinterDriverEx]] .SplAddPrinterDriverEx image::SplAddPrinterDriverEx.png[]