Tracking group modifications
Monitoring a Windows environment is a crucial security task for every network administrator. One vital monitoring task is looking for modification of sensitive information in Active Directory (AD). In this first article of a two-part series that will demonstrate how to leverage Windows Management Instrumentation (WMI) monitoring capabilities to track modifications made in AD, I explain how to track AD group modifications.
The AD WMI Providers
Windows 2000 Server and later versions include three new AD WMI providers: Microsoft|DSLDAPClassProvider|V1.0, Microsoft|DSLDAPInstanceProvider|V1.0, and Microsoft|DSLDAPClassAssociationsProvider|V1.0. These providers give WMI consumers (i.e., any application that uses WMI management information) access to AD. WMI consumers include Windows Script Host (WSH), Windows .NET Framework applications, enterprise-management software, or any other COM-compliant application. You'll find the three WMI providers in the root\directory\LDAP Common Information Model (CIM) repository namespace.
An understanding of the AD schema is a great asset when working with the AD WMI providers because WMI reflects the AD schema's logical structure. For readers with little or no AD schema experience, I recommend you learn more about the subject by reading "Diving into the AD Schema," September 2001, InstantDoc ID 21839.
The first of the three new WMI providers, Microsoft|DSLDAPClassProvider|V1.0, maps the AD classes defined in the AD schema to corresponding WMI classes. This provider is called a class provider because it exposes only classes to WMI. The second provider, Microsoft|DSLDAPInstanceProvider|V1.0, is responsible for mapping instances of AD objects. An instance is the actual representation of an object created with the definition that the class provides. For example, when you create a user in AD, Microsoft|DSLDAPInstanceProvider|V1.0 maps the user instance to a corresponding WMI user instance. The role between these two WMI providers is clearly distributed: One provider maps the AD classes to WMI classes, and the other provider maps AD instances to WMI instances. The class mapping is dynamic, which means that each time a WMI consumer accesses the root\directory\LDAP namespace, the class provider dynamically recreates the set of exposed classes within the namespace. Therefore, if you extend the AD schema, the resulting set of new classes will be automatically mapped in the root\directory\LDAP namespace.
The third provider—Microsoft|DSLDAPClassAssociationsProvider|V1.0—maps the relationships that exist between AD containers (e.g., domain object container, organizational unit—OU—object container) and the objects that these containers contain (e.g., OU; computer, user, or group objects). Just as the instance provider is related to the class provider, the association provider is related to the instance and class providers by exposing the relationships between instances of objects. All three providers act as an interface between the WMI standards and AD standards. Now that you know about the roles of each provider, let's examine the implementation of WMI's representation of AD.
Understanding the WMI AD Representation
When performing the AD-to-WMI mapping, which mirrors classes and instances from AD into the root\directory\LDAP CIM repository namespace, the AD providers follow naming rules to preserve the relationships that exist between the AD classes and instances. Let's look at an example for the AD User class. As defined in the AD schema, the AD User class is created from a class hierarchy starting from a root class called Top, as Web Figure 1 (http://www.winnetmag.com/windowsscripting, InstantDoc ID 41835) shows. To obtain the User class, the AD schema defines several subclasses. This subclass creation is called a derivation of classes, in which the parent class is called a superclass. First, the Person class is derived from the Top (parent) class. Next, the organizationalPerson class is derived from the Person class, then the User class is derived from the organizationalPerson class. Each subclass inherits the set of AD attributes from the superclass.
In AD, the User class is defined as a structural class, which allows the creation of user instances from it. However, the Top, Person, and organizationalPerson classes are abstract classes; they're used as parent templates to create their respective subclasses, but you can't create instances of an abstract class. As I mentioned earlier, AD classes are mapped to their equivalent WMI classes in the root\directory\LDAP namespace. In the case of an abstract class, the WMI equivalent abstract class always uses the Lightweight Directory Access Protocol (LDAP) display name of the AD class with a ds_ prefix. For example, the AD organizationalPerson class has a corresponding ds_organizationalPerson WMI class. Because this AD class is an abstract class, the WMI equivalent class is also an abstract class for which the abstract qualifier is set. A qualifier is an attribute of the WMI class definition. You can see qualifiers by using WMI CIM Studio, which you can download from http://download.microsoft.com/download/.netstandardserver/install/v1.1/nt5xp/en-us/wmitools.exe. Launch the program, select the class, right-click in the window showing the class properties, and select Object Qualifiers.
When the AD class is a structural class (e.g., the User class), the class maps to two WMI classes:
- One class prefixed with ads_ and implemented as a WMI abstract class (abstract qualifier set). For example, the User class will map to an ads_user WMI abstract class.
- One class prefixed with ds_ and implemented as a WMI dynamic instance class. For example, the User class maps to a ds_user dynamic instance class. A dymanic instance class has the provider qualifier, which specifies the provider that supports the class (in this case, Microsoft|DSLDAPInstanceProvider|V1.0).
Figure 1 shows the qualifiers for the ads_user class. You'll notice the presence of other qualifiers representing AD information defined in the schema and used to create the user class definition (e.g., governsID, lDAPDisplayName, objectClassCategory). In a similar fashion, the AD syntax is mapped to the WMI syntax with the help of qualifiers. Web Table 1 (http://www.winnetmag.com/windowsscripting, InstantDoc ID 41835) shows the syntax mapping and how AD values are converted to WMI-suitable values when necessary. The end result is that WMI exposes AD classes as WMI classes, as Web Figure 2 shows, and AD instances as WMI instances, with the correct syntax and value conversions. (There's one exception to the naming convention for the RootDSE Active Directory object. The RootDSE LDAP object available from any AD instance is represented by the RootDSE WMI class, which is an exact match between the names.)
When you create an AD user object, it's always created in a container. The default container for user objects is the Users container, but you can use any other supported container, such as an OU or a domain. In the AD schema, the containers that can hold user objects are defined with the possSuperiors and systemPossSuperiors attributes of the AD User class definition. These attributes reference the AD class (using their LDAP display names) representing the supported containers. For example, with the User class, you can find the domainDNS class for the Domain container and the builtinDomain class for the Builtin container in the systemPossSuperiors attribute. You can see that a relationship exists between the User class and the supported AD container class definition, but because the CIM repository doesn't have containers, WMI represents this relationship by implementing several associations. An association is a link that exists between two or more classes, which is realized by the creation of an Association class instance. The WMI AD providers implement this association with the ds_LDAP_Class_Containment association class.
After user objects are instantiated, their instances are contained in an existing container. The same kind of relationship exists between the instances and their containers but at the instance level instead of the class level. WMI represents this relationship at the instance level with the ds_LDAP_Instance_Containment association class.
Every WMI instance representing an AD object uses the object's Active Directory Service Interfaces (ADSI) path. The ADSI path is represented in the WMI class definition with the ADSIPath WMI property. For example, the ADSIPath property for a user object called "LISSOIR Alain" and located in the Users container of an AD domain called LissWare.Net will be LDAP://CN=LISSOIR Alain,CN=Users,DC=LissWare,DC=Net.
You can apply all this information about the User class and its WMI equivalents to any other class defined in the AD schema; the logic is always the same. Now that you understand how classes and objects are mapped within WMI, let's look at how to use WMI to leverage AD information to perform AD data monitoring.
Monitoring AD Group Memberships
Everyone knows the importance of AD group memberships. From a security point of view, some groups are more sensitive than others. For example, administrators don't want to see just anyone added to the Enterprise Admins group. By using the WMI AD provider and a WMI Query Language (WQL) event query, you can receive a WMI event notification when a modification is made to a group such as the Enterprise Admins group. You can monitor any AD object; you just need to select the WMI class corresponding to the AD class of the object you want to monitor, which is why understanding the mapping that exists between AD and WMI is so important.
The following code illustrates a simple WQL event query that you can use to monitor the Enterprise Admins group in AD:
Where TargetInstance ISA
This WQL event query requests a notification within 10 seconds for any modifications made to an instance of the Enterprise Admins group. I used this query in a script I created to monitor Windows group modifications, which Listing 1 shows. You can download the script and the helper functions it uses from the Windows Scripting Solutions Web site. Go to http://www.winnetmag.com/windowsscripting, enter InstantDoc ID 41835 in the InstantDoc ID box, then click Download the Code. The script uses this WQL event query with command-line parameters to specify the groups for which you want to monitor modifications. When a WMI notification is received, the script sends an HTML email alert. The sample script is an immediate application of the WMI AD providers and the WMI monitoring capabilities. You must run the script in the Administrator security context, with the groups you want to monitor listed on the command line separated by a space. The command's syntax is
The GroupName1 to GroupNameN values in the command-line definition represent the list of groups to monitor. The /Machine switch represents the name of the domain controller (DC) to which WMI should connect (the default is localhost). The /User switch specifies the username to use for the WMI connection, and the /Password switch specifies its associated password. For example, to monitor the Enterprise Admins and Domain Admins groups for modifications from a DC named ServerA.LissWare.Net, you'd use the command
Admins" "Domain Admins"
You can use any server as long as the selected server is part of an AD forest. Therefore, if you select a DC, the detection of a group modification will occur either when the modification is made on that server or when a modification made on any other DC that's part of the selected server domain is replicated to the selected server. To determine which DC is the origin of the group change, you need to analyze AD metadata replication. Unfortunately, you can't use WMI to access this information. Some tools, such as repadmin.exe (included in the Windows Support Tools) can display the origin of an AD object modification. However, the AD metadata auditing process is outside the scope of this article. Instead, let's look at leveraging the WMI interface in a sample script.
Scripting Group Monitoring
The script to monitor Windows group modifications starts with command-line parameter definitions, as the code at callout A in Listing 1 shows. The script then includes a series of helper functions to handle some specific tasks during the script's execution. The helper functions, which callout B references, are
- the GenerateHTML() function, which generates an HTML representation of the information contained in a WMI instance
- the PauseScript() function, which executes a script pause by displaying a pop-up message on the screen
- the SendMessage() function, which uses Collaboration Data Objects (CDO) statements to create and send an SMTP message to a specific mailbox
- the TinyErrorHandler() function, which handles script execution errors
Next, the script creates a series of WMI objects, as the code at callout C shows. These objects establish the WMI connection to the root\directory\LDAP namespace and submit the WQL event query. By default, the script creates an objWMIdateTime object from the SWbemDateTime WMI COM object. This COM object, which is available only under Windows Server 2003 and Windows XP, converts the Distributed Management Task Force (DMTF) datetime format to a readable string. (More information about the DMTF datetime format is available at http://msdn.microsoft.com/library/en-us/wmisdk/wmi/date_and_time_format.asp.) If you must run the code under Win2K, you must change the line
.SWbemDateTime" id="objWMIDateTime" />
SWbemDateTime.wsc is a Windows Script Component (WSC) that emulates the WMI SWbemDateTime object under Win2K to provide a backward- compatibility path for platforms released before XP. You can get more information about the WMI SWbemDateTime object at http://msdn.microsoft.com/library/en-us/wmisdk/wmi/swbemdatetime.asp. For more information about the WSC scripts, you can read the article at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnclinic/html/scripting091399.asp.
The code at callout D defines a series of constants, including the default values for the computer name, the WMI namespace, and the WQL event query. Next, the script defines constants related to the SMTP parameters. You'll need to change the SMTP constant values to match your current environment to ensure that you receive email notifications from the script.
The implementation of the script's logic begins at callout E. The script parses the command-line parameters defined in callout A. The script uses the WSH 5.6 XML command-line parsing technique explained in "Secure Script Execution with WSH 5.6," August 2002, InstantDoc ID 25644. During the command-line parsing, the list of groups specified on the command line is stored in the strGroupName array.
Next, the script establishes the WMI connection, as the code at callout F shows. The WQL query stub defined in the cWMIQuery constant at callout D is customized to include the group names specified in the command line. The script performs the WQL event query modification in a loop, which the code at callout G shows. For the two sample groups specified in the command-line sample, the resulting WQL event query will be
ModificationEvent Within 10
Where TargetInstance ISA 'ds_group' And
(TargetInstance.ds_name='Enterprise Admins' Or
Next, the script submits the WQL query for asynchronous notifications. Submitting a WQL event query for asynchronous notification issues a request for WMI to transmit events matching the event query to a custom subroutine. The code at callout F defines this subroutine, which begins with the prefix "SINK_" followed by the name of the SWbemSink object that supports the event. In this example, the subroutine handling the WMI events is called SINK_OnObjectReady(). You can visit http://msdn.microsoft.com/library/en-us/wmisdk/wmi/swbemsink.asp to read more information about the SWbemSink object and the OnObjectReady event. The script enters an idle state and waits for events matching the submitted WQL event query, as the code at callout G shows.
When a modification is made to one of the specified groups, the script invokes the event sink routine, which the code at callout H shows, and sends an email alert by using the GenerateHTML() and SendMessage() functions, which the code at callout I shows. The first parameter of the GenerateHTML() function contains the PreviousInstance object, which is the WMI instance containing the WMI AD group instance before the modification occurred. The second parameter of the GenerateHTML() function contains the TargetInstance object, which is the WMI AD instance after the modification occurred. Both WMI AD instances are formatted in HTML and stored in a MIME HTML email message. The GenerateHTML() function will highlight in the email message the difference between the original instance and the modified one to show which attribute was modified. Note that under Windows 2003 and XP, you can get a WMI instance representation in XML. By using Extensible Style Language (XSL), you can get an HTML representation similar to the one returned by the GenerateHTML() function. However, to make sure this script works under Win2K, which doesn't support the XML functionality, I used a generic GenerateHTML() function to ensure the script's backward-compatibility.
Maintain a Healthy Enterprise
AD contains information crucial to the security and health of an enterprise. Monitoring sensitive groups is just one example of what you can track in AD. Because the AD providers can retrieve and monitor any AD object, class, or setting, you can monitor any kind of AD modification. In the next article in this series, I'll show you how to monitor the five Flexible Single-Master Operation (FSMO) roles by adapting some of the WMI tricks I discuss in this article.