【原创】LDAP NULL bind导致登录绕过漏洞分析和修复方案
字数 1852 2025-08-15 21:33:02
LDAP NULL Bind导致登录绕过漏洞分析与修复方案
一、背景知识
1.1 LDAP和认证过程
LDAP(轻型目录访问协议)是一个开放的、中立的工业标准应用协议,通过IP协议提供访问控制和维护分布式信息的目录信息。特点包括:
- 优异的读性能,但写性能较差
- 支持跨平台,广泛用于统一认证服务(如SSO)
LDAP bind绑定是登录认证过程,常见认证方式:
- 用户名密码绑定:最常见方式,第三方应用常用
- 密码属性比较:检索用户条目后比较密码属性(较少见,需password属性可读)
- "两次绑定"验证:初次匿名绑定,第二次提交身份信息绑定
1.2 NULL BIND(匿名绑定)
NULL BIND(或称Anonymous bind)特点:
- 用户无需身份验证,提交空值即可连接服务器完成绑定
- 不同于匿名访问(匿名绑定不能执行访问/查询)
- 在RFC 4513的5.1.2和6.3.1小节有相关描述
RFC 4513关键内容:
- 客户端应禁止向名称/密码验证界面输入空密码
- 服务器默认应拒绝未经身份验证的绑定请求
- 客户端应主动检测未经验证的Bind请求(验证密码是否为空)
1.3 服务器对NULL BIND的支持情况
微软AD(Active Directory):
- 默认不支持对Active Directory的LDAP匿名操作
- 但支持对rootDSE的匿名搜索和绑定(无法禁用)
- Windows Server 2003前版本需手动调整禁用匿名操作
OpenLDAP:
- 现行版本默认支持匿名绑定(可手动调整禁用)
- 部分早期版本还支持匿名访问
关键概念:
- Active Directory:存储对象信息,便于管理员和用户查找使用
- rootDSE:目录服务器信息树的根,提供服务器本身的数据(如厂商、命名上下文、支持的功能等)
二、漏洞案例分析
2.1 漏洞描述
某IT系统存在以下流程:
- 管理员分配有权限账户,维护用户列表
- 登录时验证用户名是否在服务器账户列表内
- 提交到LDAP验证判断登录是否成功
漏洞表现:离职用户(LDAP中不存在但在系统用户列表内)可使用任意密码登录
2.2 漏洞成因
系统逻辑缺陷:
- 校验输入用户是否为合法用户
- 对合法用户,在LDAP服务器中查询用户DN信息
- 提交DN与密码,通过LDAP绑定是否成功判定登录状态
根本原因:
- 离职员工信息在LDAP中已删除,返回DN为空
- 上层域控(Windows Server DC)允许对rootDSE的NULL绑定
- 提交空DN与任意密码时,
ldap_bind返回结果为真 - PHP代码错误判定为登录成功
问题代码示例:
// 有漏洞的代码
if (ldap_bind($conn, $ldap_user_dn, $password)) {
// 登录成功逻辑
}
// 修复后代码
if (!empty($ldap_user_dn) && ldap_bind($conn, $ldap_user_dn, $password)) {
// 登录成功逻辑
}
三、漏洞复现方法
3.1 NESSUS扫描
NESSUS可能报告此漏洞(中危信息泄露),但描述明确:
- 不能证明存在LDAP匿名访问漏洞
- LDAP v3要求支持匿名绑定
3.2 手工测试方法
3.2.1 使用LdapAdmin工具
- 输入host
- 选择"匿名连接"
- 观察是否支持匿名绑定
3.2.2 使用Softerra LDAP Browser
- 选择服务器,Base DN设为RootDSE
- 选择"Anonymous user"
- 尝试加载根目录信息
3.2.3 Python脚本测试
import ldap
ldapconn = ldap.initialize('ldap://X.X.X.X:389')
ldapconn.simple_bind_s('', '') # 空参数
print("绑定成功" if ldapconn.whoami_s() else "绑定失败")
四、LDAP服务端调整方案
4.1 OpenLDAP配置
根据官方文档,需在配置中指定disallow bind_anon禁用匿名绑定:
- 修改配置文件:
vi /etc/openldap/slapd.d/cn\=config.ldif
添加:
olcDisallows: bind_anon
olcRequires: authc
- 修改前端配置:
vi /etc/openldap/slapd.d/cn\=config/olcDatabase\=\{-1\}frontend.ldif
添加:
olcRequires: authc
- 重启服务:
systemctl restart slapd
4.2 Windows Server AD配置
针对Active Directory的调整(无法禁用rootDSE匿名绑定):
- 运行
AdsiEdit.msc,连接到配置 - 导航至:
CN=Configuration,CN={GUID} → CN=Services → CN=Windows NT → CN=Directory Service - 右键选择属性,修改
DsHeuristics属性值为0(0000002表示允许) - 使用LDAP工具验证匿名访问是否失败
五、安全编码建议
-
输入验证:
- 对提交到LDAP的用户输入值进行空值校验
- 前后端均做控制,检查提交参数逻辑
-
LDAP绑定逻辑:
- 确保在绑定前验证DN不为空
- 正确处理绑定失败情况
-
避免LDAP注入:
- 对所有用户输入进行适当的转义和过滤
- 使用参数化查询
示例安全代码(参考VMware Harbor实现):
def ldap_auth(username, password):
if not username or not password:
return False
# 获取用户DN
user_dn = get_ldap_user_dn(username)
if not user_dn:
return False
try:
conn = ldap.initialize(LDAP_URL)
conn.simple_bind_s(user_dn, password)
return True
except ldap.INVALID_CREDENTIALS:
return False
finally:
conn.unbind()
六、参考资料
- RFC 4513关于LDAP认证的规范
- Microsoft官方文档:匿名LDAP操作在Active Directory中的配置
- OpenLDAP安全配置指南
- 相关技术博客和安全分析文章