Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9c1f931
refactor: update core lib
MatrixEditor Apr 5, 2026
f42c1fb
feat: global host config
MatrixEditor Apr 5, 2026
5cc0853
refactor: fix ldap inline docstring, WinRM config
MatrixEditor Apr 5, 2026
da402e3
refactor: ntlm and mysql updates
MatrixEditor Apr 5, 2026
a2dec42
refactor: update SMB server code and add custom error code support
MatrixEditor Apr 5, 2026
2c606ac
feat(cli): add --host option, fix suppress_output typo
MatrixEditor Apr 5, 2026
7fbcaf5
chore: update Dementor.toml to use updated variable resolution
MatrixEditor Apr 5, 2026
9288b35
tests: add basic config tests
MatrixEditor Apr 5, 2026
c1da919
tests: add filters.py tests
MatrixEditor Apr 5, 2026
2d8c027
tests: add logger tests and fix linter issues
MatrixEditor Apr 5, 2026
958be2c
docs: update Host config change
MatrixEditor Apr 5, 2026
8cf7c9e
fix: update ntlm_build_challenge_message in all handlers
MatrixEditor Apr 5, 2026
810626f
fix(ntlm): classify empty usernames as anonymous login attempt
MatrixEditor Apr 5, 2026
cf09056
Merge branch 'master' into feat/host-option
MatrixEditor Apr 6, 2026
7d015fd
fix: update mailslot permissions
MatrixEditor Apr 6, 2026
ac7fe63
fix: update browser config to use global host
MatrixEditor Apr 6, 2026
0033391
docs: updaete documentation to relfect recent changes
MatrixEditor Apr 6, 2026
521992a
chore(smb): change connection info display
MatrixEditor May 1, 2026
d9c9506
docs: update compatibility matrix
MatrixEditor May 1, 2026
e06110d
docs(README): add new CLI options
MatrixEditor May 1, 2026
f0aab7d
chore: bump version to 1.0.0.dev22
MatrixEditor May 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,23 @@ Let's take a look.
```
Usage: Dementor [OPTIONS]

╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────╮
│ --interface -I NAME Network interface to use (required for poisoning) │
│ --analyze -A Only analyze traffic, don't respond to requests │
│ --config -c PATH Path to a configuration file (otherwise standard path is used) │
│ --option -O KEY=VALUE Add an extra option to the global configuration file. │
│ --yes,--yolo -y Do not ask before starting attack mode. │
│ --target -t NAME[,...] Target host(s) to attack │
│ --ignore -i NAME[,...] Target host(s) to ignore │
│ --quiet -q Don't print banner at startup │
│ --version Show Dementor's version number │
│ --ts Log timestamps to the terminal too │
│ --paths Displays the default configuration paths │
│ --repl -F Starts Dementor in interactive mode supporting runtime configuration │
│ --help Show this message and exit. │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --interface -I NAME Network interface to use (required for poisoning) │
│ --analyze -A Only analyze traffic, don't respond to requests │
│ --config -c PATH Path to a configuration file (otherwise standard path is used) │
│ --option -O KEY=VALUE Add an extra option to the global configuration file. │
│ --host -H HOST Host FQDN for all protocol servers (e.g. DC01.contoso.lab). Shortcut for -O │
│ Globals.Host=FQDN. │
│ --yes,--yolo -y Do not ask before starting attack mode. │
│ --target -t NAME[,...] Target host(s) to attack │
│ --ignore -i NAME[,...] Target host(s) to ignore │
│ --quiet -q Don't print banner at startup │
│ --version Show Dementor's version number │
│ --ts Log timestamps to the terminal too │
│ --paths Displays the default configuration paths │
│ --repl -F Starts Dementor in interactive mode supporting runtime configuration │
│ --help Show this message and exit. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```


Expand Down
2 changes: 1 addition & 1 deletion dementor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

__version__ = "1.0.0.dev21"
__version__ = "1.0.0.dev22"
__author__ = "MatrixEditor"
127 changes: 75 additions & 52 deletions dementor/assets/Dementor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
# 3. [NTLM] section -- shared default for all NTLM-enabled protocols
# 4. [Globals] section -- last resort
#
# Host identity settings (NetBIOSComputer, NetBIOSDomain, DnsComputer,
# DnsDomain) follow the same chain as NTLM:
# 1. [[Protocol.Server]] entry
# 2. [Protocol] section
# 3. [NTLM] section
# 4. [Globals] section -- derived automatically from Globals.Host
#
# Protocol Host values (SMTP, LDAP, MSSQL, ...) resolve as:
# 1. [[Protocol.Server]] entry
# 2. [Protocol] section
# 3. [Globals] section -- Host derived from Globals.Host
#
# All other settings stop at step 2.
#
# Note: Some settings can only be used in the most local section (e.g. "Port").
Expand Down Expand Up @@ -171,6 +183,28 @@ UPnP = true
# be used if the local (protocol-specific) section does not define the value.
[Globals]

# --- Host -------------------------------------------------------------------
# Single Host (FQDN or bare hostname) that defines the server's identity for all
# protocol responses. When set, the following values are automatically derived
# and used as fallbacks throughout all protocol servers:
#
# NetBIOSDomainName = Host.split(".", 1)[1].upper()
# DNSHostName = Host.split(".", 1)[0]
# NetBIOSName = Host.split(".", 1)[0][:15].upper()
# DNSDomainName = Host.split(".", 1)[1].lower()
# Host (FQDN) = Host
#
# Individual values (NetBIOSComputer, NetBIOSDomain, DnsComputer, DnsDomain,
# Host) can be overridden in [NTLM] or any [Protocol] section. The Host
# entry is the last-resort fallback for all of them.
#
# Can also be set via the CLI:
# sudo dementor -I eth0 -H DC01.contoso.lab
# sudo dementor -I eth0 -O Globals.Host="DC01.contoso.lab"

# Host = "DC01.contoso.lab"

# --- Filtering --------------------------------------------------------------
# Describes a list of hosts to *include* for poisoning (whitelist approach).
# Supported filter formats:
#
Expand Down Expand Up @@ -210,8 +244,8 @@ UPnP = true
# shared access. Leave empty (the default) to use the SQLite Path below.
#
# Examples:
# Url = "mysql+pymysql://user:pass@127.0.0.1/dementor" # MySQL / MariaDB
# Url = "postgresql+psycopg2://user:pass@127.0.0.1/dementor" # PostgreSQL
# Url = "mysql-pymysql://user:pass@127.0.0.1/dementor" # MySQL / MariaDB
# Url = "postgresql-psycopg2://user:pass@127.0.0.1/dementor" # PostgreSQL
#
# Url =

Expand All @@ -233,7 +267,7 @@ UPnP = true

# --- DuplicateCreds -----------------------------------------------------------
# When true, every captured hash is stored even if an identical credential
# (same domain + username + type + protocol) was seen before. When false,
# (same domain - username - type - protocol) was seen before. When false,
# only the first capture is kept and repeats are silently skipped.
DuplicateCreds = true

Expand Down Expand Up @@ -321,13 +355,15 @@ CapturesPerConnection = 0
# Optional. NTSTATUS code returned after the final capture. Accepts an integer
# value or a string name from impacket.nt_errors (e.g. "STATUS_ACCESS_DENIED",
# "STATUS_LOGON_FAILURE"). The string is resolved via getattr(nt_errors, value).
# Default: "STATUS_SMB_BAD_UID"
ErrorCode = "STATUS_SMB_BAD_UID"
#
# WARNING: Enabling this feature with disable all file path capture events
# Default: Not set
# ErrorCode = "STATUS_SMB_BAD_UID"

# --- SMB1 Identity (optional) ---
# These strings appear only in SMB1 negotiate and session-setup responses.
# They are purely SMB-layer and do NOT affect the NTLM CHALLENGE_MESSAGE.
# SMB2/3 has no equivalent fields these are ignored for modern clients.
# SMB2/3 has no equivalent fields - these are ignored for modern clients.
# To control the NTLM identity (AV_PAIRs), use the [NTLM] section instead.

# Optional. ServerName in the SMB1 non-extended-security negotiate response.
Expand Down Expand Up @@ -355,7 +391,7 @@ Port = 139
[[SMB.Server]]
Port = 445
# Per-server overrides (highest priority -- override [SMB] and [NTLM] for this port only):
# FQDN = "other.corp.com"
# Host = "other.corp.com"
# ServerOS = "Windows Server 2022"
# ErrorCode = "STATUS_ACCESS_DENIED"
# SMB2Support = true
Expand All @@ -371,16 +407,16 @@ Port = 445
# Specifies the NetBIOS domain name to advertise in NETLOGON responses.
# This value is used when responding to PDC queries (LOGON_PRIMARY_QUERY)
# and DC discovery requests (LOGON_SAM_LOGON_REQUEST).
# The default value is: "CONTOSO"
# The default value is: "WORKGROUP"

# DomainName = "WORKGROUP"
# DNSDomain = "WORKGROUP"

# Specifies the hostname to advertise as the domain controller in NETLOGON
# responses. This value is used when responding to PDC queries and DC
# discovery requests.
# The default value is: "DC01"
# The default value is: "DEMENTOR"

# Hostname = "FILESERVER"
# DnsComputer = "DC01"


# =============================================================================
Expand All @@ -392,10 +428,10 @@ Port = 445

AuthMechanisms = ["NTLM", "PLAIN", "LOGIN"]

# Fully Qualified Domain Name (FQDN) used by the SMTP server.
# The first part of the FQDN is used as the hostname in responses.
# Fully Qualified Domain Name used by the SMTP server in banner and
# NTLM identity responses. Derives from Globals.Host when not set here.

FQDN = "DEMENTOR"
# Host = "DC01.contoso.lab"

# SMTP server banner (identifier and version sent to clients).

Expand Down Expand Up @@ -470,29 +506,6 @@ Port = 25

Challenge = "1337LEET"

# When true, the ESS flag (NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) is
# stripped from the CHALLENGE_MESSAGE sent to the client.
#
# false (default): ESS is echoed back when the client requests it. Clients
# that support ESS produce NTLMv1-ESS hashes (hashcat mode 5500 with
# MD5-mixed challenge). This is the modern, common NTLMv1 variant.
#
# true: ESS is suppressed regardless of what the client requests. Clients
# fall back to plain NTLMv1 (DES-only). With a fixed Challenge above,
# these hashes are vulnerable to precomputed rainbow table attacks.

DisableExtendedSessionSecurity = false

# When true, TargetInfoFields are omitted from the CHALLENGE_MESSAGE.
# Without TargetInfoFields clients cannot construct the NTLMv2 Blob
# (MS-NLMP S3.3.2), which has the following effect by client security level:
# Level 0-2 (older Windows, manually downgraded): fall back to NTLMv1.
# Level 3+ (all modern Windows defaults): refuse to authenticate -- zero
# hashes captured from these clients.
#
# Leave false unless specifically targeting legacy NTLMv1-only environments.

DisableNTLMv2 = false
# Optional. Remove the NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY flag from the
# CHALLENGE_MESSAGE, controlling which NTLMv1 hash variant clients produce.
# false (default): NTLMv1 clients produce NetNTLMv1-ESS (MD5-mixed, hashcat -m 5500).
Expand All @@ -505,15 +518,21 @@ DisableExtendedSessionSecurity = false
# prevents clients from constructing NTLMv2 responses.
# false (default): AV_PAIRs present. All clients (LmCompat 0-5) can authenticate.
# true: AV_PAIRs absent. LmCompat 0-2 clients fall back to NTLMv1.
# LmCompat 3+ clients (all modern Windows defaults) will REFUSE
# LmCompat 3- clients (all modern Windows defaults) will REFUSE
# to authenticate, producing zero hashes. Use with caution.
# Default: false
DisableNTLMv2 = false

# --- Server Identity (optional) ---
# These control the identity fields inside the NTLMSSP CHALLENGE_MESSAGE.
# They are INDEPENDENT from SMB identity [SMB] NetBIOSComputer is the SMB1
# They are INDEPENDENT from SMB identity - [SMB] NetBIOSComputer is the SMB1
# negotiate ServerName, while [NTLM] NetBIOSComputer is AV_PAIR 0x0001.
#
# Resolution order (highest priority first):
# 1. [[Protocol.Server]] entry
# 2. [Protocol] section
# 3. [NTLM] section <- override here to apply to ALL NTLM protocols
# 4. [Globals] section <- derived automatically from Globals.Host

# Optional. Controls the NTLMSSP_TARGET_TYPE flag and the TargetName field
# in the CHALLENGE_MESSAGE.
Expand All @@ -536,28 +555,32 @@ DisableNTLMv2 = false
# These appear inside the TargetInfoFields of the CHALLENGE_MESSAGE.
# AV_PAIRs 0x0001 and 0x0002 are always sent (required by MS-NLMP spec).
# AV_PAIRs 0x0003, 0x0004, 0x0005 are omitted when set to "" (empty string).
#
# When Globals.Host is configured these values are derived automatically.
# Set them here to override only the NTLM identity without changing other
# protocol server names.

# Required. MsvAvNbComputerName (AV_PAIR 0x0001). NetBIOS name of the server.
# Also used as TargetName when TargetType="server".
# Default: "DEMENTOR"
# NetBIOSComputer = "DEMENTOR"
# Default: derived from Globals.Host (hostname[:15].upper()), else "DEMENTOR"
# NetBIOSComputer = "DC01"

# Required. MsvAvNbDomainName (AV_PAIR 0x0002). NetBIOS domain name.
# Also used as TargetName when TargetType="domain".
# Default: "WORKGROUP"
# NetBIOSDomain = "WORKGROUP"
# Default: derived from Globals.Host (domain.upper()), else "WORKGROUP"
# NetBIOSDomain = "CONTOSO"

# Optional. MsvAvDnsComputerName (AV_PAIR 0x0003). DNS FQDN of the server.
# Default: "" (omitted from CHALLENGE_MESSAGE)
# DnsComputer = "server.corp.local"
# Default: derived from Globals.Host (full FQDN), else "" (omitted)
# DnsComputer = "dc01.contoso.lab"

# Optional. MsvAvDnsDomainName (AV_PAIR 0x0004). DNS domain name.
# Default: "" (omitted from CHALLENGE_MESSAGE)
# DnsDomain = "corp.local"
# Default: derived from Globals.Host (domain.lower()), else "" (omitted)
# DnsDomain = "contoso.lab"

# Optional. MsvAvDnsTreeName (AV_PAIR 0x0005). Active Directory forest name.
# Default: "" (omitted from CHALLENGE_MESSAGE)
# DnsTree = "corp.local"
# DnsTree = "contoso.lab"

# =============================================================================
# FTP Server(s)
Expand Down Expand Up @@ -610,10 +633,10 @@ EncType = "aes256_cts_hmac_sha1_96"

Timeout = 0

# Hostname + fully qualified domain name, whereby the domain name is optional
# Full example: "HOSTNAME.domain.local"
# Hostname - fully qualified domain name used in LDAP responses.
# Derives from Globals.Host when not set here.

FQDN = "DEMENTOR"
# Host = "DC01.contoso.lab"

# Global TLS option, will enable TLS on all TCP servers

Expand Down Expand Up @@ -904,7 +927,7 @@ InstanceName = "MSSQLServer"
[SSRP]

# The following values can be defined here or inherited from the [MSSQL] section:
# - FQDN
# - Host
# - ServerVersion
# - InstanceName

Expand Down
45 changes: 40 additions & 5 deletions dementor/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import sys
import pathlib
import tomllib
import warnings

from typing import Any

Expand Down Expand Up @@ -51,7 +52,7 @@ def _set_global_config(config: dict[str, Any]) -> None:
:param config: New configuration dictionary.
:type config: dict
"""
sys.modules[__name__].dm_config = config
sys.modules[__name__].dm_config = config # ty:ignore[unresolved-attribute]


def init_from_file(path: str) -> None:
Expand Down Expand Up @@ -82,8 +83,42 @@ def init_from_file(path: str) -> None:


# --------------------------------------------------------------------------- #
# Default initialisation - performed on import so that the rest of the
# package can rely on ``dementor.config.dm_config`` being available.
# Explicit entry point for application startup.
# --------------------------------------------------------------------------- #
init_from_file(DEFAULT_CONFIG_PATH) # 1. bundled defaults
init_from_file(CONFIG_PATH) # 2. user-provided overrides
def init_config(
default_path: str | None = None,
user_path: str | None = None,
) -> None:
"""Load the default and user TOML configuration files.

Call this explicitly from the application entry point (e.g. ``standalone.py``).
Using the defaults, it loads the bundled Dementor.toml first, then the
user-provided override, so user settings win.

:param default_path: Path to bundled defaults (uses :data:`DEFAULT_CONFIG_PATH`).
:param user_path: Path to user overrides (uses :data:`CONFIG_PATH`).
"""
try:
init_from_file(default_path or DEFAULT_CONFIG_PATH) # 1. bundled defaults
init_from_file(user_path or CONFIG_PATH) # 2. user-provided overrides
except Exception as exc: # pragma: no cover
warnings.warn(
f"dementor.config: failed to load configuration at startup: {exc}",
RuntimeWarning,
stacklevel=1,
)


# --------------------------------------------------------------------------- #
# Default initialisation - runs on first import so that protocol modules
# that call get_global_config() / get_value() without going through
# standalone.py still get the bundled defaults. standalone.py re-runs
# init_config() with the user config path, which overwrites these defaults.
# --------------------------------------------------------------------------- #
init_config()

__all__ = [
"get_global_config",
"init_config",
"init_from_file",
]
17 changes: 16 additions & 1 deletion dementor/config/attr.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#
# Shared attributes for all configuration classes
from dementor.config.toml import Attribute
from dementor.config.util import is_true
from dementor.config.util import HostValue, is_true

# TLS/Certificate Configuration Attributes
# These attributes are shared across protocols that support TLS and
Expand Down Expand Up @@ -113,3 +113,18 @@
ATTR_CERT_STATE,
ATTR_CERT_VALIDITY_DAYS,
]


# Host Configuration Attribute
# Single attribute representing the host FQDN from [Globals].
# Protocols that need the full HostValue object (e.g. to call get_value())
# can include this in their _fields_ list. The HostValue factory derives
# NetBIOSComputer, NetBIOSDomain, DnsComputer, DnsDomain, FQDN, etc.

ATTR_GLOBALS_HOST = Attribute(
attr_name="host",
qname="Host",
default_val=HostValue.DEFAULT,
section_local=False,
factory=HostValue,
)
Loading