0x00 前言
想学习LDAP注入,在网上找了一圈资料后,发现还是乌云上的资料比较全面,故做下学习记录。
0x01 LDAP出现背景
LDAP(Lightweight Directory Access Protocol):轻量级目录访问协议,是一种在线目录访问协议,主要用于目录中资源的搜索和查询,是x.500的一种简便实现。是运行在TCP/IP之上的协议,端口号:389,加密:636(SSL)
x.500申明了目录客户端和目录服务器使用的是目录访问协议(DAP),然而作为应用层协议,DAP要求完整的7层OSI协议栈操作,会要求比小环境(配置、资源有限的环境)所能提供更多的资源,因此需要一种轻量级的协议来代替x.500,LDAP正是因此而生。
随着互联网的广泛使用,web应用的数量呈爆炸式增长,而这些应用的资源和数据呈分布式存储于目录中。通常不同的应用会有专属于自己相关数据的目录,即专有目录。专有目录数量的增长导致了信息孤岛(一种不能与其他相关信息系统之间进行互操作或者协调工作的信息系统)的出现,系统和资源的共享及管理变得日益困难。
以查找联系人和加密证书为例,太多的目录明显会给计算机搜索带来巨大的压力,当然随之出现了相应的解决方案,如x.500,不过在介绍x.500之前先讨论一下目录和关系型数据库之间的一些关系,因为前面提到了Web应用的数据是存储在目录中,而不是数据库中。的确,目录和数据库有很多共同之处,都能存储数据,并在一定程度上进行搜索和查询。
不同之处在于目录适合于存放静态数据,而且不同于数据库,目录中存储的数据无论在类型和种类较之数据库中的数据都要更为繁多,包括音频、视频、可执行文件、文本等文件,另外目录中还存在目录的递归。相比之下,数据库中存储的数据在格式和类型都有较严格的约束,数据库有索引、视图、能处理事务(通常包含了一个序列的对数据库的读、写操作)。简单来说,数据库更多见于处理专有类型的数据,而目录则具有通用用途。目录中的内容发生变化后会给搜索操作带来不便,因而目录服务在进行优化后更适宜于读访问,而非写、修改等操作。
0x02 LDAP的特性
0x02.1 LDAP协议
简单了解一下LDAP:LDAP不定义客户端和服务端的工作方式,但会定义客户端和服务端的通信方式。另外,LDAP还会定义LDAP数据库的访问权限及服务端数据的格式和属性。LDAP有三种基本通信机制:没有处理的匿名访问;基本的用户名、密码形式的认证;使用SASL、SSL的安全认证方式。LDAP和很多其他协议一样,基于TCP/IP协议通信,注重服务的可用性、信息的保密性等。部署了LDAP的应用不会直接访问目录中的内容,一般通过函数调用或者API,应用可以通过定义的C、Java的API进行访问,Java应用的访问方式为JNDI。
0x02.2 LDAP存储
数据库主要是三个DB,TABLE,ROW数据都是按记录一条条记录存在表中。而LDAP数据库,是树结构的,数据存储在叶子节点上。
LDAP目录中的信息是按照树形结构组织的:
dn:一条记录的位置
dc:一条记录所属的区域
ou:一条记录所属的组织
cn/uid:一条记录的名字/ID
这种树结构非常有利于数据的查询。首先要说明是哪一棵树(dc),然后是从树根到目标所经过的所有分叉(ou),最后就是目标的名字(cn/uid),借用一张图来表明结构如下:
具体到如何定义,如下:
dn:cn=stan,ou=linux,ou=computer,dc=ourschool,dc=org
objectClass:organizationalPerson
cn:stan
cn:小刀
sn:小刀
description:a good boy
//保存为LDIF文件,可以导入到LDAP数据库中
LDAP目录以入口(entry,目录中存储的基本信息单元)的形式存储和组织数据结构,每个入口有一个唯一标识自己的专属名称(distnguished name),DN由一系列RDNs(relative distinguished names)组成。另外还有两个常见的结构,对象类和属性。对象类会定义独一的OID,每个属性(attribute)也会分配唯一的OID号码。
0x02.3 LDAP入口 & 对象类 & 属性
- 入口(entry):是目录中存储的基本信息单元,上图中每一个方框代表一个entry。一个entry有若干个属性和若干个值,有些entry还能包含子entry。
- 对象类(objectClass):对象类封装了可选 / 必选的属性,同时对象类也是支持继承的。一个entry必须包含一个objectClass,且需要赋予至少一个值。而且objectClass有着严格的等级之分,最顶层是top和alias。例如,organizationalPerson这个objectClass就隶属于person,而person又隶属于top。
- 属性(Attribute):用来存储字段值。被封装在objectClass里的,每个属性也会分配唯一的OID号码。
入口点和属性之间的关系为:
0x02.4 LDAP基本语法
一个圆括号内的判断语句又称为一个过滤器filter。
("&"or"|"(filter1)(filter2))
- =(等于)
查找“ 名 ”属性为“ John ”的所有对象,可以使用:
(givenName=John)
//这会返回“名”属性为“John”的所有对象。圆括号是必须的,以便强调LDAP语句的开始和结束。
- &(逻辑与)
如果具有多个条件并且希望全部条件都得到满足,则可使用此语法。例如,如果希望查找居住在Dallas并且" 名 "为 " John "的所有人员,可以使用:
(&(givenName=John)(l=Dallas))
//请注意,每个参数都被属于其自己的圆括号括起来。
整个LDAP语句必须包括在一对主圆括号中。
操作符&表明,只有每个参数都为真,才会将此筛选条件应用到要查询的对象。
- |(逻辑或)
满足一个条件即可。显示居住在Dallas或者“ 名 ”为“ John ”的所有人员。可以使用:
(|(givenName=John)(l=Dallas))
- !(逻辑非)
此操作符用来排除具有特定属性的对象。假定您需要查找“ 名 ”为“ John ”的对象以外的所有对象。则应使用如下语句:
(!givenName=John)
//此语句将查找“名”不为“John”的所有对象。
请注意:!操作符紧邻参数的前面,并且位于参数的圆括号内。
由于本语句只有一个参数,因此使用圆括号将其括起以示说明。
- *(通配符)
可使用通配符表示值可以等于任何值。使用它的情况可能是:您希望查找具有职务头衔的所有对象。为此,可以使用:
(title=*)
//这会返回“title”属性包含内容的所有对象。
另一个例子是:
您知道某个对象的“名”属性的开头两个字母是“Jo”。那么可以使用如下语法进行查找:
(givenName=Jo*)
//这会返回“名”为“Jo”开头的所有对象。
特殊说明:
- 除了使用逻辑操作符外,还允许使用下面单独符号作为两个特殊常量:(&)表示True;(|) 表示False。
- 默认情况下,LDAP的DN和所有属性都不区分大小写。
0x02.5 LDAP查询语法
LDAP主要用于搜索查询,那它是怎么查询的呢?
search语法:
attribute operator value search filter options:(