# How Doom Handles Password Authentication: A Complete Technical Guide

> Discover how Doom handles password authentication by examining credential collection, LDAP binding via get ldap connection, and secure encryption fallbacks. Learn technical details from the 000pp/doom repository.

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

---

**Doom authenticates users by collecting credentials in the LoginScreen, passing them to LoadingScreen, and binding to an LDAP server using the `get_ldap_connection` function with automatic fallback from plaintext to TLS encryption.**

Doom, an open-source penetration testing tool maintained in the `000pp/doom` repository, implements a robust password authentication flow designed for enterprise environments. Understanding how Doom handles password authentication reveals a three-stage pipeline that bridges UI components with backend LDAP operations, supporting both cleartext passwords and legacy LM hashes.

## The Three-Stage Authentication Pipeline

Doom's password authentication follows a clear separation of concerns across three distinct stages: UI collection, async credential handoff, and LDAP binding with protocol negotiation.

### Stage 1: Credential Collection in LoginScreen

The authentication journey begins in [`src/doom/screens/login_screen.py`](https://github.com/000pp/doom/blob/main/src/doom/screens/login_screen.py), where the `LoginScreen` class renders a password input field with masking enabled. When the user clicks the *Login* button, the application extracts the raw password value from the UI component.

```python

# From LoginScreen (lines 85-108)

password = self.query_one("#password-input").value

```

The password field is explicitly configured with `password=True` to ensure characters are masked during entry, protecting sensitive credentials from shoulder surfing.

### Stage 2: Async Handoff to LoadingScreen

Once collected, the credentials—including the password—are packaged into a dictionary and passed to `LoadingScreen` via screen navigation. This transition occurs in [`src/doom/screens/login_screen.py`](https://github.com/000pp/doom/blob/main/src/doom/screens/login_screen.py) (lines 98-112).

```python
login_data = {
    'ip': ip,
    'domain': domain,
    'username': username,
    'password': password
}
loading_screen = LoadingScreen(login_data)
self.app.push_screen(loading_screen)

```

This handoff decouples the UI layer from the network authentication logic, allowing the application to display loading states while performing potentially slow LDAP operations.

### Stage 3: LDAP Bind with Protocol Fallback

Upon mounting, `LoadingScreen` spawns an asynchronous task that invokes `get_ldap_connection` from [`src/doom/protocols/ldap.py`](https://github.com/000pp/doom/blob/main/src/doom/protocols/ldap.py). This function handles the actual password authentication against the directory server (LoadingScreen lines 71-84).

```python
connection_result = await asyncio.to_thread(
    get_ldap_connection,
    host=self.login_data.get('ip', ''),
    username=self.login_data.get('username', ''),
    password=self.login_data.get('password', ''),
    domain=self.login_data.get('domain', '')
)

```

The LDAP module implements automatic protocol negotiation, attempting plaintext authentication on port 389 before falling back to LDAPS on port 636 if the initial connection fails.

## Deep Dive into LDAP Password Handling

Doom's LDAP implementation in [`src/doom/protocols/ldap.py`](https://github.com/000pp/doom/blob/main/src/doom/protocols/ldap.py) contains sophisticated logic for handling various password formats and connection scenarios.

### LM Hash Detection and Formatting

The authentication system detects legacy Windows LM hashes by checking if the password consists of exactly 32 hexadecimal characters. When detected, Doom automatically prepends the standard "aad3b435b51404eeaad3b435b51404ee" string to convert the LM hash into the NTLM+LM format required by Windows authentication (ldap.py lines 12-14).

```python
if len(password) == 32 and all(c in "0123456789abcdefABCDEF" for c in password):
    password = f"aad3b435b51404eeaad3b435b51404ee:{password}"

```

This conversion ensures compatibility with legacy Active Directory configurations that still accept LM hash formats.

### Plaintext LDAP vs LDAPS Fallback Strategy

The `get_ldap_connection` function implements a resilient two-phase connection strategy. First, it attempts an NTLM bind using plaintext LDAP on port 389 (ldap.py lines 25-38). If this attempt raises an `LDAPBindError` or `LDAPSocketOpenError`, the function automatically retries using LDAPS (LDAP over TLS) on port 636 (ldap.py lines 47-60).

This fallback mechanism ensures Doom can authenticate against diverse LDAP server configurations without requiring users to manually specify encryption settings.

### Error Translation and User Feedback

When authentication fails, Doom catches specific LDAP exceptions—including `LDAPInvalidCredentialsResult` and `LDAPBindError`—and translates them into user-friendly error messages. These messages are then displayed in the `LoadingScreen` status label (ldap.py lines 44-66 and LoadingScreen lines 100-115).

```python
except LDAPInvalidCredentialsResult:
    self.status_label.update("Invalid credentials provided")
except LDAPBindError as e:
    self.status_label.update(f"Connection error: {str(e)}")

```

This error handling ensures users receive clear feedback about authentication failures rather than raw stack traces.

## Implementation Examples

### Basic LDAP Authentication

To manually authenticate using Doom's LDAP module:

```python
from doom.protocols.ldap import get_ldap_connection

host = "192.168.1.10"
domain = "EXAMPLE"
username = "john.doe"
password = "Summer@2025"

conn, base_dn = get_ldap_connection(host, username, password, domain)
print(f"Connected as {conn.user} with Base DN: {base_dn}")

```

### Handling LM Hash Authentication

When working with captured LM hashes:

```python

# 32-character LM hash detected automatically

lm_hash = "a3b435b51404eeaad3b435b51404ee"  # Example hash

conn, base_dn = get_ldap_connection(host, username, lm_hash, domain)

# Doom automatically prepends the NTLM prefix internally

```

### Async Integration Pattern

For non-blocking authentication in UI applications:

```python
import asyncio
from doom.protocols.ldap import get_ldap_connection

async def authenticate_user(login_data):
    try:
        result = await asyncio.to_thread(
            get_ldap_connection,
            host=login_data["ip"],
            username=login_data["username"],
            password=login_data["password"],
            domain=login_data["domain"]
        )
        return result
    except Exception as e:
        return None

```

## Key Source Files and Functions

Doom's password authentication implementation spans three primary modules:

- **[`src/doom/screens/login_screen.py`](https://github.com/000pp/doom/blob/main/src/doom/screens/login_screen.py)** – Contains the `LoginScreen` class responsible for UI rendering and initial password collection via `query_one("#password-input").value`.

- **[`src/doom/screens/loading_screen.py`](https://github.com/000pp/doom/blob/main/src/doom/screens/loading_screen.py)** – Houses the `LoadingScreen` class that manages the async authentication workflow through `authenticate_ldap`, which calls the LDAP module in a background thread.

- **[`src/doom/protocols/ldap.py`](https://github.com/000pp/doom/blob/main/src/doom/protocols/ldap.py)** – Implements the core `get_ldap_connection` function handling LM hash detection, NTLM formatting, dual-protocol connection attempts (LDAP/LDAPS), and LDAP-specific error translation.

## Summary

Doom handles password authentication through a structured three-stage pipeline that separates UI concerns from network operations:

- **Credential Collection**: The `LoginScreen` captures masked passwords and packages them with connection metadata.
- **Async Processing**: `LoadingScreen` offloads authentication to a background thread to prevent UI freezing.
- **LDAP Integration**: The `get_ldap_connection` function in [`ldap.py`](https://github.com/000pp/doom/blob/main/ldap.py) automatically detects LM hashes, attempts plaintext LDAP on port 389, falls back to LDAPS on port 636, and translates low-level LDAP errors into actionable user feedback.

This architecture ensures robust authentication against enterprise Active Directory environments while maintaining responsive user interfaces.

## Frequently Asked Questions

### How does Doom handle password authentication?

Doom implements a three-stage authentication flow where the `LoginScreen` collects credentials, passes them to `LoadingScreen`, and executes an LDAP bind using the `get_ldap_connection` function. The system automatically handles both plaintext passwords and legacy LM hashes, attempting connections over plaintext LDAP (port 389) before falling back to LDAPS (port 636).

### What LDAP protocols does Doom support for authentication?

According to the source code in [`src/doom/protocols/ldap.py`](https://github.com/000pp/doom/blob/main/src/doom/protocols/ldap.py), Doom supports both standard LDAP on port 389 and LDAPS (LDAP over TLS) on port 636. The `get_ldap_connection` function implements an automatic fallback strategy, attempting the plaintext connection first and only retrying with encryption if the initial bind fails.

### How does Doom handle LM hash passwords?

When the provided password consists of exactly 32 hexadecimal characters, Doom detects it as an LM hash and automatically prepends the standard NTLM prefix `aad3b435b51404eeaad3b435b51404ee` before authentication. This conversion, implemented in [`ldap.py`](https://github.com/000pp/doom/blob/main/ldap.py) lines 12-14, ensures compatibility with Windows authentication protocols that expect the combined NTLM+LM format.

### Where is the authentication logic implemented in Doom's codebase?

The authentication logic is distributed across three main files: [`src/doom/screens/login_screen.py`](https://github.com/000pp/doom/blob/main/src/doom/screens/login_screen.py) handles UI-based credential collection, [`src/doom/screens/loading_screen.py`](https://github.com/000pp/doom/blob/main/src/doom/screens/loading_screen.py) manages asynchronous authentication workflows, and [`src/doom/protocols/ldap.py`](https://github.com/000pp/doom/blob/main/src/doom/protocols/ldap.py) contains the core `get_ldap_connection` function that performs the actual LDAP bind operations and error handling.