Skip to main content

LDAP query obfuscation library - Python port of github.com/Macmod/ldapx

Project description

ldapx

PyPI version Python versions License: MIT

Python port of ldapx - LDAP query obfuscation library.

Transform LDAP filters, BaseDNs, attribute lists, and attribute entries using composable middleware chains. Zero dependencies. Works as a library or CLI tool.

Installation

pip install ldapx

Quick Start

import ldapx

# Obfuscate a filter with case mutation + OID attributes
result = ldapx.obfuscate_filter("(cn=admin)", "CO")
# → (oID.02.05.04.03 =aDmIn)

# Obfuscate a BaseDN
result = ldapx.obfuscate_basedn("DC=corp,DC=local", "COQ")
# → oID.0.9.2342.19200300.100.1.25 ="cOrP",oID.0.9.2342.19200300.100.1.25 ="lOcAl"

# Obfuscate an attribute list
result = ldapx.obfuscate_attrlist(["cn", "sAMAccountName"], "COR")
# → ['oID.1.2.840.113556.1.4.221 ', 'oID.02.5.4.3  ']

Usage Patterns

Pattern 1: High-level chain strings (simplest)

import ldapx

result = ldapx.obfuscate_filter("(sAMAccountName=user1)", "COGDR")
result = ldapx.obfuscate_basedn("DC=corp,DC=local", "CSQOX")
result = ldapx.obfuscate_attrlist(["cn", "sAMAccountName"], "CRDG")
result = ldapx.obfuscate_attrentries({"cn": [b"test"]}, "CR")

Pattern 2: Explicit chain (Go-style)

from ldapx.parser import query_to_filter, filter_to_query
from ldapx.middlewares.filter import (
    FilterMiddlewareChain,
    rand_case_filter_obf,
    oid_attribute_filter_obf,
)

chain = FilterMiddlewareChain()
chain.add("Case", lambda: rand_case_filter_obf(0.7))
chain.add("OID", lambda: oid_attribute_filter_obf(4, 4))

f = query_to_filter("(cn=admin)")
f = chain.execute(f, verbose=True)
result = filter_to_query(f)

Pattern 3: Direct composition

from ldapx.parser import query_to_filter, filter_to_query
from ldapx.middlewares.filter import rand_case_filter_obf, oid_attribute_filter_obf

f = query_to_filter("(cn=admin)")
f = rand_case_filter_obf(0.5)(f)
f = oid_attribute_filter_obf(2, 2)(f)
result = filter_to_query(f)

CLI

# Obfuscate a filter
ldapx filter -f "(cn=admin)" -c "COGDR"

# Generate 5 variants
ldapx filter -f "(cn=admin)" -c "COGDR" -n 5

# Obfuscate a BaseDN
ldapx basedn -b "DC=corp,DC=local" -c "CSQOX"

# Obfuscate attribute list
ldapx attrlist -a "cn,sAMAccountName,memberOf" -c "CRDG"

# List available codes
ldapx codes --all

# Pipe from stdin
echo "(cn=admin)" | ldapx filter -c "COGDR"

# JSON output
ldapx filter -f "(cn=admin)" -c "CO" --json

# Custom options
ldapx filter -f "(cn=admin)" -c "CO" -o FiltCaseProb=0.8 -o FiltOIDMaxSpaces=4

Middleware Codes

Filter (-f)

Code Name Description
C Random case Randomize case of attribute names and values
S Random spacing Add context-aware spacing (ANR, DN, SID)
G Garbage filters Wrap filters in OR with random garbage
T Replace tautologies Replace simple presence filters with tautologies
R Boolean reorder Randomly shuffle AND/OR clauses
O OID attributes Replace attribute names with OIDs
X Hex value encoding Hex-encode characters in DN-type values
t Timestamp garbage Add garbage to timestamp patterns
B Add random boolean Wrap with redundant AND/OR
D Double negation Apply (!(!(filter)))
M DeMorgan transform Apply De Morgan's laws
b Bitwise breakout Convert equality to bitwise matching rules
d Bitwise decompose Break bitwise values into individual bits
I Equality by inclusion (attr=val) to range + exclusion
E Equality by exclusion (attr=val) to presence + NOT range
A Approx match (attr=val) to (attr~=val)
x Extensible match (attr=val) to (attr:=val)
Z Prepend zeros Add leading zeros to numbers/SIDs
s Substring split Split equality into substring match
N Names to ANR Replace ANR-set attributes with aNR
n ANR garbage Add garbage to ANR substring queries
P dnAttributes noise Randomly toggle :dn: on extensible match (AD ignores it, [MS-ADTS 3.1.1.3.1.3.1])
L Transitive eval Convert link attr equality to LDAP_MATCHING_RULE_TRANSITIVE_EVAL (1941)

BaseDN (-b)

Code Name Description
C Random case Randomize case
S Random spacing Add spaces around DN
Q Double quotes Wrap DN values in quotes
O OID attributes Replace DN attr names with OIDs
X Hex value encoding Hex-encode DN value characters
U GUID format Replace DN with <GUID=hex> ([MS-ADTS 3.1.1.3.1.2.4]). Requires -o BaseDNGuid=hex
I SID format Replace DN with <SID=string> ([MS-ADTS 3.1.1.3.1.2.4]). Requires -o BaseDNSid=S-1-...

AttrList (-a)

Code Name Description
C Random case Randomize case
R Reorder list Shuffle attribute order
D Duplicate Add duplicate entries
O OID attributes Replace with OIDs
G Garbage (non-existing) Add random fake attributes
g Garbage (existing) Add random real attributes
W Replace with wildcard Replace list with *
w Add wildcard Append * to list
p Add plus Append + (operational attrs)
e Replace with empty Replace with empty list

AttrEntries

Code Name Description
C Random case Randomize attribute name case
R Reorder list Shuffle attribute order
O OID attributes Replace with plain OIDs

Options

Customize middleware parameters via Options:

import ldapx

opts = ldapx.Options(
    FiltCaseProb=0.8,           # Higher case mutation probability
    FiltOIDMaxSpaces=4,         # More spaces after OIDs
    FiltGarbageMaxElems=3,      # More garbage filters
    BDNSpacingMaxSpaces=4,      # More spacing in BaseDN
)

result = ldapx.obfuscate_filter("(cn=admin)", "COGDR", options=opts)

Adapters

The core library has zero dependencies and returns strings. For integration with specific LDAP libraries, use adapters:

badldap adapter

# pip install ldapx[badldap]
from ldapx.parser import query_to_filter
from ldapx.middlewares.filter import rand_case_filter_obf
from ldapx.adapters.badldap import ast_to_asn1

f = query_to_filter("(cn=admin)")
f = rand_case_filter_obf(0.5)(f)
asn1_filter = ast_to_asn1(f)  # badldap ASN1 Filter object

Compatibility Matrix

ldapx-py returns obfuscated queries as strings. How well those strings are accepted depends on the LDAP library your project uses. Below is a full compatibility matrix tested against a real Active Directory environment.

Filter codes

Code Name badldap impacket ldap3 Notes
C Case via adapter native native
S Spacing via adapter native native
G Garbage via adapter native monkey-patch ldap3 rejects unknown attr names
T Tautologies via adapter native native
R Reorder via adapter native native
O OID via adapter FAIL monkey-patch impacket/ldap3 reject oID. format
X Hex value via adapter native native
t Timestamp via adapter native native
B AddBool via adapter native native
D DblNeg via adapter native native
M DeMorgan via adapter native native
b Bitwise via adapter native native
d Decompose via adapter native native
I Inclusion via adapter native native
E Exclusion via adapter native native
A Approx via adapter native native
x Extensible via adapter native native
Z Zeros via adapter native native
s Substring via adapter native native
N ANR via adapter native native
n ANR garbage via adapter native native
P dnAttr noise via adapter native native
L Transitive via adapter native native

BaseDN codes

Code Name badldap impacket ldap3 Notes
C Case native native native
S Spacing native native FAIL ldap3 DN parser rejects spaces
Q Quotes native native FAIL ldap3 DN parser rejects quotes
O OID native native FAIL ldap3 DN parser rejects oID.
X Hex value native native native
U GUID native native native Alternative DN form, works everywhere
I SID native native native Alternative DN form, works everywhere

Tools tested

Tool LDAP library Recommended filter chain Recommended BaseDN chain
bloodyAD badldap All codes (via ASN1 adapter) All codes
bloodhound.py ldap3 All except O (or with monkey-patch) C, X, U, I
impacket (GetADUsers, GetUserSPNs, etc) impacket custom All except O All codes
NetExec impacket All except O All codes
Certipy ldap3 All except O (or with monkey-patch) C, X, U, I

For step-by-step integration examples with each tool (impacket, NetExec, Certipy, bloodhound.py, bloodyAD), see docs/integration-examples.md.

Integration notes

badldap: Requires ASN1 adapter (ldapx.adapters.badldap.ast_to_asn1) + monkey-patch of query_syntax_converter to bypass PEG parser. See bloodyAD integration for reference.

ldap3: Codes G and O in filters need monkey-patching ldap3.protocol.convert.validate_attribute_value to accept unknown attribute names. Do not use connection.check_names = False — it breaks response parsing (SIDs, GUIDs, datetimes returned as raw bytes/strings). BaseDN codes S, Q, O fail due to ldap3's strict safe_dn() parser — use U (GUID) or I (SID) instead.

impacket: Code O (OID) in filters fails due to impacket's filter parser rejecting oID. prefix. All other codes work natively. BaseDN accepts all codes including alternative DN forms.

General AD limitations (all libraries)

  • AttrEntries code O: AD rejects OID attribute names in modify/add operations
  • AttrList codes W/w/p/e: Change query semantics (what server returns), may break response parsing
  • NTLM signing/sealing: Obfuscation works (applied before encryption), but not visible on the wire with Wireshark

Proxy Mode

This library provides programmatic obfuscation (library + CLI). If you need proxy mode — intercepting and transforming LDAP packets on the fly between any tool and an LDAP server, without modifying source code — use the Go version:

  • github.com/Macmod/ldapx — LDAP proxy with real-time packet transformation, interactive shell, LDAPS/SOCKS support

Credits

License

MIT - see LICENSE

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ldapx-0.3.1.tar.gz (57.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ldapx-0.3.1-py3-none-any.whl (58.5 kB view details)

Uploaded Python 3

File details

Details for the file ldapx-0.3.1.tar.gz.

File metadata

  • Download URL: ldapx-0.3.1.tar.gz
  • Upload date:
  • Size: 57.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for ldapx-0.3.1.tar.gz
Algorithm Hash digest
SHA256 7534040617e38a80bf442f35e3f796abefcf558bc5b16cc8c0757a6d2d63aa5d
MD5 5573a6f22d5175a1560ab70b29e44ecb
BLAKE2b-256 4efde594eb3bf14ae33338f98e2de41fe1cc1a015c6038dd04fbf317f5bd7ef5

See more details on using hashes here.

File details

Details for the file ldapx-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: ldapx-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 58.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for ldapx-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5b94c093147b2639ece2509832ae943af06b08524264a5de3fa4ecc6667b91b8
MD5 ad90e3408dcecfdd254d35238bf993f0
BLAKE2b-256 72ac88d2f44a3405edc3572a9916c46d02d3ed34ad9a1b69fea08ab741f7a295

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page