Skip to content

Repository files navigation

RelayLDAP

Python Tests Protocol Authentication Use License

RelayLDAP using a compatible NTLM Sicily bind to select and reuse a retained LDAPS session

RelayLDAP is a menu-driven ldap3 client for reusing Active Directory LDAP and LDAPS sessions retained by Impacket's ntlmrelayx SOCKS server. It can also connect directly with NTLM credentials.

It provides:

  • Guided selection of active LDAP/LDAPS relays from the local ntlmrelayx API.
  • LDAP enumeration for identities, groups, delegation, LAPS, gMSA, policy, and other directory data.
  • Explicit warnings for broad or credential-sensitive reads.
  • Typed confirmation before every LDAP write.
  • Permission-restricted JSONL event and result logs for evidence collection.

Caution

Use RelayLDAP only on systems you own or have explicit permission to test. LDAP writes can disable accounts, reset passwords, change privileged group membership, create identities, or otherwise disrupt production services. Read operations may be logged or detected.

Contents

Which mode do I use?

Mode Use it when Connection Password behavior
Relay ntlmrelayx already holds the LDAP/LDAPS session you want to reuse. Run RelayLDAP through proxychains4 and select or enter the matching retained session. Use the built-in non-secret placeholder. Do not supply the account's real password.
Direct You have valid NTLM credentials and want a normal LDAP/LDAPS connection. Run RelayLDAP directly against the domain controller. Use --ask-pass. The current release does not validate LDAPS server certificates.

Installation

Requirements

  • Python 3.10 or newer
  • ldap3
  • pycryptodome (provides MD4 for NTLMv2 construction on modern OpenSSL builds)
  • For retained-relay mode: proxychains4 and an active LDAP or LDAPS relay in ntlmrelayx

Clone the repository and create an isolated Python environment:

git clone https://github.com/CyberAuth/relayldap.git
cd relayldap
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt

RelayLDAP does not import Impacket and does not require it as a Python package. Impacket is an external operational prerequisite only when ntlmrelayx is the component providing the retained SOCKS relay.

Example proxychains4.conf entry for the default ntlmrelayx SOCKS port, 1080:

strict_chain
proxy_dns

[ProxyList]
socks5 127.0.0.1 1080

Quick start

Guided relay mode

The simplest invocation prompts for every connection setting before opening the operations menu:

proxychains4 -q python3 relayldap.py

RelayLDAP first offers to query the local ntlmrelayx SOCKS HTTP API. The API defaults to http://127.0.0.1:9090; use the port passed to ntlmrelayx -http-api-port when it was customized.

Example guided setup
Authentication mode: relay or direct [relay]: relay
Discover active LDAP/LDAPS relays from the ntlmrelayx API? [Y]: Y
ntlmrelayx API URL [http://127.0.0.1:9090]:

Active LDAP/LDAPS relays
  #  Protocol Target                   Identity                         Port   ID     Admin
  1  LDAPS    192.0.2.10              ACME/RELAYUSER                   636    23     N/A
Select relay [1]: 1
[+] Selected LDAPS://192.0.2.10:636 as ACME\RELAYUSER (client ID 23)
Base DN (blank discovers it after bind):

The selection populates protocol, target, port, domain, and username directly from the active-relay record. RelayLDAP then performs its normal Sicily NTLM bind; bind success is the final confirmation that the selected retained session is currently usable.

If the API is unavailable, returns no LDAP/LDAPS rows, or the operator answers N, RelayLDAP falls back to manual entry. To skip discovery completely:

proxychains4 -q python3 relayldap.py --no-relay-discovery

To use a nondefault API port:

proxychains4 -q python3 relayldap.py \
  --relay-api http://127.0.0.1:9092

The API request is made by a short child process with proxy-preload and generic HTTP proxy variables removed. This prevents a localhost API request from being sent back into the SOCKS listener while the main RelayLDAP process remains under proxychains4 for LDAPS.

Important

Impacket's SOCKS API does not provide authentication in the tested implementation. Keep it bound to loopback or another appropriately protected management interface.

Fully specified relay mode

proxychains4 -q python3 relayldap.py \
  --dc 192.0.2.10 \
  --port 636 \
  --domain ACME \
  --username RELAYUSER \
  --log-dir ./relayldap-evidence

Do not provide an actual password for retained-relay mode. The default placeholder is sufficient.

Direct authentication

RelayLDAP can also act as an ordinary LDAPS client using a real account:

python3 relayldap.py \
  --dc dc01.example.test \
  --domain EXAMPLE \
  --username analyst \
  --ask-pass

Prefer --ask-pass to avoid placing a real password in shell history or the process list.

Warning

The current release disables LDAPS server-certificate validation for IP-addressed targets. Treat direct-authentication mode accordingly. Clear LDAP via --ldap should only be used when the transport and risk are understood.

How RelayLDAP works

flowchart LR
    A[RelayLDAP / ldap3] -->|Sicily NTLM bind| B[proxychains]
    B -->|SOCKS5| C[ntlmrelayx SOCKS server]
    C -->|Select DOMAIN/username| D[Retained LDAPS session]
    D -->|Authenticated LDAP operations| E[Domain controller]
Loading

In relay mode, the password supplied to ldap3 is only used to construct the client-side NTLM exchange. The SOCKS plugin selects an existing server-side relay by domain and username.

That means:

  • The domain and username are not arbitrary.
  • They must correspond to an active identity shown by ntlmrelayx> socks.
  • The placeholder password can be any nonempty value; RelayLDAP defaults to relayldap-placeholder in relay mode.
  • The destination and port must match the retained relay exactly.

For a SOCKS identity of ACME/RELAYUSER, enter:

Domain:   ACME
Username: RELAYUSER

If the row shows ACME.COM/RELAYUSER, use ACME.COM. Exact matching is the safest choice.

Blog

For the origin story and protocol investigation, see When SOCKS Is Not Enough.

Operations

RelayLDAP provides an interactive menu after a successful bind.

Read operations

Menu Operation What it does
1 Current bound identity Resolves the bound account and returns its direct memberships and control attributes.
2 Arbitrary LDAP search Accepts a caller-supplied base, scope, filter, attributes, and result limit. Defaults to 100 results; limit 0 requires typing UNLIMITED.
3 List users Enumerates user objects and selected identity, membership, description, and timestamp attributes.
4 List computers Enumerates computer objects, operating-system data, SPNs, and delegation-related attributes.
5 List groups Enumerates groups and their membership attributes.
6 Show group members Retrieves membership for one resolved group.
7 Recursive memberships Uses the AD matching-rule-in-chain query for nested memberships.
8 Kerberoast candidate discovery Finds user objects with SPNs. It does not request service tickets.
9 AS-REP candidate discovery Finds accounts with DONT_REQ_PREAUTH. It does not request an AS-REP.
10 Delegation configuration Searches for unconstrained, constrained, and resource-based delegation attributes.
11 LAPS password attributes (raw/readable) Finds computers for which a legacy or Windows LAPS password attribute is visible and returns the readable values. Cleartext/JSON values are shown as returned by AD; encrypted values are base64-encoded and are not decrypted.
12 gMSA accounts and raw password attributes Lists gMSA objects and requests their SPNs, managed-password blob, and membership security descriptor. Readable binary values are base64-encoded; the tool does not decode them into a password or hash.
13 Interesting text/path attributes Searches descriptions, comments, info fields, scripts, profiles, and home paths.
14 Domain policy Reads password, lockout, and machine-account-quota policy from the domain object.

SPN and AS-REP discovery includes disabled accounts and adds enabled and decoded uacFlags. A disabled account is useful configuration evidence but is not presently roastable.

Write operations

Menu Operation Potential impact
20 Create enabled user Creates an AD identity, assigns a password, and enables it.
21 Add member to group Can grant immediate privilege or application access.
22 Remove member from group Can remove administrative or business access.
23 Set user/computer password Can break services, scheduled tasks, trusts, or user access.
24 Enable account Can reactivate a retired, compromised, or intentionally disabled identity.
25 Disable account Can interrupt users, services, applications, or domain operations.
26 Create computer account Adds a domain principal and may affect machine-account quota or delegation paths.
27 Generic attribute modification Can alter nearly any writable LDAP attribute; misuse can break authentication or AD-integrated services.

Every write operation displays a summary and requires the operator to type YES. Confirmation reduces accidents; it does not make the operation safe or reversible.

The menu labels broad, bulk, potentially expensive, and credential-sensitive read operations. Selecting one prints a concise OPSEC notice without adding an extra confirmation prompt. The exception is an unlimited arbitrary search, which requires the operator to type UNLIMITED before any LDAP request is sent.

Traffic and detection considerations

Read-only does not mean stealthy. Query visibility depends on domain-controller auditing, SACL configuration, LDAP diagnostic logging, network monitoring, identity analytics, and EDR/NDR coverage.

Operation class Relative traffic Detection/impact notes
Current identity and domain policy Low Targeted base or account searches. Still attributable to the relayed identity and source connection.
SPN and AS-REP candidate discovery Low to moderate One directory-wide filtered search each. These options only discover candidates; subsequent Kerberos requests would be separate activity.
Users, computers, and groups Moderate to high Paged directory-wide enumeration. Volume scales with object count and requested multivalue attributes.
Recursive memberships Moderate; potentially expensive Matching-rule-in-chain can require significant DC work in large or deeply nested directories. Use against a specific principal.
Delegation and interesting-attribute searches Moderate Broad filters may touch large portions of the directory and can resemble attack-path or credential-discovery activity.
Arbitrary search Operator controlled A narrow indexed filter can be cheap; (objectClass=*), unlimited results, broad bases, and large attributes can be expensive. Set a result limit when possible.
LAPS and gMSA reads High sensitivity Credential-material access may be specifically audited or alerted even when the result is empty or access is denied. A zero-result or omitted attribute does not prove that LAPS or gMSA is absent. Treat returned output as secret material.
Any write High Account, password, group, computer, and attribute changes are commonly audited and may trigger identity-protection or SIEM detections. They can also cause immediate operational impact.

Depending on audit policy and object SACLs, defenders may observe Directory Service Access activity such as event 4662. Account-management auditing can expose actions such as user creation, password reset, enable/disable, group changes, and computer creation. See Microsoft's documentation for Audit Directory Service Access and account-management events.

Recommended operator practice:

  1. Start with option 1 to confirm the retained identity.
  2. Prefer a precise base DN and indexed filter over a domain-wide wildcard search.
  3. Keep the default 100-result limit for exploratory arbitrary queries. Entering 0 requires a separate UNLIMITED confirmation.
  4. Avoid repeatedly requesting LAPS or gMSA material.
  5. Record the timestamp and purpose before any write.
  6. Define rollback steps before changing passwords, membership, account state, or arbitrary attributes.
  7. Stop if the retained relay disappears, changes identity, or reports that it is already in use.

Evidence handling

RelayLDAP creates:

  • A mode-0700 evidence directory.
  • A mode-0600 JSONL activity log.
  • A mode-0600 JSONL results log.

Bind passwords and passwords entered for account changes are redacted from RelayLDAP's evidence logs. Query results are not generally redacted.

Important

LAPS passwords, gMSA blobs, descriptions, comments, paths, group membership, and arbitrary-search output may be sensitive. Protect the evidence directory, do not commit it to source control, and follow the engagement's retention requirements.

The included .gitignore excludes RelayLDAP evidence files and common local Python artifacts from commits.

Validation status

The protocol and API assumptions were rechecked on 2026-08-16 against the current upstream ntlmrelayx entry point, SOCKS API implementation, and LDAP SOCKS plugin. The built-in directory filters use Microsoft's documented LDAP matching rules, UAC values, Windows LAPS schema, and gMSA class.

Relay discovery has been tested against a local mock of Impacket's six-field API response and a live retained relay. The live read-only check used this sequence:

  1. Queried a custom local API port.
  2. Filtered and displayed seven active LDAPS records.
  3. Selected one user relay and populated protocol, target, port, domain, username, and client ID.
  4. Completed an ldap3 Sicily NTLM bind through proxychains.
  5. Discovered the domain naming context through RootDSE.
  6. Performed one targeted current-identity lookup.
  7. Exited without an LDAP write.

The relay-side debug log showed the expected empty bind, NTLM bind, session selection, intercepted unbind, and clean tunnel completion. Client names, addresses, and directory data are intentionally omitted from this repository.

Run the offline tests with:

python3 -m unittest -v test_relayldap.py

GitHub Actions runs the same test suite with the declared dependencies on Python 3.10, 3.12, and 3.14. Live validation remains limited to the read-only relay sequence described above; the repository does not claim live coverage of every directory operation or Windows Server version.

Troubleshooting

LDAP: Received an unknown LDAP binding request

The client reached the relay, but its LDAP authentication mechanism was not one understood by the SOCKS plugin. RelayLDAP uses ldap3 NTLM specifically to produce the expected Sicily sequence.

No session for USER@TARGET available

The requested domain/username does not match an active relay, the destination differs from the retained target, or the session expired. Check:

ntlmrelayx> socks

Use the exact target, port, domain, and username shown there.

Automatic discovery cannot reach the API

Confirm the port used by ntlmrelayx -http-api-port and query the endpoint locally:

curl --noproxy '*' http://127.0.0.1:9090/ntlmrelayx/api/v1.0/relays

If a custom port was used, replace 9090. RelayLDAP accepts either the API base URL or the complete /ntlmrelayx/api/v1.0/relays endpoint. Use --no-relay-discovery when manual entry is preferred.

Don't have a relay for TARGET(389)

The client attempted LDAP/389 while only an LDAPS/636 relay exists. Ensure the client is targeting 636 and does not perform a separate LDAP-signing or host-information probe against 389.

unexpected EOF or Connection closed

Check the ntlmrelayx console. If it reports an unknown bind request, this is an application-layer compatibility issue rather than a basic SOCKS routing failure.

Zero Kerberoast candidates

RelayLDAP discovers user accounts with SPNs but does not request tickets. Zero results means no matching user SPNs were returned under the current base and permissions. Disabled candidates, when present, are displayed with enabled: false.

Zero AS-REP candidates

No visible user object had the DONT_REQ_PREAUTH flag under the selected search base. RelayLDAP does not send AS-REQ traffic.

Limitations

RelayLDAP is an LDAP client, not a complete Active Directory exploitation framework. It does not:

  • Request Kerberos TGTs, TGSs, or AS-REPs.
  • Perform DCSync or DRSUAPI replication.
  • Execute commands through SMB, WMI, WinRM, RDP, or MSSQL.
  • Capture or crack NetNTLM responses.
  • Coerce authentication or start ntlmrelayx.
  • Decode every binary security descriptor or managed-password structure.
  • Guarantee that a write is operationally safe or automatically reversible.
  • Make LDAP activity invisible to EDR, NDR, domain-controller logging, or identity analytics.

The generic search and attribute-modification options cover LDAP operations not included elsewhere in the menu.

Contributing

Keep client names, domains, addresses, credentials, hashes, tickets, and evidence out of commits. New write operations should document their audit footprint, potential impact, and rollback procedure. Run the offline tests before opening a pull request.

Compatibility reports should include:

  • RelayLDAP and Python versions.
  • ldap3, Impacket, and proxychains versions.
  • LDAP versus LDAPS.
  • The bind-related lines from ntlmrelayx -debug.
  • Sanitized errors with all client identifiers removed.

Security reports

Please follow SECURITY.md for private vulnerability reporting. Do not include credentials, directory data, or client identifiers in a public issue.

Disclaimer and license

RelayLDAP is provided for lawful security research and authorized testing only. Use it only on systems you own or have explicit permission to assess and only within the agreed rules of engagement. You are solely responsible for scope, change control, evidence protection, cleanup, and compliance with applicable law and organizational policy.

The software is provided as is, without warranty. LDAP operations can expose sensitive information, change directory state, interrupt services, or trigger security controls. The project authors and contributors are not responsible for misuse, unauthorized activity, data loss, service disruption, security incidents, or other damages resulting from use of the software. No feature or warning in RelayLDAP makes an operation inherently safe, stealthy, or authorized.

RelayLDAP is licensed under the BSD 3-Clause License.

About

Reuse ntlmrelayx-retained LDAP/LDAPS sessions through a menu-driven ldap3 client.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages