Apr 01 2010

Surveiller l’application des liens de GPOs: GPLink

Nous pouvons en analysant le journal de sécurité de chaque DC surveiller les changements de liens d’objets de stratégie de groupe. L’attribut à auditer est GPLink, de cette manière nous pourrons savoir comment les GPOs sont appliquées sur notre domaine.

Pour pouvoir enregistrer les modifications dans le journal de sécurité de chaque DC vous devez opter pour des solutions payantes type Snare et/ou Kiwi, ou bien mettre en place un système maison pour effectuer de la surveillance de journaux d’événements sur plusieurs DCs en utilisant la redirection de ceux ci sous 2008, vous pouvez aussi voir si on peut surveiller ce journal de sécurité par un script, mais cela risque de trop solliciter votre DC.

Comme nous l’avons vu dans le post précédent l’attribut GPLink est audité par défaut. Nous allons voir la structure de cet attribut de chaque OU/Site/Domaine avec adsiedit.msc:

Il se présente de la forme [LDAP://CN={31F5F311-013D-11D2-125F-00D04F0E84F9},CN=Policies,CN=System,DC=ldap389,DC=info;0][LDAP://CN={31F5F311-0187-EFD2-345F-BED04F1284F9},CN=Policies,CN=System,DC=ldap389,DC=info;2], chaque GPO liée à l’objet est séparée des “[]”, chaque valeur possède deux parties: Le Distinguished Name de la GPO et comment celle ci est appliquée et liée à notre objet, se sont les chiffres de 0 à 3 indiqués après le “;”:

  • 0: GPO avec lien activé, pas de mode appliqué (enforced)
  • 1: GPO avec lien désactivé, pas de mode appliqué (enforced)
  • 2: GPO avec lien activé, mode appliqué (enforced)
  • 3: GPO avec lien désactivé, mode appliqué (enforced)

Vous pouvez lire toutes ces informations par script avec des requêtes LDAP comme montré sur cette page. Il nous faut maintenant lire dans le journal de sécurité de chaque DC les modifications de cet attribut GPLink:

Sous Windows 2003 vous n’êtes que notifié d’un changement de valeur de cet attribut, vous n’avez pas la valeur avant/après indiquée dans le journal de sécurité. L’ID 566 est généré dans le journal d’événements:

,

Nous n’avons donc d’indiqué que l’attribut GPLink a été modifié, le nom du compte ayant effectué la modification, sur quel DC, et le Distinguished Name de votre OU/Site/Domaine. A partir de cette dernière information, pour lire la nouvelle valeur il vous faut juste faire une requête LDAP sur le DC sur lequel la valeur a été modifiée. Pour lire l’ancienne valeur, il vous faut un site de LAG, ce type de DC à réplication retardée n’est pas supporté par Microsoft si l’on s’en sert dans le cadre d’un PRA, mais rien ne nous empêche de nous en servir afin de faire une requête LDAP qui nous donnera la valeur avant modification du GPLink. Restera ensuite à comparer la nouvelle valeur (prise sur le DC sur lequel la modification a eu lieu) à l’ancienne valeur (obtenue à partir du site de lag).

Sous Windows 2008 vous n’avez pas besoin de site de lag, nous pouvons directement à partir du journal de sécurité d’un DC avoir la valeur “avant” et “après” du GPLink. Pour cela il vous suffit d’activer l’audit AD sur vos DCs. Si nous filtrons le journal de sécurité sur l’ID 5136, nous pouvons avoir l’ancienne valeur (event de suppression de GPLink) et la nouvelle valeur (event de création de GPLink)

Si vous liez votre première GPO sur un objet alors la valeur avant sera nulle et la valeur après égale au nouveau GPLink. Inversement si vous supprimez le dernier lien sur un objet alors la valeur avant sera le GPLink et la valeur après sera nulle. Afin de gérer ce cas de figure sous 2003 c’est un peu plus complexe: Il faudra gérer les codes d’erreur de la requête LDAP, si vous créez le premier lien de GPO sur une OU, la requête LDAP pour obtenir la valeur de GPLink sur le DC sur lequel a été créé le lien fonctionnera, si vous interrogez la même valeur sur le DC de lag elle est à <not set>, donc la requête LDAP sortira en erreur, il faudra donc gérer ces différents cas de figure.

Vous devez donc selon la version d’OS de votre DC paramétrer l’agent SNARE pour enregistrer les événements 566 ou 5136 de votre journal de sécurité, vous pouvez bien entendu utiliser d’autres outils. Comme serveur collectant les informations nous utilisons Kiwi. A ce propos nous avons constaté que certaines valeurs ne remontaient pas avec un friendly name, c’était le cas pour le Distinguished Name de l’objet (OU,Site,domaine) sur lequel le GPLink était modifié, le nom remonté était son GUID au format chaine de caractère, afin de pouvoir retrouver l’objet utilisez cette fonction de conversion fournie par MS. On a noté d’ailleurs une erreur dans cette KB sur le nom de la fonction, l’avant dernière ligne est à remplacer par “ConvertStringGUIDToHexStringGUID = octetStr”.

Nous allons maintenant voir comment comparer les valeurs “avant/après” de GPLink afin de connaître les modifications de liens de GPOs. Comme déjà expliqué le lien d’une GPO sur une OU/Site/Domaine comporte deux parties, le DN de la GPO et une valeur indiquant l’état du lien. Voici donc les différents cas de figure possible.

  • GPLink avant [DNGPO1;0][DNGPO2;3] et GPLink après [DNGPO1;0][DNGPO2;3][DNGPO3;2] alors un nouveau lien activé et appliqué vient d’être créé.
  • GPLink avant [DNGPO1;0][DNGPO2;3] et GPLink après [DNGPO1;0] alors le lien de la GPO DNGPO2 vient d’être effacé sur notre OU/Site/Domaine.
  • GPLink avant [DNGPO1;0][DNGPO2;3] et GPLink après [DNGPO1;1][DNGPO2;3] alors le lien de la GPO DNGPO1 a été désactivé (mais pas supprimé).
  • GPLink avant [DNGPO1;0][DNGPO2;3] et GPLink après [DNGPO1;0][DNGPO2;2] alors le lien de la GPO DNGPO2 a été activé et est toujours appliqué.
  • etc…

Nous allons pour effectuer nos comparaisons utiliser les dictionnary objects. Nous aurons donc deux dictionnaires à comparer, un avec les GPLinks avant et l’autre après. Les clefs de chaque dictionnaire seront les Distinguished Name de GPOs et les valeurs les chiffres de 0 à 3 indiquant le statut du lien de GPO. Voici comment créer vos deux dictionnaires:

'After modification GPLink dicitonnary
Set GPLinkdict = CreateObject("Scripting.Dictionary")
'Before modification GPLink dicitonnary
Set GPLinkdictLAG = CreateObject("Scripting.Dictionary")
 
'strGPLinkAfter is the GPLink value after modification, you retrieved it with the event log
'under Windows 2008 and with an LDAP request on the where DC the modification occurred under Windows 2003
 
if instr(strGPLinkAfter,"]") <> 0 then
OUModifiedGPLINK = split(strGPLinkAfter,"]")
For i = UBound(OUModifiedGPLINK) -1 to LBound(OUModifiedGPLINK) Step -1
GPOLinkstatus = split(OUModifiedGPLINK(i),";")
GPLinkdict.add GPOLinkstatus(0),GPOLinkstatus(1)
Next
End if
 
 
'strGPLinkBefore is the GPLink value before modification, you retrieved it with the event log
'under Windows 2008 and with an LDAP request on the LAG DC under Windows 2003
 
 
if instr(strGPLinkBefore,"]") <> 0 then
OUModifiedGPLINKLAG = split(strGPLinkBefore,"]")
For j = UBound(OUModifiedGPLINKLAG)-1 to LBound(OUModifiedGPLINKLAG) Step -1
GPOLinkstatusLAG = split(OUModifiedGPLINKLAG(j),";")
GPLinkdictLAG.add GPOLinkstatusLAG(0),GPOLinkstatusLAG(1)
Next
End if

Pour télécharger l’exemple de code c’est ici:

Maintenant nous allons parcourir le dictionnaire GPLinkdict contenant les clefs et valeurs après modification et les comparer aux clefs et valeurs de l’autre dictionnaire. Si nous voyons qu’une des clefs parcourue n’existe pas dans le dictionnaire GPLinkdictLAG (avant modification) alors un lien de GPO vient d’être créé. Si pour une clef identique la valeur a changé alors l’état du lien de GPO à changé, si l’on passe de 0 ou 2 à 1 ou 3 alors la GPO a été désactivée. Avec ce script nous allons pouvoir avoir la création de nouveau lien de GPO et la modification d’état d’un lien:

 
'The DC where the modification was recorded, retrieved with eventlog
dcsource = "DCSOURCENAME"
 
'OU/Site/Domain name where GPO Link was modified, retrieved with eventlog
OUName = "OUName"
 
'User who made the modication, retrieved with eventlog
Username = "UsernName"
 
For Each oGPLinkdict in GPLinkdict
	If Not GPLinkdictLAG.Exists(oGPLinkdict) Then
 
	Set objGPOd = GetObject(split(replace(oGPLinkdict,"LDAP://","LDAP://"&dcsource&"/"),"[")(1))
	DNobjGPOd = objGPOd.Get("DisplayName")
	Msgbox Username&" created a link on this object: "& OUName &"  / GPOName: "&DNobjGPOd & "  / Link Value: "&GPLinkdict.Item(oGPLinkdict)
 
	Else 
		If GPLinkdictLAG.Item(oGPLinkdict) <> GPLinkdict.Item(oGPLinkdict) then
 
			if (GPLinkdictLAG.Item(oGPLinkdict) = 0 OR GPLinkdictLAG.Item(oGPLinkdict) = 2) AND (GPLinkdict.Item(oGPLinkdict) =1 OR GPLinkdict.Item(oGPLinkdict) = 3) then
 
			Set objGPOd = GetObject(split(replace(oGPLinkdict,"LDAP://","LDAP://"&dcsource&"/"),"[")(1))
			DNobjGPOd = objGPOd.Get("DisplayName")
			Msgbox Username&" disabled a link on this object: "& OUName &"  / GPOName: "&DNobjGPOd & "  / Link Value Before: "&GPLinkdictLAG.Item(oGPLinkdict)&"  / Link Value After: "&GPLinkdict.Item(oGPLinkdict)
 
			Else
 
			Set objGPOd = GetObject(split(replace(oGPLinkdict,"LDAP://","LDAP://"&dcsource&"/"),"[")(1))
			DNobjGPOd = objGPOd.Get("DisplayName")
			Msgbox Username&" enabled a link on this object: "& OUName &"  / GPOName: "&DNobjGPOd & "  / Link Value Before: "&GPLinkdictLAG.Item(oGPLinkdict)&"  / Link Value After: "&GPLinkdict.Item(oGPLinkdict)
 
			End if
 
 
		End if 
	End if
Next

Pour télécharger l’exemple de code c’est ici:

Maintenant il ne nous reste plus qu’à identifier les liens de GPOs supprimés, pour cela nous allons parcourir le dictionnaire GPLinkdictLAG qui contient les valeurs avant modification et voir si une clef n’existe pas dans le dictionnaire GPLinkdict, si c’est le cas alors un lien de GPO a été supprimé.

'Enter LAGDC if you have one, it will be usefull only in this case : if a Link is deleted and the GPO is deleted from the domain as well in the same time
'In that case we can not retrieve GPO Display Name if we do not have lag site.
'If no lag site then LAGDC = ""
LAGDC = "LAGDCNAME"
 
For Each oGPLinkdictLAG in GPLinkdictLAG
  If Not GPLinkdict.Exists(oGPLinkdictLAG) Then
	err.clear
	Set objGPOd = GetObject(split(replace(oGPLinkdictLAG,"LDAP://","LDAP://"&dcsource&"/"),"[")(1))
	if err.number <> 0 then
		if LAGDC <> "" then
		Set objGPOd = GetObject(split(replace(oGPLinkdict,"LDAP://","LDAP://"&dcsource&"/"),"[")(1))
		DNobjGPOd = objGPOd.Get("DisplayName")
		Else
		DNobjGPOd = split(oGPLinkdictLAG,"[")(1)
	Else 
	DNobjGPOd = objGPOd.Get("DisplayName")
	End if
	Msgbox Username&" deleted a link on this object: "& OUName &"  / GPOName: "&DNobjGPOd & "  / Link Value was: "&GPLinkdictLAG.Item(oGPLinkdict)
  End if
Next

Pour télécharger l’exemple de code c’est ici:

Avec ce post et le précédent vous pouvez maintenant construire un outil de surveillance des modifications de GPOs sur votre domaine. Vous pouvez, en plus de générer des rapports, lancer une sauvegarde de la GPO modifié via les scripts fournis avec GPMC.

This post is also available in: Anglais

No Comments

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

*

WordPress Themes

Blossom Icon Set

Software Top Blogs