NTLM

This is Microsoft's NT/LanManager connection oriented authentication protocol - sometimes referred to as NT Challenge/Response (NTCR). This protocol is commonly encountered on networks that use Microsoft products for firewalls, intranet servers and web proxy servers. This is also the authentication protocol used for SMB connections, which is what Windows and Samba use for file and printer sharing. The basis of the scheme is a challenge-response protocol where the client sends the server a request that contains information about the NT domain and the client hostname. The server then replies with a random nonce value and the client then creates an authentication response using the user name, user's password and the nonce value. This is similar to the HTTP Digest authentication scheme in that the password is not sent across the network, but the client can demonstrate to the server that it knows the user's password.

Well - that's the theory. In fact there are a number of weaknesses in the NTLM protocol. In fact the password is used twice to generate an NT password hash and a LM password hash. The LM hash takes the user's password, capitalises it, and sets the length to 14 characters by padding with nul characters. This value is then used as a DES key to encrypt a magic value ("\x4b\x47\x53\x21\x40\x23\x24\x25") thus providing a 16-byte value to be sent to the server.

The NT password hash uses the MD4 message-digest (RFC 1320) to hash the Unicode version of the user's password.

Pat Thoyts is currently working on supporting this scheme within Tcl as part of the autoproxy package. MD4 hashing is now part of tcllib, as is DES.


CAU wrote - There's a simple way to get around NTLM using basic authentication with Jos Decoster's http::geturl modification at HTTPS (Note: check for repeated lines in the code!) and a python-based authentication hack - ntlmaps[L1 ].

A big advantage of ntlmaps for me, is it can make http requests behave like they were issued from a web-browser, which is an unexpected bonus when trying to tunnel through firewalls!

If anyone knows of a pure Tcl NTLM hack, I'd be very interested.

PT 29-Apr-2005: There is NTLM client code in the SASL module in tcllib now.


MJ 8-Feb-2007: It is possible to use the NTLM client code tcllib to use NTLM proxies with sockets (proxy CONNECT request) and HTTP GETs (proxy GET request). Implementing the proxy authentication for http is quite messy at the moment because the http package doesn't support keepalives. If keepalives are supported the authentication scheme would be:

  • send GET request to proxy
  • proxy responds with a 407 (authentication required)
  • use a keepalive connection to authenticate
  • resend the GET request

As an example of the steps required to authenticate with an NTLM proxy look at the following code. The code is not robust and only tested on a single proxy server. Moreover it will fail in plenty of scenarios (e.g. 301 moved responses) Another issue I encountered is that the proxy can either expect the first authentication message from the client in a Proxy-Authorization: NTLM ... or in a Proxy-Authorization: Negotiate ... header. Summarizing the code below is a proof of concept and it is not intended to provide a robust http client implementation.

The code can be quite easily adapted to send a CONNECT request to the proxy and return the socket after authentication completes. This allows normal socket traffic as far as allowed by the proxy (usually only SSL encrypted traffic) to pass through the proxy.

 package require SASL::NTLM
 package require base64

 set url url
 regexp {.*//(.*?)/} $url -> host
 set proxy proxyserver
 set port port
 set user username
 set pass password
 set file filename ; # file to store the page

 proc Callback {context command args} {
    switch -exact -- $command {
        username    { return $::user }
        password { return $::pass }
        realm    { return "" }
        hostname { return [info host] }
        default  { return -code error unxpected }
    }
 }


 proc main {{mech NTLM}} {
  set challenge ""
  set s [socket $::proxy $::port]
  fconfigure $s -blocking 1 -buffering line -translation crlf
  set ctx [SASL::new -mechanism $mech -callback Callback]
  while {1} {
    set more_steps [SASL::step $ctx $challenge]
    set response [SASL::response $ctx]
    puts "----"
    puts "Sending NTLM message to proxy"
    puts "NTLM key: [base64::encode -wrapchar {} $response]"
    puts $s "GET $::url HTTP/1.1\nHost: $::host\nProxy-Authorization: NTLM [base64::encode -wrapchar {} $response]\n"
    puts "----"
    if {!$more_steps} {break}
    puts "Handling proxy reply"

    set response {}
    while {[set line [gets $s]] ne "" } {
      append response $line\r\n
      # break if headers done
      if {$line == ""} {
        break
      }
    }
    set length {}
    regexp {Content-Length: ([0-9]*)} $response -> length
    puts "response length: $length"
    # puts $response
    if {$length eq {} } {
      set body [read $s]
    } else {
      set body [read $s [expr $length-1] ]
    }
    # puts $body
    regexp {Proxy-Authenticate: NTLM (.*)\r\n} $response -> challenge
    puts "Server challenge: $challenge"
    set challenge [base64::decode $challenge]
   }
   puts "Handshake completed"
   puts "----"
   SASL::cleanup $ctx
   set response {}
   while {[set line [gets $s]] ne "" } {
    append response $line\r\n
    # break if headers done
    if {$line == ""} {
      break
     }
   }
   set length {}
   regexp {Content-Length: ([0-9]*)} $response -> length
   puts "response length: $length"
   puts $response
   fconfigure $s -translation binary -buffering full -blocking 1
   # if length is not specified, server will close the connection after this page
   if {$length eq {} } {
    set body [read $s]
   } else {
     set body [read $s $length ]
   }
   close $s
   set f [open $::file w]
   fconfigure $f -translation binary
   puts $f $body
   close $f
 }

 main

MJ - Note that Internet Explorer uses NTLM negotiation and the SSPI API[L2 ] to create NTLM tokens based on your current Microsoft Windows (domain) logon credentials. This will allow proxy authorization without providing credentials. What needs to be done for this is:

  • Create a wrapper around some of the SSPI API calls
  • Use the API calls to generate the base64 encoded keys.

For an implementation for Ruby see http://rubyforge.org/projects/rubysspi/

schlenk You don't need a SSPI wrapper if you just want NTLM. You just need to hack a bit of tcllib asn1 to decode the SPNEGO package and feed it your NTLM token instead of a Kerberos token.


MJ - I have a small extension dll now [L3 ] that can use SSPI to bypass a proxy without supplying credentials. The file at the URL is an initial version that needs to be packaged and cleaned up. It also doesn't contain any documentation, but the example script should be enough to get you started.

The download also includes the TEA3 based source tree so it easy to build a binary yourself.


APN Also see SASL and TWAPI for NTLM authentication using TWAPI's SSPI support.