LDAP 注入是一种与 SQL 注入高度相似的目录服务攻击漏洞。当应用程序不安全地将用户输入直接拼接到 LDAP 查询过滤器中时,攻击者可以通过注入特殊的 LDAP 元字符(如*、(、)、&、|、!、=等),改变查询逻辑,导致认证绕过、信息泄露、权限提升或目录数据修改。
LDAP 广泛应用于企业身份认证(如 Active Directory、OpenLDAP),承载高价值数据(如用户凭证、组织架构、权限配置)。一旦注入成功,风险往往与高危 SQL 注入相当,甚至更高,因为它可能直接影响整个域环境的认证系统。
一、漏洞简介与风险剖析
什么是 LDAP 注入?
定义:应用程序使用未验证或未转义的用户输入动态构造 LDAP 过滤器时,攻击者注入控制字符,干扰或重写查询逻辑。核心风险
风险类别 描述 影响程度 认证绕过 构造恒真过滤器(如 `*)( (objectClass=*)`),跳过用户名/密码校验,直接登录 信息枚举/泄露 使用通配符 *列出所有条目,暴露用户列表、邮箱、组、服务器等内部结构高 权限提升 枚举信息后用于密码喷洒、横向移动,最终获取域管理员权限 极高 目录修改 若绑定账号权限高,可未经授权修改/删除目录数据 中到高 拒绝服务 注入复杂嵌套过滤器导致服务器资源耗尽 中 2015 年 Ashley Madison 数据泄露事件中,攻击者利用 LDAP 注入绕过认证,泄露数百万用户数据。
二、LDAP 查询语法与注入原理
LDAP 过滤器基础
LDAP 过滤器采用前缀 notation,支持逻辑运算和通配符:元字符 含义 示例 注入影响 &逻辑 AND (&(uid=user)(pass=pwd))联合条件 ` ` 逻辑 OR `( !逻辑 NOT (!(objectClass=group))取反条件 *通配符 (cn=joh*)模糊匹配所有 ()分组/结构边界 用于定义过滤器边界 关闭原有条件,注入新逻辑 \00空字节 可用于截断某些实现 边界绕过 注入核心:未转义的元字符会被 LDAP 服务器解释为操作符,导致查询意图被篡改。
漏洞原理与 Payload 构造详解
常见模式:应用程序拼接过滤器如(&(uid=输入)(objectClass=person))。认证绕过 Payload 示例:
- 输入用户名:
admin)(|(uid=*)(密码随意)
最终过滤器:(&(uid=admin)(|(uid=*))(objectClass=person))→ OR 条件恒真,绕过密码校验。 - 输入用户名:
*)(|(objectClass=*)
最终过滤器:(&(uid=*)(|(objectClass=*))(objectClass=person))→ 匹配所有对象。 - 输入用户名:
*)(uid=*))(|(uid=*(经典 OWASP 示例)
最终过滤器始终为真。
信息泄露 Payload 示例:
- 输入搜索词:
*→ 返回所有条目。 - 输入:
*)(mail=*)→ 枚举所有邮箱。 - 输入:
)(|(cn=*)(mail=*)→ 返回所有 cn 或 mail。
权限提升示例:
- 先枚举用户列表:输入
*)(objectClass=user)→ 获取所有 uid。 - 再结合密码喷洒攻击常见弱密码。
不同语言不安全代码示例:
Python:
filter = f"(&(uid={user_input})(userPassword={password}))"Java(JNDI):
String filter = "(&(uid=" + userInput + ")(objectClass=person))";.NET:
string filter = $"(&(uid={userInput})(objectClass=person))";
- 输入用户名:
盲注(Blind LDAP Injection)
当无直接错误或结果返回时,类似 SQL 盲注:- 布尔盲注:构造真/假条件,根据响应差异(登录成功/失败、结果数量)枚举。
示例 Payload:*)(objectClass=user)(真:返回结果多;假:无结果)。
逐步枚举属性值:如检查密码首字符是否 'a':*)(userPassword=a*)。 - 时间盲注:注入复杂嵌套 OR/NOT 查询耗时,根据响应延迟判断(某些实现支持)。
- 属性发现:盲注枚举常见属性(如 department、mail)是否存在。
- 布尔盲注:构造真/假条件,根据响应差异(登录成功/失败、结果数量)枚举。
三、实战思路:探测、Payload 构造与代码审计
探测与定位方向
- 登录/SSO 页面、搜索功能(员工目录、邮箱查找)。
- 指纹探测:输入
*、(、)、&、|,观察结果增多、错误或异常行为。
常见 Payload 构造表(测试用例扩展)
编号 输入 Payload 目的 预期效果 T1 alice 基线正常 正常匹配 T2 * 通配符泄露 返回所有条目 T3 *)( (objectClass=*) 认证绕过 T4 admin)( (uid=*) 伪造管理员登录 T5 )(mail=) 枚举邮箱 泄露所有 mail T6 \00 空字节截断 绕过某些校验 T7 john(零宽字符) 不可见绕过 绕过正则/黑名单 T8 )(userPassword=a) 盲注枚举密码首字符 布尔响应差异 T9 %2A%28%29(URL 编码) 编码绕过 解码后注入 代码审计(SAST)与检测
常见危险模式:字符串拼接构造过滤器(如
+、f-string、String.format)。Semgrep 规则扩展示例(支持 Python/Java/JS/.NET):
rules: - id: ldap-injection-detect patterns: - pattern-either: - pattern: "... + $USER + ..." - pattern: f"...{$USER}..." - pattern: String.format("...(uid=%s)...", $USER) message: "潜在 LDAP 注入:避免字符串拼接构造过滤器,使用参数化或转义函数。" languages: [python, java, javascript, csharp] severity: ERROR其他语言审计点:
- Java:检测
new InitialDirContext+ 字符串拼接。 - .NET:检测
DirectorySearcher.Filter拼接。 - Python:检测未使用
ldap3.utils.conv.escape_filter_chars。
- Java:检测
单元测试示例(Python ldap3):
@pytest.mark.parametrize("input_value, expected_escaped", [ ("alice", "alice"), (")(|(objectClass=*)", "\\29\\28\\7c\\28objectClass\\3d\\2a\\29\\29"), ("*", "\\2a"), ("\\", "\\5c"), ]) def test_escape_effective(input_value, expected_escaped): safe = escape_filter_chars(input_value) assert expected_escaped in safe assert any(char in safe for char in "()*&|!") == False # 未转义形式不应出现
四、防御要点与最佳实践
遵循深度防御:输入处理 + 权限控制 + 监控。
首选防御:转义与参数化
专用转义函数(RFC 4515 标准):
语言 推荐函数/库 Python ldap3.utils.conv.escape_filter_chars()Java UnboundID SDK 或手动转义 \*→\2a等.NET System.DirectoryServices+ 自定义转义PHP ldap_escape()(LDAP_ESCAPE_FILTER)参数化查询:避免拼接,使用库提供的模板(如 Java
{0}占位)。白名单验证:用户名仅允许
[a-zA-Z0-9_-],拒绝其他。
最小权限原则
- 应用绑定账号仅授予必要读取权限,禁止修改。
- 避免使用域管理员账号。
运行时监控与辅助
- 日志异常查询:含大量
*、|、嵌套括号或结果异常多。 - 速率限制登录/搜索接口。
- WAF 规则阻挡常见 Payload(但非万能,易被零宽/编码绕过)。
- 错误遮蔽:不返回 LDAP 原始错误。
- 日志异常查询:含大量
开发者/运维 Checklist
状态 检查项 必做 输入是否经转义/白名单? 必做 绑定账号是否最小权限(只读)? 推荐 CI 是否集成 SAST 检测拼接? 推荐 是否有绕过向量单元测试? 运维 是否监控异常查询模式?
总结
LDAP 注入虽不如 SQL 注入常见,但企业环境中危害极大,可能直接导致域渗透。核心防御:绝不拼接字符串,始终使用专用转义/参数化函数;结合最小权限与监控,形成多层防线。开发阶段通过 SAST 与单元测试捕获,生产环境通过日志审计阻断,才能有效降低风险。
已在FreeBuf发表 0 篇文章
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf
客服小蜜蜂(微信:freebee1024)



