# Enumeration

Create a **Domain Map**.\
Repeat enumeration for each new access.&#x20;

Identify:&#x20;

* Network (Active hosts)
* AD Key Services (Kerberos, NetBIOS, LDAP, DNS)
* AD computers
* AD users
* Vulnerabilities

## Automated

[**BloodHound**](https://github.com/BloodHoundAD/BloodHound)

{% tabs %}
{% tab title="BloodHound" %}
Install Docker

{% code overflow="wrap" %}

```bash
sudo apt install docker.io && sudo apt install docker-compose
sudo usermod -aG docker $USER
```

{% endcode %}

Install Bloodhound

{% code overflow="wrap" %}

```bash
curl -L https://ghst.ly/getbhce > docker-compose.yml
sudo docker-compose pull
```

{% endcode %}

Start/Stop Bloodhound

{% code overflow="wrap" %}

```bash
sudo docker-compose up
sudo docker-compose down -v
```

{% endcode %}

Start neo4j

{% code overflow="wrap" %}

```bash
sudo neo4j start
```

{% endcode %}

**Neo4j**: `http://localhost:7474` \
Default credentials: `neo4j:neo4j` \
Will ask to change password after first login.

**Bloodhound**: `http://localhost:8080` \
Default credentials: `admin:<PROMPT_IN_LOG_DOCKER>` \
Will ask to change password after first login.
{% endtab %}

{% tab title="Data Collector" %}

### <mark style="color:green;">**Linux**</mark>

From remote attacker with domain credentials ([bloodhound.py](https://github.com/dirkjanm/BloodHound.py) ingestor)

```bash
pipx install bloodhound
pipx install bloodhound-ce
```

{% code overflow="wrap" %}

```bash
bloodhound-python -u <USER> -p <PASS> -ns <IP> -d <DOMAIN> -c all --zip
bloodhound-ce-python -u <USER> -p <PASS> -ns <IP> -d <DOMAIN> -c all --zip
```

{% endcode %}

Or with [netexec](https://www.netexec.wiki/ldap-protocol/bloodhound-ingestor)&#x20;

{% code overflow="wrap" %}

```bash
netexec ldap <IP> -u <USER> [-H <HASH>/-P <PASS>] -d <DOMAIN> --bloodhound --collection All --dns-server <IP>
```

{% endcode %}

<details>

<summary>Note DNS</summary>

In some cases, we would need to edit our `resolv.conf` file to run `bloodhound-python` since it requires a DNS hostname for the target Domain Controller instead of an IP address.

{% code overflow="wrap" %}

```bash
sudo vim /etc/resolv.conf
# domain INLANEFREIGHT.LOCAL
# nameserver <IP_DC>
```

{% endcode %}

</details>

### <mark style="color:blue;">Windows</mark>

On the windows host in the AD domain

* [SharpHound](https://github.com/BloodHoundAD/BloodHound/tree/master/Collectors) ([NEW](https://github.com/SpecterOps/SharpHound/releases)) (`.exe` and `.ps1`)

{% code overflow="wrap" %}

```powershell
.\SharpHound.exe -c All --zipfilename <FILE_NAME>
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
Import-Module .\Sharphound.ps1
Invoke-BloodHound -CollectionMethod All -OutputDirectory C:\<WHERE>\ -OutputPrefix "<NameSTART>"
```

{% endcode %}

See [all flags](https://bloodhound.specterops.io/collect-data/ce-collection/sharphound-flags) (ex. `loop`)

`.bin` files can be deleted.
{% endtab %}

{% tab title="Cypher" %}
Bloodhound uses Neo4j, a graphing database, which uses the Cypher language to query the data.

* [Intro](https://blog.cptjesus.com/posts/introtocypher/)
* [Cheatsheet](https://hausec.com/2019/09/09/bloodhound-cypher-cheatsheet/)
  {% endtab %}
  {% endtabs %}

## Remotely

Try with `"guest":""` or `"":""` (and `*:*` for LDAP auth)

<table><thead><tr><th width="188">Tool</th><th>Details</th></tr></thead><tbody><tr><td><a href="https://nmap.org/">nmap</a></td><td>Initial information such as Domain, Forest, FQDN<br><code>nmap -v -p 139,445 --script smb-os-discovery &#x3C;IP></code></td></tr><tr><td><a href="https://github.com/Pennyw0rth/NetExec">netexec</a></td><td><p>Enumeration with SMB (Null Session) and LDAP (Anonymous Binds)<br><code>nxc smb &#x3C;IP> -u &#x3C;USER> -p &#x3C;PASS> [-d &#x3C;DOMAIN>] --users --groups --loggedon-users --pass-pol --rid-brute </code><strong><code>[-L]</code></strong> <br><code>nxc ldap &#x3C;IP> -u &#x3C;USER> -p &#x3C;PASS> [-d &#x3C;DOMAIN>] --users --groups --dc-list --get-sid --computer [--query "(sAMAccountName=*)/(servicePrincipalName=*)/etc." "&#x3C;properties>"] [-M user-desc]</code><br></p><p><mark style="color:$danger;">Note:</mark><br>Try <strong>Kerberos</strong> authentication with <code>-k</code></p><p>Also try using <strong>hostname</strong> instead of <strong>IP</strong> (add to <code>/etc/hosts</code>)</p><p>Always try <strong>ntpdate</strong> first (error <code>KRB_AP_ERR_SKEW</code>)</p></td></tr><tr><td><a href="https://linux.die.net/man/1/ldapsearch">ldapsearch</a></td><td>LDAP Anonymous Binds<br><code>ldapsearch -H ldap://&#x3C;IP> -x -b "DC=goole,DC=com" -s sub "*"</code> </td></tr><tr><td><a href="https://github.com/cddmp/enum4linux-ng">enum4linux</a></td><td>Enumeration with SMB<br><code>enum4linux -u &#x3C;USER> -p &#x3C;PASS> &#x3C;IP> -a</code></td></tr><tr><td><a href="https://book.hacktricks.xyz/network-services-pentesting/pentesting-smb/rpcclient-enumeration">rpcclient</a></td><td>Detailed enumeration with specific queries<br><code>rpcclient -U [&#x3C;DOMAIN>/]&#x3C;USER>[%&#x3C;PASS>] &#x3C;IP></code><br><code>>> &#x3C;QUERY></code> </td></tr><tr><td><a href="https://github.com/dirkjanm/ldapdomaindump">ldapdomaindump</a></td><td>Active Directory information dumper via LDAP<br><code>ldapdomaindump -u '&#x3C;DOMAIN>\&#x3C;USER>' -p '&#x3C;PASSWORD>' &#x3C;IP> -o dump</code></td></tr><tr><td><a href="https://github.com/ropnop/windapsearch">windapsearch</a></td><td>Enumeration with LDAP query<br><code>windapsearch --dc-ip &#x3C;IP_DC> -u &#x3C;USER>@&#x3C;DOMAIN> -p &#x3C;PASSWORD> -G -U -C -PU --da</code></td></tr><tr><td><a href="https://github.com/dirkjanm/adidnsdump">adidnsdump</a></td><td><p>Enumerate all DNS records in a domain using a valid domain user account.<br><code>adidnsdump -u &#x3C;DOMAIN>\\&#x3C;USER> ldap://&#x3C;DC> [-r]</code> </p><p><code>-r</code> attempt to resolve unknown records by performing an <code>A</code> query.</p></td></tr></tbody></table>

## Manually

The [ActiveDirectory](https://learn.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2022-ps) PowerShell module is a set of PowerShell cmdlets for administering an Active Directory environment. Before using it, we need to make sure it is imported with [Get-Module](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/get-module?view=powershell-7.2).

{% code overflow="wrap" %}

```powershell
Get-Module
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
Import-Module ActiveDirectory
```

{% endcode %}

### Domain

```powershell
[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
```

{% code overflow="wrap" %}

```powershell
Get-ADDomain
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
echo %USERDOMAIN%
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
echo %logonserver%  # DC
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
netdom query /domain:<DOMAIN> dc
```

{% endcode %}

We get domain information through PowerShell, using LDAP to communicate with AD and extract information.

```ps1
function LDAPSearch {
    param (
        [string]$LDAPQuery
    )
    $PDC = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().PdcRoleOwner.Name
    $DistinguishedName = ([adsi]'').distinguishedName
    $DirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$PDC/$DistinguishedName"[, "USER", "<PASS>"])
    $DirectorySearcher = New-Object System.DirectoryServices.DirectorySearcher($DirectoryEntry, $LDAPQuery)
    return $DirectorySearcher.FindAll()
}
```

```powershell
Import-Module .\function.ps1
$querySearch = "samAccountType=805306368"   # enumerate all users in the domain
$querySearch = "name=jeffadmin"   # specific user
$querySearch = "(&(objectCategory=group)(cn=Sales Department))"   # nested groups
(LDAPSearch -LDAPQuery $querySearch).properties[.memberof/.member]

(LDAPSearch -LDAPQuery $querySearch).properties | findstr /R "\<name \<description"
```

### Workstation

{% code overflow="wrap" %}

```powershell
netdom query /domain:<DOMAIN> workstation
```

{% endcode %}

### Users

All users

{% code overflow="wrap" %}

```powershell
Get-ADUser -Filter *
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
net user /domain
```

{% endcode %}

Info about specific user

{% code overflow="wrap" %}

```powershell
Get-ADUser -Identity <USER> [-Properties *]
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
net user <USER> /domain
```

{% endcode %}

### Groups

All groups

{% code overflow="wrap" %}

```powershell
Get-ADGroup -Filter *
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
net group /domain
```

{% endcode %}

Info about specific group

{% code overflow="wrap" %}

```powershell
Get-ADGroup -Identity <GROUP> [-Properties *]
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
Get-ADGroupMember -Identity <GROUP>
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
net group "<GROUP>" /domain 
# User members only
```

{% endcode %}

### Share

{% code overflow="wrap" %}

```powershell
net share
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
net use x: \<COMPUTER>\<SHARE>
```

{% endcode %}

### Policy Password

{% code overflow="wrap" %}

```powershell
net accounts [/domains]
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
Get-ADDefaultDomainPasswordPolicy
```

{% endcode %}

### Deleted AD Objects

{% code overflow="wrap" %}

```powershell
Get-ADObject -Filter 'isDeleted -eq $true -and objectClass -eq "user"' -IncludeDeletedObjects -Properties objectSid, lastKnownParent, ObjectGUID | Select-Object Name, ObjectGUID, objectSid, lastKnownParent | Format-List
```

{% endcode %}

Restore with (if you have permissions):

{% code overflow="wrap" %}

```powershell
Restore-ADObject -Identity '<GUID>'
```

{% endcode %}

### Trust

{% code overflow="wrap" %}

```powershell
netdom query /domain:<DOMAIN> trust
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
Get-ADTrust -Filter *
```

{% endcode %}

### Tickets Kerberos

```
klist
```

### SPN

{% code overflow="wrap" %}

```powershell
setspn.exe -Q */*
```

{% endcode %}

### ACL

{% code overflow="wrap" %}

```powershell
Get-ADUser -Filter * | Select-Object -ExpandProperty SamAccountName > ad_users.txt
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
foreach($line in [System.IO.File]::ReadLines("C:\<PATH>\ad_users.txt")) {get-acl  "AD:\$(Get-ADUser $line)" | Select-Object Path -ExpandProperty Access | Where-Object {$_.IdentityReference -match '<DOMAIN>\\<USER>'}}
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
$guid= "00299570-246d-11d0-a768-00aa006e0529" # GUID to decode
Get-ADObject -SearchBase "CN=Extended-Rights,$((Get-ADRootDSE).ConfigurationNamingContext)" -Filter {ObjectClass -like 'ControlAccessRight'} -Properties * |Select Name,DisplayName,DistinguishedName,rightsGuid| ?{$_.rightsGuid -eq $guid} | fl
```

{% endcode %}

*`CN=<VALUE>` take from ActiveDirectoryRights*

## PowerView

It is a [PowerSploit](https://github.com/PowerShellMafia/PowerSploit) script and there are two versions: [Old](https://github.com/PowerShellMafia/PowerSploit/blob/master/Recon/PowerView.ps1) and [New](https://github.com/BC-SECURITY/Empire/blob/main/empire/server/data/module_source/situational_awareness/network/powerview.ps1) *(maintained by Empire)*

{% code overflow="wrap" %}

```powershell
Import-Module .\PowerView.ps1
```

{% endcode %}

See the [documentation](https://powersploit.readthedocs.io/en/latest/Recon/).

{% code overflow="wrap" %}

```powershell
Get-NetDomain # basic domain info
Get-NetUser # user info
Get-NetGroup # group info
Get-NetComputer # machine info
Get-DomainPolicy # password policy info
Find-LocalAdminAccess # machines where user has administrator privileges
Find-DomainShare [-CheckShareAccess] # domain shares
Get-DomainTrust # trust
Get-DomainTrustMapping # trust relationships
Get-DomainUser * -spn # SPN
Get-DomainSID # SID domain
Convert-SidToName <SID>
Convert-NameToSid <NAME>
# Etc.
```

{% endcode %}

### Description Field

{% code overflow="wrap" %}

```powershell
Get-DomainUser * | Select-Object samaccountname,description |Where-Object {$_.Description -ne $null}
```

{% endcode %}

### PASSWD\_NOTREQD Field

Enumerating accounts with this flag set and testing each to see if no password is required

{% code overflow="wrap" %}

```powershell
Get-DomainUser -UACFilter PASSWD_NOTREQD | Select-Object samaccountname,useraccountcontrol
```

{% endcode %}

### ACL

{% code overflow="wrap" %}

```powershell
$sid = Convert-NameToSid <USER>
Get-DomainObjectACL -ResolveGUIDs -Identity * | ? {$_.SecurityIdentifier -eq $sid} | Select @{n='SID_Name';e={Convert-SidToName $_.SecurityIdentifier}}, ActiveDirectoryRights, @{n='ObjSID_Name';e={Convert-SidToName $_.ObjectSID}} [| Format-List *]
```

{% endcode %}

### RDP

{% code overflow="wrap" %}

```powershell
Get-NetLocalGroupMember -ComputerName <PC_NAME> -GroupName "Remote Desktop Users"
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
$computers = Get-ADComputer -Filter * | Select-Object -ExpandProperty Name
foreach ($computer in $computers) {
    Get-NetLocalGroupMember -ComputerName $computer -GroupName "Remote Desktop Users"
}
```

{% endcode %}

### WinRM

{% code overflow="wrap" %}

```powershell
Get-NetLocalGroupMember -ComputerName <PC_NAME> -GroupName "Remote Management Users"
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
$computers = Get-ADComputer -Filter * | Select-Object -ExpandProperty Name
foreach ($computer in $computers) {
    Get-NetLocalGroupMember -ComputerName $computer -GroupName "Remote Management Users"
}
```

{% endcode %}

## SYSVOL

The SYSVOL share can be a treasure trove of data, especially in large organizations. We may find many different batch, VBScript, and PowerShell scripts within the scripts directory, which is readable by all authenticated users in the domain. It is worth digging around this directory to hunt for passwords stored in scripts.

```powershell
ls \\<DOMAIN/DC>\SYSVOL\
```

### GPP Passwords

When a new Group Policy Preferences is created, an .xml file is created in the SYSVOL share.\
These files can contain an array of configuration data and defined passwords.\
The `cpassword` attribute value is AES-256 bit encrypted, but Microsoft published the [AES private key](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gppref/2c15cbf0-f086-4c74-8b70-1f2fa45dd4be?redirectedfrom=MSDN) on MSDN, which can be used to decrypt the password.

The [gpp-decrypt](https://www.kali.org/tools/gpp-decrypt/) utility can be used to decrypt the cpassword

```
gpp-decrypt <VALUE>
```

You can also use several tools to locate the GPP and return the decrypted cpassword value.\
[Get-GPPPassword.ps1](https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Get-GPPPassword.ps1), the GPP Metasploit Post Module, other Python/Ruby scripts, CrackMapExec, etc.

It is also possible to find passwords in files such as Registry.xml when autologon is configured via Group Policy. We can use [CrackMapExec](https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/modules/gpp_autologin.py) or [Get-GPPAutologon.ps1](https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Get-GPPAutologon.ps1).

```bash
crackmapexec smb <IP> -u <USER> -p <PASS> -M gpp_autologin
```

## Cypher Query Bloodhound

#### WinRM

{% code overflow="wrap" %}

```cypher
MATCH p1=shortestPath((u1:User)-[r1:MemberOf*1..]->(g1:Group)) MATCH p2=(u1)-[:CanPSRemote*1..]->(c:Computer) RETURN p2
```

{% endcode %}

#### SQLAdmin

{% code overflow="wrap" %}

```cypher
MATCH p1=shortestPath((u1:User)-[r1:MemberOf*1..]->(g1:Group)) MATCH p2=(u1)-[:SQLAdmin*1..]->(c:Computer) RETURN p2
```

{% endcode %}

#### Sessions

{% code overflow="wrap" %}

```cypher
MATCH p = (c:Computer)-[:HasSession]->(m:User) RETURN p
```

{% endcode %}

## Kerberos Double Hop Problem

When connecting to a remote system using tools like `Evil-WinRM`, the user's Kerberos TGT is not forwarded to the remote session, but only the TGS (see with `klist` command). This means the remote system can authenticate the user for the initial session, but it cannot access other resources in the domain on behalf of the user because the TGT is missing. As a result, commands that require access to additional domain resources, like querying Active Directory with PowerView, fail since there's no way to prove the user's identity beyond the initial connection.\
If **unconstrained delegation** is enabled on a server, it is likely we won't face the "Double Hop" problem. In this scenario, when a user sends their TGS ticket to access the target server, their TGT ticket will be sent along with the request.

To avoid it, the simplest way is to use RDP.

### Workaround with PSCredential Object

We can also connect to the remote host via host A and set up a PSCredential object to pass our credentials again.

{% code overflow="wrap" %}

```powershell
$SecPassword = ConvertTo-SecureString '<PASSWORD>' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('<DOMAIN>\<USER>', $SecPassword)
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
# Now we can use it 
import-module .\PowerView.ps1
get-domainuser -spn -credential $Cred | select samaccountname
```

{% endcode %}

### Workaround with Register PSSession Configuration

Here we have another option to change our setup to be able to interact directly with the DC or other hosts/resources without having to set up a PSCredential object and include credentials along with every command (which may not be an option with some tools).

{% code overflow="wrap" %}

```powershell
# WinRM session on the remote host
Enter-PSSession -ComputerName <PCName> -Credential <DOMAIN>\<USER>
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
Register-PSSessionConfiguration -Name <myNameSess> -RunAsCredential <DOMAIN>\<USER>
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
Restart-Service WinRM
# This will kick us out, so we'll start a new PSSession using myNameSess
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
Enter-PSSession -ComputerName <PCName> -Credential <DOMAIN>\<USER> -ConfigurationName  <myNameSess>
```

{% endcode %}

{% code overflow="wrap" %}

```powershell
# Now we can run command without PSCredential 
get-domainuser -spn | select samaccountname
```

{% endcode %}

<details>

<summary>Note!</summary>

We cannot use `Register-PSSessionConfiguration` from an evil-winrm shell because we won't be able to get the credentials popup. Furthermore, if we try to run this by first setting up a PSCredential object and then attempting to run the command by passing credentials like `-RunAsCredential $Cred`, we will get an error because we can only use `RunAs` from an elevated PowerShell terminal. Therefore, this method will not work via an evil-winrm session as it requires GUI access and a proper PowerShell console. Furthermore, we could not get this method to work from PowerShell on a Parrot or Ubuntu attack host due to certain limitations on how PowerShell on Linux works with Kerberos credentials. This method is still highly effective if we are testing from a Windows attack host and have a set of credentials or compromise a host and can connect via RDP to use it as a "jump host" to mount further attacks against hosts in the environment.

</details>

### [Other Workaround](https://posts.slayerlabs.com/double-hop/)

We can also use other methods such as CredSSP, port forwarding, or injecting into a process running in the context of a target user (sacrificial process).

## Useful Tools

<table><thead><tr><th width="188">Tool</th><th>Details</th></tr></thead><tbody><tr><td><a href="https://github.com/61106960/adPEAS">adPEAS</a></td><td>Powershell tool to automate Active Directory enumeration.<br><code>Import-Module .\adPEAS.ps1</code> <br><code>Invoke-adPEAS</code> <br><code>Invoke-adPEAS -Module [Domain/Rights/GPO/ADCS/Creds/Delegation/Accounts/Computer/Bloodhound -Scope All]</code></td></tr><tr><td><a href="https://learn.microsoft.com/en-us/sysinternals/downloads/psloggedon">PsLoggedOn</a></td><td>Allows you to see which users are connected to a specified computer. For this to work, the machine must have Remote Registry enabled. <em>(we can get false positives because we are not sure that Remote Registry is enabled on the target)</em><br><code>.\PsLoggedon.exe \&#x3C;COMPUTER_NAME></code></td></tr><tr><td><a href="https://github.com/SnaffCon/Snaffler">Snaffler</a></td><td>Tool to acquire credentials or other sensitive data in an Active Directory environment by searching between shares.<br><code>Snaffler.exe -s -d &#x3C;DOMAIN> -o &#x3C;OUTPUT_FILE.log> -v data</code></td></tr><tr><td><a href="https://github.com/Group3r/Group3r">Group3r</a></td><td>A tool purpose-built to find vulnerabilities in Active Directory associated Group Policy.<br><code>group3r.exe -f &#x3C;OUTPUT_FILE.log></code> </td></tr><tr><td><a href="https://www.pingcastle.com/documentation/">PingCastle</a></td><td>Powerful tool that evaluates the security posture of an AD environment and provides us the results in several different maps and graphs.<br><code>PingCastle.exe --help</code>  <em>(Terminal User Interface)</em></td></tr><tr><td><a href="https://github.com/adrecon/ADRecon">ADRecon</a></td><td>Tool which gathers information about the Active Directory and generates a report which can provide a holistic picture of the current state of the target AD environment.<br><code>.\ADRecon.ps1</code></td></tr></tbody></table>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ivalexev.gitbook.io/rednote/pentesting-process/active-directory/enumeration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
