可以简单的把LDAP理解成一种数据库,不过和mysql这类关系数据库不一样的是,LDAP服务器采用的是树状存储结构
LDAP服务器既然是一种类似数据库的存储方式,那自然而然的就有查询的功能,由于LDAP的结构特性,它在读取方面的效率是非常高的,但在写入方面却不尽如人意.
[^目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以ldap天生是用来查询的。]:
LDAP,轻量目录访问协议
====|dn :一条记录的位置|==
==|dc :一条记录所属区域|==
==|ou :一条记录所属组织|==
|cn/uid:一条记录的名字/ID|==
例如一条具体的LDAP记录如下
1 | dn:cn=stan,ou=linux,ou=computer,dc=ourschool,dc=org |
查询语法
=(等于)
查询”名”属性为”Zhhhy”的所有对象
1 | (Name=Zhhhy) |
这会返回“名”属性为“Zhhhy”的所有对象。圆括号是必需的,以便强调 LDAP 语句的开始和结束。
&(逻辑与)
如果具有多个条件并且希望全部条件都得到满足,则可使用此语法。例如,如果希望查找居住在 mnnu 并且“名”为“Zhhhy”的所有人员,可以使用:
1 | (&(Name=Zhhhy)(Addr=mnnu)) |
请注意,每个参数都被属于其自己的圆括号括起来。整个 LDAP 语句必须包括在一对主圆括号中。操作符 & 表明,只有每个参数都为真,才会将此筛选条件应用到要查询的对象。
*(通配符)
可使用通配符表示值可以等于任何值。使用它的情况可能是:您希望查找具有职务头衔的所有对象。为此,可以使用
1 | (title=*) |
这会返回“title”属性包含内容的所有对象。另一个例子是:您知道某个对象的“名”属性的开头两个字母是“Jo”。那么,可以使用如下语法进行查找
1 | (givenName=Jo*) |
LDAP注入
只要有用户输入,并且未进行数据清洗,很大一个可能就会存在漏洞.LDAP注入的成因和sql注入的相同,对用户数据的过滤不够严格,导致拼接而成的命令,使得程序按非预期的进行.
LDAP拼接的几种情况,
1 | (attribute=value) |
当我们构造的是
1 | value)(injected_filter |
拼接之后产生
1 | (attribute=value)(injected_filter) ps:injected_filter |
用于替换恒成真的语句,这样查询语句就会验证成功
通常,在OpenLDAP实施中,第二个过滤器会被忽略,只有第一个会被执行。
而在ADAM中,有两个过滤器的查询是不被允许的,因而这个注入毫无用处。
1 | `(|(``attribute``=value)(second_filter)) ``or`` (&(``attribute``=value)(second_filter))` |
如果第一个用于构造查询的过滤器有逻辑操作符,形如
1 | **value)(injected_filter)** |
的注入会变成如下过滤器:
1 | (&(attribute=value)(injected_filter)) (second_filter) |
虽然过滤器语法上并不正确,OpenLDAP还是会从左到右进行处理,忽略第一个过滤器闭合后的任何字符。
但是有的浏览器会进行检查,检查过滤器是否正确,这种情况下
1 | **value)(injected_filter))(&(1=0** |
于是就出现了下述payload
1 | (&(attribute=value)(injected_filter))(&(1=0)(second_filter)) |
既然第二个过滤器会被LDAP服务器忽略,有些部分便不允许有两个过滤器的查询。这种情况下,只能构建一个特殊的注入以获得单个过滤器的LDAP查询,如
1 | **value)(injected_filter** |
得到
1 | (&(attribute=value)(injected_filter)(second_filter)) |
利用方式
AND注入
通常情况下,验证一个用户登录是
1 | (&(username=value1)(password=value2)) |
首先加上正常结束代码 得:
1 | value1 = hacker) |
加上一个永真条件 得:
1 | value1 = hacker)(cn=*) |
闭合整个ldap语句 得:
1 | value1 = hacker)(cn=*)) |
注释掉后面没用代码 得:
1 | value1 = hacker)(cn=*))%00 |
拼接之后
1 | (&(username=value1 = hacker)(cn=*))%00)(password=value2)) |
实际上执行的是
1 | (&(username=value1 = hacker)(cn=*)) |
当只要username的值存在时就可以不需要密码登录了
OR注入
OR和AND利用条件差不多,OR可以不需要正确的用户名
1 | (|(username=value1 = hacker)(cn=*)) |
也就是当存在or型注入的时候,可以直接用以上语句无用户名登录
LDAP盲注
原理和sql盲注是一样的,由于没有直接的信息回显,只能根据页面的显示状态是否正确来进行判断是否存在注入,以及注出的数据是哪些.
此处留坑,待填
在之后CTF的题目里体现一下LDAP盲注的知识.
参考博客