Skip to main content

Kerberos Authentication

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