作者: daiker@360RedTeam
0x00 前言
这是LDAP篇的第一篇文章。在域渗透中,可能大家对Active Directory,LDAP,Naming Context这些概念既熟悉又模糊,比如Active Directory跟LDAP有啥关系等等。在LDAP篇,我们将系统地介绍跟Active Directory相关的方方面面。第一篇文章主要介绍一些基本概念,让大家对整个Active Directory有个基本印象。在之后的文章里面会陆续介绍Active Directory相关的方方面面。包括但不限于。
- 域内用户介绍
- 域内组介绍
- 域内ACL介绍
- 域信任关系介绍
- 组策略介绍
这篇文章对一些专业词汇统一保留英文
0x01 LDAP简介
LDAP全称是Lightweight Directory Access Protocol,轻量目录访问协议。顾名思义,LDAP是设计用来访问目录数据库的一个协议。
在这之前我们先介绍一下目录服务。目录数据库是由目录服务数据库和一套访问协议组成。
目录服务数据库也是一种数据库,这种数据库相对于我们熟知的关系型数据库(比如MySQL,Oracle),主要有以下几个方面的特点。
- 它成树状结构组织数据,类似文件目录一样。
- 它是为查询、浏览和搜索而优化的数据库,也就是说LDAP的读性能特别强,但是写性能差,而且还不支持事务处理、回滚等复杂功能。
为了能够访问目录数据库,必须设计一台能够访问目录服务数据库的协议,LDAP是其中一种实现协议。
为了方便大家理解下面举个例子来介绍一些LDAP相关的东西。
如上图所示是目录服务数据库,它成树状结构组织数据。下面介绍一些基本概念
- 目录树:如上图所示,在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目。
- 条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)。比如图中的每个圆圈都是一条记录。
- DN,RDN:比如说第一个叶子条目,他有一个唯一可区分的名称DN:uid=bob,ou=people,dc=acme,dc=org。类似于文件目录的相对路径绝对路径,他除了有个DN之外,还有个RDN,他与目录结构无关,比如之前咱们提过的uid=bob,ou=people,dc=acme,dc=org,他的RDN就是uid=bob
- 属性:描述条目具体信息。比如`uid=bill,ou=people,dc=acme,dc=org,他有属性name 为bill,属性age为11,属性school 为xx。
0x02 Active Directory简介
不同厂商对目录服务数据库的实现不一,常见的如下实现。
可以看出Active Directory,是微软的对目录服务数据库的实现,而LDAP是设计用来对目录服务数据库(在这里的实现就是微软的Active Directory)的访问的协议。Active Directory存储着整个域内所有的计算机,用户等的所有信息。
如果我们想访问域内的Active Directory,有两种办法
- 域内的每一台域控都有一份完整的本域的Active Directory,可以通过连接域控的389/636端口(636端口是LDAPS)来进行连接查看修改。
- 如果用户知道某个对象处于哪个域,也知道对象的标识名,那么通过上面第一种方式搜索对象就非常容易。但是考虑到这种情况,不知道对象所处的域,我们不得不去域林中的每个域搜索。为了解决这个问题,微软提出全局编录服务器(GC,Global Catalog), 全局编录服务器中除了保存本域中所有对象的所有属性外,还保存林中其它域所有对象的部分属性,这样就允许用户通过全局编录信息搜索林中所有域中对象的信息。也就是说如果需要在整个林中进行搜索,而不单单是在具体的某个域进行搜索的时候,可以连接域控的3268/3269端口。
0x03 Naming Context和Application Partitions
之前的内容都是在讲Active Directory的基本概念,接下来我们来具体的探究下Active Directory具体有啥东西。
Naming Context
首先有一点得明确,Active Directory具有分布式特性,一个林中有若干个域,每个域内有若干台域控,每台域控有一个独立的Active Directory。这个时候就有必要将数据隔离到多个分区中,如果不隔离的话,则每个域控制器都必须复制林中的所有数据。若隔离为若干个分区之后,就可以有选择性的复制某几个分区。微软将Active Directory划分为若干个分区(这个分区我们称为Naming Context,简称NC),每个Naming Context都有其自己的安全边界。
Active Directory预定义了三个Naming Context
- Configuration NC(Configuration NC)
- Schema NC(Schema NC)
- Domain NC(DomainName NC)
我们使用ADExplorer连接进来就可以看到这三个(后面两个是引用程序分区,后面会讲)
我们来简单的介绍下这三个Naming Context
- Configuration NC(Configuration NC)
配置NC,林配置信息的主要存储库,包含有关站点,服务,分区和Active DirectorySchema 的信息,并被复制到林中的每个域控制器。配置NC的根位于配置容器中,该容器是林根域的子容器。例如,test.local林将为CN=Configuration,DC=test,DC=local
下面我们来看看这个Naming Context的顶级容器有哪些。
说明 | |
---|---|
CN=DisplaySpecifiers | 定义了Active Directory管理单元的各种显示格式 |
CN=Extended-Rights | 扩展权限对象的容器,我们将在域内ACL那篇文章里面详解 |
CN=ForestUpdates | 包含用于表示森林状态和与域功能级别更改的对象 |
CN=Partitions | 包含每个Naming Context,Application Partitions以及外部LDAP目录引用的对象 |
CN=Physical Locations | 包含位置对象,可以将其与其他对象关联 以表示该对象的位置。 |
CN=Services | 存储有关服务的配置信息,比如文件复制服务 |
CN=Sites | 包含所有站点拓扑和复制对象 |
CN=WellKnown Security Principals | 包含常用的外部安全性主题的对象,比如Anonymous,Authenticated Users,Everyone等等 |
- Schema NC(Schema NC)
包含Schema 信息,该Schema 信息定义Active Directory中使用的类,对象和属性。与域NC和配置 NC 不同,模式 NC 不维护容器或组织单位的层次结构。相反,它是具有 classSchema ,attributeSchema 和 subSchema 对象的单个容器。关于这个Naming Context的详细内容我们将在下一节里面详细讲。
- Domain NC(DomainName NC)
每个域都有一个域Naming Context,不同的域内有不同的域Naming Context,其中包含特定于域的数据。这个域Naming Context(的根由域的专有名称(DN)表示,比如corp.test.local域的DN将为dc=corp,dc=test,dc=local。之前我们说过,域内的所有计算机,所有用户的具体信息都存在Active Directory底下,具体来说,就是在Active Directory的这个Naming Context里面。我们用工具查看的默认Naming Context选的也是这个Naming Context。后面对域内很多东西的查看都在这个Naming Context里面。下面我们来看看这个Naming Context的顶级容器有哪些。
说明 | |
---|---|
CN=Builtin | 内置本地安全组的容器,包括管理员,域用户和账号操作员等等 |
CN=Computers | 机器用户的容器,包括加入域的所有机器 |
OU=Domain Controllers | 域控制器的容器,包括域内所有域控 |
CN=ForeignSecurityPrincipals | 代表域中来自森林外部域的组中的成员 |
CN=Keys | Server 2016之后才有,关键凭证对象的默认容器 |
CN=Managed Service Accounts | 托管服务帐户的容器。 |
CN=System | 各种预配置对象的容器。包括信任对象,DNS对象和组策略对象 |
CN=TPM Devices | 可信平台模块(TPM)密钥的恢复信息的容器。 |
CN=Users | 用户和组对象的默认容器 |
Application Partitions
从 Windows Server 2003 开始,微软允许用户自定义分区来扩展Naming Context的概念。Application Partitions其实就是Naming Context的一个扩展,它本质上还是属于Naming Context。管理员可以创建分区(这个分区我们称为区域),以将数据存储在他们选择的特定域控制器上,Application Partitions主要有以下特点:
- Naming Context是微软预定义的,用户不可以定义自己的Naming Context。而如果用户想要定义一个分区,可以通过Application Partitions。虽然微软也预置了两个Application Partitions,但是Application Partitions的设计更多是为了让用户可以自定义自己的数据。设计Application Partitions最大的用途就是,让用户自己来定义分区。
- Application Partitions可以存储动态对象。动态对象是具有生存时间(TTL) 值的对象,该值确定它们在被Active Directory自动删除之前将存在多长时间。也就说Application Partitions可以给数据设置个TTL,时间一到,Active Directory就删除该数据。
下面演示通过ntdsutil创建Application Partitions:
创建成功
我们可以通过查看rootDSE查看域内的所有Naming Context以及Application Partitions,在属性namingContexts里面。
0x05 Schema NC
Schema NC里面包含Schema 信息,定义了Active Directory中使用的类和属性。所以在详细讲Schema NC之前我们先来讲一下LDAP里面的类和继承。
LDAP里面的类和继承,跟开发里面的面向对象一样,相信有过面向对象开发经验的,理解起来并不困难。
1.LDAP 中的类和继承
- 类和实例
域内每个条目都是类的实例。而类是一组属性的集合。
举个例子:
域内机器CN=WIN7,CN=Computers,DC=test,DC=local在Active Directory里面是一个条目,里面有众多属性描述条目具体信息。
而这个条目有哪些属性是由他的类决定的。比如说这里的条目是CN=WIN7,CN=Computers,DC=test,DC=local是类Computer的实例,在objectClass属性可以看到
- 类是可继承的。子类继承父类的所有属性,Top类是所有类的父类。在之前我们看objectClass的时候,可以看到条目是CN=WIN7,CN=Computers,DC=test,DC=local是类Computer的实例。但是我们注意到objectClass里面的值除了Computer之外,还有top,person,organizationPerson,user。这是因为objectClass保存了类继承关系。user是organizationPerson的子类,organizationPerson是person的子类,person是top的子类。
- 类的分类
类有三种类型- 结构类(Structural)
结构类规定了对象实例的基本属性,每个条目属于且仅属于一个结构型对象类。前面说过域内每个条目都是类的实例,这个类必须是结构类。只有结构类才有实例。比如说前面说过的Computer类。 - 抽象类(Abstract)
抽象类型是结构类或其他抽象类的父类,它将对象属性中公共的部分组织在一起。跟面对对象里面的抽象方法一样,他没有实例,只能充当结构类或者抽象类的父类。比如说top 类。注意抽象类只能从另一个抽象类继承。 - 辅助类(Auxiliary)
辅助类型规定了对象实体的扩展属性。虽然每个条目只属于一个结构型对象类,但可以同时属于多个辅助型对象类。注意辅助类不能从结构类继承
- 结构类(Structural)
接下来让我们结合Schema NC中的类来具体理解下LDAP 中的类和继承
2.Schema NC中的类
如果我们要查看Schema NC的内容,除了传统使用LDAP编辑器查看
比如说ADExplorer
还可以使用微软自带的Active Directory Schema
默认没有注册,运行regsvr32 schmmgmt.dll注册该dll
然后在mmc里面添加即可
域内每个条目都是类的实例。所有的类都存储在Schema NC里面,是Schema NC的一个条目。
我们以一个实例来说明。前面说过条目CN=WIN7,CN=Computers,DC=test,DC=local是类Computer的实例。那么类Computer就存储在Schema NC里面,是Schema NC的一个条目CN=Computer,CN=Schema,CN=Configuration,DC=test,DC=local。
我们下面来具体分析下这个条目的一些通用属性,希望大家对类条目有个大概的认识。
(1)前面说过每个条目都是类的实例,而类是是Schema NC的一个条目。因此类条目也是一个类的实例,这个类是classSchema(CN=Class-Schema,CN=Schema,CN=Configuration,DC=test,DC=local)。所有的类条目都是classSchema类的实例。
我们可以在objectclass属性里面看到。
(2)名称是Computer(通过adminDescription,adminDisplayName,cn,name属性)
(3)defaultSecurityDescriptor这个属性表明,如果在创建Computer这个类的实例的时候,如果没指定ACL,就用这个属性的值作为实例的ACL。在实例的nTSecurityDescriptor里面。
注意跟nTSecurityDescriptor区分开来,nTSecurityDescriptor是这个条目的ACL,而defaultSecurityDescriptor是实例默认的ACL。举个例子。
CN=Computer,CN=Schema,CN=Configuration,DC=test,DC=local 有两个属性nTSecurityDescriptor,defaultSecurityDescriptor。nTSecurityDescriptor是这条条目的ACL。
那Computer的实例化对象CN=WIN7,CN=Computers,DC=test,DC=local,如果在创建的时候,没有指定ACL,那么他的nTSecurityDescriptor的值就是CN=Computer,CN=Schema,CN=Configuration,DC=test,DC=local 的属性defaultSecurityDescriptor的值。
(4)属性rDNAttID表明通过LDAP连接到类的实例的时候,使用的两个字母的前缀用过是cn。
所以他的实例CN=WIN7,CN=Computers,DC=test,DC=local,使用的前缀是cn。
这个我们再举个例子
比如条目OU=Domain Controllers,DC=test,DC=locals 是一个ou,它是类organizationalUnit的实例
我们查看类organizationalUnit对应的条目CN=Organizational-Unit,CN=Schema,CN=Configuration,DC=test,DC=local,就可以看到
所以对于他的一个实例,他的前缀是OU,OU=Domain Controllers
(5)属性objectClassCategory为1说明他是一个结构类
- 1 代表是个结构类
- 2 代表是个抽象类
- 3代表是个辅助类
(6)属性subClassOf 表明他的父类是user类
(7)systemPossSuperior约束了他的实例只能创建在这三个类container,organizationalUnit,domainDNS的实例底下。
比如computer类的一个实例,CN=WIN7,CN=Computers,DC=test,DC=local,它位于容器CN=Computers,DC=test,DC=local底下,而CN=Computers,DC=test,DC=local是container的实例,container在systemPossSuperior底下,这不违反这个约束。
(8)最后一点也是最核心的,我们来讲下他的实例是怎么获取到基本属性的。
- 这个类没有属性systemMustContain和MustContain,因此强制属性
- 这个类属性systemMayContain和MayContain是可选的属性
上面这四个属性里面的属性集合是这个类独有的属性集合,我们之前说过,类是可继承的。因此一个类的属性集合里面除了前面的四个属性里面的值,还可能来自父类以及辅助类。
- 辅助类的属性字段是systemAuxiliaryClass,这里面的computer类没有辅助类
- 父类 可以通过subClassOf查看,这里是computer类的父类是user类。然后网上递归,user类查看那四个属性,以及他的辅助类,父类。直到top类。
所以最后我们用Active DirectorySchema 查看的时候,就会看到属性的类型是可选还是强制,源类是哪个类。
3.Schema NC中的属性
Schema NC除了定义了Active Directory中使用的类,还定义了Active Directory中使用的属性。
关于属性,我们之前接触的够多了。这里不再多做解释。
每个属性都是一个条目,是类attributeSchema的实例
在域内的所有属性必须在这里定义,而这里的条目,最主要的是限定了属性的语法定义。其实就是数据类型,比如 Boolean类型,Integer类型等。
以CN=Object-Sid,CN=Schema,CN=Configuration,DC=test,DC=local为例。
他的attributeSyntax是2.5.5.17
oMSyntax是
通过查表
关于各种语法定义在这里不再这里一个个介绍,过于抽象,将在后面文章里面实际的案例根据需要详细讲解。
0x06 搜索Active Directory
通过查询目录,可以直接收集到要求的数据。查询目录需要指定两个要素
- BaseDN
- 过滤规则
BaseDN
BaseDN指定了这棵树的根。
比如指定BaseDN为DC=test.DC=local就是以DC=test.DC=local为根往下搜索
BaseDN为CN=Users,DC=test.DC=local就是以CN=Users,DC=test.DC=local为根往下搜索
过滤规则
LDAP 过滤规则相对简单,很方便入手
LDAP 搜索过滤器语法有以下子集:
- 用与号 (&) 表示的 AND 运算符。
- 用竖线 (|) 表示的 OR 运算符。
- 用感叹号 (!) 表示的 NOT 运算符。
- 用名称和值表达式的等号 (=) 表示的相等比较。
- 用名称和值表达式中值的开头或结尾处的星号 (*) 表示的通配符。
下面举几个例子
- (uid=testuser)
匹配 uid 属性为testuser的所有对象 - (uid=test*)
匹配 uid 属性以test开头的所有对象 - (!(uid=test*))
匹配 uid 属性不以test开头的所有对象 - (&(department=1234)(city=Paris))
匹配 department 属性为1234且city属性为Paris的所有对象 - (|(department=1234)(department=56*))
匹配 department 属性的值刚好为1234或者以56开头的所有对象。
一个需要注意的点就是运算符是放在前面的,跟我们之前常规思维的放在中间不一样
关于查询目录还有一些高级点的用法,比如 LDAP 控件,位掩码等。这里不一一列举,将在后面实际用到的时候再列举。
0x07 相关工具介绍
下面介绍一些能够访问Active Directory的工具
ADSI 编辑器
微软自带,输入adsiedit.msc可访问
LDP
微软自带,输入ldp可访问
ADExplorer
sysinternals系列的工具,相较于ADSI 编辑器,更方便
The LDAP Explorer
付费版的神器,特别强大,比ADExplorer都强大,自己感受下
ldapsearch
openldap里面的工具,在*nix里面比较常用
导出的格式为LDIF格式,有人写了个工具支持导出为sqlite文件,然后阅读sqlite文件
adfind 与 admod
这个是最牛逼的命令行工具了,在域渗透里面的出场率极高,adfind用于查询,admod用于修改。这个系列的主要文章会围绕着这两个工具展开。
0x09 引用
- LDAP概念和原理介绍
- LDAP基础概念
- Active Directory Domain Services
- LDAP search filter expressions
- Active Directory: Designing, Deploying, and Running Active Directory Fifth Edition