Offensive WMI - Active Directory Enumeration (Part 5)
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
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
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
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
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
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
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}
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}
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}
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}
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
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
}
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! 🥂