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


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

Target (Domain): HACKIN

Version: Server 2012 / Windows 8 (build 9200)

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

Negotiate Flags:

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:


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:


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):


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
ActiveSync401 Basic Auth/Microsoft-Server-ActiveSync
OWA302 Location
401 Basic Auth

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


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

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: 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:


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 = parse_challenge(challenge_message)
Target (Domain): HACKIN

Version: Server 2012 / Windows 8 (build 9200)

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

Negotiate Flags:

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

Download Custom Slack Emojis

This post will demonstrate how to use Slack’s emoji.list API method to extract and export custom emojis from Slack using the web client. This API is called by the web client to fetch all emojis when initializing a workspace, and can be called with a user’s xoxc token.


Instructions for downloading the emojis can be found in the Gist at:



API Call

When Slack first loads a channel, it fetches custom emojis for the workspace using the emoji.list API. These values do not appear to be stored in a JavaScript variable (although at one point they were…), but it is possible to re-call the API.

The documentation shows that a “token” argument is required for authentication. During normal use, the web client sends the xoxc token, which is saved in localStorage. The following JavaScript will extract this token:


The token will follow the format xoxc-11111111111-121212121212-121212121212-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef.

The API can be called using the multipart/form-data Content-Type. The following JavaScript will make the API request and dump the response to the console:

// get auth token for API request
var authToken = JSON.parse(localStorage.localConfig_v2).teams[slackDebug.activeTeamId].token;

// setup request
var formData = new FormData();
formData.append('token', authToken);

// make request
(async () => {
  const rawResponse = await fetch('/api/emoji.list', {
    method: 'POST',
    body: formData

  const emojisApi = await rawResponse.json();
  // dump to console

Saving the Data

Due to the restricted (lack of) Access-Control-Allow-Origin header on emoji.slack-edge.com, it is not possible to download the emojis using JavaScript straight from a workspace (located on app.slack.com). Attempting to do so will result in a Cross-Origin Resource Sharing (CORS) error.

Therefore, the download.js script of the Gist saves the results of the API call to a CSV. The CSV will have two columns: one for the image name and the other for the URL. Paste this script into the browser’s console, accept the popup warning, and save the file.

Downloading the Emojis

Since custom emojis do not require authentication, it is possible to download them from outside the browser.

On Linux and Mac, the wget utility can be leveraged to easily download these files. Ensure the terminal is open in the same directory as the emojis.csv file, and run the following script (from download.sh):

for emoji_line in $(cat emojis.csv); do
  name=$(echo "$emoji_line" | cut -d, -f1)
  url=$(echo "$emoji_line" | cut -d, -f2)

  wget -O "$name" "$url"

On Windows, PowerShell can be used to download all emojis in the CSV. Run the following commands from the same folder as the emojis.csv file (from download.ps1, requires PowerShell 3):

$emojis = Import-Csv -Path .\emojis.csv -Header 'Name','Url'

foreach ($emoji in $emojis) {
  Invoke-WebRequest -Uri $emoji.Url -OutFile $emoji.Name

After running the scripts, all custom emojis from the Slack workspace should be present in the current directory/folder.