Automatizando a detecção de possíveis caminhos de interceptação de DLL (DLL Hijacks)

Olá, Khabrovites. Recrutamento para a nova vertente do curso “Pentest. Prática de teste de penetração " . Antecipando o início do curso, estamos compartilhando com vocês a tradução de um material interessante.







Introdução



Neste artigo, veremos o conceito de sequestro de DLL e como ele pode ser usado para alcançar a persistência do ambiente do usuário em sistemas Windows. Este método é descrito em MITER ATT & CK em: "Intercepting DLL Search Order (T1038) ".



A falsificação de DLL pode ser usada por invasores para muitas finalidades diferentes, mas este artigo se concentrará em obter resiliência com aplicativos de inicialização automática. Por exemplo, como o Slack e o Microsoft Teams são iniciados na inicialização (por padrão), a falsificação de DLL em um desses aplicativos permitiria a um invasor obter acesso persistente ao seu alvo - sempre que um usuário efetuar login.



Depois de apresentar o conceito de DLLs, ordem de pesquisa de DLL e falsificação de DLL, orientarei você no processo de automação da detecção de interceptação de DLL . Este artigo falará sobre a detecção de caminhos de interceptação de DLL no Slack, Microsoft Teams e Visual Studio Code.



Por fim, descobri vários caminhos de interceptação de DLL usados ​​por diferentes aplicativos, investiguei a causa raiz e descobri que os aplicativos que usam certas chamadas de API do Windows são propensos à interceptação de DLL quando não estão em execução C:\Windows\System32\.



Quero agradecer ao meu colega Josiah Massari ( @Airzero24) por ser o primeiro a descobrir alguns desses ganchos de DLL, explicar sua metodologia e me inspirar a automatizar a detecção.



O que é uma DLL?



Uma DLL é uma biblioteca que contém código e dados que podem ser usados ​​simultaneamente por mais de um programa. ( Fonte ) A



funcionalidade de uma DLL pode ser usada por um aplicativo do Windows usando uma das funções LoadLibrary*. Os aplicativos podem fazer referência a DLLs projetadas especificamente para esses aplicativos ou a DLLs do Windows já existentes no disco do System32. Os desenvolvedores podem carregar DLLs do System32 para usar a funcionalidade já implementada no Windows em seus aplicativos sem ter que escrever essa funcionalidade do zero.



Por exemplo, um desenvolvedor que precisa fazer solicitações HTTP pode usar a biblioteca WinHTTP ( winhttp.dll) em vez de implementar solicitações HTTP usando soquetes brutos.



Ordem de pesquisa de DLL e interceptação



Como as DLLs existem como arquivos no disco, você deve estar se perguntando como um aplicativo sabe de onde carregar a DLL. A Microsoft documentou a ordem de pesquisa da DLL em detalhes aqui .



A partir do Windows XP SP2, o modo de pesquisa segura de DLL é habilitado por padrão ( HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode). Quando o modo de segurança está ativado, a ordem de pesquisa da DLL é a seguinte:



  1. O diretório do qual o aplicativo foi carregado.
  2. Diretório do sistema. Use a função GetSystemDirectory para obter o caminho para este diretório.
  3. Diretório do sistema de 16 bits. Não há função que forneça um caminho para esse diretório, mas ele é pesquisado.
  4. Diretório do Windows. Use a função GetWindowsDirectory para obter o caminho para este diretório.
  5. Diretório atual.
  6. , PATH. , , App Paths. App Paths DLL.


Um sistema pode conter várias versões da mesma DLL. Os aplicativos podem controlar a escolha do local de onde a DLL deve ser carregada, especificando o caminho completo ou usando outro mecanismo, como um manifesto. ( Fonte )



Se o aplicativo não especificar de onde carregar a DLL, o Windows usará a ordem de pesquisa padrão da DLL mostrada acima. A primeira posição na ordem de pesquisa da DLL (o diretório de onde o aplicativo é carregado) é de interesse dos invasores.



Se o desenvolvedor do aplicativo pretende carregar a DLL deC:\Windows\System32, mas não o escreveu explicitamente no aplicativo, a DLL mal-intencionada colocada no diretório do aplicativo será carregada antes da DLL legítima do System32. O carregamento de uma DLL mal-intencionada é chamado de falsificação de DLL (ou interceptação) e é usado por invasores para carregar código mal-intencionado em aplicativos confiáveis ​​/ assinados.



Usando DLL Spoofing para Obter Resiliência



A falsificação de DLL pode ser usada para obter resiliência quando um aplicativo / serviço vulnerável é iniciado e uma DLL mal-intencionada é colocada em um local vulnerável. Um colega meu @Airzero24descobriu o spoofing de DLL no Microsoft OneDrive, Microsoft Teams e Slack as userenv.dll.



Foram esses programas que se tornaram o alvo da interceptação, porque por padrão eles são configurados para iniciar na inicialização do Windows. Isso pode ser visto abaixo no Gerenciador de tarefas:





Aplicativos Windows configurados para inicialização automática



Para testar a falsificação de DLL, criei um carregador de shellcode DLL que iniciou o Cobalt Strike Beacon. Renomeei a DLL maliciosa para userenv.dlle copiei-a para o diretório do aplicativo afetado. Iniciei o aplicativo e vi meu novo retorno de chamada do Beacon.





Cobalt Strike Beacon por meio de interceptação de DLL



usandoProcess Explorer , posso verificar se minha DLL maliciosa foi realmente carregada por um aplicativo vulnerável.





Process Explorer mostrando DLL malicioso carregado



Detecção automática do potencial de interceptação de DLL



Depois de confirmar o sequestro de DLL conhecido anteriormente, eu queria ver se conseguia encontrar outros recursos de falsificação de DLL que pudessem ser explorados.



O código usado em meu checkout pode ser encontrado aqui .



Usando o Slack como exemplo



Para iniciar este processo, executei o Process Monitor (ProcMon) com os seguintes filtros:



  • Nome do processo -slack.exe
  • Resultado contémNOT FOUND
  • O caminho termina com .dll.




Encontre DLLs ausentes no ProcMon.



Então eu iniciei o Slack e examinei o ProcMon em busca de qualquer DLL que o Slack estava procurando, mas não conseguiu encontrar.





Possíveis caminhos de interceptação de DLL descobertos pelo ProcMon



Eu exportei esses dados do ProcMon como um arquivo CSV para facilitar a análise no PowerShell.



Com minha DLL atual do carregador de código de shell, não consegui descobrir facilmente os nomes das DLL que foram carregados com êxito pelo Slack. Eu criei uma nova DLL, que é usada GetModuleHandleEx, e GetModuleFileNamepara determinar o nome da DLL carregada e gravá-la em um arquivo de texto .



Meu próximo objetivo era analisar o arquivo CSV para caminhos DLL na lista, visualizar essa lista, copiar minha DLL de teste para o caminho especificado, iniciar o processo de destino, interromper o processo de destino e excluir a DLL de teste. Se a DLL de teste foi carregada com êxito, ela gravará seu nome no arquivo resultante.



Quando esse processo terminar, terei uma lista de possíveis sequestros de DLL (espero) gravada em um arquivo de texto.



Toda a mágica em meu projeto DLLHijackTest é feita por um script PowerShell . Ele aceita o caminho para o arquivo CSV gerado pelo ProcMon, o caminho para sua DLL maliciosa, o caminho para o processo que você deseja executar e quaisquer argumentos que deseja passar para o processo.





Parâmetros





Get-PotentialDLLHijack Get-PotentialDLLHijack.ps1



Depois de alguns minutos, verifico o arquivo de texto listado em minha DLL "maliciosa" para possíveis sequestros de DLL. Encontrei os seguintes caminhos de interceptação possíveis para o Slack:



PS C:Users\John\Desktop> Get-PotentialDLLHijack -CSVPath .\Logfile.CSV -MaliciousDLLPath .\DLLHijackTest.dll -ProcessPath "C:\Users\John\AppData\Local\slack\slack.exe"
C:\Users\John\AppData\Local\slack\app-4.6.0\WINSTA.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\LINKINFO.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\ntshrui.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\srvcli.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\cscapi.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\KBDUS.DLL


Usando o Microsoft Teams como exemplo



Realizamos o processo descrito acima novamente:



  1. Use o ProcMon para identificar caminhos de interceptação de DLL em potencial, exporte esses dados como um arquivo CSV.
  2. Determine o caminho para iniciar o processo.
  3. Defina os argumentos que deseja passar para o processo.
  4. Execute Get-PotentialDLLHijack.ps1com os argumentos apropriados.


Encontrei os seguintes caminhos de interceptação possíveis para o Microsoft Teams:



PS C:Users\John\Desktop> Get-PotentialDLLHijack -CSVPath .\Logfile.CSV -MaliciousDLLPath .\DLLHijackTest.dll -ProcessPath "C:\Users\John\AppData\Local\Microsoft\Teams\Update.exe" -ProcessArguments '--processStart "Teams.exe"'
C:\Users\John\AppData\Local\Microsoft\Teams\current\WINSTA.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\LINKINFO.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\ntshrui.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\srvcli.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\cscapi.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\WindowsCodecs.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\TextInputFramework.dll


Nota : Tive que fazer pequenas alterações no script do PowerShell para ser concluído Teams.exe, pois meu script está tentando encerrar o processo que estava tentando iniciar, neste caso, está Update.exe.


Usando o código do Visual Studio como exemplo



Ao repetir o processo acima, encontrei os seguintes caminhos de interceptação em potencial para o Visual Studio Code:



PS C:Users\John\Desktop> Get-PotentialDLLHijack -CSVPath .\Logfile.CSV -MaliciousDLLPath .\DLLHijackTest.dll -ProcessPath "C:\Users\John\AppData\Local\Programs\Microsoft VS Code\Code.exe"
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\WINSTA.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\LINKINFO.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\ntshrui.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\srvcli.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\cscapi.dll


Compartilhando DLLs



Percebi que o Slack, o Microsoft Teams e o Visual Studio Code compartilham as seguintes DLLs:



  • WINSTA.dll
  • LINKINFO.dll
  • ntshrui.dll
  • srvcli.dll
  • cscapi.dll


Achei isso interessante e queria entender o que causa esse comportamento.



Metodologia: Compreendendo as formas de interceptação de DLL compartilhada



Eu assisti Tracy pilha quando falha tentou carga WINSTA.dll, LINKINFO.dll, ntshrui.dll, srvcli.dlle cscapi.dll.



DLL com carregamento lento



notei semelhanças em Tracy pilha ao carregar WINSTA.dll, LINKINFO.dll, ntshrui.dlle srvcli.dll.





Rastreamento de pilha quando Code.exe tenta carregar WINSTA.dll





um rastreamento de pilha ao Teams.exetentar carregar LINKINFO.dll,





rastreamento de pilha quando o Slack tenta carregarntshrui.dll



um rastreamento de pilha constantemente contém uma chamada _tailMerge_<dllname>_dll, delayLoadHelper2seguido LdrResolveDelayLoadedAPI. Esse comportamento foi o mesmo para todos os três aplicativos.



Eu determinei que esse comportamento está relacionado ao carregamento lento de DLL . Da pilha de rastreamento na inicializaçãoWINSTA.dllPude ver que o módulo responsável por esse carregamento lento era wtsapi32.dll.



Abri wtsapi32.dllem Ghidra e usei Search -> For Strings -> Filter: WINSTA.dll. Clique duas vezes na linha encontrada para levá-lo à sua localização na memória.





A linha " WINSTA.dll" emwtsapi32.dll



Clicando com o botão direito em um local da memória, podemos encontrar qualquer referência a este endereço.





Links paraWINSTA.dll



Seguindo os links, podemos ver que a string WINSTA.dllé passada para uma estrutura chamada ImgDelayDescr. Olhando a documentação para esta estrutura, podemos confirmar que ela está relacionada ao carregamento lento de DLL.



typedef struct ImgDelayDescr {
   DWORD        grAttrs;        // 
   RVA          rvaDLLName;     // RVA   dll 
   RVA          rvaHmod;        // RVA  
   RVA          rvaIAT;         // RVA IAT
   RVA          rvaINT;         // RVA INT
   RVA          rvaBoundIAT;    // RVA   IAT
   RVA          rvaUnloadIAT;   // RVA    IAT
   DWORD        dwTimeStamp;    // 0,   ,
                                // O.W. / DLL,   (Old BIND)
   } ImgDelayDescr, * PImgDelayDescr;


Essa estrutura pode ser passada para __delayLoadHelper2, que usará LoadLibrary/ GetProcAddresspara carregar a DLL especificada e corrigir o endereço da função importada na tabela de endereços de importação de carregamento lento (IAT).



FARPROC WINAPI __delayLoadHelper2(
   PCImgDelayDescr pidd,  //     ImgDelayDescr
   FARPROC * ppfnIATEntry //     IAT  
);


Ao encontrar outras referências à nossa estrutura ImgDelayDescr, podemos encontrar uma chamada __delayLoadHelper2que então chama ResolveDelayLoadedAPI. Eu renomei o nome da função, tipos e variáveis ​​para torná-lo mais fácil de entender.





__delayLoadHelper2e ResolveDelayLoadedAPIna Ghidra



Excelente! Isso é consistente com o que vimos em nosso rastreamento de pilha do ProcMon quando o Slack tentou carregar WINSTA.dll.





__delayLoadHelper2 e ResolveDelayLoadedAPIno ProcMon.



Este comportamento foi uniformemente para WINSTA.dll, LINKINFO.dll, ntshrui.dlle srvcli.dll. A principal diferença entre cada DLL de carregamento lento era a DLL "pai". Em todos os três aplicativos:



  • wtsapi32.dll carregado adiado WINSTA.dll
  • shell32.dll carregado preguiçosamente LINKINFO.dll
  • LINKINFO.dll carregado adiado ntshrui.dll
  • ntshrui.dll carregado adiado srvcli.dll


Você notou algo interessante? Parece que ele shell32.dllbaixa LINKINFO.dll, que baixa ntshrui.dll, que finalmente baixa srvcli.dll. Isso nos leva à nossa última opção comum de spoofing de DLL - cscapi.dll.



Substituição de DLL em NetShareGetInfo e NetShareEnum



Eu segui o rastreamento da pilha quando o Slack tentou carregar cscapi.dlle vi uma chamada LoadLibraryExWque aparentemente veio de srvcli.dll. Abri o





rastreamento de pilha na inicializaçãocscapi.dll no Ghidra e usei . Clicar duas vezes na linha encontrada e seguir os links leva à chamada esperada . chama LoadLibrary para renomear a função que contém a chamada e, seguindo os links, obtive dois lugares onde a função é usada:



srvcli.dllSearch -> For Strings -> Filter: cscapi.dllLoadLibrary





srvcli.dllcscapi.dll



LoadLibrary







NetShareEnum downloads cscapi.dll





NetShareGetInfo downloadscscapi.dll



Eu verifiquei isso com programas PoC que chamam NetShareEnume NetShareGetInfo:





NetShareEnum.exedownloads cscapi.dll





NetShareGetInfo.exedownloadscscapi.dll



resultados



Os seguintes caminhos de spoofing de DLL estão disponíveis no Slack:



C:\Users\John\AppData\Local\slack\app-4.6.0\WINSTA.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\LINKINFO.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\ntshrui.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\srvcli.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\cscapi.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\KBDUS.DLL


Os seguintes caminhos de spoofing de DLL estão disponíveis no Microsoft Teams:



C:\Users\John\AppData\Local\Microsoft\Teams\current\WINSTA.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\LINKINFO.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\ntshrui.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\srvcli.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\cscapi.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\WindowsCodecs.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\TextInputFramework.dll


Os seguintes caminhos de spoofing de DLL estão disponíveis no Visual Studio Code:



C:\Users\John\AppData\Local\Programs\Microsoft VS Code\WINSTA.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\LINKINFO.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\ntshrui.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\srvcli.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\cscapi.dll


Além disso, descobri que os programas usam NetShareEnume NetShareGetInfofornecem a capacidade de substituir a DLL na forma cscapi.dlldevido à chamada embutida no código LoadLibrary. Eu verifiquei esse comportamento com Ghidra e PoC.



Conclusão



Como um lembrete, a interceptação de DLL é um método pelo qual invasores podem interferir na execução de código em aplicativos assinados / confiáveis. Criei ferramentas para ajudar a automatizar a detecção do caminho de interceptação de DLL. Usando essa ferramenta, descobri caminhos de interceptação de DLL no Slack, Microsoft Teams e Visual Studio Code.



Percebi que os caminhos de interceptação de DLL desses três aplicativos se sobrepõem e investiguei a causa. Eu destaquei meu método de compreensão dessa coincidência. Aprendi sobre DLLs de carregamento lento e descobri duas chamadas de API que tornam possível interceptar DLLs em qualquer programa que as chame:



  • NetShareEnum cargas cscapi.dll
  • NetShareGetInfo cargas cscapi.dll


Obrigado por reservar um tempo para ler este artigo, espero que você tenha aprendido alguma coisa sobre APIs do Windows, Ghidra, ProcMon, DLLs e interceptação de DLLs!



Links



Um grande olá aos meus colegas Daniel Heinsen ( @hotnops), Lee Christensen ( @tifkin_) e Matt Hand ( @matterpreter) por ajudarem com o Ghidra / ProcMon!






Verificando PoCs públicos para uso em pentesting






Consulte Mais informação:






All Articles