matousec.com (site map)

Poll

Is leak(test) protection important?

  Yes, of course! (82.33%)

  I think so. (4.89%)

  I do not know. (3.04%)

  I do not think so. (1.95%)

  No, not at all! (7.81%)

more

results

Articles

Plague in (security) software drivers

by Jakub Břečka & David Matoušek, members of www.matousec.com Research team,
special thanks to Ondřej Vlček of ALWIL Software for corrections

Published: 2007/09/18
Last update: 2008/04/14 - BSODhook version 2.0.0 released, the information about it moved to a separate web page

During our security analyses of personal firewalls and other security-related software that uses SSDT hooking, we found out that many vendors simply do not implement the hooks in a proper way. This allows local Denial of Service by unprivileged users or even privilege escalations exploits to be created. 100% of tested personal firewalls that implement SSDT hooks do or did suffer from this vulnerability! This article reviews the results of our testing and describes how a proper SSDT hook handler should be implemented. We also introduce BSODhook – a handy tool for every developer that deals with SSDT hooks and a possible cure for the plague in today's Windows drivers world.

Contents:


Back to contents

Introduction

Hooking kernel functions by modifying the System Service Descriptor Table (SSDT) is a very popular method of implementation of additional security features and is used frequently by personal firewalls and other security and low-level software. Although undocumented and despised by Microsoft, this technique can be implemented in a correct and stable way. However, many software vendors do not follow the rules and recommendations for kernel-mode code writing and many drivers that implement SSDT hooking do not properly validate the parameters of the hooking functions.

Microsoft's Common Driver Reliability Issues document describes the correct parameter checking and contains many important notes to problems related to writing Windows drivers. Many vendors of today's software do not bother to read such documents and their implementations are thus vulnerable, and making the stable and trustworthy Windows kernel unreliable.

Parameters to SSDT function handlers are passed directly from user-mode and therefore must be checked before they are used. This article shows some incorrect implementations of SSDT hooking functions and describes how a proper validity check should be performed on various parameter types. To understand the following text, you will need some knowledge of Windows NT architecture. We do not cover a proper implementation of SSDT hooking techniques here. We focus on parameter validation problems. Some related and interesting information can be also found in Microsoft's Memory Management: What Every Driver Writer Needs to Know document.


Back to contents

The bug

For a demonstration of the bug, see the following code sample:

NTSTATUS HookNtOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,
  IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL)
{
  if (ClientId->UniqueProcess==ProtectedProcess)
    return STATUS_ACCESS_DENIED;

  ...
}
Example 1: No parameter validation at all.

This code shows a hook handler for a Windows Native API function NtOpenProcess and it implements a simple security check that will deny all requests to open a protected process with a specified process ID. For the purpose of this article, it is not important how such a hook is set, which is also a task that is pretty complicated if it is supposed to be performed correctly, or what is done after the security check, which is also non-trivial.

Unfortunately, the implementation in Example 1 is incorrect, because this hook handler function receives the parameters exactly as they were sent from user-mode, and it must therefore check all parameters' validity before using them. If a similar hook was implemented in user-mode (e.g. to intercept kernel32.OpenProcess or ntdll.NtOpenProcess calls), the need for a parameter validation would not be so necessary, because an invalid memory read or write in user-mode cannot corrupt the system's stability nor can it be exploited to escalate privileges. Such a lack of parameter checking will only corrupt or crash the application that invoked the invalid API call.

In the kernel however, the situation is different. All parameters coming from user-mode must be checked for validity before they are used. This is especially related to pointers to various structures. To access structure contents, the pointer must be dereferenced, which is a risky operation that can result in one of the following:

SEH exceptions can be caught by enclosing the unsafe operations in try/except blocks. However, using try/except only is not enough, see the following code sample:

NTSTATUS HookNtOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,
  IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL)
{
  HANDLE ProcessId;
  try
  {
    ProcessId=ClientId->UniqueProcess;
  } except(EXCEPTION_EXECUTE_HANDLER)
  {
    return STATUS_INVALID_PARAMETER;
  }

  if (ProcessId==ProtectedProcess)
    return STATUS_ACCESS_DENIED;

  ...
}
Example 2: Invalid parameter validation, only user-mode pointers are validated properly.

In this function, the parameter ClientId is properly validated only for user-mode pointers. If ClientId points into an invalid kernel memory, the dereference will cause a BSOD to occur. A correct approach is to use the ProbeForRead function:

NTSTATUS HookNtOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,
  IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL)
{
  HANDLE ProcessId;
  if (ClientId!=NULL)
  {
    try
    {
      if (KeGetPreviousMode()!=KernelMode)
        ProbeForRead(ClientId,sizeof(CLIENT_ID),1);
      ProcessId=ClientId->UniqueProcess;
    } except(EXCEPTION_EXECUTE_HANDLER)
    {
      return STATUS_INVALID_PARAMETER;
    }

    if (ProcessId==ProtectedProcess)
    {
      return STATUS_ACCESS_DENIED;
    }
  }

  ...
}
Example 3: Correct parameter validation for single-pointer structures like CLIENT_ID.

The call to ProbeForRead must be enclosed in the try/catch block as well as the access to the user supplied memory, because if the probing fails, an exception will be raised. You should also notice that ClientId is an OPTIONAL parameter. This means that it might not be present in a correct call. If it was NULL and we did not check this, the assignment to ProcessId would fail and we will return STATUS_INVALID_PARAMETER, which would disqualify some of correct NtOpenProcess calls.

See the following pseudo-code implementation of ProbeForRead:

VOID ProbeForRead(IN CONST VOID *Address,IN SIZE_T Length,IN ULONG Alignment)
{
  // check for zero Length
  if (Length==0)
    return;

  // check the alignment of Address
  if (!CheckAlignment(Address,Alignment))
    RaiseException(DATATYPE_MISALIGNMENT);

  // check for an integer overflow
  if (IsSumOverflow((ULONG)Address,Length))
    RaiseException(ACCESS_VIOLATION);

  // check if Address points into user-mode memory
  if (((ULONG)Address+Length)>MmUserProbeAddress)
    RaiseException(ACCESS_VIOLATION);

  return;
}
Example 4: Internals of ProbeForRead in pseudo-code.

ProbeForRead checks whether the given pointer points into user-mode memory by comparing it to MmUserProbeAddress, which is the lowest invalid address for user-mode buffers. MmUserProbeAddress is typically set to 0x7FFF0000, or to 0xBFFF0000 on systems with 3GB user-space memory, activated by the /3GB switch in boot.ini.

It might seem odd, that the function does not perform any memory access to the desired memory area. But in fact, it is perfectly valid to access any user-mode memory, if the access is performed inside a try/except block. So, ProbeForRead only checks if the structure lies in the user-mode memory. This also means, that a function with a ProbeForRead call cannot be called from kernel-mode with a kernel memory pointer, because only user-mode addresses will pass the probe. This is why we use KeGetPreviousMode check, to allow kernel calls to pass without checking. You can find some information about it in Argument Validation in Windows NT part of Microsoft Windows NT: Design Goals article.

Note that you cannot use ProbeForRead or any similar function to perform a validity check on a kernel memory pointer. There is no easy mechanism that checks if an address in the kernel is valid or not. All kernel-mode pointers are trusted and there is no need to validate them – that is also the reason, why an invalid kernel memory access results in a BSOD and not a SEH exception. Simply, invalid kernel memory access is, unlike a user-mode memory access, a fatal error and should never occur in properly written drivers.


Back to contents

Structures with pointers

All we discussed so far were single-pointer structures. However, some Native API functions require complex structures with additional pointers in them as parameters. See the following example of a hook handler for NtQueryValueKey:

NTSTATUS HookNtQueryValueKey(IN HANDLE KeyHandle,IN PUNICODE_STRING ValueName,
  IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,OUT PVOID KeyValueInformation,
  IN ULONG KeyValueInformationLength,OUT PULONG ResultLength)
{
  if (_wcsicmp(ValueName->Buffer,L"ProtectedValue")==0)
    return STATUS_ACCESS_DENIED;

  ...
}
Example 5: No validation for a PUNICODE_STRING parameter.

One of NtQueryValueKey function's parameters is a pointer to a UNICODE_STRING structure, which consist of three values: Length, MaximumLength and another pointer to an array of WCHARs. The code sample in Example 5 implements no parameter checking, so we know already that this is wrong.

If a function with a UNICODE_STRING is to be hooked properly, the hook handler must check the validity of the pointer to the Unicode string, before accessing the member values of this structure. Then the pointer to the array of wide characters must be validated. See the following example, which shows a correctly performed validity check:

NTSTATUS HookNtQueryValueKey(IN HANDLE KeyHandle,IN PUNICODE_STRING ValueName,
  IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,OUT PVOID KeyValueInformation,
  IN ULONG KeyValueInformationLength,OUT PULONG ResultLength)
{
  UNICODE_STRING name;
  WCHAR *buffer=NULL;

  try
  {
    if (KeGetPreviousMode()!=KernelMode)
      ProbeForRead(ValueName,sizeof(UNICODE_STRING),1);

    RtlCopyMemory(&name,ValueName,sizeof(name));

    if (name.Length==wcslen(L"ProtectedValue")*sizeof(WCHAR))
    {
      if (KeGetPreviousMode()!=KernelMode)
      {
        ProbeForRead(name.Buffer,name.Length,1);
        buffer=(WCHAR*)ExAllocatePoolWithTag(NonPagedPool,name.Length,TAG);
        if (!buffer) return STATUS_INSUFFICIENT_RESOURCES;
        RtlCopyMemory(buffer,name.Buffer,name.Length);
      } else buffer=name.Buffer;

      if (_wcsnicmp(buffer,L"ProtectedValue",name.Length/sizeof(WCHAR))==0)
        return STATUS_ACCESS_DENIED;
    }
  } except(EXCEPTION_EXECUTE_HANDLER)
  {
    return STATUS_INVALID_PARAMETER;
  }

  ...
}
Example 6: Correct double-pointer structure (UNICODE_STRING) validation.

Note that we have to make a copy of ValueName before we can use its contents. If we did not make a copy and used its contents directly, another thread could change its contents after we checked its Length or after we probed its Buffer.

There are some other structures with additional pointers that appear as parameters in some Native API functions. All these pointers must be checked before they are dereferenced. See the following example, which checks the validity of a POBJECT_ATTRIBUTES parameter, which contains a pointer to a UNICODE_STRING structure. Therefore, a triple pointer check must be performed:

NTSTATUS HookNtCreateFile(OUT PHANDLE FileHandle,IN ACCESS_MASK DesiredAccess,
 IN POBJECT_ATTRIBUTES ObjectAttributes,OUT PIO_STATUS_BLOCK IoStatusBlock,
 IN PLARGE_INTEGER AllocationSize OPTIONAL,IN ULONG FileAttributes,IN ULONG ShareAccess,
 IN ULONG CreateDisposition,IN ULONG CreateOptions,IN PVOID EaBuffer OPTIONAL,
 IN ULONG EaLength)
{
  UNICODE_STRING name;
  WCHAR *buffer=NULL;

  try
  {
    if (KeGetPreviousMode()!=KernelMode)
      ProbeForRead(ObjectAttributes,sizeof(OBJECT_ATTRIBUTES),1);

    PUNICODE_STRING ObjectName=ObjectAttributes->ObjectName;
    if (KeGetPreviousMode()!=KernelMode)
      ProbeForRead(ObjectName,sizeof(UNICODE_STRING),1);

    RtlCopyMemory(&name,ObjectName,sizeof(name));

    if (name.Length==wcslen(L"ProtectedValue")*sizeof(WCHAR))
    {
      if (KeGetPreviousMode()!=KernelMode)
      {
        ProbeForRead(name.Buffer,name.Length,1);
        buffer=(WCHAR*)ExAllocatePoolWithTag(NonPagedPool,name.Length,TAG);
        if (!buffer) return STATUS_INSUFFICIENT_RESOURCES;
        RtlCopyMemory(buffer,name.Buffer,name.Length);
      } else buffer=name.Buffer;

      if (_wcsnicmp(buffer,L"ProtectedValue",name.Length/sizeof(WCHAR))==0)
        return STATUS_ACCESS_DENIED;
    }
  } except(EXCEPTION_EXECUTE_HANDLER)
  {
    return STATUS_INVALID_PARAMETER;
  }

  ...
}
Example 7: Correct triple-pointer structure (OBJECT_ATTRIBUTES) validation.

Back to contents

Other issues and exploitability

Even more necessary is to perform validation checks on output parameters. If a hook handler writes to the memory through a user-mode supplied pointer (which again might be wrapped in a structure), it must check that the whole write memory area is valid for writing, for example using ProbeForWrite. It will similarly produce an exception, if some part of the memory is not writable.

Note that there also is a possibility of invalidating a memory during the processing of the hook handler. Therefore, even if a user-mode memory address is validated, a driver still cannot consider it safe to read/write. The memory can be invalidated at any time, so every consequent read or write attempt must be enclosed in a try/except block.

If a driver communicates with user-mode using Windows I/O or any other mechanism, all user-mode addresses must be validated in exactly the same way (ProbeForRead and/or ProbeForWrite) as stated before. If the application logic treats such address as a structure with additional pointers, all of these must be checked too. Especially, this validation must be performed on user buffers supplied through METHOD_NEITHER I/O.

Generally, there is no common pattern for exploiting these bugs. An invalid memory read will only produce a BSOD. However, some special cases of missing ProbeForWrite validation can certainly be exploited and may lead to a privilege escalation or even a local root exploit. For example, a missing parameter validation on an OUT PHANDLE argument may, in some cases, be exploited to bypass system's security checks or modify kernel objects. The outcoming value of a newly opened handle can be predicted and if we set this parameter to point somewhere in the kernel, for example inside the kernel structures or a carefully selected address inside kernel code, we can alter the code flow and bypass access checks.

Since Windows XP, a memory write protection is keeping any driver from altering the kernel code, which effectively blocks these kinds of exploits. However, we can still overwrite any part of the kernel stack or kernel objects (for example modify current EPROCESS structure to gain privileges). In general, in case of incorrect OUT parameter validation implementation, we may have arbitrary kernel mode write possibility, which is usually enough to take over the whole machine.


Back to contents

About BSODhook utility

We have developed a tool codenamed BSODhook that helps finding improper validation bugs in drivers that implement (not only) SSDT hooks. BSODhook (aka Kernel hooks probing tool) calls native functions with both valid and invalid parameters to produce a system crash (bugcheck). However, this tool comes with a kernel driver, which intercepts certain system functions to catch these bugchecks. Instead of crashing the system, an invalid memory access or other faulty behaviour that invoke the bugcheck will be caught, the calling thread will be terminated and the application will report that the tested API function is improperly validated. Moreover, BSODhook writes out the exact parameters of the function call that caused the crash. This allows vendors to find bugs in their drivers very quickly and efficiently.

In the second version, we have added support for SSDT GDI functions. We have also created a separate BSODhook web page and moved the information about this tool there.

You can download BSODhook right now and start probing. If you find some bugs in a software you use, which is not listed below, or if your Windows 2000 or XP kernel is not supported, please contact us. If your kernel is not supported, be sure to include a full information about your kernel version. If you are a vendor of a software that implements SSDT hooks and our tool helped you to improve it, we will be very glad if you tell us about it. Everyone is allowed to use BSODhook freely as is. There is no warranty or support for this product, but we will be glad to receive the feedback. There are also number of ways how to improve BSODhook itself, if you are an experienced Windows coder interested in doing so, feel free to contact us too.


Back to contents

Research results

Many personal firewalls use kernel-mode hooks to enforce their security measures. We began testing individual firewall distributions for these kind of vulnerabilities in spring 2006, when the first predecessor of BSODhook was implemented. During the first phase of testing, every single personal firewall that implemented SSDT hooks had at least one function vulnerable to the described bug of improper parameter validation. 100% of tested personal firewalls that implement SSDT hooks do or did suffer from this vulnerability!

After we released several advisories describing these vulnerabilities for 7 personal firewalls during 2006 and 2007, only Comodo and Sunbelt fixed their software products and remain not vulnerable to this bug. In some of tested software the problem has been fixed and reintroduced later again. We performed additional and updated testing of several types of software products that are using SSDT hooks for various reasons.

The following table shows individual vulnerable functions in the tested personal firewalls and similar software. Note that if BSODhook does not mark the function as vulnerable it does not mean it is implemented correctly.

Product and version Hooked SSDT functions, vulnerable functions are in red
BlackICE PC Protection 3.6.cqn
see advisory published on September 01, 2006
NtCreateSection, NtOpenSection
Comodo Personal Firewall 2.4.18.184
old version was vulnerable, see advisory published on February 01, 2007
NtConnectPort, NtCreateFile, NtCreatePort, NtCreateSection, NtCreateThread, NtDeleteFile, NtDeleteKey, NtDeleteValueKey, NtOpenProcess, NtOpenSection, NtOpenThread, NtSetInformationFile, NtSetValueKey, NtShutdownSystem, NtTerminateProcess, NtWriteFile, NtWriteFileGather
G DATA InternetSecurity 2007 NtClose, NtCreateKey, NtDeleteKey, NtDeleteValueKey, NtOpenKey, NtOpenProcess, NtSetValueKey
Ghost Security Suite beta 1.110 NtCreateKey, NtCreateSymbolicLinkObject, NtCreateThread, NtDeleteKey, NtDeleteValueKey, NtDeviceIoControlFile, NtEnumerateKey, NtEnumerateValueKey, NtOpenSection, NtProtectVirtualMemory, NtQueryKey, NtQueryValueKey, NtSetContextThread, NtSetSystemInformation, NtSetValueKey, NtSuspendProcess, NtSuspendThread, NtTerminateProcess, NtTerminateThread, NtWriteVirtualMemory
Ghost Security Suite alpha 1.200
alpha version lent for testing after reporting vulnerabilities in version 1.110
NtCreateKey, NtCreateSymbolicLinkObject, NtCreateThread, NtDeleteKey, NtDeleteValueKey, NtOpenSection, NtProtectVirtualMemory, NtSetContextThread, NtSetSystemInformation, NtSetValueKey, NtSuspendProcess, NtSuspendThread, NtTerminateProcess, NtTerminateThread, NtWriteVirtualMemory
Kaspersky Internet Security 7.0.0.125
partially fixed, see advisory published on June 15, 2007, NtLoadDriver does not cause BSOD but it can be used to DoS KIS service avp.exe
NtClose, NtCreateKey, NtCreateProcess, NtCreateProcessEx, NtCreateSection, NtCreateSymbolicLinkObject, NtCreateThread, NtDeleteKey, NtDeleteValueKey, NtDuplicateObject, NtEnumerateKey, NtEnumerateValueKey, NtFlushKey, NtInitializeRegistry, NtLoadDriver, NtLoadKey, NtLoadKey2, NtNotifyChangeKey, NtOpenFile, NtOpenKey, NtOpenProcess, NtOpenSection, NtQueryKey, NtQueryMultipleValueKey, NtQuerySystemInformation, NtQueryValueKey, NtReplaceKey, NtRestoreKey, NtResumeThread, NtSaveKey, NtSetContextThread, NtSetInformationFile, NtSetInformationKey, NtSetSecurityObject, NtSetValueKey, NtSuspendThread, NtSystemDebugControl, NtTerminateProcess, NtUnloadKey, NtWriteVirtualMemory
Norton Internet Security 2008 15.0.0.60
vulnerable since 2006, see advisory published on April 01, 2007
NtAlertResumeThread, NtAlertThread, NtAllocateVirtualMemory, NtConnectPort, NtCreateKey, NtCreateMutant, NtCreateThread, NtDebugActiveProcess, NtDeleteKey, NtDeleteValueKey, NtFreeVirtualMemory, NtImpersonateAnonymousToken, NtImpersonateThread, NtMapViewOfSection, NtOpenEvent, NtOpenProcessToken, NtOpenSection, NtOpenThreadToken, NtResumeThread, NtSetContextThread, NtSetInformationProcess, NtSetInformationThread, NtSetValueKey, NtSuspendProcess, NtSuspendThread, NtTerminateProcess, NtTerminateThread, NtUnmapViewOfSection, NtWriteVirtualMemory
Online Armor Personal Firewall 2.0.1.215
the vendor supposedly fixed the vulnerabilities immediately, the latest version is said to have these bugs fixed
NtAllocateVirtualMemory, NtAssignProcessToJobObject, NtConnectPort, NtCreateFile, NtCreateKey, NtCreatePort, NtCreateProcess, NtCreateProcessEx, NtCreateSection, NtCreateThread, NtDebugActiveProcess, NtDeleteFile, NtDeleteKey, NtDeleteValueKey, NtEnumerateKey, NtEnumerateValueKey, NtLoadKey, NtOpenFile, NtOpenKey, NtOpenProcess, NtOpenSection, NtOpenThread, NtProtectVirtualMemory, NtQueryKey, NtQueryValueKey, NtReadFile, NtReplaceKey, NtRequestWaitReplyPort, NtRestoreKey, NtResumeThread, NtSaveKey, NtSetContextThread, NtSetInformationFile, NtSetValueKey, NtSuspendProcess, NtSuspendThread, NtSystemDebugControl, NtTerminateProcess, NtTerminateThread, NtWriteFile, NtWriteVirtualMemory
Outpost Firewall Pro 4.0.1025.7828
old version was vulnerable, see advisory published on November 15, 2006, later version was fixed, but the bugs were reintroduced, the vendor declared version 5.x to be fixed
NtAssignProcessToJobObject, NtClose, NtConnectPort, NtCreateFile, NtCreateKey, NtCreateProcess, NtCreateProcessEx, NtCreateSection, NtCreateSymbolicLinkObject, NtCreateThread, NtDeleteFile, NtDeleteKey, NtDeleteValueKey, NtLoadDriver, NtMakeTemporaryObject, NtOpenFile, NtOpenKey, NtOpenProcess, NtOpenSection, NtOpenThread, NtProtectVirtualMemory, NtQueryDirectoryFile, NtQueryKey, NtQueryValueKey, NtReplaceKey, NtRestoreKey, NtSaveKey, NtSaveKeyEx, NtSecureConnectPort, NtSetContextThread, NtSetInformationFile, NtSetValueKey, NtTerminateProcess, NtTerminateThread, NtUnloadDriver, NtWriteVirtualMemory
Privatefirewall 5.0.14.2
the vendor expessed that the bugs will be fixed in upcoming version 6.x
NtCreateFile, NtCreateKey, NtCreateThread, NtOpenFile, NtOpenKey, NtOpenProcess, NtOpenSection, NtOpenThread, NtSetValueKey, NtTerminateProcess
ProcessGuard 3.410 NtCreateFile, NtCreateKey, NtCreateThread, NtDeleteKey, NtDeleteValueKey, NtFsControlFile, NtOpenFile, NtOpenKey, NtOpenSection, NtProtectVirtualMemory, NtReadVirtualMemory, NtRequestWaitReplyPort, NtSetContextThread, NtSetSystemInformation, NtSetValueKey, NtSuspendProcess, NtSuspendThread, NtTerminateProcess, NtTerminateThread, NtWriteVirtualMemory
ProSecurity 1.40 Beta 2
the vendor promised to fix the vulnerabilities as soon as possible
NtClose, NtCreateFile, NtCreateKey, NtCreateSection, NtCreateThread, NtDebugActiveProcess, NtDeleteFile, NtDeleteKey, NtDeleteValueKey, NtFsControlFile, NtLoadDriver, NtOpenFile, NtOpenSection, NtProtectVirtualMemory, NtQueueApcThread, NtReadFile, NtReadFileScatter, NtReadVirtualMemory, NtRequestWaitReplyPort, NtRestoreKey, NtResumeThread, NtSetContextThread, NtSetInformationFile, NtSetSecurityObject, NtSetSystemInformation, NtSetSystemPowerState, NtSetSystemTime, NtSetValueKey, NtShutdownSystem, NtSuspendProcess, NtSuspendThread, NtSystemDebugControl, NtTerminateJobObject, NtTerminateProcess, NtTerminateThread, NtWriteFile, NtWriteFileGather, NtWriteVirtualMemory
Sunbelt Personal Firewall 4.5.916.0
old version was vulnerable, see advisory published on October 01, 2006
NtClose, NtCreateFile, NtCreateKey, NtCreateProcess, NtCreateProcessEx, NtCreateThread, NtDeleteFile, NtDeleteKey, NtDeleteValueKey, NtLoadDriver, NtMapViewOfSection, NtOpenFile, NtOpenKey, NtResumeThread, NtSetInformationFile, NtSetValueKey, NtWriteFile
ZoneAlarm Pro 7.0.362.000
old version was vulnerable, see advisory published on April 15, 2007, the vendor fixed the bugs, but later they were reintroduced
NtConnectPort, NtCreateFile, NtCreateKey, NtCreatePort, NtCreateProcess, NtCreateProcessEx, NtCreateSection, NtCreateWaitablePort, NtDeleteFile, NtDeleteKey, NtDeleteValueKey, NtDuplicateObject, NtLoadDriver, NtLoadKey, NtMapViewOfSection, NtOpenFile, NtOpenProcess, NtOpenThread, NtRenameKey, NtReplaceKey, NtRequestWaitReplyPort, NtRestoreKey, NtSecureConnectPort, NtSetInformationFile, NtSetSystemInformation, NtSetValueKey, NtTerminateProcess, NtUnloadDriver

The following table shows individual vulnerable functions for other tested (not personal firewall) software.

Product and version Hooked SSDT functions, vulnerable functions are in red
Daemon Tools Lite 4.10 X86
unofficial winner of our tests
NtCreateKey, NtEnumerateKey, NtEnumerateValueKey, NtOpenKey, NtQueryKey, NtQueryValueKey, NtSetValueKey
Process Monitor 1.22 NtClose, NtCreateKey, NtDeleteKey, NtDeleteValueKey, NtEnumerateKey, NtEnumerateValueKey, NtFlushKey, NtLoadKey, NtOpenKey, NtQueryKey, NtQueryValueKey, NtSetValueKey, NtUnloadKey
RegMon 7.04 NtClose, NtCreateKey, NtDeleteKey, NtDeleteValueKey, NtEnumerateKey, NtEnumerateValueKey, NtFlushKey, NtLoadKey, NtOpenKey, NtQueryKey, NtQueryValueKey, NtSetValueKey, NtUnloadKey

The following table is dedicated to vulnerabilities found by readers of this article using our BSODhook utility. We thank them for the reports and feedback we have received. The following results were not verified.

Product and version Hooked SSDT functions, vulnerable functions are in red
AntiHook 2.6 NtCreateProcess, NtCreateSection, NtCreateThread, NtOpenThread, NtResumeThread, NtSetContextThread, NtSetValueKey, NtSuspendThread, NtTerminateProcess, NtWriteVirtualMemory
avast! Professional 4.7
the vendor noted that avast! hooks SSDT functions on Windows NT4 and 2000 only
NtClose, NtCreateDirectoryObject, NtCreateFile, NtCreateProcess, NtCreateSection, NtOpenFile, NtSetInformationFile, NtWriteFile
Avira AntiVir Personal Edition (Premium) 7 NtCreateThread, NtOpenProcess, NtOpenThread, NtTerminateProcess, NtWriteVirtualMemory
Core Force HIPS 0.95 NtClose, NtCreateKey, NtDeleteKey, NtDeleteValueKey, NtEnumerateKey, NtEnumerateValueKey, NtLoadKey, NtOpenKey, NtQueryKey, NtQueryValueKey, NtSetValueKey, NtUnloadKey
Dynamic Security Agent 1.0.8.8 NtCreateFile, NtCreateKey, NtopenFile, NtOpenKey, NtOpenProcess, NtOpenSection, NtOpenThread, NtSetValueKey
Panda Internet Security 2008 NtCreateKey, NtDeleteKey, NtDeleteValueKey, NtEnumerateKey, NtEnumerateValueKey, NtQueryKey, NtQueryValueKey, NtSetValueKey, NtTerminateProcess, NtTerminateThread
PC Tools ThreatFire 3.0 NtCreateKey, NtDeleteKey, NtDeleteValueKey, NtOpenKey, NtSetValueKey, NtTerminateProcess
Sygate Personal Firewall Pro 5.6 build 2818 NtAllocateVirtualMemory, NtCreateThread, NtMapViewOfSection, NtProtectVirtualMemory, NtShutdownSystem, NtTerminateProcess, NtWriteVirtualMemory
Symantec Client Firewall 8.7.4.97 NtDeleteValueKey, NtSetValueKey
Trend Micro Internet Security 2007 NtClose, NtConnectPort, NtCreateProcess, NtOpenProcess, NtRequestWaitReplyPort, NtWriteVirtualMemory
VirusBlokAda Vba32 3.12.2 NtCreateThread, NtOpenProcess, NtOpenThread, NtTerminateProcess

After seeing these results, one could say that having security software installed makes your machine unstable and more vulnerable than without it – sad, but at least a little bit true. You can see software products from Check Point, Kaspersky, Symantec, and even Sysinternals (or Microsoft if you consider that they acquired Sysinternals some time ago) are vulnerable! On the other side Daemon Tools shows us that it is possible to have SSDT hooks implemented without such basic mistakes.


Back to contents

Conclusion

Almost every software that implements SSDT hooks is vulnerable to the bug we introduce in this article. BlackICE PC Protection, G DATA InternetSecurity, Ghost Security Suite, Kaspersky Internet Security, Norton Internet Security, Online Armor Personal Firewall, Outpost Firewall Pro, Privatefirewall, ProcessGuard, ProSecurity, ZoneAlarm Pro, Process Monitor, RegMon are just a few examples of badly written, not properly tested, vulnerable software. There were only two personal firewalls that passed our argument validation testing successfully, Comodo Personal Firewall and Sunbelt Personal Firewall. Our tests revealed, that the current versions of these products are probably not vulnerable, but earlier versions of both these personal firewalls contained the bug and they were both fixed after our notifications to their vendors. So in fact, the only product that passed the tests was Daemon Tools.

We also found many articles, tutorials and papers that described either SSDT hooking or other driver code and contained improper parameter validation. Even more disturbing is that these bugs are present in professional software products and also in official Sysinternals (Microsoft) tools – Process Monitor and RegMon. Even Mark Russinovich and Bryce Cogswell, the authors of these tools and two of the most famous Windows kernel hackers, seem to have forgotten about validation in their tools. Process Monitor and RegMon have been vulnerable for ages.

We advise all vendors of affected products to download and use our tool and/or contact us and order our software testing services. Back to contents