Skip to content

Lab 4.1: Enumerating Active Directory with Different Tools

Objectives

  • Learning to enumerate Active Directory with a variety of tools
  • Interpret output of enumeration commands and form conclusions on observed results

TTPs Emulated in this Lab

Preparation

Preparation Steps

For this lab, we will use the student.draconem.io machine that is located in the user subnet of draconem.io, this machine can be accessed through guacamole. Go ahead and log into the machine with the following credentials:

If you need to copy paste commands, you can do so by opening the guacamole menu. On a Windows device, the Guacamole menu is displayed by pressing Ctrl + Alt + Shift. On a Mac, the Guacamole menu is displayed by pressing Ctrl ^ + Command ⌘ + Shift.

A new guacamole menu will appear in which you'll be able to paste your text. afterwards you'll be able to paste the text of the guacamole menu in your guacamole session, just like any normal paste.

Attention

In a real red team operation, it will be unlikely you are just going to be able to "drop in" through RDP, especially without proxying your RDP connection over some sort of command-and-control channel. The only reason you are allowed to do this is so you can get acquainted with all the tooling that is available, do not expect that this will be the case in real life operations!

On your own

Enumerate the following items in the domain:
1. Users
2. Computers
3. Domain trusts
4. Password policy
5. Fine grained password policy
6. Local Administrator Password Solution
7. Group managed Service Accounts
8. Identify Ip addresses and/or computernames without using nslookup

Walkthrough

In order to make sure you choose the right tactic and the right targets to achieve your objectives, enumeration is required. Throughout the course we have already discussed how to do host based enumeration, in this lab however, we are going to enumerate everything related to Active Directory.

During the lecture, we have discussed various tools and have given you various nudges to what could potentially be interesting for you to enumerate. However, do not let this be a constraint, in this lab you will follow a pre defined path to enumeration, but feel free to explore the environment any way you see fit.

It is in your best interest to perform enumeration with a multitude of tooling and using any command you deem interesting. Do not be afraid to make errors in this class, this is what the lab is for! It is much better to try new things in a controlled environment than in a real red team operation. On that note, let us jump into the lab!

User Enumeration

Probably the number one most important enumeration step you can do is start looking for valid usernames. This will let you get a feel for the Active Directory, understand their naming convention and potentially already identify some key targets that will aid you in your conquest to reach your objectives.

As mentioned in the lecture, keep a close eye on the description field of users, as they can contain valuable intelligence. Additionally, keep an eye open for svc_xxx accounts or xxx_svc accounts, those typically are service accounts that could be useful later when we would want to kerberoast some of these. In the C:\Tools folder you will find all of your favorite tools already available for you, no need to download anything!

Computer Enumeration

Much like user accounts, computer accounts provide a red team operator vital information about the architecture of the environment you are operating under. Take a close look at the naming conventions, most organizations have a policy when naming computer objects. Figuring out the naming convention will help you identify potentially interesting servers, as well as increase your opsec in the event you need to create a new computer object in the AD.

Domain Trust Enumeration

Chances are that your target environment has multiple domains that are interconnected with each other. This interconnectivity is provided by setting up domain trust(s). As a red team operator it is important to know which trusts are present in the environment, as there is a chance you will have to traverse one or several trust(s) to achieve the objective.

Password Policy Enumeration

The password policy is not really that important in a full fleged red team operation, as password spraying in a red team operation is generally not the best idea because it is extremely noisy. However, it can still be useful information for kerberoasting or as-rep roasting or if you are really stuck... yes, password spraying.

Fine-Grained Password Policy Enumeration

Fine-grained password policies are policies that are in place to serve as an additional layer of security. These password policies are applied to the user(s) and/or group(s) object(s) in Active Directory. One object can have multiple Fine-grained policies, in which case the one with the lowest precedence counter "wins". These policies are not often encountered (yet) based on our real world experience, but when they are, they provide insight in what probably are sensitive and privileged accounts. Let's take a look at how we can enumerate this.

Attention

This requires read rights on the Password Setting Container object, which is only granted to domain/enterprise administrators by default.

LAPS Enumeration

Local Administrator Password Solution is a Microsoft solution to help prevent local administrator password reuse, which is a common problem in large organizations. By pushing LAPS using a GPO, it is possible to automatically manage local administrator passwords per asset, including automatic password rotation as well. This makes it harder for an adversary to compromise multiple servers, as all local admin passwords are now random and quite secure.

As we mentioned in the lecture however, this does require a change in the basic Active Directory schema. It introduces a new attribute to the computer object under the form of the ms-Mcs-AdmPwd property. This property stores the local admin password in plain-text and is secured by a DACL. In other words, if you can compromise a user that has read privileges on the aforementioned property, you can extract the local administrator password in plain-text, effectively compromising the asset. It should come as no surprise that identifying where LAPS is deployed is beneficial for a red teamer, as we can effectively rule out password reuse on those assets and start hunting for users that are privileged enough to read LAPS passwords. Although unprivileged users cannot query the ms-Mcs-AdmPwd property, they can query the ms-Mcs-AdmPwdExpirationTime property, which will show us which computers have LAPS enabled. There is also another enumeration approach, as LAPS gets deployed using GPO, we can enumerate which computers have LAPS enabled through GPO enumeration.

gMSA Enumeration

Group managed service accounts are very similar to LAPS, but instead of local admin passwords, they are passwords for service accounts. This ensures that service accounts have strong and regularly rotated passwords, which makes it impossible for them to get kerberoasted. As a red teamer, it pays off to learn about these types of accounts as they, much like LAPS, have accounts configured that are able to retrieve the password.

DNS enumeration

When a server is responsible for DNS and there is no external appliance often reffered to as an IPAM (IP Address Management) solution, it is possible to perform DNS enumeration both reverse and forward lookups, from LDAP.

RSAT

Using RSAT for User Enumeration

1. Probably the easiest and most straight forward way of enumerating Active Directory is to use the Remote Server Administration Tool. Unfortunately, the AD RSAT modules are usually not installed by default on a workstation or server other than the Domain Controller. When this is the case, no need to panic, we can install it ourselves using PowerShell with the following command:

Add-WindowsCapability –online –Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0

If you are interested what other RSAT modules can be installed, we can figure that out with PowerShell too!

Get-WindowsCapability -Name RSAT* -Online | Select-Object -Property DisplayName, State

2. For our lab environment, we went ahead and already preinstalled the necessary RSAT tooling for you. To launch RSAT, go to the windows button on the left hand side to open the search menu and type in mmc.exe. Alternatively you could also use the Windows key + R shortcut to open the run prompt, in this prompt you can type mmc to achieve the same results:

opening the MMC snapin

3. Once the MMC window is open, go to file -> Add/Remove Snap-in...

add new snapin

4. We can now add the Active Directory Users and Computers snap-in to our view.

ad users and computers snapin

5. Now that we added the snap-in to our view, we can start using it to enumerate Active Directory, for example taking a peek at the IT Organizational Unit. Go ahead and browse through some of the units, maybe you can find some interesting descriptions...
enumerating users with RSAT

Attention

During a real life engagement, chances are slim that you will be able to use the GUI component of the RSAT toolkit, nevertheless it is still a useful piece of software to keep in the back of your mind.

Using RSAT for Computer Enumeration

Computer enumeration in RSAT is not much different than user enumeration in RSAT is, as a reminder:

6. To launch RSAT, go to the windows button on the left hand side to open the search menu and type in mmc.exe. Alternatively you could also use the Windows key + R shortcut to open the run prompt, in this prompt you can type mmc to achieve the same results:

opening the MMC snapin

7. Once the MMC window is open, go to file -> Add/Remove Snap-in...

add new snapin

8. We can now add the Active Directory Users and Computers snap-in to our view.

ad users and computers snapin

Now that we added the snap-in to our view, we can start using it to enumerate Active Directory.

computer enum in RSAT

Using RSAT for Password Policy Enumeration

9. This is a nice time to explore a little bit, try to identify the password policy using the mmc snap-in manager. As a red team operator, you will on occassions be forced to use tooling you are not very familiar with and will have to think outside the box. If you need hints, feel free to use them.

Hint 1

The answer cannot be found in a snap-in that we have already used so far, take a look at other available snap-ins...

Hint 2

The snap-in you are looking for will allow you to view LDAP queries and is something we have so far done using PowerShell ...

Answer

In order to figure out the password policy from mmc, we will need to utilize the ADSI Edit snap-in. Once we have this snap-in opened, we can view properties of AD objects. The object we would like to inspect is the default naming context. In the properties window, we will find the information we are looking for: lockoutwindow, password length, treshold, ...

rsat pwpol

rsat pwpol2

Using RSAT for Fine-Grained Password Policy Enumeration

10. To enumerate fine-grained password policies in RSAT, we will need the ADSI Edit snap in once again:

fine grained enum using RSAT

fine grained enum using RSAT2

Using RSAT for LAPS Enumeration

11. We can use the GPO Management snap in to show us a visual representation of all GPOs that are in use hoping to find any reference to LAPS deployment. Keep in mind that a large organization likely has a lot of GPOs applied to their environment and it will not always be very clear which GPOs will be worth investigating manually. For this reason, RSAT might not be your best option this time.

LAPS enum using RSAT

12. We can also use the ADSI Edit snap-in to inspect the ms-Mcs-AdmPwdExpirationTime property, however, much like the aformentioned method, a large environment will have hundreds or even thousands of computer objects. Querying these by hand is very time consuming and not really worth the time investment considering there are several automation methods available.

LAPS enum using RSAT2

Using RSAT for gMSA Enumeration

13. Any group managed service account will be placed in the Managed Service Accounts container, which makes them easy to query:

gmsa enum using RSAT

Using RSAT for DNS enumeration

14. With RSAT, we can use the DNS snap in to view the DNS records of the domain:

Attention

Only domain administrators are able to enumerate DNS using this method. The screenshot below serves as an illustration of what this looks like. You will NOT be able to reproduce this as the student user!

dns enum using RSAT

AD Module

Using the AD Module for User Enumeration

The next enumeration method we are going to use will take advantage of the Microsoft signed PowerShell module. An obvious advantage of this method is that it has legitimate use cases and is backed by Microsoft. A downside of this method is that it uses PowerShell and therefore generates quite a bit of logs, be aware of this when you decide to utilize this method for enumeration on engagements.

For lab facilitation purposes, we went ahead and already pre-installed the AD module for you (it comes bundled with RSAT). In case you are in a real operation and want to utilize this technique you could either use a C2 framework with the capablitiy to consume PowerShell scripts or use the GitHub repository of Nikhil Mittal to reflectively load the required dependencies in memory without leaving obvious artifacts on disk.

15. Let's open up a PowerShell prompt and use the following commandlet to enumerate all AD users:

Get-AdUser -Filter *

user enum with AD Module

16. If you want, you could also introduce object filtering in your PowerShell commands. For example list all object properties using the -Properties * flag. In case you want a refresher on PowerShell filters, a good resource is the following URL: https://www.itprotoday.com/powershell/powershell-basics-filtering-objects

For example, list only users from the IT OU:

Get-ADUser -SearchBase "OU=IT,DC=Draconem,DC=corp" -Filter *

user enum, only IT OU

17. Another nice option, return only the users that have a description

Get-ADUser -Filter "Description -like '*'" -Properties Description | select name,Description

user enum, only users with description

Feel free to experiment with various filters and property selectors.

Using the AD Module for Computer Enumeration

18. Much like we did when enumerating users, we can utilize the Microsoft signed PowerShell AD Module to enumerate computers. Let's open up a PowerShell prompt and use the following commandlet to enumerate all Computers in the draconem.io domain:

Get-AdComputer -Filter *

computer enum using ad module

Using the AD Module for Domain Trust Enumeration

19. We can use the AD Module to verify any existings trusts accross domains. Let's open up a PowerShell prompt and use the following commandlet to enumerate all trusts in the draconem.io domain:

Get-ADTrust -Filter * | select name, Direction

trust enum using ad module

Using the AD Module for Password Policy Enumeration

20. We can use the Microsoft signed Active Directory module to enumerate the password policy as follows:

Get-ADDefaultDomainPasswordPolicy

pw pol enum using ad module

Using the AD Module for Fine-Grained Password Policy Enumeration

21. Enumerating fine-grained password policies with the Active Directory module is very straightforward (launch PowerShell with Administrative privilleges):

Get-ADFineGrainedPasswordPolicy -Filter *

fine grained enum using ADModule

Using the AD Module for LAPS Enumeration

22. We can leverage the AD Module as well to enumerate all computers with a value in the ms-Mcs-AdmPwdExpirationTime property:

Get-ADComputer -Filter * -Properties * | Where-Object { $_.'ms-Mcs-admpwdexpirationtime' -ne $null } | select name

LAPS enum using ADModule

Using the AD Module for gMSA Enumeration

23. The AD Module is one of the best ways to enumerate these types of accounts as the commandlet also reveals who can retrieve the password:

Get-ADServiceAccount -Filter * -Properties * | select name, PrincipalsAllowedToRetrieveManagedPassword

gmsa enum using admodule

Using the AD Module for DNS enumeration

24. Using the AD Module, we can use the IPv4Address property of the computer object that is returned when invoking the Get-ADComputer cmdlet.

#forward lookup
Get-ADComputer -filter * -Properties * | select name,ipv4address

#reverse lookup
Get-ADComputer -Properties IPv4Address -Filter * | where IPv4Address -eq 'SOME IP ADDRESS' | select name
Note

If you want a fun read on why we have to do a double pipe for the reverse lookup:

https://www.reddit.com/r/PowerShell/comments/4bbzbo/getadcomputer_returns_null_when_filtering_by/

Thanks Microsoft! :)

dns enum using AD Module

Powerview & Sharpview

Using PowerView & SharpView for User Enumeration

It should come as no surprise that PowerView and SharpView syntax are very similar, as SharpView is a plain C# port of the popular PowerView project.

Attention

Since SharpView is a C# port, we cannot utilize the PowerShell built-in filtering like we can with PowerView, not that big of a deal, but we need to keep it in the back of our heads.

25. Enumerating users with PowerView is very similar as using the ADModule:

#Import the PowerView module, this is typically done in memory using (Iex -iwr(<url>)), but since this is a lab, we just import from disk
cd C:\Tools
. .\Powerview-Modded.ps1
#use the Get-DomainUser PowerView method to enumerate domain users
 Get-DomainUser

powerview userenum

Using PowerView & SharpView for Computer Enumeration

26. SharpView and PowerView use the same syntax, the only caveat is that SharpView does not have the PowerShell built-in filtering. The syntax to enumerate domain computers is as follows:

Get-DomainComputer | select Dnshostname

computer enum powerview

Using PowerView & SharpView for Domain Trust Enumeration

27. SharpView and PowerView use the same syntax, the only caveat is that SharpView does not have the PowerShell built-in filtering. The syntax to enumerate domain computers is as follows:

Get-DomainTrust

trust enum powerview

Using PowerView & SharpView for Password Policy Enumeration

28. We can also use PowerView or SharpView to achieve this task, in fact this will also give us the kerberos policy which could increase our opsec when we start forging tickets:

Get-DomainPolicy

pw pol enum using PowerView

Using PowerView & SharpView for Fine-Grained Password Policy Enumeration

29. PowerView (and SharpView) do not have built-in functionality of enumerating fine-grained password policies. This will be a nice red team exercise. Using all the knowledge you have gained so far about enumerating fine-grained password policies, could you create a script to do the enumeration for you? You can base your scripting on the following source: https://github.com/leoloobeek/LAPSToolkit/blob/master/LAPSToolkit.ps1#L1924

Solution

We can expand LAPSToolkit with the following function:

    function Convert-LDAPProperty {
<#
    .SYNOPSIS

        Helper that converts specific LDAP property result fields.
        Used by several of the Get-Net* function.
    .PARAMETER Properties
        Properties object to extract out LDAP fields for display.
#>
    param(
        [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
        [ValidateNotNullOrEmpty()]
        $Properties
    )

    $ObjectProperties = @{}

    $Properties.PropertyNames | ForEach-Object {
        if (($_ -eq "objectsid") -or ($_ -eq "sidhistory")) {
            # convert the SID to a string
            $ObjectProperties[$_] = (New-Object System.Security.Principal.SecurityIdentifier($Properties[$_][0],0)).Value
        }
        elseif($_ -eq "objectguid") {
            # convert the GUID to a string
            $ObjectProperties[$_] = (New-Object Guid (,$Properties[$_][0])).Guid
        }
        elseif( ($_ -eq "lastlogon") -or ($_ -eq "lastlogontimestamp") -or ($_ -eq "pwdlastset") -or ($_ -eq "lastlogoff") -or ($_ -eq "badPasswordTime") ) {
            # convert timestamps
            if ($Properties[$_][0] -is [System.MarshalByRefObject]) {
                # if we have a System.__ComObject
                $Temp = $Properties[$_][0]
                [Int32]$High = $Temp.GetType().InvokeMember("HighPart", [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
                [Int32]$Low  = $Temp.GetType().InvokeMember("LowPart",  [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
                $ObjectProperties[$_] = ([datetime]::FromFileTime([Int64]("0x{0:x8}{1:x8}" -f $High, $Low)))
            }
            else {
                $ObjectProperties[$_] = ([datetime]::FromFileTime(($Properties[$_][0])))
            }
        }
        elseif($Properties[$_][0] -is [System.MarshalByRefObject]) {
            # try to convert misc com objects
            $Prop = $Properties[$_]
            try {
                $Temp = $Prop[$_][0]
                Write-Verbose $_
                [Int32]$High = $Temp.GetType().InvokeMember("HighPart", [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
                [Int32]$Low  = $Temp.GetType().InvokeMember("LowPart",  [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
                $ObjectProperties[$_] = [Int64]("0x{0:x8}{1:x8}" -f $High, $Low)
            }
            catch {
                $ObjectProperties[$_] = $Prop[$_]
            }
        }
        elseif($Properties[$_].count -eq 1) {
            $ObjectProperties[$_] = $Properties[$_][0]
        }
        else {
            $ObjectProperties[$_] = $Properties[$_]
        }
    }

    New-Object -TypeName PSObject -Property $ObjectProperties
}

    filter Get-DomainSearcher {
<#
    .SYNOPSIS
        Helper used by various functions that takes an ADSpath and
        domain specifier and builds the correct ADSI searcher object.
    .PARAMETER Domain
        The domain to use for the query, defaults to the current domain.
    .PARAMETER DomainController
        Domain controller to reflect LDAP queries through.
    .PARAMETER ADSpath
        The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
        Useful for OU queries.
    .PARAMETER ADSprefix
        Prefix to set for the searcher (like "CN=Sites,CN=Configuration")
    .PARAMETER PageSize
        The PageSize to set for the LDAP searcher object.
    .PARAMETER Credential
        A [Management.Automation.PSCredential] object of alternate credentials
        for connection to the target domain.
    .EXAMPLE
        PS C:\> Get-DomainSearcher -Domain testlab.local
    .EXAMPLE
        PS C:\> Get-DomainSearcher -Domain testlab.local -DomainController SECONDARY.dev.testlab.local
#>

    param(
        [Parameter(ValueFromPipeline=$True)]
        [String]
        $Domain,

        [String]
        $DomainController,

        [String]
        $ADSpath,

        [String]
        $ADSprefix,

        [ValidateRange(1,10000)]
        [Int]
        $PageSize = 200,

        [Management.Automation.PSCredential]
        $Credential
    )

    if(!$Credential) {
        if(!$Domain){
            $Domain = (Get-NetDomain).name
        }
        elseif(!$DomainController) {
            try {
                # if there's no -DomainController specified, try to pull the primary DC
                #   to reflect queries through
                $DomainController = ((Get-NetDomain).PdcRoleOwner).Name
            }
            catch {
                throw "Get-DomainSearcher: Error in retrieving PDC for current domain"
            }
        }
    }
    elseif (!$DomainController) {
        try {
            $DomainController = ((Get-NetDomain -Credential $Credential).PdcRoleOwner).Name
        }
        catch {
            throw "Get-DomainSearcher: Error in retrieving PDC for current domain"
        }

        if(!$DomainController) {
            throw "Get-DomainSearcher: Error in retrieving PDC for current domain"
        }
    }

    $SearchString = "LDAP://"

    if($DomainController) {
        $SearchString += $DomainController
        if($Domain){
            $SearchString += "/"
        }
    }

    if($ADSprefix) {
        $SearchString += $ADSprefix + ","
    }

    if($ADSpath) {
        if($ADSpath -like "GC://*") {
            # if we're searching the global catalog
            $DN = $AdsPath
            $SearchString = ""
        }
        else {
            if($ADSpath -like "LDAP://*") {
                if($ADSpath -match "LDAP://.+/.+") {
                    $SearchString = ""
                }
                else {
                    $ADSpath = $ADSpath.Substring(7)
                }
            }
            $DN = $ADSpath
        }
    }
    else {
        if($Domain -and ($Domain.Trim() -ne "")) {
            $DN = "DC=$($Domain.Replace('.', ',DC='))"
        }
    }

    $SearchString += $DN
    Write-Verbose "Get-DomainSearcher search string: $SearchString"

    if($Credential) {
        Write-Verbose "Using alternate credentials for LDAP connection"
        $DomainObject = New-Object DirectoryServices.DirectoryEntry($SearchString, $Credential.UserName, $Credential.GetNetworkCredential().Password)
        $Searcher = New-Object System.DirectoryServices.DirectorySearcher($DomainObject)
    }
    else {
        $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString)
    }

    $Searcher.PageSize = $PageSize
    $Searcher
}

filter Get-NetDomain {
<#
    .SYNOPSIS
        Returns a given domain object.
    .PARAMETER Domain
        The domain name to query for, defaults to the current domain.
    .PARAMETER Credential
        A [Management.Automation.PSCredential] object of alternate credentials
        for connection to the target domain.
    .EXAMPLE
        PS C:\> Get-NetDomain -Domain testlab.local
    .EXAMPLE
        PS C:\> "testlab.local" | Get-NetDomain
    .LINK
        http://social.technet.microsoft.com/Forums/scriptcenter/en-US/0c5b3f83-e528-4d49-92a4-dee31f4b481c/finding-the-dn-of-the-the-domain-without-admodule-in-powershell?forum=ITCG
#>

    param(
        [Parameter(ValueFromPipeline=$True)]
        [String]
        $Domain,

        [Management.Automation.PSCredential]
        $Credential
    )

    if($Credential) {

        Write-Verbose "Using alternate credentials for Get-NetDomain"

        if(!$Domain) {
            # if no domain is supplied, extract the logon domain from the PSCredential passed
            $Domain = $Credential.GetNetworkCredential().Domain
            Write-Verbose "Extracted domain '$Domain' from -Credential"
        }

        $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain, $Credential.UserName, $Credential.GetNetworkCredential().Password)

        try {
            [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
        }
        catch {
            Write-Warning "The specified domain does '$Domain' not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid."
            $Null
        }
    }
    elseif($Domain) {
        $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
        try {
            [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
        }
        catch {
            Write-Warning "The specified domain '$Domain' does not exist, could not be contacted, or there isn't an existing trust."
            $Null
        }
    }
    else {
        [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
    }
}
  function Get-FineGrainedPasswordPolicies {
  [CmdletBinding()]
    Param (
      [Parameter(ValueFromPipeline=$True)]
      [String]
      $PolicyName = '*',

      [String]
      $Domain,

      [String]
      $DomainController,

      [String]
      $ADSpath,

      [Switch]
      $FullData,

      [ValidateRange(1,10000)]
      [Int]
      $PageSize = 200,

      [Management.Automation.PSCredential]
      $Credential
    )

  begin {
      $FineGrainedSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
  }
  process {
      if ($FineGrainedSearcher) {
              $FineGrainedSearcher.filter="(&(ObjectClass=msDS-PasswordSettings)(name=$PolicyName))"
          }

          try {
              $FineGrainedSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
                  if ($FullData) {
                      # convert/process the LDAP fields for each result
                      Convert-LDAPProperty -Properties $_.Properties
                  }
                  else {
                      # otherwise just returning the ADS paths of the OUs
                      $_.properties.adspath
                  }
              }
          }
          catch {
              Write-Warning $_
          }
      }
  }

fine grained enum custom script

Using PowerView & SharpView for LAPS Enumeration

30. PowerView (and SharpView) do not have LAPS enumeration by default, but behaves very similar to the AD Module when applying filtering:

Get-DomainComputer -Properties * | Where-Object { $_.'ms-Mcs-admpwdexpirationtime' -ne $null } | select name
LAPS enumeration using PowerView

Using PowerView & SharpView for gMSA Enumeration

31. Much like fine-grained password policies, PowerView does not have built-in enumeration for group managed service accounts. Can you extend LAPSToolKit like we did for fine-graind password policies, but this time for group managed service accounts?

Solution

We can expand LAPSToolkit with the following function:

function Get-NetGmsa {

    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline=$True)]
        [String]
        $gmsaName = '*',

        [String]
        $Domain,

        [String]
        $DomainController,

        [String]
        $ADSpath,

        [Switch]
        $FullData,

        [ValidateRange(1,10000)]
        [Int]
        $PageSize = 200,

        [Management.Automation.PSCredential]
        $Credential
    )

    begin {
        $GMSASearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
    }
    process {
        if ($GMSASearcher) {
                $GMSASearcher.filter="(&(ObjectClass=msDS-GroupManagedServiceAccount)(name=$gmsaName))"
            }

            try {
                $GMSASearcher.FindAll() | Where-Object {$_} | ForEach-Object {
                    if ($FullData) {
                        # convert/process the LDAP fields for each result
                        Convert-LDAPProperty -Properties $_.Properties
                    }
                    else {
                        # otherwise just returning the ADS paths of the OUs
                        $_.properties.adspath
                    }
                }
            }
            catch {
                Write-Warning $_
            }
        }
  }

find gmsa custom script

BONUS - ADSI

Using ADSI for User Enumeration

As you might have already seen during the lecture, this method is the most prefered method of seasoned red teamers.

It will depend on your tooling and tradecraft, but if you are using a command-and-control framework that has built-in functionality to handle LDAP filters, this type of searching will give you full control over your results and will be very stealthy.

Unfortunately, when executing these commands in a C2 that does not have this functionality, the same IoCs apply as any other PowerShell based command. Neverthless learning how to use LDAP syntax is a very nice to have skill as a red team operator.

32. For example, get the last entry in the domain admin group:

([adsisearcher]'(memberof=cn=Domain Admins,cn=Users,dc=draconem,dc=corp)').FindAll().GetDirectoryEntry() | Select-Object -Last 1 -Property sAMAccountName

adsi domain admin

33. Or simply get all the users in the environment:

([adsisearcher]'(objectclass=user)').FindAll().GetDirectoryEntry() | Select-Object -Property sAMAccountName

adsi users

Using ADSI for Computer Enumeration

34. In case you are feeling up for some LDAP querying by hand, we can also use ADSI. Let's open up a PowerShell prompt (or use the same one from before) and enumerate the computers as follows:

([adsisearcher]'(ObjectCategory=Computer)').FindAll().GetDirectoryEntry() | Select-Object -Property samaccountname

computer enum adsi

Using ADSI for Domain Trust Enumeration

35. In case you are feeling up for some LDAP querying by hand, we can also use ADSI. Let's open up a PowerShell prompt (or use the same one from before) and enumerate the computers as follows:

([adsisearcher]'(objectClass=trustedDomain)').FindAll().GetDirectoryEntry() | select name,trustdirection

With ADSI the trustdirection is a numeric value, which can be interpreted as follows:

  • 1: Inbound
  • 2: Outbound
  • 3: Bi-directional

trust enum adsi

Using ADSI for Password Policy Enumeration

Using ADSI for default password policy configuration is a bit more complex, as the domain policy is set through a GPO. ADSI in itself is not able to simply read the GPO data, so retrieving the policy is a two-step process:

36. First, we will have to query the groupPolicyContainer object class in LDAP to get the filepath to the GPOs:

([adsisearcher]'(objectClass=groupPolicyContainer)').FindAll().GetDirectoryEntry()
Then we would have to parse the \MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf file for all GPO objects until we find the right one. This is rather tedious, so ADSI is not the most recommended way to tackle this problem, although not impossible.

Attention

The GUID of the default domain policy will always be the same ({31B2F340-016D-11D2-945F-00C04FB984F9})

The same is also true for the default domain controller policy ({{6AC1786C-016F-11D2-945F-00C04fB984F9}})

type "\\draconem.corp\sysvol\draconem.corp\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf"

pwpol enum using ADSI

37. We could also take a much more straight-forward approach using adsi to make a LDAP connection to the base object and then list the base object properties

$base=[adsi]'LDAP://DC=draconem,DC=corp'
$base | Select-Object -Property lockoutThreshold,minPwdLength

pw pol enum using ADSI

Using ADSI for Fine-Grained Password Policy Enumeration

38. ADSI can of course also be used to achieve the same result:

([adsisearcher]'(ObjectClass=msDS-PasswordSettings)').FindAll().getDirectoryEntry() | Select-Object -Property  name,msDS-PSOAppliesTo
fine grained enum using ADSI

Using ADSI for LAPS Enumeration

39. Through the power of LDAP queries we can also enumerate computer objects with LAPS enabled. No need for dependencies! :)

([adsisearcher]"(&(objectCategory=computer)(ms-MCS-admpwdexpirationtime=*))").findAll().GetdirectoryEntry() | select name

LAPS enum using ADSI

Using ADSI for gMSA Enumeration

40.

([adsisearcher]'(ObjectClass=msDS-GroupManagedServiceAccount)').FindAll().getDirectoryEntry()

gmsa enum using ADSI

BONUS - ADFind

Using ADFind for User Enumeration

A crowd favorite with threat actors is a little tool called ADFind it is free to download and written in C++. Under the hood, ADFind is using a bunch of Windows API calls to enumerate Active Directory. Let's utilize ADFind to enumerate users, you will see that the majority of tools follow familiar (LDAP) syntax. ADFind is a complex program and has very detailed documentation, a nice cheat sheet can also be found here: https://social.technet.microsoft.com/wiki/contents/articles/7535.adfind-command-examples.aspx

41. To find all users:

.\AdFind.exe -f objectclass=user sAMAccountName Description

adfind user enum

42. To list all the users in the staff OU:

.\AdFind -f "sAMAccountName=Staff" member -list
adfind user enum filter on ou

Using ADFind for Computer Enumeration

43. Adfind is following LDAP syntax, we can enumerate computer objects as follows:

.\AdFind.exe -f objectclass=computer samaccountname

computer enum adfind

Using ADFind for Domain Trust Enumeration

44. Adfind is following LDAP syntax, we can enumerate computer objects as follows:

.\AdFind.exe -f objectclass=trusteddomain name trustdirection

Much like with ADSI,the trustdirection is a numeric value, which can be interpreted as follows:

  • 1: Inbound
  • 2: Outbound
  • 3: Bi-directional

trust enum adfind

Using ADFind for Password Policy Enumeration

45.

.\AdFind.exe -default -s base lockoutduration lockoutthreshold lockoutobservationwindow maxpwdage minpwdage minpwdlength

pw pol enum using ADFind

Using ADFind for Fine-Grained Password Policy Enumeration

46. We can use ADFind in a similar fashion as we use ADSI to enumerate the fine-grained password policies:

.\AdFind.exe -f ObjectClass=msDS-PasswordSettings

fine grained enum using ADFind

Using ADFind for LAPS Enumeration

47. ADFind has the capabilities of parsing LDAP queries much like ADSI does. As can be noticed, it is very worthwile to learn LDAP syntax for enumeration purposes as every single enumeration tool relies on LDAP queries one way or another:

.\AdFind.exe -f "(&(objectclass=computer)(ms-Mcs-admpwdexpirationtime=*))" samaccountname

LAPS enum using ADFind

Using ADFind for gMSA Enumeration

48. Using ADFind to enumerate gmsa:

Note

the -sddl -resolvesids flag in adfind is used to force adfind to parse the security descriptor and translate the security identifiers to their respective human-readable names.

.\AdFind.exe -sddl -resolvesids -f '(ObjectClass=msDS-GroupManagedServiceAccount)' samaccountname msDS-GroupMSAMembership

gmsa adfind

BONUS - ADExplorer

Using ADExplorer for User Enumeration

ADExplorer is a tool that is part of the sysinternals suite. This toolkit is developed by Microsoft to facilitate the life of administrators (and redteamers). Using ADExplorer it is possible to create a snapshot of the environment which can then be parsed offline. This is a very nice reconnaisance tactic as it limits the amount of queries being sent into the live environment. An added benefit of sysinternals tooling is that it gets hosted online and is reachable over SMB. This is rather interesting as this allows you to execute the tool without actually dropping it to disk, of course this will create an outbound SMB connection which is an IOC. ADExplorer has a GUI which allows you to view the environment or load a snapshot.

49. For the sake of illustration, we are first going to create a snapshot using the cli and then use our snapshot in ADExplorer.

.\ADExplorer.exe -snapshot "dc01.draconem.corp" "snapshot.dat"

adexplorer from disk

50. Once you got the snapshot taken, we can use ADExplorer's GUI to parse the snapshot. Launch ADExplorer from the C:\Tools directory and load the snapshot you created which is located in C:\tools\snapshot.dat

adexplorer gui

51. Which we can use to enumerate the Active Directory:

adexplorer user enum

Using ADExplorer for Computer Enumeration

52. Since ADExplorer has GUI capabilities, enumerating the computers is rather trivial:

computer enum using ADExplorer

Using ADExplorer for Domain Trust Enumeration

53. Domain trusts can be found in the system container in ADExplorer:

trust enum using ADExplorer

54. You could also use ADExplorer's search function that supports LDAP like querying:

trust enum using ADExplorer 2

Using ADExplorer for Password Policy Enumeration

55. Much like the RSAT approach, ADExplorer works by displaying the properties of the default naming context:

pw pol using ADExplorer

Now that we know the "basic" password policy, it makes sense that we take a look to try and identify if there is any fine-grained password policy enabled in the environment.

Using ADExplorer for Fine-Grained Password Policy Enumeration

56. Be sure to launch ADExplorer as an administrator, or the password-settings container will appear empty:

fine grained enum using ADExplorer

Note

As shown in the lecture earlier, fine-grained password policies are not enumeratable by normal users by default. However, we can still enumerate domain groups. If a group has a fine-grained password policy applied to it the msDS-PSOApplied property will point to the policy name, unfortunately, other than the name of the policy we will not be able to reveal further details. If you have some time to kill, feel free to try and figure out which AD objects have a fine-grained policy attached to themselves from an unprivileged perspective.

Using ADExplorer for LAPS Enumeration

57. Much like RSAT, ADExplorer is a GUI based application. As already stated, in large organizations it is unfeasible to go over all computer objects by hand to go and hunt for LAPS enabled computers.

LAPS enum using ADExp

Using ADExplorer for gMSA Enumeration

58. Using ADExplorer to enumerate gmsa is trivial and very similar to using RSAT:

Note

You can use the ConvertFrom-SddlString to translate security descriptors into a more readable format:

security descriptor translator

Let's now take a look at how we can leverage LDAP to be a stealthier replacement than nslookup

BONUS - Using LAPSToolkit for LAPS Enumeration

59. LAPSToolkit by LeoLoobeek was specifically built to automate the process for us. This is by far the easiest method of enumerating LAPS.

Attention

Because we created a custom script to enumerate fine grained password policies, we have to close and reopen our PowerShell session. The fine grained password policies script sources a lot of code from the LAPSToolkit, which makes the Toolkit misbehave because of functions that have been defined multiple times. Opening a new PowerShell window unloads previously loaded scripts, fixing this problem.

#import LAPSToolkit (from within the c:\Tools directory)
. .\LapsToolkit.ps1
#enumerate computers with LAPS enabled
Get-LAPSComputers

#enumerate who is allowed to access LAPS password (no output means default configuration (domain admins only))
Find-AdmPwdExtendedRights

LAPS enumeration with LAPSToolKit

BONUS - Using SharpAdidnsdump for DNS enumeration

60.

 .\SharpAdidnsdump.exe <DCIP>

dns enum using sharpadidnsdump

Note

Unfortunately, it is not feasible to perform this kind of enumeration using ADFind, ADExplorer or PowerView/SharpView. This is because the IPV4address property is not stored in an AD object and needs to get parsed in order to be interpreted.

BONUS - Enumeration over C2

We could probably spend the entire day in the lab performing enumeration tasks, if you've got some time left feel free to use your command-and-control implant in combination with the tools we have just used to get a feel of how they behave when using a C2. This will help you out tremendously during the day 6 CTF!

P.S: Did you spot any honeypot objects in the AD? If not, you might want to take a step back and enumerate some more ;-).

Conclusion

In this lab, you have learned how to enumerate an Active Directory environment with a multitude of tools. This lab should act as a nice reference point whenever you need to enumerate AD in various situations ranging from loud to stealth.