This blog is the fifth installation of the “Offensive WMI” series that I’ve been writing on, and this post will cover Active Directory enumeration.

Active Directory (AD) is Microsoft’s implementation of a directory and IAM service for Windows domain networks – which enables admins to manage permissions and access to resources. Anything used for managing multiple resources is handy for administrators, however, the same is also useful for evil-doers in gathering information and lateral movement.

It is important to note that there are several ways to interact with any AD environment, but for this blog we’ll stick with pure WMI.

AD and WMI

WMI has a provider called root\directory\ldap which can be used for interaction with the active directory environment. If we try to list the classes under that provider, we can see that there are a lot of classes that come with either of these prefixes, viz. ads_ or ds_. The classes starting with ads_ are abstract, and the ones with ds_ are dynamic (the only ones useful to us since they allow retrieval of instances).

We’ll quickly list out the available classes using the following:

Get-WmiObject -Namespace root\directory\ldap -Class ds_* -List
classes

Let’s get started!

Finding the domain name

After gaining access to a box on a domain, one of the first steps in basic reconnaissance would be to try to figure out the domain name on which we are on:

Get-WmiObject -Namespace root\directory\ldap -Class ds_domain | select ds_dc,
    ds_distinguishedname, pscomputername
basic

In the above command, we fetched the domain name, the FQDN, and the name of the computer we’re currently on.

Getting the domain policy

Now, let’s try to look at the current domain policy. The same class ds_domain can help us out with additional information about the policy:

Get-WmiObject -Namespace root\directory\ldap -Class ds_domain | select ds_lockoutduration,
    ds_lockoutobservationwindow, ds_lockoutthreshold, ds_maxpwdage,
    ds_minpwdage, ds_minpwdlength, ds_pwdhistorylength, ds_pwdproperties
policy

NOTE: As you might have noticed, the output of the above command includes a lot of negative values. All the timestamps in the above output are stored as negative “filetimes”, i.e. represented as negative integers of 100 nanosecond timeslices. For example, a time period of 20 minutes would be represented as -12000000000.

We can fairly derive that both the lockout duration and lockout observation window is set at 30 mins, while the password expiry duration is infinite, i.e. never expires. The minimum password length is 7, while, the password history length is 24.

Finding the domain controller

Cool, let us try to find out the domain controller. Before that, we should keep in mind that in WMI, different UAC (User Account Control) values are defined via constants. The table below presents a mapping of the UAC values to user types:

User Type Hex Value Constants
Normal User 0x200 512
Workstation/Server 0x1000 4096
Domain Controller 0x82000 532480

Now that we know the values, we can easily query the ds_computer class with the following command. Note the usage of the where Powershell utility to find the domain controller via the piped variable.

Get-WmiObject -Namespace root\directory\ldap -Class ds_computer | where {
    $_.ds_useraccountcontrol -match 532480
} | select ds_cn, ds_dnshostname, ds_operatingsystem, ds_lastlogon, ds_pwdlastset
policy

The output gives us a lot of data about the domain controller (DC), including its name, DNS hostname, OS, last logon timestamp and even the DC’s last password update timestamp. All time-based properties in the output are filetimes. You can use this neat converter to convert filetimes into human-readable datetime format.

That’s a lot of information that we can enumerate with just an ordinary domain user account without admin privileges!

Searching user accounts

How about user accounts in the domain as well as other domains in trust relationship with the current domain? It is as simple as querying users using the Win32_UserAccount class:

Get-WmiObject -Class win32_useraccount | select name, domain, accounttype
policy

As you might have already guessed, the AccountType property is a constant value depicting the user type. The table below defines the type of accounts with their constant values:

Account Type Identifier Constant
Temporary Duplicate Account UF_TEMP_DUPLICATE_ACCOUNT 256
Normal Account UF_NORMAL_ACCOUNT 512
Interdomain Trust Account UF_INTERDOMAIN_TRUST_ACCOUNT 2048
Workstation Trust Account UF_WORKSTATION_TRUST_ACCOUNT 4096
Server Trust Account UF_SERVER_TRUST_ACCOUNT 8192

In case we want to filter out just the user accounts for a single domain, we can use the -Filter switch of the cmdlet like this:

Get-WmiObject -Class win32_useraccount -Filter 'domain="infected"' | select caption
policy

Enumerating currently logged-on users

The Win32_LoggedOnUser classes provide information about the logged-on users in that system as well as the domain. To filter out logged-on local users, we can use the following command:

Get-WmiObject -Class win32_loggedonuser | where {
    $_ -match 'infected'
} | foreach {[wmi]$_.antecedent}
policy

Fetching groups

Fetching the groups for a domain is simple as querying the Win32_GroupInDomain class. We’ll use a bit of Powershell magic to give us a cleaner output.

Get-WmiObject -Class win32_groupindomain | foreach {[wmi]$_.partcomponent}
policy

The same could be done with the Win32_Group class, but the output would include the local groups as well.

Figuring out group memberships

Group membership data is provided by the Win32_GroupUser class. The output obtained includes all membership information for the current domain, the trusted domains as well as the trusted forest with bi-directional trust. For our example, let’s say we want to fetch the accounts from the “Domain Admins” group:

Get-WmiObject -Class win32_groupuser | where {
    $_.groupcomponent -match 'domain admins'
} | foreach {[wmi]$_.partcomponent}
policy

This is equally applicable for the reverse use case. If we want to enumerate the groups that a particular user is in (Administrator in this case), then we can do something like:

Get-WmiObject -Class win32_groupuser | where {
    $_.partcomponent -match 'Administrator'
} | foreach {[wmi]$_.groupcomponent}
policy

NOTE: Since we’re using Powershell’s Where-Object cmdlet, we can use the -match argument efficiently to find any substring within a property. This helps in exploring a lot of possibilities in creatively filtering out things on specific criteria.

Finding machines in the domain

To get a list of all unique machines in our active directory environment, we can do something like:

Get-WmiObject -Namespace root\directory\ldap -Class ds_computer | select ds_cn
policy

Enumerating admin privileges across AD

An important thing to remember is, by default WMI provides remote access only to local administrators. Already noticed the catch there? If we can run WMI commands against a remote computer, it means we have local administrator access on that computer. We can use this information to write a very simple script that can enumerate local administrator privileges on remote computers.

We can chain the output of the last command with a bit of Powershell to achieve the above:

$pcs = Get-WmiObject -Namespace root\directory\ldap -Class ds_computer | select -ExpandProperty ds_cn
foreach ($pc in $pcs) {
    (Get-WmiObject -Class win32_computersystem -ComputerName $pc -ErrorAction silentlycontinue).name
}
policy

Now we have all the boxes on which our user has admin privileges. With this information, things now become super easy for us in lateral movement.

Conclusion

We saw how easily we could enumerate a lot of stuff from an Active Directory environment using a few WMI classes and some Powershell magic. Once again, this blog is not comprehensive and there are a lot of possibilities when it comes to reconnaissance. In our next blog, we’ll focus on lateral movement via WMI.

That’s it for now folks. I hope you enjoyed reading the blog. Cheers! 🥂