Comment PROCESS_DUP_HANDLE se transforme en PROCESS_ALL_ACCESS

Il y a une remarque intéressante dans l'article MSDN sur la sécurité des processus et les droits d'accès :


... si le processus A a un handle pour traiter B avec un accès PROCESS_DUP_HANDLE , il peut dupliquer le pseudo handle pour le processus B. Cela crée un handle qui a un accès maximal au processus B.

Si vous traduisez librement cela en russe, il est dit ici qu'ayant un descripteur pour un processus avec le droit d' accès PROCESS_DUP_HANDLE , nous pouvons, en utilisant la fonction DuplicateHandle (...) , obtenir un descripteur avec les masques d'accès maximum autorisés pour ce processus.



Démonstration


Le code source exploitant cette fonctionnalité est assez simple:


#include <Windows.h> int wmain(int argc, PWSTR argv[]) { HANDLE ProcessAllAccessHandle; HANDLE ProcessDuplicateHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, _wtoi(argv[1])); if (ProcessDuplicateHandle) { if (DuplicateHandle(ProcessDuplicateHandle, GetCurrentProcess(), GetCurrentProcess(), &ProcessAllAccessHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { CloseHandle(ProcessAllAccessHandle); } CloseHandle(ProcessDuplicateHandle); } return 0; } 

À la suite de la compilation et de la liaison, nous obtenons un utilitaire de test qui prend l'identificateur de processus cible (PID) comme argument. Ensuite, l'utilitaire ouvre le processus spécifié avec le droit PROCESS_DUP_HANDLE . Ainsi, nous simulons la condition nécessaire pour la disponibilité d'un descripteur pour un processus avec le bon PROCESS_DUP_HANDLE (== 0x40).


En guise de démonstration, je vais tracer l'utilitaire assemblé dans WinDbg:


 0:000> lsa @$ip 0,3 > 13: if (ProcessDuplicateHandle) 14: { 15: if (DuplicateHandle(ProcessDuplicateHandle, 0:000> !handle @@C++(ProcessDuplicateHandle) 3 Handle 80 Type Process Attributes 0 GrantedAccess 0x40: None DupHandle HandleCount 9 PointerCount 260518 

Et puis petit coup de poignet En appelant DuplicateHandle (...) nous obtenons le deuxième descripteur pour le même processus, mais avec les autorisations les plus larges:


 0:000> lsa @$ip 0,3 > 23: CloseHandle(ProcessAllAccessHandle); 24: } 25: CloseHandle(ProcessDuplicateHandle); 0:000> !handle @@C++(ProcessAllAccessHandle) 3 Handle 84 Type Process Attributes 0 GrantedAccess 0x1fffff: Delete,ReadControl,WriteDac,WriteOwner,Synch Terminate,CreateThread,,VMOp,VMRead,VMWrite,DupHandle,CreateProcess,SetQuota,SetInfo,QueryInfo,SetPort HandleCount 10 PointerCount 292877 

Le point clé est la valeur GrantedAccess, qui pour le nouveau descripteur est 0x1fffff, ce qui correspond à PROCESS_ALL_ACCESS . Malheureusement, WinDbg n'affiche pas le PID du processus cible. Mais pour vous assurer que le descripteur est reçu pour le processus souhaité, vous pouvez consulter les descripteurs par Process Explorer (après avoir spécifié le PID spécifié dans les arguments de ligne de commande dans le débogueur):


 0:000> dx argv[1] argv[1] : 0x1b7c2e2412c : "21652" [Type: wchar_t *] 


Dans la capture d'écran, l'utilitaire ouvre les descripteurs pour exécuter notepad.exe.


Pourquoi cela se produit-il?


Premièrement, parce que lors de la duplication du descripteur, si le masque d'accès pour l'objet n'est pas développé (et l'indicateur d'opération DUPLICATE_SAME_ACCESS est spécialement spécifié pour nous), il n'y a aucune vérification que le processus (dans lequel le descripteur dupliqué sera créé) a accès à cet objet. Il est uniquement vérifié que les descripteurs de processus transmis à la fonction DuplicateHandle (...) ont le masque d'accès autorisé PROCESS_DUP_HANDLE . Et puis la copie du descripteur entre les processus se produit sans vérifier les droits d'accès (je répète: si le nouveau descripteur a le masque de droits autorisé pas plus large que le descripteur dupliqué d'origine).


Et puis il convient de noter que l'appel à GetCurrentProcess () renvoie une constante, ce même pseudo-descripteur (pseudo descripteur) mentionné au tout début de cette publication. Il existe deux pseudo-descripteurs documentés avec des valeurs constantes physiquement manquantes dans la table des descripteurs de processus. Mais ces descripteurs sont traités par toutes les fonctions du noyau (avec les descripteurs habituels de la table des descripteurs de processus):


MacroValeurLa description
ZwCurrentProcess / NtCurrentProcess(POIGNÉE) -1Descripteur de processus
ZwCurrentThread / NtCurrentThread(POIGNÉE) -2Descripteur de fil

C'est la valeur de NtCurrentProcess (== -1) qui renvoie l'appel GetCurrentProcess () .


Ce pseudo-descripteur dans le cadre d'un processus spécifique signifie un objet de ce processus avec les droits PROCESS_ALL_ACCESS (en fait, il y a des nuances, mais l'article ne les concerne pas). Il se révèle un tel lien avec lui-même, mais à travers le descripteur:


Autrement dit, notre appel à DuplicateHandle (ProcessDuplicateHandle, GetCurrentProcess (), ...) sera interprété comme suit: à partir du processus ouvert (cible), dupliquez le handle avec la valeur -1. Et pour le processus cible (celui sur lequel nous avons le descripteur stocké dans la variable ProcessDuplicateHandle), la valeur -1 fera référence à ce même processus cible avec les droits PROCESS_ALL_ACCESS . Par conséquent, en conséquence, nous obtenons un descripteur pour le processus cible avec des droits maximaux.


Au lieu d'un épilogue


Je répète la pensée écrite au tout début: si quelqu'un reçoit un descripteur pour le processus avec le droit PROCESS_DUP_HANDLE , alors sous le modèle de sécurité Windows, il pourra obtenir un autre descripteur pour le même processus, mais avec les droits PROCESS_ALL_ACCESS (et faire tout ce qu'il veut avec le processus )


Merci à tous ceux qui ont lu la publication jusqu'au bout. J'invite tout le monde à répondre à l'enquête pour découvrir comment ces publications peuvent être intéressantes / utiles pour le public.

Source: https://habr.com/ru/post/fr448472/


All Articles