Manually Enumerating Active Directory

Overview

Active Directory serves as the nucleus of the majority of corporate networks, and it’s no secret that effectively enumerating AD is a critical element in any engagement. Active Directory can provide a wealth of information, and it’s important to note as defenders and attackers that as long as you’re authenticated AD will tell you pretty much anything you’d like to know about what’s in the database (short of password information, obviously).

PowerShell, and perhaps more specifically PowerView, have become instrumental in the enumeration of Active Directory objects.

Now I am a massive fan of PowerView, however there may be situations where it is much more opsec friendly to use native commands to perform post-exploitation Active Directory enumeration. Some other considerations are :

  • Unmodified versions of well known scripts associated with “hacking” are likely to be blocked or flagged by AV.
  • Constrained Language Mode can prevent execution of scripts, or functions of those scripts.

With this in mind, I often find it beneficial to write small, tailor-made queries in an attempt to query Active Directory leveraging native .NET assemblies.

Prior to beginning, an exhaustive list as to Active Directory objects that can be queried can be found HERE.

A Simple Query

The following is a very simple query that will ask Active Directory to return all objects within Active Directory running any flavour of Windows Server 2016.

The Result

Let’s break it down :

  • [adsisearcher] – Calls the adsisearcher type accelerator, which is an alias for the system.directoryservices.directorysearcher .NET framework class.
  • (OperatingSystem=Windows Server 2016*) – This serves as the filter for our query. In this case we’re looking for all items that have the OperatingSystem property in Active Directory that starts with “Windows Server 2016”
  • FindAll() – Returns all objects matching the filter specified prior.

Expanding Upon The Query

The first query was a great starting point, but didn’t provide too much valuable information. Active Directory maintains a large list of properties for each object, and naturally, we’d like to see those properties. The updated query then becomes :

Note, the screenshot below has been truncated for the purposes of brevity.

Specifying a Search Base

If you happen to be conducting an engagement in large environments you are going to want to know how to specify the search base of where the query should start. In order to do this you’ll need to know a little something about LDAP Notation, however the learning curve for that is quite small. I’ve found it can be helpful to look at the Attribute Editor for a given object in Active Director, and look for the Distinguished Name attribute to understand the proper nomenclature.

Let’s say we wanted to search for all Server 2016 servers within the Domain Controllers Organizational Unit. Our query now changes a bit.

Let’s look at the new lines.

  • $enum.SearchRoot = [adsi]”LDAP://OU=Domain Controllers,dc=soslab,dc=local” – Now we’re introducing the [adsi] .NET framework class, which is an alias for system.directoryservices.directoryentry . We assign this to the variable $enum and specify the Distinguished Name for the Domain Controllers Organizational Unit, which is where you will usually find Domain Controllers within Active Directory.
  • $enum.FindAll() | ForEach { $_.Properties } | ft – This line takes all of the items returned, lists their properties and then formats them into a nice table.

Loading Specific Properties

Say you’re really only interested in a small group of properties for each given object in Active Directory. You could change your query to the following to ensure you only load properties of relevance to you.

As you can see, rather than displaying all properties for a given object in Active Directory, we’ve decided just to display only properties of interest by utilizing the PropertiesToLoad.Add variable property.

Other Useful Queries

Query Members of “Domain Admins” group

Query Users which are members of Protected Groups and not disabled

Members of groups protected by AdminSdHolder typically have elevated domain-level privileges.

Finding Users with Non-Null SPN’s

Finding AS REP Roastable Users

Users that do not require Kerberos Pre-Authentication.

Enumerating Domain Trusts

Note: Does not work with Constrained Language Mode. If you know of a way to do this natively please reach out to me.

Enumerating Forest Trusts

Note: Does not work with Constrained Language Mode. If you know of a way to do this natively please reach out to me.

Interesting Active Directory Attributes

adminCount – If the value is set to “1” the user is a has ever been a member of an adminSDHolder protected group. (Thanks to spotless for pointing out that this attribute remains set to 1, even if the user has been removed from a protected group).
badPwdCount – How many incorrect password attempts the user currently has. Can help in avoiding a lockout scenario. Note: This value is not replicated and may differ depending on which domain controller responds to your query.
comment – You never know what an administrator might put in here thinking that nobody but them will ever see it.
description – Same reasoning as comment.
lastLogon – Can provide insight into how often the account is utilized. Note: This value is not replicated and may differ depending on which domain controller responds to your query.
msDS-AllowedToDelegateTo – Useful for enumerating delegation rights of a particular user.
objectSid – The Security Identifier for an object
pwdLastSet – States when the password was last set for the user. Can be useful in password spray attacks when attempting Season + year (or variant) passwords. Is also useful for understanding if compromised credentials will be swapped out due to expiration of a password.
sIDHistory – May be useful for SID History Persistence
title – Help narrow in on targets by understanding their role in an organization
userAccountControl – Information about whether the user can change their password, whether the account is disabled, etc