Enumerating Machines

Finding AD-Joined Hosts

The first step you can do is use simple tools to enumerate the internal network, identifying hosts with SMB ports open, as this is the main service used for communication between AD-joined machines and servers. There are 3 main alternatives for that:

nmap

Do a simple port scan across the entire network to find hosts with port 139 and 445 open:

nmap -p139,445 -Pn <IP_RANGE> -oG smb.txt --open && cat smb.txt | grep Up | cut -d ' ' -f2 > smbHosts.txt

Demo on GOAD Lab
nmap -p139,445 -Pn 192.168.56.0/24 -oG smb.txt --open && cat smb.txt | grep Up | cut -d ' ' -f2 > smbHosts.txt

finding_ad_joined_hosts_nmap

netexec

Useful both to enumerate the machines and also identify their hostnames within the domain (which could point to their role):

nxc smb <IP_RANGE>

Demo on GOAD Lab
nxc smb 192.168.56.0/24

finding_ad_joined_hosts_nxc

responder

In this case, this is a passive enumeration method. We can use responder in Analyze mode to listen to traffic sent in broadcast within the internal network. Active Directory environments usually send a bunch of traffic in broadcast to resolve names, and with those communications you can gather details about the machines.

sudo responder -I <NETWORK_INTERFACE> -A

# Example: sudo responder -I eth0 -A

Demo on GOAD Lab
sudo responder -I eth1 -A

finding_ad_joined_hosts_responder

cat /usr/share/responder/logs/Responder-Session.log | grep 192.168.56
cat /usr/share/responder/logs/Responder-Session.log | grep 192.168.56 | grep hostname

finding_ad_joined_hosts_responder_results

Enumerating Available Services

With the list of hosts in hand, leave a network scan running on all of them, so you have a list of available services afterwards.

nmap -T4 -p- -Pn -v -iL smbHosts.txt

It’s also useful to enumerate which of them have the WinRM and RDP services available:

# WinRM
nmap -p5985 -Pn <IP_RANGE> -oG winrm.nmap --open && cat winrm.nmap | grep Up | cut -d ' ' -f2 > winrmHosts.txt

# RDP
nmap -p3389 -Pn <IP_RANGE> -oG rdp.nmap --open && cat rdp.nmap | grep Up | cut -d ' ' -f2 > rdpHosts.txt

Demo on GOAD Lab
nmap -p5985 -Pn -iL smbHosts.txt -oG winrm.nmap --open && cat winrm.nmap | grep Up | cut -d ' ' -f2 > winrmHosts.txt
nmap -p3389 -Pn -iL smbHosts.txt -oG rdp.nmap --open && cat rdp.nmap | grep Up | cut -d ' ' -f2 > rdpHosts.txt

enumerating_services_winrm_nmap

Finding the Domain Controller (DC)

For our luck, usually the server hostname will be intuitive and just have DC in it, buuut there are other ways we can check.

A good one is by consulting default DNS srv entries in the domain. To query that, we first have to identify the DNS servers:

sudo nmap -sU -p53 -Pn -iL smbHosts.txt --open

If nmap is not giving good results, we can just try to do DNS queries for all IPs we found for the domain and see which of them work… Here is a quick bash script I wrote to do so, just replace the DOMAIN_FQDN in it and you’re good to go:

for ip in $(cat smbHosts.txt);do 
    results=$(nslookup -retry=0 -type=srv _ldap._tcp.dc._msdcs.<DOMAIN_FQDN> $ip | grep -Ev "timed out|no servers")
    if [ ! -z "${results}" ]; then 
        echo "Valid DNS Server found on $ip"
    fi
done

After finding the DNS Server(s), we can go ahead and do the query in one of them to check who is the DC for the domain:

nslookup -type=srv _ldap._tcp.dc._msdcs.<DOMAIN> <DNS_SERVER_IP>

# Example: nslookup -type=srv _ldap._tcp.dc._msdcs.sevenkingdoms.local 192.168.56.10

And finally, just do a simple DNS query to resolve that hostname, if you don’t know the IP for it:

nslookup <HOSTNAME> <DNS_SERVER_IP>

# Example: nslookup kingslanding.sevenkingdoms.local 192.168.56.10

Demo on GOAD Lab
for ip in $(cat smbHosts.txt);do results=$(nslookup -retry=0 -type=srv _ldap._tcp.dc._msdcs.sevenkingdoms.local $ip | grep -Ev "timed out|no servers"); if [ ! -z "${results}" ]; then echo "Valid DNS Server found on $ip" ;fi;done

finding_dc_dns_script

nslookup -type=srv _ldap._tcp.dc._msdcs.sevenkingdoms.local 192.168.56.10

finding_dc_dns

nslookup kingslanding.sevenkingdoms.local 192.168.56.10

finding_dc

Setting up DNS and Resolution

Now that we now the DC and probably the hostnames of most of the machines, we can setup our local DNS hostnames and resolution. This is specially important to avoid troubleshooting steps in a lot of the tools we use. However, as setting up the DC as our main DNS can also cause some resolution troubles for us in some cases, this is something we’ll have to test in each context and, if necessary, only enable it when we need it.

To configure the hostnames to resolve to their corresponding IP addresses, we need to edit our /etc/hosts file, inserting first the IP of the machine and after the name we want to be resolved to that (FQDN machine domain name). Multiple names can be specified to each one, and usually when inserting the DC we’ll specify both the machine name and also the domain:

# Example of entry in /etc/hosts
192.168.56.10   kingslanding.sevenkingdoms.local sevenkingdoms.local

To specify the DC or DNS server as the primary DNS resolver for our machine, we need to add its IP to the top of /etc/resolv.conf:

# Example of entry in /etc/resolv.conf
nameserver 192.168.56.10
nameserver 1.1.1.1

Enumerating Users

Testing Default Users

SMB

There are 2 default SMB accesses you should always test in AD environments: Null sessions and Guest users. The first one is simply trying to access with a null user and null password:

nxc smb smbHosts.txt -u '' -p ''

And the second is with the username Guest and no password again:

nxc smb smbHosts.txt -u 'Guest' -p ''

If any of these work you’ll probably be able to enumerate the rest of the users through SMB:

# Null Session
nxc smb <IP> -u '' -p '' --users

# Guest User
nxc smb <IP> -u '' -p '' --rid-brute

Demo on GOAD Lab
nxc smb smbHosts.txt -u '' -p ''
Null session allowed on 3 machines.

Null session allowed on 3 machines.

nxc smb 192.168.56.11 -u '' -p '' --users
User enumeration worked on one of the machines.

User enumeration worked on one of the machines.

RPC

Note that this could also happen with RPC, so test it with this protocol too:

# Null Session
rpcclient -U "" -N <IP>

# Guest User
rpcclient -U "Guest" <IP>

If any of these work, you can enumerate the rest of the users by using the enumdomusers command inside the tool.

Demo on GOAD Lab
rpcclient -U "" -N 192.168.56.11

rpcclient $> enumdomusers

LDAP

Null users are also a thing in the LDAP protocol, but here it’s called a Null Bind, and it’s usually only available in the DC or very specific servers. In this example, the domain FQDN is sevenkingdoms.local:

ldapsearch -H ldap://<IP>/ -x -s base -b 'DC=sevenkingdoms,DC=local' "(objectClass=*)" "*" +

Bruteforcing

A more intrusive, but very efficient approach, is through bruteforcing, using the Kerberos protocol. This works because Kerberos returns different messages when sending TGT requests with no pre-authentication for a user that is valid in comparison with when it’s invalid. In the first case, the KDC prompts for pre-authentication, but in the second, it responds with a PRINCIPAL_UNKNOWN error.

Because it doesn’t go through the authentication, it’s even stealthier than normal bruteforcing, and does not block accounts. The only thing it generates is a Windows event ID 4768 if Kerberos logging is enabled, which relates to when a TGT is requested.

In order to do that, I recommend trying kerbrute and the wordlists available in the statistically-likely-usernames repository, by InsideTrust. This repo contains very good wordlists that follow different username conventions, for example, the john.smith.txt wordlist follows the convention user.name.

kerbrute userenum -d <DOMAIN> --dc <DC_IP> <USER_WORDLIST>.txt -o <OUT_FILE>

Demo on GOAD Lab
./kerbrute userenum -d sevenkingdoms.local --dc 192.168.56.10 ./got_characters.lst -o brute_users.out

user_enum_bruteforce

Hunting for Valid Accounts with Password Spraying

If you manage to find valid usernames with the bruteforce process, you can try out password spraying with some possible passwords being used. When doing that, be very careful not to exceed the failed authentication limit and block any accounts (I know we can’t know the precise limit yet, but just guess it’s low). Some tips for the process:

  • Try using the username as password:

    # Sprays each user with his username as password (1 try each)
    nxc smb <DC_IP> -u validUsers.txt -p validUsers.txt --no-bruteforce --continue-on-success
    
  • If you already discovered some password in another component of that company, or through leaked credentials for example, or just had an idea that they might use some kind of pattern for passwords, try that too:

    # Sprays each user with a specific password
    nxc smb <DC_IP> -u validUsers.txt -p 'Company@2024!' --continue-on-success
    

Demo on GOAD Lab
nxc smb 192.168.56.10 -u validUsers.txt -p 'Heartsbane' --continue-on-success

nxc smb 192.168.56.10 -u validUsers.txt -p validUsers.txt --no-bruteforce --continue-on-success

hunting_accounts_spray_users

Going forwards

From this point on we have some options to go to:

  • First, we can try carrying out Unauthenticated Attacks. Some of them will require us to have a list of valid usernames, but there are options for when we haven’t been able to enumerate that too;
  • Buuut, if we found a valid AD account (through null sessions, guest users, password spraying…) we can attempt Authenticated Enumeration aaand Authenticated Attacks too!