NTLM-authenticated HTTP endpoints remain common in Windows-centric intranets and older web applications. Command-line access using curl enables scripted checks, automated monitoring, and quick diagnostics without relying on a browser. Consistent CLI usage also simplifies reproducing authentication problems across different machines, load balancers, and reverse proxies.

NTLM adds a multi-step challenge-response handshake on top of standard HTTP status codes and headers. A server or proxy starts by returning HTTP/1.1 401 Unauthorized with a WWW-Authenticate: NTLM header, which prompts an NTLM-aware client to negotiate, construct the response, and resend the request. The curl options --ntlm and --proxy-ntlm enable this handshake, while --user and --proxy-user carry the domain, username, and password as a single credential string.

Because NTLM is a legacy authentication scheme, transport security and careful credential handling are critical. Credentials placed directly on the command line are visible to process inspection and shell history, so usage is best limited to controlled environments and short-lived tests. The commands below assume a Linux system with a recent curl build, TLS connectivity to the NTLM-protected endpoint, and either direct access or an HTTP proxy that can issue its own NTLM challenge.

Steps to authenticate with NTLM using curl:

  1. Check the installed curl binary for NTLM support in the features list.
    $ curl --version
    curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.2.11
    Release-Date: 2023-11-20
    Protocols: dict file ftp ftps gopher gophers http https imap imaps pop3 pop3s rtsp smtp smtps telnet tftp
    Features: AsynchDNS HSTS HTTP2 HTTP3 IPv6 Largefile NTLM SSL TLS-SRP
    ##### snipped #####

    The appearance of NTLM in the Features line confirms that the local curl build can perform NTLM authentication.

  2. Send a verbose unauthenticated request to observe the NTLM challenge from the server or proxy.
    $ curl --verbose --output /dev/null https://ntlm.example.internal/secure/page
    *   Trying 10.10.10.20:443...
    * Connected to ntlm.example.internal (10.10.10.20) port 443 (#0)
    * ALPN, offering h2
    * ALPN, offering http/1.1
    ##### snipped #####
    > GET /secure/page HTTP/1.1
    > Host: ntlm.example.internal
    > User-Agent: curl/8.5.0
    ##### snipped #####
    < HTTP/1.1 401 Unauthorized
    < WWW-Authenticate: NTLM
    ##### snipped #####

    A response that combines HTTP/1.1 401 Unauthorized with WWW-Authenticate: NTLM indicates that the endpoint expects an NTLM-capable client.

  3. Perform an NTLM-authenticated request directly to the origin server using a domain account.
    $ curl --ntlm --user 'EXAMPLE\alice:Str0ngPass!' https://ntlm.example.internal/secure/page
    &lt;!DOCTYPE html&gt;
    &lt;html&gt;
      &lt;head&gt;&lt;title&gt;Secure area&lt;/title&gt;&lt;/head&gt;
      &lt;body&gt;
        &lt;h1&gt;Welcome, Alice&lt;/h1&gt;
    ##### snipped #####
      &lt;/body&gt;
    &lt;/html&gt;

    Placing the password inline with --user exposes credentials through shell history and process listings on multi-user systems, which can leak access to NTLM-protected services.

  4. Apply NTLM authentication to an HTTP proxy when the NTLM challenge originates from a proxy instead of the origin server.
    $ curl --proxy http://proxy.example.internal:8080 \
           --proxy-ntlm \
           --proxy-user 'EXAMPLE\alice:Str0ngPass!' \
           https://ntlm.example.internal/secure/page
    &lt;h1&gt;Welcome, Alice&lt;/h1&gt;
    ##### snipped #####

    Use --proxy-ntlm and --proxy-user for NTLM-secured proxies, keeping the regular --ntlm and --user options reserved for NTLM challenges issued by the origin web server.

  5. Check the HTTP status of an authenticated request to verify successful NTLM negotiation and access.
    $ curl --ntlm --user 'EXAMPLE\alice:Str0ngPass!' \
           --silent --output /dev/null \
           --write-out '%{http_code}\n' \
           https://ntlm.example.internal/secure/page
    200

    Success signals include an expected status code such as 200, the presence of protected content instead of an NTLM or 401 challenge, and repeat requests succeeding without additional prompts.

Discuss the article:

Comment anonymously. Login not required.