Resume
Type de vulnerabilite: probleme de logiqueVecteur: IOCTL a \\.\aswSP_Open
Impact: contournement de la protection personnelle (rendre un processus "de confiance")
Verifie sur: avast! Free aswSP.sys v9.0.2018.391
Description
La protection personnelle d'avast! (self-protection en Anglais) permet au programme de se proteger de programmes malicieux. Elle est implemente dans le module noyau aswSP.sys et utilise un concept de niveau de confiance pour les processus executes sur le systeme. aswSP.sys offre une variete de peripheriques et IOCTLs associes, mais une grande partie d'entre eux requiert des privileges administratifs, ou d'etre appele depuis un processus de confiance. Cependant, certain d'entre eux sont accessibles par des utilisateurs non privilegies, notamment au travers de \\.\aswSP_Open.Par example, pour savoir si la protection personnelle est activee, on peut interroger l'IOCTL 0xb2d60190, et pour savoir si un processus est de confiance, 0xb2d600cc. Les processus de confiance executes par defaut sont System, AvastSvc.exe, AvastUI.exe et afwServ.exe sur les versions ayant le parefeu. Cela est illustre par le script Python suivant:
Un processus de confiance peut modifier le niveau de confiance d'un autre processus. Un IOCTL (0xb2d60198) permet a un processus de devenir de confiance, mais son fonctionnement est quelque peu alambique. Cet IOCTL prend pour parametre en entree un buffer de 0x19 octets qui contient, entre autres, deux pointeurs de fonction en mode utilisateur (Ring 3). Le code de l'IOCTL va determiner dans quel module se situent ces deux pointeurs, et verifier sa signature. Il ne s'agit pas d'une signature de binaire normale de Windows, mais une signature specifique a avast!. Si le binaire n'est pas signe, ou si la signature est invalide, l'appel va echouer. Par contre si tout se passe bien, le pilote noyau va mettre en queue un APC utilisateur qui executera un des pointeurs de fonction. En fonction de ce que va faire cette procedure (modifier les parametres passes), le pilote finira par appeler une fonction qui monte le niveau de confiance du processus dont le PID a ete passe dans le buffer d'entree.
.text:0001981C kk_SetProcessTrustCallback proc near ; DATA XREF: kk_aswSP_Open_DispatchIoControl+2B7 o
.text:0001981C
.text:0001981C arg_0 = dword ptr 8
.text:0001981C
.text:0001981C mov edi, edi
.text:0001981E push ebp
.text:0001981F mov ebp, esp
.text:00019821 mov eax, [ebp+arg_0]
.text:00019824 movzx ecx, byte ptr [eax+8]
.text:00019828 push ecx ; char
.text:00019829 push dword ptr [eax+4] ; PVOID
.text:0001982C call kk_SetProcessTrust0Or2
.text:00019831 pop ebp
.text:00019832 retn 4
.text:00019832 kk_SetProcessTrustCallback endp
Afin de prevenir certains abus possibles, le pilote verifie que le processus appelant l'IOCTL n'est pas en train d'etre debogue:
.text:00019496 push ebx ; ReturnLength
.text:00019497 push 4 ; ProcessInformationLength
.text:00019499 lea eax, [ebp+var_3C]
.text:0001949C push eax ; ProcessInformation
.text:0001949D push ProcessDebugPort ; ProcessInformationClass
.text:0001949F push 0FFFFFFFFh ; ProcessHandle
.text:000194A1 call ds:NtQueryInformationProcess
Un des scenarios qui semble-t-il n'a pas ete pris en compte par les developpeurs d'avast! est la possibilite de lancer un binaire avast! signe en mode suspendu, puis d'y injecter une tache. Bien evidemment cela necessite que vous fournissions des pointeurs de fonctions pour le buffer d'entree de l'IOCTL au sein du binaire en question, et que ces pointeurs soient suffisamment interesssants pour qu'on finisse par executer du code sous notre controle. On peut par exemple utiliser un trampoline qui lit un pointeur de fonction depuis la section .data du binaire et l'execute:
.text:005E0BCD mov eax, dword_7114F8
.text:005E0BD2 test eax, eax
.text:005E0BD4 jz short loc_5E0BE4
.text:005E0BD6 lea ecx, [ebp+var_30]
.text:005E0BD9 push ecx
.text:005E0BDA push 3
.text:005E0BDC call eax ; dword_7114F8
Ce gadget se trouve dans AvastUI.exe, un binaire signe par avast!
Afin de transformer notre code en code de confiance, il nous suffit de suivre les etapes suivantes:
- creer AvastUI.exe (ou un autre binaire signe contenant un gadget acceptable) en mode suspendu
- injecter une tache (en fait j'ai ecrit une DLL pour ca) qui va:
- trouver le gadget dans le binaire (ici 005E0BCD)
- ecrire le pointeur de fonction que nous voulons executer (ici a dword_7114F8)
- appeler l'IOCTL 0xb2d60198 en contruisant correctement le buffer d'entree
Ainsi les verifications faites par le pilote vont reussir, et notre fonction va etre executee via un APC utilisateur. Maintenant pour que le pilote change le niveau de confiance du processus, il faute que cette fonction modifie un parametre de la facon suivant:
__declspec( naked ) DWORD UserModeAPCFunction( )
{
__asm
{
//int 3
mov eax, dword ptr [esp + 10h]
test eax,eax
jz skip
mov dword ptr [eax], 41414141h
skip:
xor eax,eax
add esp, 0Ch
ret
}
}
A partir d'ici, notre code est de confiance, et on peut faire ce que l'on veut avec l'antivirus (EoP, desactivation, etc).
Voici le code de la DLL a injecter:
No comments:
Post a Comment