Skip to main content

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
  • 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.

image.png

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:

  1. Command Line: The -c option with kinit always takes the highest priority.

  2. Environment Variable: The KRB5CCNAME environment variable has the next highest priority.

  3. Configuration File: The default_ccache_name setting in krb5 configuration file.

  4. 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

List shares on the server

smbclient --use-kerberos=required //FS01.ad.lab/MyShare

Map a share

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 ldapsearchnetsmbclient, 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