Skip to main content

Using Faketime for Ad-Hoc Kerberos Authentication

Installing Faketime

sudo apt install faketime
faketime -h

This will run the specified 'program' with the given 'arguments'.
The program will be tricked into seeing the given 'timestamp' as its starting date and time.
The clock will continue to run from this timestamp. Please see the manpage (man faketime)
for advanced options, such as stopping the wall clock and make it run faster or slower.

Example Usage

Running bloodhound-python in my local lab, the KDC on the target domain controller throws a clock skew error. So, we'll use ntpdate to synchronize with the KDC and feed that to faketime and then make the connection.

Before

bloodhound-python -c All -u joan.hesther -p 'madison' -d ad.lab -ns 10.80.80.2

image.png

KRB_AP_ERR_SKEW(Clock skew too great) indicates the Kerberos client (Kali in this case) and the KDC have too wide a drift between their clocks. To resolve, we can run an ad-hoc ntpdate sync and feed that to faketime to spoof the clock to bloodhound-python (or whichever Kerberos-authenticated app you're using).

Afterimage.png

faketime "$(ntpdate -q dc01.ad.lab | cut -d ' ' -f 1,2)" \
bloodhound-python -c All -u joan.hesther -p 'madison' -d ad.lab -ns 10.80.80.2

Internal or via ligolo-ng (or similar) layer 3 tunnel

proxychains -q faketime "$(ntpdate -q dc01.ad.lab | cut -d ' ' -f 1,2)" \ 
bloodhound-python -c All -u joan.hesther -p 'madison' -d ad.lab -ns 10.80.80.2 --dns-tcp

External via SOCKS proxy -- e.g. chisel or ssh

Function for Convenience

You can add this to your shell .rc file -- e.g. .zshrc -- so that it is sourced in every time you open your terminal.

function faketime () {

  # Store the time server globally for future function calls
  if [[ -z "$TIME_SERVER" ]] ; then
    if [[ $(echo $0) -eq '-zsh' ]] ; then
      # zsh-style user prompt
      read TIME_SERVER?"Enter the FQDN or IP of the time server: "
    else
      # bash-style user prompt
      read -p "Enter the FQDN or IP of the time server: " TIME_SERVER
    fi
    export TIME_SERVER="$TIME_SERVER"
  fi

  # Make sure the user has passed a valid target server
  if ! /usr/sbin/ntpdate -t 1 -q "$TIME_SERVER" > /dev/null ; then
    echo "Connectivity test to time server failed: ${TIME_SERVER}"
    unset TIME_SERVER
    return
  # Target NTP server is reachable, proceed
  else
    /usr/bin/faketime "$(/usr/sbin/ntpdate -q $TIME_SERVER | /usr/bin/cut -d ' ' -f 1,2)" $@
  fi
  
}

This function does the following:

  1. Checks if a global $TIME_SERVER variable containing the KDC FQDN or IP has been set
    • And if not set, prompt the user to set it
  2. Do a quick NTP client connectivity check to the specified server
    • If connectivity fails, throw an error
    • Otherwise proceed to invoke the process and arguments designated by $@

Usage of faketime after these changes looks like:

faketime <process> [args]

Removes the need for adding the sub-shell call to ntpdate and cut

faketime impacket-getTGT -hashes ':NT_HASH_HERE' -dc-ip "10.10.100.20" 'ad.lab/john.doe'@dc01.ad.lab
KRB5CCNAME=john.doe.ccache faketime net rpc user password 'jane.doe' --use-kerberos=required -S "$TIME_SERVER"