|
LDAP注入的深入利用前言
在最近的一次的src测试中遇到了ldap注入漏洞,目标是一个管理平台的单点登陆入口,漏洞存在于用户名存在判断处.
之前渗透测试的时候我也遇到过几个生产环境中ldap注入的漏洞,但是都只能获取到有限的敏感信息(用户名 手机号 邮箱) 危害程度与ldap匿名绑定相同.
在研究ldap查询语法时,我找到了一种可以外带ldap储存的用户密码的方法,实现了对ldap注入的进一步利用.
什么是ldap注入
ldap注入是指ldap过滤器语句(filter)的注入
ldap过滤器的基本语法如下
- =
- >=
- <=
- | 或
- & 与
- ! 非
- * 通配符
- (语句)
复制代码
例如一个简单的查询语句如下
搜索cn值属性为admin的条目 成功会返回完整条目属性
实际使用时可能会比较复杂
比如说同时搜索匹配用户输入的用户名/邮箱/手机号
- (|(cn=admin)(mail=admin)(mobile=admin))
复制代码
ldap条目常见的属性值
- cn (Common Name 通用名称) 常被用做用户名
- Surname 姓
- mobile 手机号
- mail 邮箱
复制代码
在实战中,判断注入点可以尝试插入半个括号
过滤器中如果存在未闭合的括号会使ldap查询出错 观察返回是否出现异常 即可判断注入点
也可以直接输入*(星号) 通配符观察返回是否为用户存在但密码错误,或者是服务器错误.(ldap查询可以同时返回多条结果 如果查询结果不唯一 后端未做好处理可能会报错)
ldap注入常见于在判断用户名是否存在的点 很少出现在用户名密码同时判断的地方.
经过使用括号盲测发现目标可能的登陆逻辑如下.
- $ds=ldap_connect($ldapSrv,$port);//建立ldap连接
- if($ds) {
- $r=ldap_bind($ds, "cn=".$username.",".$dn, $passwd);/绑定ldap区域(相当于登陆ldap服务器) 使用域管用户登陆 检索用户列表
- if($r) {
- $sr=ldap_search($ds, $dn, "(user=".$_GET["user"].")");//在ldap中使用过滤器搜索用户名
- $info = ldap_get_entries($ds, $sr);
- if($info["count"]==0){
- die('用户不存在');
- }
- ldap_close($ds);
- $ds=ldap_connect($ldapSrv,$port);//建立ldap连接
- $bd = ldap_bind($conn, $_GET["user"], $passwd); // 绑定ldap区域(相当于登陆ldap服务器) 以普通用户登陆 判断是否登陆成功
- if ($bd) {
- echo '登陆成功';
- } else {
- echo '密码错误';
- }
- ldap_close($ds);
- } else {
- echo "Unable to connect to LDAP server.";
- }
- }
复制代码
ldap的注入简单利用
ldap注入通常需要构造通配符查询来实现布尔注入,从而带出ldap中储存的数据.
比如ldap中存在一个admin的用户名 查询的注入点为cn(用户名)
那么可以使用*匹配先猜测出用户名
- (cn=a*) 返回密码错误
- (cn=b*) 返回用户名不存在
复制代码
只要判断为密码错误即为匹配成功
构造脚本递归匹配字符
- (cn=a*)
- (cn=ad*)
- (cn=adm*)
- (cn=admi*)
- (cn=admin*)
复制代码
当然*也可以插在开头和中间或者是单独使用
构造语句猜测admin用户的手机号
到这里已经可以跑出ldap中保存的一些敏感信息(手机号 邮箱 用户名).
那么对ldap注入的利用只能到这了吗?
获取ldap中的密码
作为用于用户认证鉴权场景的ldap服务,当然是要拿到ldap中储存的用户的密码
查阅ldap文档 ldap的密码储存在userPassword属性
尝试构造查询
(cn=admin)(userPassword=a*)
多次尝试发现都无法匹配记录.
但是直接使用*可以匹配成功
为什么使用*号不能匹配部分密码呢?
经过查阅ldap rfc4519文档 发现userPassword属性类型不是常规的字符串,而是(Octet String 字节序列)
*通配符只能用于匹配字符串
那如何达到匹配字节序列目的呢
通过阅读ldapwiki发现过滤器除了可以使用常规的运算符外,还有一种特殊的匹配规则(MatchingRule)
其中有两个专门用于匹配Octet String的规则
octetStringMatch
octetStringOrderingMatch
第一个规则在完全匹配时才会返回真,这显然不能利用.
另一个wiki上面没有详细介绍 最后在 rfc4517 找到了octetStringOrderingMatch规则的详细介绍
- The rule evaluates to TRUE if and only if the attribute value appears
- earlier in the collation order than the assertion value. The rule
- compares octet strings from the first octet to the last octet, and
- from the most significant bit to the least significant bit within the
- octet. The first occurrence of a different bit determines the
- ordering of the strings. A zero bit precedes a one bit. If the
- strings contain different numbers of octets but the longer string is
- identical to the shorter string up to the length of the shorter
- string, then the shorter string precedes the longer string.
复制代码
逐字节比较两字节之间的大小 后者大于前者就返回真 显然这个规则可以用于注入
这里在查询时使用十六进制转义\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密码
新版本ldap的密码很少有明文储存 基本上都是哈希后的密码
哈希格式为 {哈希类型}base64后的值
ldap有四种常见哈希
- {SHA}(SHA1)
- (SSHA) 加盐 SHA1
- {MD5} MD5
- {SMD5} 加盐MD5
复制代码
带盐的hsah储存格式为 加盐hash值+盐值
将base64解码出的hash部分转换为十六进制字符串就可以使用hashcat进行常规的hash猜测了
修复方法
转义可能会改变ldap过滤器语法的字符
LDAP注入与防御剖析
- function ldapspecialchars($string) {
- $sanitized=array('\\' => '\5c',
- '*' => '\2a',
- '(' => '\28',
- ')' => '\29',
- "\x00" => '\00');
- return str_replace(array_keys($sanitized),array_values($sanitized),$string);
- }
复制代码
来源:【https://xz.aliyun.com/】,感谢【白帽酱 】
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|