Device Drivers Vulnerability Research, Avast a real case

September 24, 2009

In the past days I worked intensively on Antivirus’s Device Drivers bugs, at the actual state of art the major part of well known AVs suffer of basical and more hidden bugs. The totality of AVs that I’ve checked presents defects that could be maliciously used to takeover an Antivirus Infrastructure and in some case the entire Operating System with attacks like DoS and/or Remote/Local Privilege Escalation.

I want to make a precisation here, exists an important difference between Bug and Vulnerability, simply bugs does not affects the integrity of a system and does not constitute a true danger. Vulnerabilities constitutes an effective risk for systems integrity, included informations contained inside it. When we are dealing with applications specifically devoted to security, every bug could be considered a vulnerability, because an attacker could block/kill overcome checks performed by the application itself and propagate in system and produce damages. Just think to a basical crash that could affect an Antivirus could be implemented into a malicious application that checks the presence of AVs and induces the bug.

In this little post we are going to see some defects of last device drivers used by Avast, I’m precisely talking of

  • Build Number: 4.8.1351.0

Avast loads the following drivers:

  • Aasvmker4.sys
  • aswMon2.sys
  • aswRdr.sys
  • aswSP.sys

Avast loads the following Drivers could be tested by fuzzing IOCTLs, for this task could be used IOCTL Fuzzer and Kartoffel. Let’s disassemble the first driver, Aavmker4.sys that from DeviceIoControl hook appears to be heavy used. This is the DriverEntry()drivers

00010748 mov eax, [ebp+DriverObject]
0001074B push offset NotifyRoutine ; NotifyRoutine
00010750 mov dword ptr [eax+70h], offset sub_1098C ; DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_1098C;
00010757 call PsSetCreateProcessNotifyRoutine

sub_1098C contains the switch statement to handle various IOCTL notifications, essentially IOCTL check is structured in form of nested If and Switches.

001098C ; int __stdcall sub_1098C(int, PIRP Irp)
000109C4 mov ecx, 0B2D6002Ch
000109C9 cmp eax, ecx
000109CB ja loc_10D12
000109D1 jz loc_10CE9

Checks if IOCTL is less or equal to 0x0B2D6002C, if condition is true checks if IOCTL is exactly 0x0B2D6002C a certain task is performed by the device driver and finally ends with a classical
epilogue:

IofCompleteRequest(X, 0);
return value;

By monitoring Aavmker4.sys activity, with a DeviceIoControl hook emerges that the most used IOCTLs are:

  • 0xB2D60030
  • 0xB2D60034

Now we have two possibilities the first is to fuzz these IOCTLs and check crash dump if happens and after check code for more details, the second possibility is to invert the check order.

This the xml configuration to test Aavmker4.sys

<allow>

<drivers>

<entry>Aavmker4.sys</entry>

</drivers>

<devices>

<entry>\Device\AavmKer4</entry>

</devices>

<!–

IRP I/O Control Code

–>

<ioctls>

<entry>0xb2d60030</entry>

<entry>0xb2d60034</entry>

</ioctls>

<processes>

<entry>ashServ.exe</entry>

</processes>

</allow>

launch fuzzer and Avast Scan, as you can see Driver resists to Fuzzing attempts, its time to see code referred to 0xB2D60030 and 0xB2D60034.

0xB2D60030

00010D25 cmp eax, 0B2D60030h
00010D2A jz short loc_10DA8
00010D2C cmp eax, 0B2D60034h
00010D31 jz short loc_10D5B

00010DC5 mov edi, [ebx+0Ch]
00010DC8 cmp esi, 878h
00010DCE jz short loc_10DDA ;Check buffer size
00010DD0 push offset aIoctl_aavm_sta ; “******* IOCTL_AAVM_START_REQUEST_AND_SE”…
00010DD5 jmp loc_10ABA ;Jump to Io Completion

If buffer size differs from 878h Dbg Prints an error message, else supplied buffer is correctly sanitized via MmUserProbeAddress, MmIsAddressValid. We can say that this IOCTL is correctly handled.

0xB2D60034:

00010D5B cmp esi, 8
00010D5E jnz loc_10AC0 ;If differs from 8 return STATUS_INVALID_PARAMETER
00010D64 call PsGetCurrentProcessId

Now let’s test aswSP.sys. In Device Driver vulnerabilty research it’s fundamental to have a complete log of all activities of a driver, this can be obtained by correctly planning a battery of test unit. Each test should correspond to a primitive logic operation performed by an application that makes use of driver. I usually build several mini logs for each activity and finally a complete log. Here a little list of monitoring primitives:

  • On Load
  • On apparent Idle
  • On Working
  • On Shutdown
  • Various, like On Surprise Stop

This will give us a complete report of all activities and involved IOCTL. In the specific case of aswMon2.sys we can isolate the following:

  • 0xb2c80018
  • 0xb2c80014
  • 0xb2c80020
  • 0xB2c800C0
  • 0xB2c800C4
  • 0xB2c800C8

From IOCTL Logger we can see that 0xB2c800C0 is heavly used, time to locate Ioctl Dispatcher:

0001178B and dword ptr [ebx+34h], 0
0001178F mov dword ptr [ebx+6Ch], offset sub_11FB6
00011796 mov dword ptr [ebx+28h], offset off_18988

C like:
v2->DriverUnload = 0;
v2->MajorFunction[13] = (PDRIVER_DISPATCH)sub_11FB6;
v2->FastIoDispatch = (PFAST_IO_DISPATCH)&unk_18988;

with a bit of research we land to sub_10B82 that contains the switch for Ioctls.

00010BBD mov eax, 0B2C80018h
00010BC2 cmp ecx, eax
00010BC4 push edi
00010BC5 ja loc_11066
00010BCB jz loc_10F70
00010BD1 cmp ecx, 0B2C80008h
00010BD7 jz short loc_10C3C
00010BD9 cmp ecx, 0B2C8000Ch
00010BDF jz short loc_10C16
00010BE1 cmp ecx, 0B2C80010h
00010BE7 jz short loc_10BFF
00010BE9 cmp ecx, 0B2C80014h
00010BEF jnz loc_111AC
00010BF5 call sub_108BC
00010BFA jmp loc_11055

From logs emerged that the most frequently used is 0B2C8000C so it’s obvious that we will study this for first:

0xB2C8000C:

00010C16 cmp [ebp+arg_C], 1448h
00010C1D jnz loc_111AC ;check len
00010C23 mov esi, [ebp+SourceString]
00010C26 mov ecx, 512h
00010C2B mov edi, offset dword_18A58
00010C30 rep movsd
00010C32 call sub_108F0
00010C37 jmp loc_112C1 ;go out

In this case user supplied input is correctly sanitized, so 0xB2C8000C can be excluded from fuzz testing. From the log On Shutdown emerged the massive presence of 0xB2c80018, so let’s fuzz it. Here the configuration for IOCTL Fuzzer:

<?xml version=”1.0″ encoding=”windows-1251″?>

<cfg>

<log_file>C:\ioctls.txt</log_file>

<hex_dump>true</hex_dump>

<log_requests>true</log_requests>

<debug_log_requests>true</debug_log_requests>

<fuze_requests>true</fuze_requests>

<fuze_size>true</fuze_size>

<allow>

<drivers>

<entry>aswMon2.SYS</entry>

</drivers>

<devices>

<entry>\Device\aswMon</entry>

</devices>

<ioctls>

<entry>0xb2c80018</entry>

</ioctls>

<processes>

<entry>ashServ.exe</entry>

</processes>

</allow>

<deny>

<drivers>

<entry>aswSP.SYS</entry>

<entry>Aavmker4.SYS</entry>

<entry>aswTDI.SYS</entry>

</drivers>

<ioctls>

<entry>0xb2c8000c</entry>

<entry>0xb2c80014</entry>

<entry>0xb2c80020</entry>

</ioctls>

</deny>

</cfg>

The config script allows only 0xB2c80018 sent from aswMon, other drivers are locked. Obviously fuzzing need to follow the log unit that evidenced out IOCTL, so run fuzzer and stop all Avast services.

Bang..a BSOD, discovered an Avast vulnerability into aswMon2.sys :)

From crashdump:

kd> !analyze -v

UNEXPECTED_KERNEL_MODE_TRAP_M
Arguments:
Arg1: 00000008, EXCEPTION_DOUBLE_FAULT
Arg2: 80042000
Arg3: 00000000
Arg4: 00000000_KERNEL_MODE_TRAP_M (1000007f)

STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
f76f3234 8053d251 f76f3250 00000000 f76f32a4 nt+0x600fa
f76f32a4 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0×66251
f76f3328 8052c793 41414141 00000000 f76f377c nt+0×55712
f76f33a4 804fc700 f76f377c f76f3478 05050505 nt+0×55793
f76f3760 8053d251 f76f377c 00000000 f76f37d0 nt+0×25700
f76f37d0 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0×66251
f76f3854 8052c793 41414141 00000000 f76f3ca8 nt+0×55712
f76f38d0 804fc700 f76f3ca8 f76f39a4 05050505 nt+0×55793
f76f3c8c 8053d251 f76f3ca8 00000000 f76f3cfc nt+0×25700
f76f3cfc 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0×66251
f76f3d80 8052c793 41414141 00000000 f76f41d4 nt+0×55712
f76f3dfc 804fc700 f76f41d4 f76f3ed0 05050505 nt+0×55793
f76f41b8 8053d251 f76f41d4 00000000 f76f4228 nt+0×25700
f76f4228 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0×66251
f76f42ac 8052c793 41414141 00000000 f76f4700 nt+0×55712
f76f4328 804fc700 f76f4700 f76f43fc 05050505 nt+0×55793
f76f46e4 8053d251 f76f4700 00000000 f76f4754 nt+0×25700
f76f4754 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0×66251
f76f47d8 8052c793 41414141 00000000 f76f4c2c nt+0×55712
f76f4854 804fc700 f76f4c2c f76f4928 05050505 nt+0×55793
f76f4c10 8053d251 f76f4c2c 00000000 f76f4c80 nt+0×25700
f76f4c80 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0×66251
f76f4d04 8052c793 41414141 00000000 f76f5158 nt+0×55712
f76f4d80 804fc700 f76f5158 f76f4e54 05050505 nt+0×55793
f76f513c 8053d251 f76f5158 00000000 f76f51ac nt+0×25700
f76f51ac 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0×66251
f76f5230 8052c793 41414141 00000000 f76f5684 nt+0×55712
f76f52ac 804fc700 f76f5684 f76f5380 41414141 nt+0×55793
f76f5668 8053d251 f76f5684 00000000 f76f56d8 nt+0×25700
f76f56d8 f7756a04 badb0d00 8055b256 00000000 nt+0×66251
f76f576c 41414141 41414141 41414141 41414141 aswMon2+0xa04
f76f5770 41414141 41414141 41414141 41414141 0×41414141
f76f5774 41414141 41414141 41414141 41414141 0×41414141
f76f5778 41414141 41414141 41414141 41414141 0×41414141
f76f577c 41414141 41414141 41414141 41414141 0×41414141
etc..

here the evidece of buffer corruption.

0xB2D60018:

00010F70 cmp [ebp+arg_C], 288h ;Insufficent input validation
00010F77 jnz loc_111AC
00010F7D mov esi, [ebp+SourceString]
00010F80 cmp [esi], ebx
00010F82 mov [ebp+arg_C], ebx
00010F85 jz short loc_10FEB
00010F87 mov eax, dword_18A48
00010F8C cmp eax, 80h
00010F91 jge loc_11055
00010F97 lea eax, [eax+eax*4]
00010F9A lea eax, unk_231A4[eax*4]
00010FA1 mov [ebp+arg_10], eax
00010FA4 lea eax, [esi+8]
00010FA7 mov [ebp+arg_18], eax
00010FAA jmp short loc_10FAF

Privates, Companies and/or Software House interested into a security review of their Device Drivers can contact me for Professional Consulting, please use PGP key that you can obtain here:
http://evilcry.netsons.org/contacts.html

Regards,
Giuseppe ‘Evilcry’ Bonfa’


Avast aswMon2.sys kernel memory corruption and Local Privilege Escalation

September 23, 2009

Redirection:

http://evilcodecave.blogspot.com/2009/09/avast-aswmon2sys-kernel-memory.html

Regards,

Giuseppe ‘Evilcry’ Bonfa’


Vista IRP Struct with offset references for consult

September 10, 2009

Redirection:

http://evilcodecave.blogspot.com/2009/09/vista-irp-struct-with-offset-references.html


Device Driver Development for Beginners

September 4, 2009

Redirection:

http://evilcodecave.blogspot.com/2009/09/device-driver-development-for-beginners.html

Regards,

Giuseppe ‘Evilcry’ Bonfa’


TheGreenBow VPN Client tgbvpn.sys DoS and Potential Local Privilege Escalation Vulnerability.

August 17, 2009

Hi,

Redirection:

http://evilcodecave.blogspot.com/2009/08/thegreenbow-vpn-client-tgbvpnsys-dos.html

Regards,

Giuseppe ‘Evilcry’ Bonfa’


OffensiveC0ding section Opened

February 1, 2009

Hi there,

I’ve opened a new section in my Website, called OffensiveC0ding.

I’m going to collect Source Code Samples of Applications that performs Spy/Surveillance operations, Covert Channels Applications and various other services that needs to perform hidden tasks. The Applications that you will find here will be published without binary files, easly because the scope of this section is to demonstrate how insecure could be a not well hardened system, this kind of demonstration could be only acheived by showing how effectively works a Real OffensiveCode.

Actually there is a little source that acts as Sandbox Detector, Joe of Joebox kindly noticed me that this detection system does not longer affects JoeBox.

Here you can reach my page:

http://evilcry.netsons.org/OC0/offensivec0ding.htm

See you to the next post.. :)


End Year – New Year

December 31, 2008

Hi there people!

Another year seems passed! definitely a Good Year..and I hope a better 2009 :)

In these last weeks I was quite busy with Study and Research/Coding tasks.

I’m actively coding and researching new tools related to Evilfingers, but I will not leave obviously my Cave or the Blog, all Work that I realize is done principally for my own pleasure and satisfaction, mine is only an Insane Computer Science Passion :)

A sad news shadowed this last days, the Big CastleCops Died!

CastleCops was a Great Service for People, and also a great source for Malware Researchers, cause could seems strange..but often its HARD TO CATCH New Virus Samples!

So if you have every kind of Virus Sample feel free to submit me It!

For New Year I’ll release other Mw Analysis/Win Internals Papers and hopefully new tools!

Actually I’m also working on FreeBSD, specifically on ACPI Project, in this moment I’m working on the correction of AcpiOsDerivePciId() function, that is not quit right, hope soon to release patch and for readers a little tech report on it!

Another work in TODO List is a little Coding Paper on Thread Deadlock Barrier (TDB) Implementation to Enhance Hook Stability

Have a nice Year!

Giuseppe ‘Evilcry’ Bonfa’



Kernel Pool Overflows

June 22, 2008

Hi,

Device Drivers Security is not a really spreaded and known, not many researchers are involved into this field, one of my scope, in this blog is to summarize all material related to Windows Kernel Mode Security..

Here two intersing new papers about Kernel Pool Overflows and Driver Impersonation Attack:

http://immunityinc.com/downloads/KernelPool.odp

http://immunityinc.com/downloads/DriverImpersonationAttack_i2omgmt.pdf

See you to the next post.. :)


On USB Driver Coding #7

November 30, 2007

Hi,

In the previous post we have seen how to completely Dump an URB but as you should remember exists a particular structure _URB_CONTROL_TRANSFER, that USB client drivers sets up to transfer data to or from a control pipe. So we need to implement two external Dump Functions, DumpPipeHandle and DumpTransferBuffer.

void DumpPipeHandle(
in struct Buffer *b,const char *s,
in USBD_PIPE_HANDLE inPipeHandle
)
{
unsigned char ep;

if (GetEndpointInfo(inPipeHandle,&ep))
KPrintf(b,”%s = %p [endpoint 0x%x]\n”,s,inPipeHandle,ep);
else
KPrintf(b,”%s = %p\n”,s,inPipeHandle);
}

As you should remember it’s necessary for any PipeHandle to know the Endpoint. This can be accomplished by tracing the USBD_PIPE_HANDLE for each Endpoint (the number of Endpoints is declared at our choise (use for example MAXEP = 50).

Now is time to rip the TransferBuffer with DumpTransferBuffer:

void DumpTransferBuffer(
struct Buffer *b,
PUCHAR pBuffer,
PMDL pMdl,
ULONG uBufferSize,
in BOOLEAN bPrintHeader,
ULONG uBufferOffset = 0
)

if(pMdl)
{
DumpBuffer(b,pBuffer+uBufferOffset,uBufferSize);

else if(pMdl)
{
PUCHAR pMDLBuf = (PUCHAR)MmGetSystemAddressForMdlSafe(pMdl,NormalPagePriority);
if(pMDLBuf)
DumpBuffer(b,pMDLBuf+uBufferOffset,uBufferSize);
}
}

Here ends the USB Coding Series (source code I’ve used is taken from SniffUSB 2.0), but surely i’ll come back with other arguments related to USB..

See you to the next post.. :)


On USB Coding #5

November 17, 2007

Hi,

In the previous post We talked about the general foundamentals of USB Coding, many other thing should be said, for example about Power Management, but this will became a commentary upon USB_WDF.doc provided by Microsoft, our scope is a general overview of USB Coding and related USB Security/Forensics.

USB Security can be divided into two parts:

1) Intrusion Risk Security of USB Device Drivers
2) Forensics of USB Secure Devices

As you have seen USB Data Transactions does not have any kind of Encryption or Anti-Sniff protection by Default. Actually USB devices are extremely diffused,
these devs are applied in every kind of periferials, also in Secure Devices such as Fingerprint, Hardware Protectors, Sensitive Data Auth Devices and many many i
mportant as you know Usb is spreaded also for Mass Storage Devices. And where there are large collection of data born the problem of Sensitive Information Protection
and consequently we have to consider the Forensics Attacks that could be performed.

Around here you can download many USB-Tracers / UsbSniffers, such as Usbtrace and SnoopyPro, which sets an UsbFilter Driver and are make you able dump the entire code transaction. Let’s take a look at the general architecture..

Every Device Sniffer is based upon Data Filtering, because we need obviously to have a Dynamic Capture System, so he only way is to dispose a Filter Driver, this is the case of our USB Sniffer.

So we need to provide a Filter PNP Filter Driver, this kind of driver can be coded in two different ways, the Classical one without using any Framework, or by using KMDF, which is more easy and fast to code (just you have to provide WdfFdoInitSetFilter during EvtDeviceAdd Event)

We will follow the first “old school” way.

As every Filter Driver we the skeleton is based on a DeviceEntry, Device Adding, Dispatching and Unloading, so let’s view in detail each one of them.

NTSTATUS DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
)

in this nothing special, but inside the Method we have to provide, an IRP stack filtering context, that can be stored into an lookaside list

ExInitializeNPagedLookasideList(
&g_ContextLookasideList, NULL, NULL, 0, sizeof(IRP_STACK_CONTEXT),
IRP_STACK_CONTEXT_TAG, 0 );

After defining our context, as usual there is the Dispatch Procedure creation, in the particular case of a PNP Filter are used:

DriverObject->MajorFunction[IRP_MJ_PNP] = FilterDispatchPnp;
DriverObject->MajorFunction[IRP_MJ_POWER] = FilterDispatchPower;
DriverObject->DriverExtension->AddDevice = FilterAddDevice;

Cause the presence of multiple DeviceObject, is also necessary a Mutex, to synchronize all threads:

ExInitializeFastMutex(&ControlMutex);

Now is time to Add our Filter, so let’s study FilterAddDevice:

NTSTATUS
FilterAddDevice(
PDRIVER_OBJECT DriverObject,
PDEVICE_OBJECT PhysicalDeviceObject
)
At this point we are dealing directly with the PnP system, we need to determine if we need to be in the driver stack for the device, and next we have to initialize this device object.

{

PDEVICE_EXTENSION deviceExtension;
ULONG deviceType = FILE_DEVICE_UNKNOWN;

deviceObject = IoGetAttachedDeviceReference(PhysicalDeviceObject);
deviceType = deviceObject->DeviceType;
ObDereferenceObject(deviceObject);

This because windows check if the filter attached to a storage device doesn’t specify the same DeviceType as the device that’s attempts to attach to it.

Now we can attach our Filter

IoCreateDevice (DriverObject,
sizeof (DEVICE_EXTENSION),
NULL, // No Name
deviceType,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject);

Jump in the stack:

deviceExtension->LowerDeviceObject = IoAttachDeviceToDeviceStack (deviceObject, PhysicalDeviceObject);

this is to define the supported I/O of our driver

deviceObject->Flags |= deviceExtension->LowerDeviceObject->Flags &
(DO_BUFFERED_IO | DO_DIRECT_IO |
DO_POWER_PAGABLE );

Device properties can be retrieved by calling

IoGetDeviceProperty(PhysicalDeviceObject,
DevicePropertyPhysicalDeviceObjectName, 0,
NULL, &nameLength);

Essentially we need a copy (slighty modified) of Next Lower Device’s Driver Object (FDO) so all transactions will pass also on our device. These are the modifications

deviceExtension->ModifiedLowerDriverObject.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = FdoHookDispatchInternalIoctl;
deviceExtension->ModifiedLowerDriverObject.MajorFunction[IRP_MJ_PNP] = FdoHookDispatchPnp;

and next we save our copy

InterlockedExchangePointer(
(PVOID * )&( (deviceExtension->LowerDeviceObject)->DriverObject ),
&deviceExtension->ModifiedLowerDriverObject
);

Now we can grab all IRPs with FilterDispatchAny

NTSTATUS
FilterDispatchAny (
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)

PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

if (irpStack->MajorFunction == IRP_MJ_PNP)
{
const char * MinorFunctionName = PnPMinorFunctionString(irpStack->MinorFunction);
if (MinorFunctionName != NULL)
LogTheCapturedIrp();

it’s important to remember that this is only a filter, so we don’t need to know ALL about data transactions, so unknown IRPs are delivered untouched.

NTSTATUS
FilterDispatchPnp (
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)

if( irpStack->MinorFunction == IRP_MN_QUERY_INTERFACE )
{
if( IsEqualGUIDAligned(
*irpStack->Parameters.QueryInterface.InterfaceType,
USB_BUS_INTERFACE_HUB_GUID
)
)
LogTheCapturedIrp();

with USB_BUS_INTERFACE_HUB_GUID we obtain the interface for USB Hub drivers

In the next post we will see the final part of the Filter Driver.

See you to the next post.. :)


Follow

Get every new post delivered to your Inbox.