前言
在最近的一次的src测试中遇到了ldap注入漏洞,目标是一个管理平台的单点登陆入口,漏洞存在于用户名存在判断处.
之前渗透测试的时候我也遇到过几个生产环境中ldap注入的漏洞,但是都只能获取到有限的敏感信息(用户名 手机号 邮箱) 危害程度与ldap匿名绑定相同.
在研究ldap查询语法时,我找到了一种可以外带ldap储存的用户密码的方法,实现了对ldap注入的进一步利用.
ldap注入点判断
ldap注入是指ldap过滤器语句(filter)的注入
ldap过滤器的基本语法如下
|
例如一个简单的查询语句如下
|
搜索cn值属性为admin的条目 成功会返回完整条目属性
实际使用时可能会比较复杂
比如说同时搜索匹配用户输入的用户名/邮箱/手机号
|
ldap条目常见的属性值
|
在判断注入点的时候可以插入半个括号
多余的未闭合的括号会使ldap查询出错 观察返回是否出现异常 即可判断注入点
也可以直接输入*(星号) 通配符观察返回是否为用户存在但密码错误 或者是服务器错误(ldap查询可以同时返回多条结果 如果查询结果不唯一 后端未做好处理可能会报错)
ldap注入常见于在判断用户名是否存在的点 很少出现在用户名密码同时判断的地方
经过盲测发现目标可能的登陆逻辑如下
|
ldap的注入简单利用
ldap通常构造通配符查询 控制返回的结果
实现布尔注入从而带出ldap中储存的数据
比如ldap中存在一个admin的用户名 查询的注入点为cn
那么可以使用*匹配先猜测出用户名
(cn=a*) 返回密码错误
(cn=b*) 返回用户名不存在
只要判断为密码错误即为匹配成功
|
构造语句猜测admin用户的手机号
(cn=admin)(mobile=13*)
到这里已经可以跑出ldap中保存的一些敏感信息(手机号 邮箱 用户名)
那么对ldap注入的利用只能到这了吗?
获取ldap中的密码
作为用于用户认证鉴权场景的ldap服务,当然是要拿到ldap中储存的用户的密码
查阅ldap文档 ldap的密码储存在userPassword属性
尝试构造查询
(cn=admin)(userPassword=a*)
多次尝试发现都无法匹配记录.
但是直接使用*可以匹配成功
既然密码是一个属性为什么使用*号不能匹配部分字符串呢?
经过查阅ldap rfc4519文档 发现userPassword属性类型不是常规的字符串,而是(Octet String 字节序列)
*通配符只能匹配字符串
那么怎么匹配字节序列呢
通过阅读ldapwiki发现过滤器除了可以使用常规的运算符外,还有一种特殊的匹配规则(MatchingRule)
其中有两个专门匹配Octet String的规则
octetStringMatch
octetStringOrderingMatch
第一个规则在完全匹配时才会返回真,这显然不能利用.
在 rfc4517 找到了octetStringOrderingMatch规则的详细介绍
|
逐字节比较两字节之间的大小 后者大于前者就返回真 显然这个规则可以用于注入
使用 十六进制转义\xx匹配单个字节 (ldap过滤器的语法之一)
…. …. 用户名错误
(cn=admin)(userPassword:2.5.13.18:=\7b) 用户名错误
(cn=admin)(userPassword:2.5.13.18:=\7c) 密码错误 第一个字节为7b 继续尝试
…. …. 用户名错误
(cn=admin)(userPassword:2.5.13.18:=\7b\4d) 用户名错误
(cn=admin)(userPassword:2.5.13.18:=\7b\4e) 密码错误 第二个字节为4d 继续尝试
…. ….
注意要将匹配到的每个字节-1再进行下一个匹配
最后直接转为字符串得到密码
最后成功跑出了目标账号的密码
ldap密码格式
新版本ldap的密码很少有明文储存 基本上都是哈希后的密码
哈希格式为 {哈希类型}base64后的值
ldap有四种常见哈希
{SHA}(SHA1)
(SSHA) 加盐 SHA1
{MD5} MD5
{SMD5} 加盐MD5
带盐的hsah储存格式为 加盐hash值+盐值
将base64解码出的hash部分转换为十六进制字符串就可以使用hashcat进行常规的hash猜测了
修复方法
转义可能会改变ldap过滤器语法的字符
LDAP注入与防御剖析
|