vADC Docs

HowTo: Decode and log the username in an NTLM connection

by on ‎03-25-2013 07:32 AM (5,858 Views)

This user-contributed article describes how to parse and decode credentials in NTLM authentication. You can then log these credentials for audit reasons.


It was a requirement that we needed to log all usernames against incoming requests, so that should there be a case of misuse, we would know which user generated the request, and which workstation was used.   This rule extracts the NTLM authentication details and logs them to the Stingray Event Log.

NTLM background

NTLM uses three different NTLM message types to complete a handshake for a given request. These are:

  • NTLM Type-1 Message: This contains the hostname, the domain name, and the fact that it is a NTLM request type1, to initiate the correct stage in the handshake.
  • NTLM Type-2 Message: This contains a NTLM challenge from the server.
  • NTLM Type-3 Message: This contains DOMAIN, USERNAME, and Workstation\Hostname.

For further information on the NTLM handshake go here: http://davenport.sourceforge.net/ntlm.html

The Plan

To fulfill the requirements and to ease load on the servers we will ignore Type-1/Type-2 messages, and instead just decode/process Type-3 Messages.

We want to make the data available to the traffic manager to log, but still keep the request secure, i.e. not transmit the decoded information in headers. We will use the connection.data.set($key, $value) function; to access the values, use the %{key}d macro in the Virtual Server -> Edit -> Request Logging -> log!format parameter.

The Code

Please note Basic Authentication is included as a just in case you need it:


$h = http.getHeader( "Authorization" );


# Although none of our websites use this,i have still included Basic Authenticaton.


if( string.startsWith( $h, "Basic " ) ) {


   $enc = string.skip( $h, 6 );


   $userpasswd = string.base64decode( $enc );


   log.info( "Basic: User used: ".$userpasswd );


}



# Test to see if the Authorization token, begins with NTLM


if(string.startsWith( $h, "NTLM " )) {


   # Skip Authorization Token Header 'NTLM ', so we can decode just token.


   $enc = string.skip( $h, 5 );


   $ntlmpacket= string.base64decode( $enc ); #Decode TOKEN


  


   # Username, DOMAIN, and Workstation are only in the third NTLM handshake.


   # So we ignore the initial handshakes and test for the third, '3'.


   if((string.bytesToInt(string.substring($ntlmpacket, 8, 8))) == 3) {


      # Extract header fields.


      $B = string.substring($ntlmpacket, 28, 51);


     


      # Select and decode the Domain Offset, Domain length, Username length and Workstation length.


      # Numbers are little-endian, so can't just use string.bytesToInt() on the entire substring


      $dLen = ((string.bytesToInt(string.substring($B, 0, 0)))


         +((string.bytesToInt(string.substring($B, 1, 1)))*256));


      $dOff = ((string.bytesToInt(string.substring($B, 4, 4)))


         +((string.bytesToInt(string.substring($B, 5, 5)))*256)


         +((string.bytesToInt(string.substring($B, 6, 6)))*(256*256))


         +((string.bytesToInt(string.substring($B, 7, 7)))*(256*256*256)));


      $uLen = ((string.bytesToInt(string.substring($B, 8, 8)))


         +((string.bytesToInt(string.substring($B, 9, 9)))*256));


      $wLen = ((string.bytesToInt(string.substring($B, 16, 16)))


         +((string.bytesToInt(string.substring($B, 17, 17)))*256));


      # The data we are after is back to back, so we only need the initial offset;


      # we can decode the rest with the lengths of the previous key.


      connection.data.set( "NTLM_Domain", string.substring($ntlmpacket, $dOff, $dOff+$dLen-1 ) );


      connection.data.set( "NTLM_User", string.substring($ntlmpacket, $dOff + $dLen, $dOff + $dLen + $uLen - 1 ) );


      connection.data.set( "NTLM_Workstation", string.substring($ntlmpacket, $dOff + $dLen + $uLen, $dOff + $dLen + $uLen + $wLen - 1 ) );


   }


}


You can log the NTLM authentication parameters in the Request log using the appropriate log format macros: %{NTLM_Domain}d etc.