0x00 LDAP注入
学习web_for_pentester
时,碰到了ldap
注入,想着把环境移植到自己的环境中来,于是需要自己来搭建ldap
,之前都是在windows
下完成的操作,这次用linux
完成openldap
的安装,虽然是第一次装,但是感觉还行,在安装phpldapadmin
时由于更新了下php
把我原来的环境弄炸了,修复环境,加上导出导入数据确实折腾了很久,对ldap
的了解更深了点,记录下
openldap
安装过程就不记录了,网上有不少帖子,跟着来就行了,先了解一些常见的缩写
dn 唯一标示,类似linux文件系统的绝对路径,通过dn我们可以快速查找到目录信息树中的任意一个节点
basedn也就是我们所说的目录信息树的根,rdn 则是相对标示名,即相对路径
dc 一般用于将完整域名划分几部分,如,pentestlab.org就会被表示为dc=pentesterlab,dc=com
uid 用户id
cn 姓名
sn 姓
o 组织[公司]名称,像这些只是从逻辑上分开
ou 单位[部门]名称
search语法:
attribute operator value search filter options:( "&" or "|" (filter1) (filter2) (filter3) ...) ("!" (filter))
LDAP过滤器定义于RFC4515中,这些过滤器的结构可概括如下:
Fileter = (filtercomp)
Filtercomp = and / or / not / item
And = & filterlist
Or = | filterlist
Not = ! filter
Filterlist = 1*filter
Item = simple / present / substring
Simple = “=” / “~=” / ”>=” / “<=”
Present = attr =*
Substring = attr “=” [initial]*[final]
Initial = assertion value
Final = assertion value
所有过滤器必须置于括号中,只有简化的逻辑操作符(AND、OR、NOT)
和关系操作符(=、>=、<=、~=)
可用于构造它们。特殊符“*”
可用来替换过滤器中的一个或多个字符。
除使用逻辑操作符外,RFC4256还允许使用下面的单独符号作为两个特殊常量:
(&) ->Absolute TRUE
(|) ->Absolute FALSE
Web应用中的LDAP注入
LDAP
注入攻击和SQL
注入攻击相似,因此接下来的想法是利用用户引入的参数生成LDAP
查询。
LDAP
过滤器的结构和使用得最广泛的LDAP
:ADAM
和OpenLDAP
,下面的结论将会致代码注入:
(attribute=value)
:如果过滤器用于构造查询但缺少逻辑操作符,如value)(injected_filter
的注入会导致两个过滤器(attribute=value)(injected_filter)
。在OpenLDAP
实施中,第二个过滤器会被忽略,只有第一个会被执行。而在ADAM
中,有两个过滤器的查询是不被允许的,因而这个注入毫无用处。
(|(attribute=value)(second_filter)) or (&(attribute=value)(second_filter))
:如果第一个用于构造查询的过滤器有逻辑操作符,形如value)(injected_filter)
的注入会变成如下过滤器:(&(attribute=value)(injected_filter)) (second_filter)
。虽然过滤器语法上并不正确,OpenLDAP
还是会从左到右进行处理,忽略第一个过滤器闭合后的任何字符。一些LDAP
客户端或者Web
会忽略第二个过滤器,将第一个完整的过滤器发送给ADAM
和OpenLDAP
,因而存在注入。
一些应用框架在将请求发送给服务器之前会检查过滤器是否正确,在这种情况下,过滤器语义上必须是正确的,其注入如:value)(injected_filter))(&(1=0
。这会导致出现两个不同的过滤器,第二个会被忽略:(&(attribute=value)(injected_filter))(&(1=0)(second_filter))
。
既然第二个过滤器会被LDAP
服务器忽略,有些部分便不允许有两个过滤器的查询。这种情况下,只能构建一个特殊的注入以获得单个过滤器的LDAP
查询。value)(injected_filter
这样的注入产生的结果是:(&(attribute=value)(injected_filter)(second_filter))
。
测试一个应用是否存在代码注入漏洞典型的方法是向服务器发送会生成一个无效输入的请求。因此,如果服务器返回一个错误消息,攻击者就能知道服务器执行了他的查询,他可以利用代码注入技术。回想一下之前讨论的,我们可以将注入环境分为两种:AND
注入环境和OR
注入环境以及盲注AND
和盲注OR
环境。
总结一下符号如下表
符号 | 含义 |
& | 与(列表中所有项必须为true) |
| 或(列表中至少一个必须为true) |
! | 非(求反的项不能为true) |
= | 相等(根据属性的匹配规则) |
~= | 近似等于(根据属性的匹配规则) |
>= | 大于(根据属性的匹配规则) |
<= | 小于(根据属性的匹配规则) |
=* | 存在(条目中必须有这个属性,但值不做限制) |
* | 通配符(表示这个位置可以有一个或多个字符),当指定属性值时用到 |
\ | 转义符(当遇到“*”,“(”,“)”时进行转义) |
模模糊糊的看完了,虽然有点小复杂,不过还是感觉懂了一点入门的东西,wooyun
牛逼,感谢前辈大佬们,了解后直接肝靶场。
0x01 Example1
点进去后,提示未授权,payload
是直接把所有的username
和password
都去掉就行了。
分析下源码
$ld = ldap_connect("localhost") or die("Could not connect to LDAP server");
ldap_set_option($ld, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ld, LDAP_OPT_REFERRALS, 0);
if ($ld) {
if (isset($_GET["username"])) {
$user = "uid=".$_GET["username"]."ou=people,dc=pentesterlab,dc=com";
}
$lb = @ldap_bind($ld, $user,$_GET["password"]);
if ($lb) {
echo "AUTHENTICATED";
}
else {
echo "NOT AUTHENTICATED";
}
}
ldap_connect()
用于连接ldap
ldap_set_option($ld, LDAP_OPT_PROTOCOL_VERSION, 3)
设置ldap
的版本为3
ldap_set_option($ld, LDAP_OPT_REFERRALS, 0)
设置LDAP
库是否自动追随LDAP
服务器返回的引用,0
为否ldap_bind()
使用指定的 RDN($bind_rdn)
和密码($bind_password)
绑定到LDAP
目录,如果未指定则使用匿名认证。
这里就是利用的不提交验证方式进行匿名登陆访问成功的。
0x02 Example2
直接用admin
登陆成功,admin)(%26))%00&password=x
解释下原因,看下源码。
下面这个划蓝线的是我们利用的过滤器,当传入参数后就变成了
(&(cn=admin)(&))%00(userPassword=x)
,注意%26
是&
的URL
编码,这里要使用%00
将第二个过滤器即(userPassword=x)
截断掉,不然会出现语法错误。
当然了,如果在不知道用户名为admin
的情况下,可以使用上文所说的*
来进行模糊匹配。
比如a*)(%26))%00&password=x
当为b*
的时候的时候,由于没有账户就出错了。
同理还可以结合其余的过滤器查寻到更多的信息,有机会再进行深入的学习(太菜了!!!)。
转一点其它人的例子
下列过滤器将搜索包含一个或多个 manager 属性值的条目。这也称为存在搜索: manager=*
下列过滤器将搜索包含通用名 Ray Kultgen 的条目。这也称为等价搜索:cn=Ray Kultgen
下列过滤器返回所有不包含通用名 Ray Kultgen 的条目:(!(cn=Ray Kultgen))
下列过滤器返回的所有条目中都有包含子字符串 X.500 的说明属性:description=*X.500*
下列过滤器返回所有组织单元为 Marketing 且说明字段中不包含子字符串 X.500 的条目: (&(ou=Marketing)(!(description=*X.500*)))
下列过滤器返回所有组织单元为 Marketing 且 manager 为 Julie Fulmer 或 Cindy Zwaska 的条目: (&(ou=Marketing)(|(manager=cn=Julie Fulmer,ou=Marketing,dc=siroe,dc=com)(manager=cn=Cindy Zwaska,ou=Marketing,dc=siroe,dc=com)))
下列过滤器返回所有不代表人员的条目: (!(objectClass=person))
下列过滤器返回所有不代表人员且通用名近似于 printer3b 的条目:(&(!(objectClass=person))(cn~=printer3b))
最后,其实可以直接ldap
客户端直接连,连上去,啥都有了。