Kerberos Authentication from Kali
Install Kerberos Client Packages
Upon installing, you will see an interactive prompt for information. The answers you provide populate the /etc/krb5.conf file.
Production Environments
- Answer the prompts with the FQDN of the KDC and according to your needs
Testing Environments / CTF / Labs
- Answer the prompts with junk data -- e.g.
KDC.INTERNAL - This will ensure that you don't send data to unintended servers
sudo apt install -y krb5-user libsasl2-modules-gssapi-mit
Authentication Requirements
Example Environment
- Kerberos realm:
ad.lab - Kerberos KDC:
DC01.ad.lab
Name Resolution
Option A) Internal DNS Resolver
More common in production environments
- Your Linux host sends a DHCP Discover request and is configured with:
- IP + Network mask + Default gateway + local domain of
ad.lab - DNS server address
- IP + Network mask + Default gateway + local domain of
- Internal DNS resolver will tell Linux host the IP of
DC01.ad.lab
- Linux host will contact the KDC accordingly
Option B) Hosts File - /etc/hosts
More common in test environments and CTFs where only temporary name resolution is required
You must ensure the entries in the /etc/hosts file are defined in a specific order:
192.168.100.2 DC01.ad.lab ad.lab
✅ Correct order in /etc/hosts
192.168.100.2 ad.lab DC01.ad.lab
❌ Incorrect order in /etc/hosts
Configuration Files
The realm name AD.LAB -- or whichever domain you're authenticating to -- must be in ALLCAPS in certain parts of the realm configuration
Default Configuration File
The default configuration file for Kerberos clients is at /etc/krb5.conf
Ideally, you'd use this file when pointing to a permanent Kerberos server. If you only need to authenticate temporarily to a Kerberos server, then see below on how to setup a custom configuration.
Custom Configuration File
Use a custom configuration file when working in test environments and CTFs
Create a Working Directory
mkdir -p /tmp/ad.lab
cd /tmp/ad.lab
Create the Configuration File
LOWER_REALM='ad.lab'
Define the target domain in lowercase
UPPER_REALM=$(echo "$LOWER_REALM" | tr '[:lower:]' '[:upper:]')
Convert the domain to uppercase
DC_HOSTNAME='DC01'
Define the hostname of the target domain controller
cat << EOF | sed \
-e "s/{{REALM_PLACEHOLDER}}/$UPPER_REALM/g" \
-e "s/{{realm_placeholder}}/$LOWER_REALM/g" \
-e "s/{{dc_hostname}}/$DC_HOSTNAME/g" > custom_krb5.conf
[libdefaults]
default_realm = {{REALM_PLACEHOLDER}}
dns_lookup_realm = true
dns_lookup_kdc = true
[realms]
{{REALM_PLACEHOLDER}} = {
kdc = {{dc_hostname}}.{{realm_placeholder}}
admin_server = {{dc_hostname}}.{{realm_placeholder}}
default_domain = {{realm_placeholder}}
}
[domain_realm]
{{realm_placeholder}} = {{REALM_PLACEHOLDER}}
.{{realm_placeholder}} = {{REALM_PLACEHOLDER}}
EOF
Substitute the placeholders with the data defined above
The KRB5_CONFIG environment variable shown below only exists in your current terminal window / tab. It ceases to exist when you close the terminal window / tab. Any other terminal windows and tabs that are currently open or newly opened will require that the variable be reinitialized.
export KRB5_CONFIG="/tmp/ad.lab/custom_krb5.conf"
Use environment variable to force Kerberos clients to use custom realm configuration
echo "$KRB5_CONFIG"
Ensure the variable has been initialized with the path to the configuration file
Function to create custom KRB5 config (SHOW/HIDE)
You can add this to your shell .rc file and invoke the customkrb5 command as-needed
The echo output uses some ANSI color escapes, so you can add those to your environment or remove them from the echo strings if needed.
function customkrb5 () {
if ! [ $# -eq 2 ] ; then
echo "Usage example: $0 domain.tld DC01.domain.tld"
return
else
LOWER_REALM=$(echo "$1" | tr '[:upper:]' '[:lower:]')
UPPER_REALM=$(echo "$LOWER_REALM" | tr '[:lower:]' '[:upper:]')
DC_HOSTNAME=$(echo "$2" | cut -d '.' -f 1)
KRB5_FILE="$PWD/custom_krb5.conf"
if ! [ -f "$KRB5_FILE" ]; then
touch "$KRB5_FILE" 2>/dev/null
if [ $? -ne 0 ] ; then
echo -e "\n${RED}[-] Unable to create Kerberos configuration file ${KRB5_FILE}.${RESTORE}"
echo -e "${RED}[-] Please ensure you can write to current directory.${RESTORE}"
return
fi
fi
echo '[libdefaults]' > "$KRB5_FILE"
echo " default_realm = ${UPPER_REALM}" >> "$KRB5_FILE"
echo ' dns_lookup_realm = true' >> "$KRB5_FILE"
echo ' dns_lookup_kdc = true\n' >> "$KRB5_FILE"
echo '[realms]' >> "$KRB5_FILE"
echo " ${UPPER_REALM} = {" >> "$KRB5_FILE"
echo " kdc = ${DC_HOSTNAME}.${LOWER_REALM}" >> "$KRB5_FILE"
echo " admin_server = ${DC_HOSTNAME}.${LOWER_REALM}" >> "$KRB5_FILE"
echo " default_domain = ${LOWER_REALM}" >> "$KRB5_FILE"
echo '}\n' >> "$KRB5_FILE"
echo '[domain_realm]' >> "$KRB5_FILE"
echo " ${LOWER_REALM} = ${UPPER_REALM}" >> "$KRB5_FILE"
echo " .${LOWER_REALM} = ${UPPER_REALM}" >> "$KRB5_FILE"
echo -e "\n${GREEN}[+] Custom KRB5.conf file created at: ${RESTORE}${YELLOW}${KRB5_FILE}${RESTORE}"
echo -e "${GREEN}[+] Exported environment variable: ${RESTORE}${YELLOW}export KRB5_CONFIG=${KRB5_FILE}${RESTORE}"
export KRB5_CONFIG="$KRB5_FILE"
return
fi
}
Kerberos Tickets
Get a New TGT
kinit john.doe
Begin authentication as john.doe@ad.lab to the KDC using kinit
klist
If authentication is successful, a TGT should be cached and visible when running the klist command
When running klist, you'll see that TGT/TGS are cached at /tmp/krb5cc_????. In this example, the ticket for john.doe@ad.lab is cached at /tmp/krb5cc_1001, because my UID on my Linux host is 1001.
Changing the Kerberos Client Save Path (SHOW/HIDE)
When running kinit, the default is to save tickets at /tmp/krb5cc_%{uid}, where %{uid} is the user ID number of the user running kinit.
echo "Your user ID is: $(id --user)"
Run this command to see your current UID on your Linux host
You can specify a custom save path for kinit in a few ways, based on precedence:
-
Command Line: The
-coption withkinitalways takes the highest priority. -
Environment Variable: The
KRB5CCNAMEenvironment variable has the next highest priority. -
Configuration File: The
default_ccache_namesetting inkrb5configuration file. -
Hardcoded Default: The traditional default (e.g.,
/tmp/krb5cc_%{uid})
Here are examples of each by precedence:
Command Line
kinit -c "/tmp/john.doe.ccache" john.doe
ls -l /tmp/john.doe.ccache
Since this ticket was created using a custom path, klist will not automatically discover it. You'll have to use the KRB5CCNAME variable as shown below in the Working with Tickets section.
KRB5CCNAME=/tmp/john.doe.ccache klist
Environment Variable
export KRB5CCNAME=/tmp/jane.doe.ccache
kinit jane.doe
klist
klist shows jane.doe TGT data and cache path of /tmp/jane.doe.ccache
Configuration File
You can add a line under the [libdefaults] section of /etc/krb5.conf or your custom KRB5.conf stored in $KRB5_CONFIG.
[libdefaults]
default_ccache_name = FILE:/home/%{username}/.cache/krb5.ccache
kinit domain.admin
klist
Should show Ticket cache: FILE:/home/ben/.cache/krb5.ccache
assuming your username is ben on your Linux host
Default Behavior
If you've done none of the above, then the default cache will be used as described at the top of this expanding block.
Working with Tickets
Setting Persistent Tickets
Using the export command, we can set a path to ticket that we want to use persistently for all Kerberos-related tasks. As long as the KRB5CCNAME variable exists, and holds the path to a valid ticket, this ticket will be used.
kinit jane.doe
You run kinit jane.doe, which prompts for jane.doe@ad.lab password
Authentication is successful and overwrites any existing tickets at /tmp/krb5cc_1001
cp /tmp/krb5cc_1001 /tmp/kerberos_tickets/jane.doe.ccache
You make a copy of jane.doe ticket and cache it in /tmp/kerberos_tickets for future use
kinit john.doe
You run kinit john.doe, which prompts for john.doe@ad.lab password
Authentication is successful and kinit overwrites /tmp/krb5cc_1001 with john.doe TGT
export KRB5CCNAME=/tmp/kerberos_tickets/jane.doe.ccache
You want to use jane.doe@ad.lab ticket again, so you run the export command
Kerberos client applications will respect whichever path is stored in KRB5CCNAME
Ad-Hoc Tickets
Using the KRB5CCNAME variable in this way takes precedence over any other method. The KRB5CCNAME variable does not persist once the processes in <command> is terminated.
KRB5CCNAME=/tmp/kerberos_tickets/domain.admin.ccache <command> [args]
You had a ticket for domain.admin@ad.lab backed up at this path, use this ticket just this once
See below for some example applications to use in place of <command> [args]
Destroy Cached Tickets
If you have not run export KRB5CCNAME=/path/to/ticket.ccache, then kdestroy will use the default ticket path, which is /tmp/krb5cc_%{uid} or in my case, /tmp/krb5cc_1001.
If you have declared export KRB5CCNAME=/path/to/ticket.ccache, then kdestory will destroy this ticket. Ensure you are destroying the desired ticket!
klist
List the currently selected Kerberos ticket
kdestroy
Remove all data for this Kerberos ticket
klist
Running this command again should result in no credentials being returned
Example Applications Using Kerberos
Recall that the KRB5CCNAME variable has been initialized to use john.doe TGT at /tmp/krb5cc_1001, which causes these programs to automatically read the TGT at this path
SSH
ssh -K john.doe@ad.lab@FILESRV01.ad.lab
Use cached ticket for john.doe at /tmp/krb5cc_1001 from previous kinit
KRB5CCNAME='/tmp/janedoe.ccache' ssh -K jane.doe@ad.lab@FILESRV01.ad.lab
Use custom cached Kerberos ticket as jane.doe
SCP
scp does not have a -K flag, so we need to pass some inline options to force GSSAPI authentication.
scp -o 'GSSAPIAuthentication=yes' \
-o 'GSSAPIDelegateCredentials=yes' \
john.doe@ad.lab@FILESRV01.ad.lab:'C:/Users/john.doe/document.txt' "$PWD"
Use cached ticket for john.doe at /tmp/krb5cc_1001 from previous kinit
KRB5CCNAME='/tmp/janedoe.ccache' scp \
-o 'GSSAPIAuthentication=yes' \
-o 'GSSAPIDelegateCredentials=yes' \
jane.doe@ad.lab@FILESRV01.ad.lab:'C:/Users/jane.doe/document.txt' "$PWD"
Use custom cached Kerberos ticket as jane.doe
smbclient
smbclient --use-kerberos=required -L //FS01.ad.lab
smbclient --use-kerberos=required //FS01.ad.lab/MyShare
ldapsearch
ldapsearch -Q -Y GSSAPI -H ldap://dc01.ad.lab -b 'DC=AD,DC=LAB' '(objectClass=user)'
Query all user objects in the domain
ldapsearch -Q -Y GSSAPI -H ldap://dc01.ad.lab -b 'DC=AD,DC=LAB' '(objectClass=group)'
Query all groups in the domain
KRB5CCNAME='/tmp/john.doe.ccache' ldapsearch -Q -Y GSSAPI -H ldap://dc01.ad.lab -b 'DC=AD,DC=LAB' '(objectClass=computer)'
Query all computer accounts in the domain using a custom .ccache ticket
group="Domain Users"
ldapsearch -Q -Y GSSAPI -H ldap://dc01.ad.lab -b 'DC=AD,DC=LAB' "(sAMAccountName=${group})"
Query members of the Domain Users group
ldapsearch -Q -Y GSSAPI -H 'ldap://dc01.ad.lab' -b 'DC=ad,DC=lab' '(objectClass=group)' | grep sAMA | cut -d ':' -f 2 | sed 's/^\ //g' > ad_groups.txt
First, output group names to a file...
while read group ; do echo -e "${group}\n" ; ldapsearch -Q -Y GSSAPI -H ldap://dc01.ad.lab -b 'DC=AD,DC=LAB' "(sAMAccountName=${group})" | grep 'member: ' ; echo '' ; done < ad_groups.txt
...then, use the file from above to loop over group names and list members
ldapmodify
ldapmodify -Q -Y GSSAPI -H ldap://dc01.ad.lab << EOF
dn: CN=jane.doe,OU=Users,DC=ad,DC=lab
changetype: modify
replace: userAccountControl
userAccountControl: 66048
EOF
Modify an account and set userAccountControl: 66048 (password never expires)
KRB5CCNAME='/tmp/john.doe.ccache' ldapmodify -Q -Y GSSAPI -H ldap://dc01.ad.lab << EOF
dn: CN=jane.doe,OU=Users,DC=ad,DC=lab
changetype: modify
add: servicePrincipalName
servicePrincipalName: cifs/cifs
EOF
Modify an account and set servicePrincipalName: cifs/cifs using a custom .ccache ticket
ldapmodify -Q -Y GSSAPI -H ldap://dc01.ad.lab << EOF
dn: CN=domain.admin,OU=Users,DC=ad,DC=lab
changetype: modify
replace: userPassword
userPassword: P@$$word123!
EOF
Modify an account and change a domain.admin password using the cached ticket
net
net rpc group MEMBERS "Domain Admins" --use-kerberos=required -S DC01.ad.lab
View members of the Domain Admins group
Communicate with the domain controller over DCE-RPC protocol using Kerberos. Many of these commands share similar naming conventions with their Windows counterparts.
net rpc group ADDMEM "Domain Admins" "jane.doe" --use-kerberos=required -S DC01.ad.lab
Add user to the Domain Admins group
KRB5CCNAME='/tmp/john.doe.ccache' net rpc user password jane.doe --use-kerberos=required -S DC01.ad.lab
Set a new password on an account using a custom .ccache ticket
NetExec
nxc smb DC01.domain.tld -d 'domain.tld' -u 'username' -p 'P@$$word123!' -k
Use a username and password with the -k flag to authenticate using Kerberos
KRB5CCNAME='/tmp/john.doe.ccache' nxc smb DC01.domain.tld -d 'domain.tld' -u 'username' -k --use-kcache
Use -k flag to indicate Kerberos authentication (requires server FQDN)
Use --use-kcache to authenticate with ticket in KRB5CCNAME
WinRM
evil-winrm-py
KRB5CCNAME='/tmp/john.doe.ccache' evil-winrm-py -k -i dc01.ad.lab
See this documentation for use as an evil-winrm alternative
evil-winrm
evil-winrm will not authenticate using Kerberos if it cannot find the Kerberos realm configuration for the target domain (see this step here, if you haven't already, to create your custom Kerberos realm configuration).
KRB5CCNAME='/tmp/john.doe.ccache' evil-winrm -i dc01.ad.lab -r ad.lab
BloodyAD
In my brief testing TGTs generated with Impacket did not play well with bloodyAD. Use kinit to get the TGT for the authenticating user.
KRB5CCNAME=/tmp/krb5cc_1001 bloodyAD --host 'DC01.ad.lab' -u 'john.doe' -k get writable --detail
Impacket
impacket-getTGT
impacket-getTGT 'ad.lab/john.doe:P@$$word123!'
Cache a new TGT as john.doe using a password
When you generate a TGT with impacket-getTGT, it creates the .ccache file with the mode 0644 or -rw-r--r--. This is too open for many of the Kerberos clients such as ldapsearch, net, smbclient, etc. So, if you see errors related to reading the .ccache file, be sure to change the mode to 0600 or -rw-------.
impacket-getTGT -hashes ':NT_HASH_HERE' 'ad.lab/john.doe'
Overpass-the-hash (NT hash) to get a new TGT as john.doe
This will save a TGT in the current working directory with the .ccache file extension. You can then use the .ccache file for future Kerberos-authenticated tasks by running export KRB5CCNAME="$PWD/file.ccache"
impacket-ticketConverter
impacket-ticketConverter '[0;ca99f8]-2-0-40e10000-t1_toby.beck@krbtgt-ZA.TRYHACKME.COM.kirbi' 't1_toby.beck.ccache'
Convert a ticket dumped from a Windows box to Linux-compatible format
export KRB5CCNAME='t1_toby.beck.ccache'
Set the Kerberos ticket in an environment variable for persistent authentication
impacket-smbclient
export KRB5CCNAME=/tmp/krb5cc_1001
impacket-smbclient -k -no-pass -dc-ip 10.80.80.2 'ad.lab/john.doe'@filesrv.ad.lab
Assuming john.doe has rights to access CIFS on the domain controller, this example demonstrates use of the existing TGT cached earlier by the kinit command
impacket-GetUserSPNs
impacket-GetUserSPNs -k -dc-host dc01.ad.lab 'AD.LAB/john.doe:P@$$word123!' -request
Using a password to complete the Kerberos authentication
KRB5CCNAME="./john.doe.ccache" impacket-GetUserSPNs -k -no-pass -dc-host dc01.ad.lab 'AD.LAB/john.doe' -request
Using a cached TGT to complete the Kerberos authentication
