How Doom Handles NT Hashes for Authentication: Technical Implementation Guide

Doom automatically converts 32-character hexadecimal NT hashes into the NTLM "LM:NT" format and authenticates to directory services using the ldap3.NTLM mode, enabling pass-the-hash attacks against LDAP and LDAPS servers without clear-text credentials.

Doom, an open-source penetration testing framework maintained at 000pp/doom, streamlines Windows domain enumeration by embedding NT hash support directly into its LDAP client. This implementation allows security professionals to authenticate using captured hash dumps rather than plaintext passwords, significantly reducing credential exposure during authorized security assessments.

Hash Detection and Conversion Logic

Doom identifies NT hashes through pattern matching in the get_ldap_connection() function. When the supplied password parameter contains exactly 32 hexadecimal characters, the framework treats the input as a raw NT hash instead of a clear-text password.

According to the source code in src/doom/protocols/ldap.py (lines 12-14), Doom prepends the static LM hash placeholder aad3b435b51404eeaad3b435b51404ee: to the detected hash. This constructs the LM:NT string format that the underlying ldap3 library requires for NTLM authentication. The empty LM hash placeholder indicates that only the NT hash portion contains valid credential material, while the LM hash field remains null.

NTLM Bind Implementation

The framework initializes all LDAP connections using authentication=ldap3.NTLM to support hash-based authentication workflows. In src/doom/protocols/ldap.py, the get_ldap_connection() function instantiates the connection object at lines 30-33 for standard LDAP and lines 52-55 for LDAPS, passing the potentially transformed password string directly to the ldap3.Connection constructor.

This approach leverages the ldap3 library's native NTLM support, which accepts the combined LM:NT format and manages the cryptographic challenge-response handshake required by Windows Active Directory. The authentication logic remains transparent to the end user regardless of whether they supply a clear-text password or a pre-computed NT hash.

LDAPS Fallback with Hash Preservation

When initial LDAP connectivity fails, Doom automatically retries the authentication sequence over LDAPS (port 636) using identical credential handling. This fallback mechanism preserves the NT hash transformation logic, ensuring that hash-based authentication attempts continue over encrypted channels when cleartext LDAP (port 389) is unavailable.

The retry implementation at lines 52-55 maintains the same authentication=ldap3.NTLM configuration and credential string format, allowing the previously constructed LM:NT value to authenticate over the secure LDAPS channel without requiring manual re-entry or additional format conversion.

Practical Usage Examples

The get_ldap_connection() function accepts both clear-text passwords and NT hashes through its password parameter. Doom distinguishes between the two based on the 32-character hexadecimal pattern detection.

from doom.protocols.ldap import get_ldap_connection

# Standard clear-text authentication

conn, base_dn = get_ldap_connection(
    host="dc.corp.local",
    username="administrator",
    password="SuperSecret123!",
    domain="CORP"
 )

# Pass-the-hash authentication using NT hash only

nt_hash = "5F4DCC3B5AA765D61D8327DEB882CF99"
conn, base_dn = get_ldap_connection(
    host="dc.corp.local",
    username="administrator",
    password=nt_hash,  # Doom detects 32-char hex format

    domain="CORP"
 )

# Query the directory using the authenticated connection

conn.search(
    search_base=base_dn,
    search_filter='(sAMAccountName=administrator)',
    attributes=['cn', 'mail']
)
print(conn.entries)

When supplying an NT hash, provide exactly 32 hexadecimal characters. Doom automatically handles the LM hash placeholder prefixing internally, constructing the full LM:NT structure required by the NTLM protocol before passing credentials to the ldap3 connection layer.

Summary

  • Automatic Detection: Doom recognizes 32-character hexadecimal strings as NT hashes in src/doom/protocols/ldap.py (lines 12-14)
  • Format Conversion: The framework prepends the static LM hash placeholder aad3b435b51404eeaad3b435b51404ee: to construct valid NTLM credentials
  • Dual Protocol Support: Both LDAP (port 389) and LDAPS (port 636) connections support NT hash authentication via ldap3.NTLM mode with automatic fallback
  • Transparent Interface: The get_ldap_connection() function handles credential transformation without requiring additional parameters or authentication mode flags

Frequently Asked Questions

How does Doom differentiate between a password and an NT hash?

Doom checks if the password parameter contains exactly 32 hexadecimal characters. If the pattern matches, the code treats the input as a raw NT hash and automatically prepends the LM hash placeholder to form the NTLM authentication string required by the ldap3 library. Clear-text passwords of different lengths pass through unmodified.

What is the significance of the LM hash placeholder in Doom's implementation?

The string aad3b435b51404eeaad3b435b51404ee: represents a null LM hash (indicating no LM hash is present). Doom prepends this placeholder to the 32-character NT hash to create the LM:NT format that Windows NTLM authentication expects, effectively enabling pure NT hash authentication without requiring LM hash material.

Can Doom use NT hashes for encrypted LDAPS connections?

Yes. Doom implements identical NT hash handling for both standard LDAP and LDAPS connections. If the initial LDAP bind fails, the framework automatically retries on port 636 (LDAPS) using the same ldap3.NTLM authentication mode and transformed credential string, ensuring hash-based authentication works seamlessly across encrypted channels.

Which Python library handles the actual NTLM authentication in Doom?

Doom relies on the ldap3 library to perform the low-level NTLM authentication handshake. The framework passes the constructed LM:NT credential string to ldap3.Connection with authentication=ldap3.NTLM, delegating the cryptographic operations and bind negotiations to the library's established NTLM implementation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →