NTLM Challenge Message Decoder

On premise Exchange, OWA, Skype, and AD FS servers leak information through NTLM challenge messages, including the NetBIOS and DNS domains, computer name, and server version. These services, if exposed to the internet, can aid red teams in information gathering for brute force/password spray attacks. This post will describe discovering these services and using the ntlm_challenger tool to pull relevant information.

The Tool

ntlm_challenger can be found at: https://github.com/b17zr/ntlm_challenger

Example

$ python3 ntlm_challenger.py 'https://autodiscover.hackin.club/autodiscover/autodiscover.xml' 

Target (Domain): HACKIN

Version: Server 2012 / Windows 8 (build 9200)

TargetInfo:
        MsvAvNbDomainName: HACKIN
        MsvAvNbComputerName: EXCH01
        MsvAvDnsDomainName: hackin.club
        MsvAvDnsComputerName: EXCH01.hackin.club
        MsvAvDnsTreeName: hackin.club
        MsvAvTimestamp: Nov 3, 2019 01:07:16.573170

Negotiate Flags:
        NTLMSSP_NEGOTIATE_UNICODE
        NTLMSSP_REQUEST_TARGET
        NTLMSSP_NEGOTIATE_ALWAYS_SIGN
        NTLMSSP_TARGET_TYPE_DOMAIN
        NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
        NTLMSSP_NEGOTIATE_TARGET_INFO
        NTLMSSP_NEGOTIATE_VERSION

Affected Services

Exposed Autodiscover, ActiveSync, OWA, Skype, and AD FS services can leak information through the challenge message. These HTTP services may accept NTLM authentication, so it’s possible to pull information from the NTLM challenge response.

Discovering Hosts

These services are usually on predictable subdomains. Below are some commonly used subdomain names for targeting certain services.

Exchange servers commonly use the following subdomains:

autodiscover
mail
webmail
owa

TrustedSec released a blog post titled Attacking Self-Hosted Skype for Business/Microsoft Lync Installations (by @nyxgeek) which describes a number of subdomains exposed when running Skype for Business:

lyncdiscover
lyncdiscoverinternal
dialin
scheduler
meet
lync-fe

Douglas Bienstock and Austin Baker listed a few subdomains for AD FS endpoints in their “I am AD FS and so can you” talk at Troopers 19 [slides | presentation] (I’d also include federation):

adfs
sts
fs

Discovering Paths

@nyxgeek recently released an automated tool to brute force common NTLM-protected paths given a subdomain: https://github.com/nyxgeek/ntlmscan. The tool provides a solid list for discovery, but may create unwanted noise during an engagement.

It’s possible to send targeted requests if you can identify the service being hosted. I often find Exchange and OWA services listening externally. The TrustedSec blog lists common endpoints for Lync and Skype services. The following table was taken directly from Nate Power’s talk “Hacking Corporate Em@il Systems” [slides | presentation]. The IIS Paths columns lists endpoints used by Autodiscover, ActiveSync, and OWA services.

ServiceVuln HeaderIIS Paths
Autodiscover401 Basic Auth/Autodiscover
/Autodiscover/Autodiscover.xml
ActiveSync401 Basic Auth/Microsoft-Server-ActiveSync
/Microsoft-Server-ActiveSync/default.eas
OWA302 Location
401 Basic Auth
/ECP
/EWS
/EWS/Exchange.asmx
/Exchange
/OWA

AD FS servers configured with Windows Integrated Authentication (WIA) will accept NTLM authentication at the following endpoint:

/adfs/ls/wia

NTLM Over HTTP Protocol

Brief Overview

The NTLM protocol is a three-way handshake used to authenticate a client to a server. In an Active Directory environment, this is commonly used by Windows machines when Kerberos is not an option (as is the case in external services or standalone systems). The steps taken during an authentication attempt are below:

  1. Client sends a NEGOTIATE_MESSAGE
  2. Server sends a CHALLENGE_MESSAGE containing a nonce to prevent replay attacks
  3. Client does some fancy crypto with the nonce and their password hash, then sends the AUTHENTICATE_MESSAGE

After this handshake, the server will verify that the fancy crypto was done with the client’s password hash and the client will be authenticated.

The Challenge Message

The challenge message sent by the server contains all of the information we’re interested in. Specifically, hostname, domain, and OS version information. The message can be requested pre-authentication.

We can coax the server into coughing up this message by sending a negotiate message, shown below:

GET /autodiscover/autodiscover.xml HTTP/1.1
Host: autodiscover.hackin.club
Authorization: NTLM TlRMTVNTUAABAAAAB4IIAAAAAAAAAAAAAAAAAAAAAAA=

The server response will look similar to the example:

HTTP/1.1 401 Unauthorized
Server: Microsoft-IIS/8.0
request-id: 5def1285-5b0e-ae5b-8a42-fa90218b0fb9
Set-Cookie: ClientId=CEBKNAAONVXLCNBEQ; expires=Fri, 16-Oct-2020 01:13:09 GMT; path=/; HttpOnly
WWW-Authenticate: NTLM TlRMTVNTUAACAAAADAAMADgAAAAFgokCiw/BR0FOzkIAAAAAAAAAAIwAjABEAAAABgLwIwAAAA9IAEEAQwBLAEkATgACAAwASABBAEMASwBJAE4AAQAMAEUAWABDAEgAMAAxAAQAFgBoAGEAYwBrAGkAbgAuAGMAbAB1AGIAAwAkAEUAWABDAEgAMAAxAC4AaABhAGMAawBpAG4ALgBjAGwAdQBiAAUAFgBoAGEAYwBrAGkAbgAuAGMAbAB1AGIABwAIADL+6wmIhNUBAAAAAA==
WWW-Authenticate: Negotiate
WWW-Authenticate: Basic realm="autodiscover.hackin.club"
X-Powered-By: ASP.NET
X-FEServer: EXCH01
Date: Thu, 17 Oct 2019 01:13:09 GMT
Content-Length: 0

The WWW-Authenticate header will contain the base64-encoded challenge message. Decoding this, we see the following:

$ echo -n 'TlRMTVNTUAACAAAADAAMADgAAAAFgokCiw/BR0FOzkIAAAAAAAAAAIwAjABEAAAABgLwIwAAAA9IAEEAQwBLAEkATgACAAwASABBAEMASwBJAE4AAQAMAEUAWABDAEgAMAAxAAQAFgBoAGEAYwBrAGkAbgAuAGMAbAB1AGIAAwAkAEUAWABDAEgAMAAxAC4AaABhAGMAawBpAG4ALgBjAGwAdQBiAAUAFgBoAGEAYwBrAGkAbgAuAGMAbAB1AGIABwAIADL+6wmIhNUBAAAAAA==' | base64 -d | xxd

00000000: 4e54 4c4d 5353 5000 0200 0000 0c00 0c00  NTLMSSP.........
00000010: 3800 0000 0582 8902 8b0f c147 414e ce42  8..........GAN.B
00000020: 0000 0000 0000 0000 8c00 8c00 4400 0000  ............D...
00000030: 0602 f023 0000 000f 4800 4100 4300 4b00  ...#....H.A.C.K.
00000040: 4900 4e00 0200 0c00 4800 4100 4300 4b00  I.N.....H.A.C.K.
00000050: 4900 4e00 0100 0c00 4500 5800 4300 4800  I.N.....E.X.C.H.
00000060: 3000 3100 0400 1600 6800 6100 6300 6b00  0.1.....h.a.c.k.
00000070: 6900 6e00 2e00 6300 6c00 7500 6200 0300  i.n...c.l.u.b...
00000080: 2400 4500 5800 4300 4800 3000 3100 2e00  $.E.X.C.H.0.1...
00000090: 6800 6100 6300 6b00 6900 6e00 2e00 6300  h.a.c.k.i.n...c.
000000a0: 6c00 7500 6200 0500 1600 6800 6100 6300  l.u.b.....h.a.c.
000000b0: 6b00 6900 6e00 2e00 6300 6c00 7500 6200  k.i.n...c.l.u.b.
000000c0: 0700 0800 32fe eb09 8884 d501 0000 0000  ....2...........

Interpreting the message

Information in the above message can stick out without parsing (ex: hostname EXCH01 and domain hackin.club). The ntlm_challenger tool can decode the full domain, version, target, and flag information from this message without making a request. The following Python script will do just that:

from ntlm_challenger import *

challenge_message = base64.b64decode('TlRMTVNTUAACAAAADAAMADgAAAAFgokCiw/BR0FOzkIAAAAAAAAAAIwAjABEAAAABgLwIwAAAA9IAEEAQwBLAEkATgACAAwASABBAEMASwBJAE4AAQAMAEUAWABDAEgAMAAxAAQAFgBoAGEAYwBrAGkAbgAuAGMAbAB1AGIAAwAkAEUAWABDAEgAMAAxAC4AaABhAGMAawBpAG4ALgBjAGwAdQBiAAUAFgBoAGEAYwBrAGkAbgAuAGMAbAB1AGIABwAIADL+6wmIhNUBAAAAAA==')
challenge = parse_challenge(challenge_message)
print_challenge(challenge)
Target (Domain): HACKIN

Version: Server 2012 / Windows 8 (build 9200)

TargetInfo:
  MsvAvNbDomainName: HACKIN
  MsvAvNbComputerName: EXCH01
  MsvAvDnsDomainName: hackin.club
  MsvAvDnsComputerName: EXCH01.hackin.club
  MsvAvDnsTreeName: hackin.club
  MsvAvTimestamp: Oct 17, 2019 01:13:09.417733

Negotiate Flags:
  NTLMSSP_NEGOTIATE_UNICODE
  NTLMSSP_REQUEST_TARGET
  NTLMSSP_NEGOTIATE_ALWAYS_SIGN
  NTLMSSP_TARGET_TYPE_DOMAIN
  NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
  NTLMSSP_NEGOTIATE_TARGET_INFO
  NTLMSSP_NEGOTIATE_VERSION

For more information, the NT LAN Manager (NTLM) Authentication Protocol Specification lives at https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4.

Prior Art

NTLM Challenge Decoder Burp plugin: https://github.com/PortSwigger/ntlm-challenge-decoder

nmap’s http-ntlm-info script: https://nmap.org/nsedoc/scripts/http-ntlm-info.html