从一次漏洞挖掘入门ldap注入

admin 2022年3月4日05:09:50评论264 views字数 3958阅读13分11秒阅读模式

在最近的一次测试中,随缘摸到了一个sso系统,留给前台的功能只有登陆。

没有验证码,但是登陆点强制要求每个用户更改强密码,而且除了管理员和测试账号其他大部分都是工号形式,所以不考虑撞库。直接fuzz一把梭

测试过程中发现username对于下面payload会存在两种不同回显

从一次漏洞挖掘入门ldap注入

从一次漏洞挖掘入门ldap注入

当时我并不理解这种payload是什么库的数据格式。但是看到存在"!"字符时,页面的回显是不同的,而"!"在绝大多数语言中都是取反的表达形式,自然会产生不同的布尔值,那么无疑就是个xxx注入点了

何为LDAP

通过payload的类型,看到是经典的ldap注入语句。一种老协议和数据存储形式了

LDAP协议

LDAP(Lightweight Directory Access Protocol):即轻量级目录访问协议。是一种运行于TCP/IP之上的在线目录访问协议,主要用于目录中资源的搜索和查询。使用最广泛的LDAP服务如微软的ADAM(Active Directory Application Mode)和OpenLDAP

LDAP存储

MySQL数据库,数据都是按记录一条条记录存在表中。而LDAP数据库,是树结构的,数据存储在叶子节点上。

LDAP目录中的信息是按照树形结构组织的:

dn:一条记录的位置
dc:一条记录所属的区域
ou:一条记录所属的组织
cn/uid:一条记录的名字/ID

这种树结构非常有利于数据的查询。首先要说明是哪一棵树(dc),然后是从树根到目标所经过的所有分叉(ou),最后就是目标的名字(cn/uid),借用一张图来表明结构如下:

从一次漏洞挖掘入门ldap注入

条目&对象类&属性

  • 条目(entry):是目录中存储的基本信息单元,上图每一个方框代表一个entry。一个entry有若干个属性和若干个值,有些entry还能包含子entry

  • 对象类(obejectclass):对象类封装了可选/必选属性,同时对象类也是支持继承的。一个entry必须包含一个objectClass,且需要赋予至少一个值。而且objectClass有着严格的等级之分,最顶层是top和alias。例如,organizationalPerson这个objectClass就隶属于person,而person又隶属于top

    从一次漏洞挖掘入门ldap注入

  • 属性(atrribute):顾名思义,用来存储字段值。被封装在objectclass里的,每个属性(attribute)也会分配唯一的OID号码

LDAP查询语句

一个圆括号内的判断语句又称为一个过滤器filter。

( "&" or "|" (filter1) (filter2) (filter3) ...) ("!" (filter))

逻辑与&

(&(username=Hpdoger)(password=ikun))

查找name属性为Hpdoger并且password属性值为ikun的所有条目

逻辑或|

(|(username=Hpdoger)(displayname=Hpdoger))

查找username或者displayname为Hpdoger的所有条目

特殊说明

除使用逻辑操作符外,还允许使用下面的单独符号作为两个特殊常量

(&)     ->Absolute TRUE
(|) ->Absolute FALSE
* ->通配符

另外,默认情况下,LDAP的DN和所有属性都不区分大小写,即在查询时:

(username=Hpdoger) <=> (username=HPDOGER)

LDAP注入

由于LDAP的出现可以追溯到1980年,关于它的漏洞也是历史悠久。LDAP注入攻击和SQL注入攻击相似,利用用户引入的参数生成LDAP查询。攻击者构造恶意的查询语句读取其它数据/跨objectclass读取属性,早在wooyun时代就有师傅详细的剖析了这类漏洞。

上文说到LDAP过滤器的结构和使用得最广泛的LDAP:ADAM和OpenLDAP。然而对于下面两种情况

无逻辑操作符的注入

情景:(attribute=$input)

我们构造输入:$input=value)(injected_filter

代入查询的完整语句就为:

(attribute=value)(injected_filter)

由于一个括号内代表一个过滤器,在OpenLDAP实施中,第二个过滤器会被忽略,只有第一个会被执行。而在ADAM中,有两个过滤器的查询是不被允许的。

因而这类情况仅对于OpenLDAP有一定的影响。

例如我们要想查询一个字段是否存在某值时,可以用$input=x*进行推移,利用页面响应不同判断x*是否查询成功

带有逻辑操作符的注入

(|(attribute=$input)(second_filter))
(&(attribute=$input)(second_filter))

此时带有逻辑操作符的括号相当于一个过滤器。此时形如value)(injected_filter)的注入会变成如下过滤器结构

(&(attribute=value)(injected_filter))(second_filter)

虽然过滤器语法上并不正确,OpenLDAP还是会从左到右进行处理,忽略第一个过滤器闭合后的任何字符。一些LDAP客户端Web组成会忽略第二个过滤器,将ADAM和OpenLDAP发送给第一个完成的过滤器,因而存在注入。

举个最简单的登陆注入的例子,如果验证登陆的查询语句是这样:

(&(USER=$username)(PASSWORD=$pwd))

输入$username = admin)(&)(使查询语句变为

(&(USER=admin)(&))((PASSWORD=$pwd))

即可让后面的password过滤器失效,执行第一个过滤器而返回true,达到万能密码的效果。

后注入分析

注入大致分为and、or类型这里就不赘述,感兴趣的可以看之前wooyun的文章:
LDAP注入与防御剖析

还有一个joomla的一个userPassword注入实例:
Joomla! LDAP注入导致登录认证绕过漏洞

回到实例

大致了解注入类型,就开始了第一轮尝试

当通配符匹配到用户名时返回

从一次漏洞挖掘入门ldap注入

用户名不存在时返回

从一次漏洞挖掘入门ldap注入

构造用户名恒真username=admin)(%26&password=123

从一次漏洞挖掘入门ldap注入

说明它判断用户的形式并不是(&(USER=$username)(PASSWORD=$pwd)),因为我们查到的用户名是true,但是验证密码false

由于自己也没搞过LDAP的开发..就盲猜后端应该就是这种情况:
执行了
(&(USER=$username)(objectclass=xxx))后,取password与$password进行对比

ACTION

那么首先要知道它继承了哪些objectclass?因为树结构都有根,使我们能顺藤摸瓜。首先是top肯定存在,回显如下:

从一次漏洞挖掘入门ldap注入

但是top的子类太多了,先fuzz一下objectclass的值缩小范围,payload:

username=admin)(objectclass%3d$str

发现存在personuser两个objectclass

再fuzz一下attribute得到的值如下:

username=admin)($str%3d*

从一次漏洞挖掘入门ldap注入

凭借这些信息去LDAP文档里溯继承链,先去找user类,继承自organizationalPerson

从一次漏洞挖掘入门ldap注入

同理organizationalperson又是继承自person的,person继承自top,最终的继承链为:

top->person->organizationalperson->user

也就是说这些类存在的属性都可能被调用。很遗憾的是我并没有fuzz到password类型参数,一般来说password会以userPassword的形式存储在person对象中,很多基于ldap的开发demo中也是这样写的。

但是userPassword毕竟也只是person类可选的属性,开发大概率是改名或者重写属性了,这也是这个漏洞没有上升到严重危害的瓶颈点

从一次漏洞挖掘入门ldap注入

不过依然可以注出一些有用的数据。例如所有用户的用户名、邮箱、手机号、姓名、性别等等,说不定以后可以越权修改某账号性别呢-3-

盲注mobile

尝试注入管理员的手机号mobile

username=admin)(mobile=%s*&password=123

从一次漏洞挖掘入门ldap注入

利用通配符不断添加数字,同理邮箱也可以注出来,与sql盲注的思路相同。

从一次漏洞挖掘入门ldap注入

盲注username

毕竟对于sso,收集username是很有用的信息。那么问题来了,我们是可以通过生成字典来遍历存在的用户名,但是这个工作量是指数倍的增长,一天能跑完一个字母开头的就不错了,而且浪费了通配符的作用。

可是又想做到无限迭代把所有用户一个不漏的跑完,passer6y师傅提醒我用笛卡尔积

最后画出来的流程图大致如下:

从一次漏洞挖掘入门ldap注入

最后测试用户大概有1w多,然而这些大部分是测试帐号,未授权的情况下也不能跑具体数据,但也算是验证了思路的可执行性。

总结

网上关于这类漏洞的fuzz思路也比较久远了,第一次接触这种漏洞,若文章思路如果有什么不对的地方还请师傅们斧正。自己对这类漏洞的姿势理解很浅,现在漏洞已经修复,但是如果有师傅对于password的注入有想法,可以私下交流一下

参考链接

https://wooyun.js.org/drops/LDAP%E6%B3%A8%E5%85%A5%E4%B8%8E%E9%98%B2%E5%BE%A1%E5%89%96%E6%9E%90.html
https://www.cnblogs.com/pycode/p/9495808.html
https://zhuanlan.zhihu.com/p/32732045


作者:hpdoger,已授权转载



扫描关注乌云安全

从一次漏洞挖掘入门ldap注入

本文始发于微信公众号(乌雲安全):从一次漏洞挖掘入门ldap注入

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月4日05:09:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   从一次漏洞挖掘入门ldap注入http://cn-sec.com/archives/488303.html

发表评论

匿名网友 填写信息