Windows Notification Facility: la surface d'attaque la plus non documentée

Sous la coupe se trouve une traduction de la présentation "La fonction de notification Windows: la surface d'attaque de noyau la plus non documentée à ce jour" présentée par Alex Ionescu et Gabrielle Viala lors de la conférence BlackHat 2018 .




Qu'est-ce que la fonction de notification Windows (WNF)


Windows Notification Facility est un mécanisme de notification (disponible à la fois dans le noyau et en mode utilisateur), qui est basé sur le modèle éditeur-abonné ( pubsub , Publisher / Subscriber). Le mécanisme a été ajouté dans Windows 8: en partie pour résoudre certaines limitations de conception de longue date dans le système d'exploitation, mais aussi pour servir de base à la mise en œuvre de notifications push similaires à iOS / Android.


Sa principale caractéristique est qu'il s'agit d'un modèle aveugle (généralement sans enregistrement) qui permet un abonnement et une publication non ordonnés. Cela implique qu'un consommateur peut souscrire à une notification avant même que la notification ait été publiée par sa source. Et que celui qui génère les événements n'est pas tenu de "s'inscrire" à l'avance.


De plus, le mécanisme prend en charge:


  • notifications permanentes et temporaires
  • augmenter de façon monotone les identifiants uniques
  • tampon de charge utile (jusqu'à 4 kilo-octets) pour chaque événement
  • modèle de notification de pool de threads avec sérialisation basée sur les groupes
  • un modèle de sécurité basé sur la portée qui implémente des descripteurs de sécurité via le mécanisme DACL / SACL standard


Pourquoi WNF est apparu


Prenons un exemple canonique: il y a un pilote qui veut savoir qu'un volume avec accès en lecture et en écriture a été connecté. Pour vous en informer, Autochk (un analogue de fsck sous Windows) signale un événement appelé VolumesSafeForWriteAccess. Mais pour signaler un événement, vous devez d'abord créer l'objet événement lui-même.


Nous devons également savoir qu'Autochk travaille déjà sur le volume, mais n'a pas encore signalé l'événement que nous attendons. Mauvaise solution: asseyez-vous en boucle avec sleep (), vérifiez la présence d'un événement, et quand l'événement est créé - attendez-le.


Mais après avoir quitté l'application Windows, tous ses descripteurs sont fermés. Et lorsque l'objet n'a pas de descripteurs, il est détruit. Alors, qui organisera cet événement?


Sans WNF, la solution consiste pour le noyau du système d'exploitation à générer un événement avant le chargement des pilotes et à Autochk de l'ouvrir comme le ferait un consommateur, mais au lieu d'attendre, il devrait signaler cet événement.



Noms des états WNF


Dans le monde WNF, un nom d'état est un nombre 64 bits. Mais il y a une astuce - en fait, c'est une structure codée. Le nom de l'état a une version , une durée de vie , une étendue , un indicateur de persistance des données et un numéro de série unique .


typedef struct _WNF_STATE_NAME_INTERNAL { ULONG64 Version:4; ULONG64 NameLifetime:2; ULONG64 DataScope:4; ULONG64 PermanentData:1; ULONG64 Unique:53; } WNF_STATE_NAME_INTERNAL, *PWNF_STATE_NAME_INTERNAL; 

Mais ces données ne seront disponibles que si nous pro-XOR un nombre 64 bits avec une constante magique:


 #define WNF_STATE_KEY 0x41C64E6DA3BC0074 


Durée de vie du nom de l'État


Le nom d'état WNF peut être (WNF_STATE_NAME_LIFETIME):


  • bien connu
  • permanent
  • persistant
  • temporaire

Les trois premiers sont associés aux clés correspondantes dans le registre, où les informations d'état seront stockées:


  • des noms connus se trouvent dans HKLM \ SYSTEM \ CurrentControlSet \ Control \ Notifications
  • les noms persistants vivent dans HKLM \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Notifications
  • les noms persistants vivent dans HKLM \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ VolatileNotifications

Les noms connus ont leur particularité: ils ne peuvent pas être enregistrés. Un tel nom doit déjà être présenté dans le registre au moment du démarrage du système. Les noms persistants et persistants nécessitent le privilège SeCreatePermanentPrivilege inclus (comme les autres objets globaux) pour les créer. Les noms persistants vivent en dehors du processus d'enregistrement, tandis que les noms persistants survivent à un redémarrage du système.



Portée des données


L'étendue des données définit la première limite de sécurité autour du nom d'état WNF; elle détermine qui la voit et y a accès. La portée du nom d'état peut être:


  • le système
  • la voiture
  • session utilisateur
  • l'utilisateur
  • le processus

En plus de fournir des limites de sécurité, les étendues WNF peuvent être utilisées pour fournir différentes instances de données pour le même nom. Le noyau (comme avec d'autres mécanismes de sécurité) contourne les vérifications d'accès à l'état. Le privilège TCB permet un accès croisé aux noms d'état WNF.


L'étendue "système" et l'étendue "machine" sont des étendues globales. Ils n'ont pas leurs propres identifiants (ils utilisent différents conteneurs globaux). La portée de la session utilisateur utilise l'identifiant de session (ID de session) comme ID. La portée d'un utilisateur spécifique utilise le SID de cet utilisateur comme identifiant. L'adresse de l'objet EPROCESS est l'identifiant de la portée du processus.



Numéros de séquence


Pour garantir l'unicité, chaque nom d'état possède un numéro de séquence unique de 51 bits. Les noms bien connus incluent une étiquette de famille à 4 caractères dans leur numéro de série, et les 21 bits restants sont utilisés comme identifiant unique. Les noms permanents stockent leur numéro d'incrémentation avec la valeur de registre "SequenceNumber". Les noms persistants et temporaires utilisent un compteur d'incrémentation commun, qui se trouve dans une variable globale. Ces données sont stockées et traitées séparément pour chaque conteneur (par silo) et sont disponibles dans PspHostSiloGlobals-> WnfSiloState.


Dans Microsoft, chaque nom WNF a un identifiant "convivial" qui est utilisé dans le code, parfois il est stocké dans l'espace de noms global avec le même nom. Par exemple, le symbole nt! WNF_BOOT_DIRTY_SHUTDOWN, qui a la valeur 0x1589012fa3bc0875. Après XOR avec la constante magique WNF_STATE_KEY, nous obtenons la valeur 0x544f4f4200000801, qui peut être interprétée au niveau du bit comme:


 BOOT1, Well-Known Lifetime, System Scope, Version 1 


Appels système pour travailler avec WNF


Les appels système du noyau vous permettent d'enregistrer et de supprimer des noms d'état WNF, de publier et de recevoir des données de nom d'état WNF et de recevoir diverses notifications de WNF.



Enregistrer le nom du statut WNF


À l'exception des noms bien connus (comme mentionné précédemment), le nom d'état WNF peut être enregistré pendant que le système d'exploitation est en cours d'exécution:


 NTSTATUS ZwCreateWnfStateName ( _Out_ PWNF_STATE_NAME StateName, _In_ WNF_STATE_NAME_LIFETIME NameLifetime, _In_ WNF_DATA_SCOPE DataScope, _In_ BOOLEAN PersistData, _In_opt_ PCWNF_TYPE_ID TypeId, //      _In_ ULONG MaximumStateSize, //   4-  _In_ PSECURITY_DESCRIPTOR SecurityDescriptor // **  ); 

Il existe un système symétrique appelé ZwDeleteWnfStateName avec lequel vous pouvez supprimer le nom d'état enregistré (à nouveau, sauf pour les plus connus).



Publier les données d'état WNF


Pour définir ou modifier les données de nom d'état WNF, vous pouvez utiliser l'appel système ZwUpdateWnfStateData:


 NTSTATUS ZwUpdateWnfStateData ( _In_ PCWNF_STATE_NAME StateName, _In_reads_bytes_opt_(Length) const VOID* Buffer, _In_opt_ ULONG Length, //   ,   MaximumSize,    _In_opt_ PCWNF_TYPE_ID TypeId, //      _In_opt_ const PVOID ExplicitScope, //  , SID ,  (ID)  _In_ WNF_CHANGE_STAMP MatchingChangeStamp, //     _In_ LOGICAL CheckStamp //         ); 

Il existe un système symétrique appelé ZwDeleteWnfStateData pour supprimer (nettoyer) les données du nom d'état WNF.



Obtention des données d'état WNF


Afin de demander les données de nom d'état WNF, l'appel système suivant peut être utilisé (la plupart des paramètres sont similaires à la fonction de mise à jour):


 NTSTATUS ZwQueryWnfStateData ( _In_ PCWNF_STATE_NAME StateName, _In_opt_ PCWNF_TYPE_ID TypeId, _In_opt_ const VOID* ExplicitScope, _Out_ PWNF_CHANGE_STAMP ChangeStamp, _Out_writes_bytes_to_opt_(*BufferSize, *BufferSize) PVOID Buffer, _Inout_ PULONG BufferSize //   0,      ); 

La véritable force réside dans le fait que les fonctions API de mise à jour et de requête ne nécessitent en fait pas de nom d'état WNF enregistré . Et si le nom n'est pas temporaire (et que le code appelant dispose de privilèges suffisants), une instance du nom peut être enregistrée en temps réel!



Notifications WNF


Jusqu'à présent, nous avons supposé que le consommateur savait quand appeler la fonction d'acquisition de données. Mais il y a aussi la lecture bloquante , qui fonctionne à l'aide d'un système de notification (qui est plus proche du vrai modèle éditeur-abonné).


Tout d'abord, le processus doit enregistrer l'événement en appelant la fonction ZwSetWnfProcessNotificationEvent. Ensuite, vous devez appeler la fonction ZwSubscribeWnfStateChange, en spécifiant un masque d'événement pour obtenir l'identifiant d'abonnement sur la sortie. Les événements peuvent être de deux types:


  • Notifications de données:
    • 0x01 - apparence des données
    • 0x10 - destruction de nom
  • Méta métanotifications
    • 0x02 - apparence d'un abonné recevant des notifications de données (abonné aux données)
    • 0x04 - apparition d'un abonné recevant des méta-notifications (Meta Subscriber)
    • 0x08 - l'apparition d'un abonné recevant des notifications de données et des méta-notifications (abonné générique)

Ensuite, vous devez attendre l'événement qui a été enregistré. Et chaque fois que l'événement devient un signal, vous devez appeler la fonction ZwGetCompleteWnfStateSubscription, qui renvoie WNF_DELIVERY_DESCRIPTOR.


Mais ces fonctions API de bas niveau ont un problème (merci à Gabi de l'avoir étudié): chaque processus ne peut avoir qu'un seul événement enregistré.



API de mode utilisateur de haut niveau (ntdll)


En ce qui concerne les notifications, les choses se compliquent, donc la couche rtl de ntdll.dll fournit une interface plus simple:


 NTSTATUS RtlSubscribeWnfStateChangeNotification ( _Outptr_ PWNF_USER_SUBSCRIPTION* Subscription, _In_ WNF_STATE_NAME StateName, _In_ WNF_CHANGE_STAMP ChangeStamp, _In_ PWNF_USER_CALLBACK Callback, _In_opt_ PVOID CallbackContext, _In_opt_ PCWNF_TYPE_ID TypeId, _In_opt_ ULONG SerializationGroup, _In_opt_ ULONG Unknown ); 

En fait, il n'est pas nécessaire d'appeler directement les services système: il suffit d'utiliser une seule file d'attente d'événements gérée par ntdll.dll.


Dans les coulisses, le contenu de WNF_DELIVERY_DESCRIPTOR est converti en paramètres de rappel:


 typedef NTSTATUS (*PWNF_USER_CALLBACK) ( _In_ WNF_STATE_NAME StateName, _In_ WNF_CHANGE_STAMP ChangeStamp, _In_opt_ PWNF_TYPE_ID TypeId, _In_opt_ PVOID CallbackContext, _In_ PVOID Buffer, _In_ ULONG BufferSize); 

Pour chaque nouvel abonnement, une entrée est effectuée, qui est placée dans la liste pointée par la variable globale RtlpWnfProcessSubscriptions. La liste est construite sur l'un des champs WNF_NAME_SUBSCRIPTION, qui est de type LIST_ENTRY. Chacun de WNF_NAME_SUBSCRIPTION, à son tour, a un autre champ LIST_ENTRY pour organiser une liste de WNF_USER_SUBSCRIPTION avec un rappel et un contexte.



API de niveau noyau de haut niveau (Ex)


WNF fournit également des fonctions presque identiques pour le code en mode noyau (qui peut être utilisé à partir du pilote): à la fois via des appels système exportés et via des fonctions API de haut niveau lors de l'exécution (couche Ex).


La fonction ExSubscribeWnfStateChange accepte le nom d'état, les masques de type et l'adresse de la fonction de rappel + contexte en tant qu'entrée et renvoie un descripteur d'abonnement. Les fonctions de rappel reçoivent le nom cible, le masque d'événement, le libellé de changement, mais pas le tampon ni sa taille.


La fonction ExQueryWnfStateData, basée sur le descripteur d'abonnement transmis, lit les données d'état actuelles. En fait, chaque rappel finit par appeler la fonction ExQueryWnfStateData pour obtenir les données associées à la notification.


Pour les abonnements en mode noyau et les abonnements en mode utilisateur, WNF (pour suivre les abonnements) crée une instance de la structure WNF_SUBSCRIPTION. Mais pour le mode utilisateur, certains champs ne seront pas remplis, par exemple Callback et Context, car pour le mode utilisateur, les adresses des gestionnaires sont stockées et traitées par ntdll.dll.



Structures de données WNF



D'un traducteur : voir la section suivante.



Utilitaires d'analyse WNF


D'un traducteur : ici, il convient de rappeler à nouveau que la présentation a été réalisée non seulement par Alex, mais aussi par Gabrielle Viala . En particulier, sa paternité appartient au module WnfCom décrit ci-dessous. De plus, Gabrielle a décrit les structures internes de WNF avec suffisamment de détails (voir l'illustration dans la section précédente). La plupart de ses diapositives, malheureusement, sont absentes dans le pdf de la présentation (indiquée comme l'original) ou indiquées exclusivement par des titres. Mais:



Et du traducteur : Si quelqu'un veut compléter la traduction actuelle avec le contenu des diapositives de Gabrielle ou étendre la traduction de la sténographie de n'importe quelle partie de la vidéo du discours - bienvenue. Pour la commodité d'ajouter / modifier de gros morceaux, je peux publier la source de traduction sur github (ou un autre serveur de contrôle de version).



Wnfcom


WnfCom est un module python ( code source github ) qui montre l'interopérabilité via WNF. Caractéristiques clés:


  • vous permet de lire / écrire des données à partir d'instances d'instance existantes
  • vous permet de créer des noms d'état temporaires (en tant que serveur )
  • vous permet d'obtenir une instance d'un objet côté client qui traitera les notifications concernant la modification d'une instance spécifique d'un nom

Exemple d'utilisation:


 >>> from wnfcomimport Wnfcom >>> wnfserver = Wnfcom() >>> wnfserver.CreateServer() [SERVER] StateNamecreated: 41c64e6da5559945 >>> wnfserver.Write(b"potatosoup") Encoded Name: 41c64e6da5559945, Clear Name: 6e99931 Version: 1, Permanent: No, Scope: Machine, Lifetime: Temporary, Unique: 56627 State update: 11 bytes written 

 >>> from wnfcomimport Wnfcom >>> wnfclient = Wnfcom() >>> wnfclient.SetStateName("41c64e6da5559945") >>> wnfclient.Listen() [CLIENT] Event registered: 440 [CLIENT] Timestamp: 0x1 Size: 0xb Data:00000000: 70 6F 74 61 74 6F 20 73 6F 75 70 potato soup 


Wnfdump


WnfDump est un utilitaire de ligne de commande écrit en C. Le fichier exécutable peut être trouvé à https://github.com/ionescu007/wnfun en sélectionnant le sous-répertoire de la profondeur de bits requise. L'utilitaire peut être utilisé pour rechercher des informations sur les noms d'état WNF:


  • -d ( D ump) Vide tous les noms d'état WNF à l'aide d'une énumération basée sur le registre. Il peut être complété par des options:
    • -v ( V erbose) Une sortie détaillée qui inclut un vidage hexadécimal des données d'état WNF;
    • -s ( S ecurity) Descripteurs de sécurité - Chaînes SDDL d'autorisations pour le nom d'état WNF.
  • -b ( B rute-force) Énumération directe des noms d'états WNF temporaires (voir ci-dessous)
  • -i (Informations) Affiche des informations sur un seul nom d'état WNF spécifié
  • -r (Lire) Lire les données du nom d'état WNF spécifié
  • -w ( W rite) Écrire des données dans le nom d'état WNF spécifié
  • -n ( N otification) Enregistrer un abonné de notification pour le nom d'état WNF spécifié (ci-après sera un cas d'utilisation plus spécifique avec Edge)


Surface d'attaque WNF


Cette section (plus précisément, ses sous-sections) discutera des attaques possibles et des données WNF sensibles intéressantes.



Divulgation de données privilégiée


En lisant les milliers de noms d'états WNF qui existent dans le système, plusieurs peuvent être notés, dont les données semblent très intéressantes. Parmi eux, certains dont les données sont étrangement similaires à des pointeurs ou à d'autres données privilégiées.


Après avoir joué sur plusieurs machines, dans certains cas, il était possible de trouver un groupe, une pile et d'autres informations privilégiées divulguées au-delà des limites de privilèges. Des rapports de bogues / vulnérabilités ont été soumis à MSRC en juillet, mais ont été corrigés en novembre (après la présentation). Par exemple: 4 kilo-octets de pile ont fui lors de l'événement WNF_AUDC *!


Les principaux problèmes sont les mêmes que ceux que nous avons vus dans les études précédentes de j00ro, taviso et autres. Certains noms d'état WNF contiennent des structures de données codées avec divers problèmes de remplissage et / ou d'alignement. Dans certains cas, des fuites de mémoire non initialisées.
Du traducteur : traduction de la partie introductive du document Detecting Kernel Memory Disclosure with x86 Emulation and Taint Tracking de Mateusz Jurczyk aka j00ro .



Découverte des noms d'état et des autorisations


La première approche a consisté à découvrir tous les noms d’états possibles pouvant être manipulés avec malveillance. Pour les noms connus, permanents et persistants, l'énumération est possible en énumérant les clés de registre. Ensuite, les valeurs trouvées peuvent être comparées à des identifiants conviviaux (il existe plusieurs endroits où vous pouvez les trouver :))


Ensuite, nous pouvons également regarder le descripteur de sécurité dans le registre (c'est la première chose dans le tampon de données). Le descripteur de sécurité n'est pas canonique: il n'a ni propriétaire ni groupe, il n'est donc pas techniquement valide. Mais il n'y a aucun problème à remplacer un faux propriétaire et un groupe pour corriger le descripteur de sécurité.



Détection des noms d'état temporaires et de leurs autorisations


Mais avec des noms temporaires, les astuces décrites ci-dessus ne fonctionneront pas: elles ne sont pas dans le registre. Et seul le noyau stocke des structures de données pour eux (! Wnf) en mémoire. Mais les noms temporaires ne sont en fait pas si difficiles à utiliser brutalement:


  • La version compte toujours 1
  • La durée de vie compte toujours WnfTemporaryStateName
  • Le drapeau permanent est toujours effacé (le nom d'état temporaire ne peut pas avoir de données permanentes)
  • La portée (portée) peut prendre l'une des 4 valeurs

Oui, mais le numéro de séquence restant est de 51 bits! En effet ... mais n'oubliez pas que les numéros de série augmentent de façon monotone. Et pour les noms temporaires, la séquence est réinitialisée à 0 à chaque démarrage. Classiquement, vous pouvez prendre une fenêtre d'un million de numéros de série: dans une boucle, vérifiez l'existence de chaque nom (à partir de 0) en appelant ZwQueryWnfStateNameInformation avec la classe d'informations demandée WnfInfoStateNameExist (étant donné que l'erreur d'accès indique également l'existence d'un nom). Si un autre million de noms n'existe pas, vous pouvez arrêter la recherche.


Les descripteurs de sécurité des noms temporaires (comme les autres données de noms temporaires) sont stockés dans le noyau. Par conséquent, la seule façon de les demander est l'extension! Wnf lors du débogage du mode noyau. Mais nous pouvons:


  • Faites une conclusion sur les autorisations de lecture lorsque vous essayez de lire des données.
  • Pour conclure que l'enregistrement est autorisé en tentant d'écrire des données. Mais il vaut la peine de considérer qu'une écriture réussie, même 0 octet, détruit les données que le véritable consommateur n'a pas encore réussi à obtenir. Et encore une fois, il y a une astuce: nous pouvons appliquer le tampon de changement approprié. Nous essayons d'écrire avec l'étiquette 0xFFFFFFFF: l'étiquette est vérifiée après la vérification d'accès, par conséquent, la valeur d'erreur entraîne une fuite de l'autorisation d'écriture.

Cela ne nous donne pas un descripteur de sécurité complet, mais en exécutant le code avec différents privilèges, nous pouvons avoir une idée des restrictions pour différents comptes système (IL faible / Utilisateur / Admin / SYSTÈME).



Inscription des abonnés


Dans la structure WNF_PROCESS_CONTEXT, l'un des champs est la tête de liste (LIST_ENTRY) de tous les abonnements de ce processus. Chaque abonnement est une instance distincte de WNF_SUBSCRIPTION.


Les abonnés en mode noyau appartiennent principalement au processus système. Nous pouvons utiliser la commande! List debugger pour vider les gestionnaires et leurs paramètres enregistrés dans le processus système WNF_SUBSCRIPTION. Il convient de noter que dans certains cas, un agrégateur d'événements (CEA.SYS) est utilisé, qui masque les adresses de rappel réelles dans sa structure de contexte.


Nous pouvons répéter cette approche pour les processus en mode utilisateur, mais l'adresse de rappel sera NULL, car ce sont des abonnés en mode utilisateur. Par conséquent, nous devons rejoindre l'espace utilisateur du processus, obtenir la table RtlpWnfProcessSubscriptions, puis vider la liste des instances WNF_USER_SUBSCRIPTION, dont chacune contient déjà l'adresse de rappel. Malheureusement, ce caractère est statique, ce qui signifie qu'il n'est pas en caractères ouverts, mais il peut être trouvé en le désassemblant. Et encore une fois, il convient de faire attention (par analogie avec le mode noyau CEA.SYS) que de nombreux gestionnaires en mode utilisateur utilisent l'agrégateur d'événements (EventAggregation.dll), qui stocke le rappel dans son contexte.



Noms d'état WNF intéressants et sensibles


Cette section fournit des exemples intéressants de la façon dont certains noms d'état WNF révèlent des informations système.



Détermination de l'état du système et du comportement de l'utilisateur à l'aide de WNF


Certains identifiants WNF peuvent être utilisés pour obtenir des informations sur l'état de la machine qui vous intéresse:


  • WNF_WIFI_CONNECTION_STATUS - État sans fil
  • WNF_BLTH_BLUETOOTH_STATUS - de même, mais pour Bluetooth (également WNF_TETH_TETHERING_STATE)
  • WNF_UBPM_POWER_SOURCE - affiche la source d'alimentation (batterie ou adaptateur secteur)
  • WNF_SEB_BATTERY_LEVEL - contient le niveau de la batterie
  • WNF_CELL_ * - sur Windows Phone contient des informations sur: réseau, numéro, puissance du signal, EDGE ou 3G, ...

WNF :


  • WNF_AUDC_CAPTURE/RENDER — ( PID), /
  • WNF_TKBN_TOUCH_EVENT — ,
  • WNF_SEB_USER_PRESENT/WNF_SEB_USER_PRESENCE_CHANGED — Windows


API


, API , API , , /. WNF . , , WNF .


: WNF_SHEL_(DESKTOP)_APPLICATION_(STARTED/TERMINATED) modern- ( , ) DCOM, Win32. — ShellExecute: Explorer, cmd.exe, ...


, WNF API , :


  • WNF_SHEL_LOCKSCREEN_ACTIVE —
  • WNF_EDGE_LAST_NAVIGATED_HOST — URL, ( ) Edge

: Edge



WNF


WNF, . : WNF_FSRL_OPLOCK_BREAK — , (/), PID' !


WNF , . : WNF_SHEL_DDC_(WNS/SMS)_COMMAND – 4 , .


, WNF, . : WNF_CERT_FLUSH_CACHE_TRIGGER ( ), WNF_BOOT_MEMORY_PARTITIONS_RESTORE, WNF_RTDS_RPC_INTERFACE_TRIGGER_CHANGED, ...



WNF


:


  • WriteProcessMemory —
  • ( ) —
  • (Atom) —
  • — , WM_COPYDATA DDE,
  • GUI — ( ) ,

WNF :


  • WNF, (, )
  • Rtl/ZwQueryWnfStateData WNF

, :


  • APC s
  • (Remote Threads)
  • (Changing Thread Context)
  • " window long " — , ,

WNF_USER_SUBSCRIPTION ( WNF_NAME_SUBSCRIPTION, RtlpWnfProcessSubscriptions). ( CFG ), ( 5 6 ).


, : , , , -.




WNF SEB_, ( S ystem E vents B roker). SystemEventsBrokerServer.dll SystemEventsBrokerClient.dll API . , SEB SEB, .


CEA.SYS EventAggregation.dll. " " (Event Aggregation Library), , : , , WNF , . WNF, . .




: .






, Windows Notification Facility Alex' Gabrielle. ( ) redp .



WNF ( ) wincheck . , Gabrielle Viala , redp, : http://redplait.blogspot.com/search/label/wnf .




PoC ( github ) explorer ( — notepad). modexp : Callback WNF_USER_SUBSCRIPTION. :


  • explorer.exe
  • WNF_USER_SUBSCRIPTION
  • RWX- , WriteProcessMemory (, VirtualAllocEx + WriteProcessMemory)
  • WNF_USER_SUBSCRIPTION ( WriteProcessMemory)
  • ntdll!NtUpdateWnfStateData(...) ,
  • Restaurer le gestionnaire d'origine dans WNF_USER_SUBSCRIPTION et libérer les ressources allouées

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


All Articles