Security Considerations When Using Doom: A Complete Guide to Safe ADCS Enumeration
Doom is a Python-based Active Directory Certificate Services (ADCS) enumeration tool that requires careful handling of credentials, LDAP connections, and TLS validation to prevent credential exposure and man-in-the-middle attacks.
Doom, developed by 000pp, is an open-source utility designed for security professionals conducting ADCS reconnaissance. While it provides valuable insights into certificate template configurations, several implementation details in the codebase introduce significant security considerations when using Doom in production environments. Understanding these risks is essential for maintaining operational security during penetration testing or red team exercises.
Credential Handling Risks in Doom
Clear-Text Password Storage in Memory
The authentication flow in Doom begins at src/doom/screens/login_screen.py, where the UI captures credentials using Input(..., password=True). While this masks the display, the password is stored as a clear-text string in a plain dictionary within the LoadingScreen class before being passed to the LDAP connector.
This implementation means passwords exist in memory in an unencrypted state and may be written to logs if an exception occurs during the connection phase. The current codebase does not implement secure memory clearing after authentication attempts.
Mitigation Strategies for Secure Credential Management
To reduce exposure when using Doom, implement the following practices:
- Clear variables immediately after use: Explicitly delete password variables from memory using
del passwordafter the LDAP connection is established. - Integrate secure credential vaults: Replace raw input methods with Windows Credential Manager or similar secure storage solutions instead of passing clear-text passwords through the UI layer.
LDAP Connection Security Vulnerabilities
Unencrypted LDAP Fallback on Port 389
The get_ldap_connection() function in src/doom/protocols/ldap.py attempts an unencrypted LDAP bind on port 389 before falling back to LDAPS on port 636. The code explicitly sets use_ssl=False for the initial connection attempt.
If the Active Directory server permits simple LDAP on port 389, credentials are transmitted in clear text across the network, exposing them to packet sniffers and network-level attackers. This fallback behavior creates an unnecessary attack surface.
Insecure TLS Configuration and Certificate Validation
When LDAPS is used, the TLS configuration in src/doom/protocols/ldap.py (lines 15-18) disables critical security checks:
validate=ssl.CERT_NONE
ciphers="ALL:@SECLEVEL=0"
Setting validate=ssl.CERT_NONE disables server certificate verification, making the tool vulnerable to man-in-the-middle (MITM) attacks. The cipher override "ALL:@SECLEVEL=0" enables insecure cryptographic algorithms that modern security standards have deprecated.
To secure these connections, modify the code to use validate=ssl.CERT_REQUIRED and supply a proper CA bundle, removing the insecure cipher override entirely.
Password Pre-Hash Detection Logic
The LDAP implementation contains logic that detects if a supplied password is a 32-character hexadecimal string. If detected, the code prefixes it with the NTLM hash marker aad3b435b51404eeaad3b435b51404ee (found in src/doom/protocols/ldap.py, lines 12-14).
This behavior introduces two risks: an attacker could intentionally supply a hash to bypass NTLM challenges, or the code could unintentionally treat a user-chosen password as a hash, weakening authentication strength. The tool should require an explicit flag for "hashed password mode" and validate that hashes truly originate from NTLM computations.
Information Disclosure Risks
Verbose Error Messages and LDAP Information Leakage
Error handling in src/doom/protocols/ldap.py re-raises exceptions with raw error text: raise Exception(f"LDAP bind error: {e}"). Detailed LDAP error messages may reveal sensitive configuration details, such as which authentication mechanisms are permitted or the internal structure of the directory.
Operators should modify error handling to log detailed messages at a debug level only, presenting generic error messages to end users.
ADCS Template Enumeration Output
The enumerate_templates() function in src/doom/modules/enumerate_templates.py returns all raw LDAP attributes, including potentially sensitive flags like msPKI-Enrollment-Flag. While this is the tool's intended purpose, the output provides attackers with a complete map of privileged certificate templates, facilitating privilege escalation attacks.
Run Doom only in controlled, authorized environments. Consider adding filters to hide high-risk attributes unless explicitly requested by the operator.
Supply Chain and Dependency Security
The project declares dependencies in pyproject.toml and pulls code from external sources, including Certipy structs. Compromised third-party packages could introduce backdoors or malicious code into the enumeration workflow.
Mitigate supply chain risks by pinning exact dependency versions in pyproject.toml, verifying package integrity with cryptographic hashes, and using reproducible build environments such as containerized deployments.
Secure Implementation Examples
Replace the default insecure LDAP connection with this hardened implementation:
import ssl
import ldap3
def get_secure_ldap_connection(host: str, username: str, password: str, domain: str):
"""Enforce LDAPS with certificate validation."""
user = f"{domain}\\{username}"
# Force LDAPS only
tls = ldap3.Tls(
validate=ssl.CERT_REQUIRED,
# Use system default CA bundle
ca_certs_file=ssl.get_default_verify_paths().cafile,
version=ssl.PROTOCOL_TLS_CLIENT,
)
ldaps_server = ldap3.Server(
f"ldaps://{host}",
port=636,
use_ssl=True,
get_info=ldap3.ALL,
tls=tls,
)
conn = ldap3.Connection(
server=ldaps_server,
user=user,
password=password,
authentication=ldap3.NTLM,
auto_bind=True,
raise_exceptions=True,
)
base_dn = conn.server.info.other.get("defaultNamingContext", [""])[0]
return conn, base_dn
Clear sensitive data from memory immediately after authentication:
password = self.query_one("#password-input").value
login_data = {"ip": ip, "domain": domain, "username": username, "password": password}
# ... use login_data ...
del password # remove clear‑text password from locals
Summary
- Credential exposure: Doom stores passwords in clear text in memory during the authentication flow in
src/doom/screens/login_screen.pyandsrc/doom/screens/loading_screen.py. - Unencrypted transmission: The default
get_ldap_connection()function attempts unencrypted LDAP on port 389 before trying LDAPS, risking clear-text credential exposure. - Disabled TLS validation: The current implementation uses
validate=ssl.CERT_NONEand insecure ciphers, making connections vulnerable to MITM attacks. - Information leakage: Verbose error messages and raw LDAP attribute output in
src/doom/modules/enumerate_templates.pymay reveal sensitive ADCS configuration details. - Supply chain risks: External dependencies in
pyproject.tomlrequire version pinning and integrity verification to prevent backdoor injection.
Frequently Asked Questions
Is Doom safe to use in production Active Directory environments?
Doom is designed for authorized security assessments, but its default configuration introduces significant risks for production environments. The tool transmits credentials over potentially unencrypted channels and stores passwords in clear text in memory. Only use Doom in isolated lab environments or with explicit authorization, and apply the security hardening measures described in this guide before connecting to production domains.
How does Doom handle password storage securely?
Currently, Doom does not handle password storage securely. The login_screen.py module captures passwords using Input(..., password=True), which only masks the UI display, then stores the value in a plain dictionary passed to LoadingScreen. The password remains in memory as a clear-text string until the Python garbage collector reclaims it. Users should manually clear password variables using del password immediately after authentication and consider integrating Windows Credential Manager or similar secure vaults instead of manual entry.
What are the risks of using Doom over unencrypted LDAP?
The get_ldap_connection() function in src/doom/protocols/ldap.py attempts a connection to port 389 with use_ssl=False before falling back to LDAPS on port 636. If the Active Directory server accepts simple LDAP binds, this transmits credentials in clear text across the network, exposing them to packet sniffers and network-level attackers. Additionally, the TLS configuration disables certificate validation with validate=ssl.CERT_NONE, making the tool vulnerable to man-in-the-middle attacks even when LDAPS is used.
How can I verify the integrity of Doom's dependencies?
Doom relies on external packages including ldap3 and code from Certipy structs, declared in pyproject.toml. To verify integrity, pin exact dependency versions in your pyproject.toml or requirements.txt using hash verification. Use tools like pip install --require-hashes or containerized environments with reproducible builds to ensure that compromised third-party packages cannot introduce backdoors into your ADCS enumeration workflow.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →