# How Doom Handles NT Hashes for Authentication: Technical Implementation Guide

> Learn how Doom handles NT hashes for authentication, converting them to NTLM LM:NT format for pass-the-hash attacks against LDAP and LDAPS servers without clear-text credentials.

- Repository: [000pp/doom](https://github.com/000pp/doom)
- Tags: technical-implementation-guide
- Published: 2026-02-22

---

**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`](https://github.com/000pp/doom/blob/main/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`](https://github.com/000pp/doom/blob/main/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.

```python
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`](https://github.com/000pp/doom/blob/main/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.