Skip to content
Security
Skill

/hunt-ldap

Hunt LDAP Injection and XPath Injection — authentication bypass, blind char-by-char attribute exfiltration, AD user/group enumeration, XML-store XPath bypass. Covers the LDAP special-character set (* ( ) \\ NUL /), search-filter-context vs DN-injection, parenthesis-balancing,

From plugin
claude-bughunter
3.3k82 skills15 commands
Install
$ npx -y skills add elementalsouls/Claude-BugHunter --skill hunt-ldap --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/hunt-ldap

Context preview

The summary Claude sees to decide when to auto-load this skill.

Hunt LDAP Injection and XPath Injection — authentication bypass, blind char-by-char attribute exfiltration, AD user/group enumeration, XML-store XPath bypass. Covers the LDAP special-character set (* ( ) \\ NUL /), search-filter-context vs DN-injection, parenthesis-balancing,

SKILL.md

hunt-ldap.SKILL.md
name: hunt-ldap
description: "Hunt LDAP Injection and XPath Injection — authentication bypass, blind char-by-char attribute exfiltration, AD user/group enumeration, XML-store XPath bypass. Covers the LDAP special-character set (* ( ) \\ NUL /), search-filter-context vs DN-injection, parenthesis-balancing, AND/OR filter logic, and {SSHA}/{CRYPT} userPassword exfil on non-AD directories. Use when target uses LDAP/AD authentication, corporate SSO with a directory backend, an address-book/people-search API, or XML-based data stores queried with XPath."
sources: hackerone_public, owasp, portswigger
report_count: 0

HUNT-LDAP — LDAP Injection & XPath Injection

> Grounding note: LDAP injection is rarely disclosed with verbatim payloads on > public platforms (most live on internal-pentest reports). This skill is > grounded in the **OWASP LDAP Injection Prevention / Testing Guide > (WSTG-INPV-06)**, **PortSwigger Web Security Academy (LDAP injection)**, and > the **RFC 4515** filter grammar — all publicly verifiable references rather > than invented HackerOne IDs. Do not cite a report you cannot link.

Crown Jewel Targets

LDAP injection that bypasses authentication = **Critical**. Blind attribute exfiltration of credentials/secrets = **High**. AD enumeration alone = Medium-High.

**Highest-value chains:**

  • **LDAP auth bypass** — close the `uid` filter and append an always-true OR so the

bind/search returns the admin entry without a valid password.

  • **Blind attribute exfil** — char-by-char extraction of an attribute value via a

boolean oracle (login success/failure, result count, or response length).

  • **userPassword hash exfil (non-AD only)** — on OpenLDAP/389-DS the

`userPassword` attribute can hold `{SSHA}`/`{CRYPT}` hashes that ARE readable by query. See the AD-vs-generic warning below.

  • **XPath injection auth bypass** — `' or '1'='1` against XML-backed auth.

---

CRITICAL — Active Directory vs generic LDAP

Do **not** conflate the two. They behave very differently:

| | Generic LDAP (OpenLDAP, 389-DS, ApacheDS) | Active Directory | |---|---|---| | Password attribute | `userPassword` — may hold `{SSHA}`/`{MD5}`/`{CRYPT}` and **is readable** if ACL allows | `unicodePwd` — **write-only**, never returned by any search | | Hash exfil via injection | **Possible** where ACLs leak `userPassword` | **Not possible** — there is no readable hash attribute over LDAP | | Useful enum attrs | `uid`, `cn`, `mail`, `userPassword` | `sAMAccountName`, `userPrincipalName`, `mail`, `memberOf`, `description` (often holds plaintext secrets!) |

**Do not tell a reader that blind LDAP injection yields AD password hashes — it does not.** `unicodePwd` is write-only. Against AD, the win is enumeration (`sAMAccountName`, `memberOf`, `description`/`info` fields that admins misuse to store passwords) and auth bypass — not hash dumping. The hash-exfil technique applies **only** to non-AD directories exposing `userPassword`.

---

Attack Surface Signals

Corporate SSO / intranet login pages (often legacy Java/Spring/PHP)
Windows + IIS + "integrated" directory auth
/api/ldap/*  /api/directory/*  /people  /address-book  /search?dir=
"Find a colleague" / org-chart / employee-search features
XML-backed config or auth → XPath injection candidate
Error strings that confirm an LDAP backend:
  javax.naming.NameNotFoundException
  javax.naming.directory.InvalidSearchFilterException
  LDAP: error code 49 - 80090308  (AD invalid creds / bind failure)
  com.sun.jndi.ldap.*  /  System.DirectoryServices  /  ldap_search():
  "Bad search filter"  /  net.ldap (Go)  /  python-ldap SERVER_DOWN

---

LDAP filter grammar (RFC 4515) — why injection works

A login filter is typically built by string-concat:

(&(uid=<USERNAME>)(userPassword=<PASSWORD>))

`&` = AND, `|` = OR, `!` = NOT. **Filters are prefix/Polish notation** — the operator comes first and every sub-filter is parenthesised. To inject you must (a) escape the current `(uid=...)` group, (b) inject your own logic, and (c) leave the overall parenthesis count **balanced** or the server throws a filter-syntax error instead of executing.

The special-character set — TEST EACH ONE

These characters are syntactically meaningful and MUST be escaped by a safe app (RFC 4515 §3). If the app reflects an error or behaves differently when you send them raw, the input is unescaped → injectable:

| Char | Filter escape | Why it matters | |------|---------------|----------------| | `*` | `\2a` | wildcard — matches any value | | `(` | `\28` | opens a filter group | | `)` | `\29` | closes a filter group | | `\` | `\5c` | escape char itself | | NUL | `\00` | string terminator — truncates filter in C-backed servers | | `/` | (DN context) | RDN separator — relevant for DN injection |

**Search-filter context vs DN injection** are different bugs:

  • **Search-filter injection** (most common): your input lands inside a

`(attr=VALUE)` filter. Payloads use `* ( ) & | !`.

  • **DN injection**: your input is concatenated into a Distinguished Name

(`uid=VALUE,ou=people,dc=corp`). Here `,` `=` `+` `"` `\` `<` `>` `;` and `/` matter, and a `*` is NOT a wildcard. Test both — the payloads do not transfer.

---

Step-by-Step Hunting Methodology

Phase 1 — Confirm an LDAP backend (baseline first)

# ALWAYS capture a control response first — you compare everything to this.
BASE=$(curl -s -o /dev/null -w "%{http_code}|%{size_download}|%{time_total}" \
  -X POST https://$TARGET/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"validlookinguser","password":"wrongpass"}')
echo "BASELINE (valid-format, wrong pw): $BASE"

# Send a single unbalanced paren. A SAFE (escaping) app → identical baseline.
# An INJECTABLE app → 500 / filter-syntax error / different size.
curl -s -X POST https://$TARGET/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"test)","password":"x"}' | grep -iE \
  "naming|InvalidSearchFilter|
Read more
Ships withclaude-bughunter

A self-contained Claude skill bundle for bug hunting and external red-team work · 82 skills · 15 slash commands · 681 disclosed-report patterns across 24 core vulnerability classes · enterprise identity + infrastructure attack matrices · engagement-folder

Get the whole plugin

Other skills on claude-bughunter.